@mikrojs/firmware 0.21.1-next.20260927212749 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,11 @@
1
1
  #include "mikrojs/platform.h"
2
2
  #include "mikrojs_esp32.h"
3
3
 
4
+ #include <esp_attr.h>
4
5
  #include <esp_log.h>
5
6
  #include <esp_heap_caps.h>
6
7
  #include <esp_mac.h>
8
+ #include <esp_netif.h>
7
9
  #include <nanocbor/nanocbor.h>
8
10
  #include <nvs.h>
9
11
  #include <esp_random.h>
@@ -64,13 +66,72 @@ static void esp32_feed_watchdog(void) {
64
66
  }
65
67
  #endif
66
68
 
69
+ /* When the loop task last blocked. A wake already pending ends a wait at once,
70
+ * so a producer that wakes faster than a pass (a fast GPIO signal) could keep
71
+ * the task from blocking, and IDLE, which the task watchdog checks, would
72
+ * starve. Like mik__maybe_yield, sleep one tick per second of that. */
73
+ static int64_t s_last_block_us = 0;
74
+
67
75
  static void esp32_yield(void) {
68
- /* Every cooperative yield feeds the TWDT: between jobs, in the serve
69
- * loop's wait, and while the app is paused. */
76
+ /* Every cooperative yield feeds the TWDT: between jobs and when the
77
+ * serve loop has work due now. */
70
78
  #if CONFIG_ESP_TASK_WDT_EN
71
79
  esp_task_wdt_reset();
72
80
  #endif
73
81
  vTaskDelay(1);
82
+ s_last_block_us = esp_timer_get_time();
83
+ }
84
+
85
+ /* The task that blocks in esp32_wait (the main task), bound on its first
86
+ * wait. Producers on other tasks and in ISRs notify it. */
87
+ static TaskHandle_t s_loop_task = nullptr;
88
+
89
+ /* Wakes use notification index 1. Index 0 is the one ulTaskNotifyTake, driver
90
+ * code and pthread_join use, and native code on the JS task may block on it. */
91
+ #define MIK__WAKE_INDEX 1
92
+ static_assert(configTASK_NOTIFICATION_ARRAY_ENTRIES > MIK__WAKE_INDEX,
93
+ "the loop's wake needs a second task notification: set "
94
+ "CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 in sdkconfig, or delete sdkconfig "
95
+ "and re-run `mikro idf set-target` so sdkconfig.defaults applies");
96
+
97
+ static void esp32_wait(int64_t timeout_us) {
98
+ if (!s_loop_task) {
99
+ /* Bind and return: a wake that raced this first call is not lost. */
100
+ s_loop_task = xTaskGetCurrentTaskHandle();
101
+ return;
102
+ }
103
+ #if CONFIG_ESP_TASK_WDT_EN
104
+ esp_task_wdt_reset();
105
+ #endif
106
+ /* Whole ticks, rounded up; FreeRTOS counts from the current partial tick,
107
+ * so the wait can still end up to one tick early and the next pass waits
108
+ * the remainder. */
109
+ TickType_t ticks = (TickType_t)((timeout_us + portTICK_PERIOD_MS * 1000 - 1) /
110
+ (portTICK_PERIOD_MS * 1000));
111
+ if (ticks == 0) ticks = 1;
112
+ if (ulTaskNotifyTakeIndexed(MIK__WAKE_INDEX, pdTRUE, 0) == 0) {
113
+ ulTaskNotifyTakeIndexed(MIK__WAKE_INDEX, pdTRUE, ticks);
114
+ s_last_block_us = esp_timer_get_time();
115
+ } else if (esp_timer_get_time() - s_last_block_us >= 1000 * 1000) {
116
+ vTaskDelay(1);
117
+ s_last_block_us = esp_timer_get_time();
118
+ }
119
+ #if CONFIG_ESP_TASK_WDT_EN
120
+ esp_task_wdt_reset();
121
+ #endif
122
+ }
123
+
124
+ static void esp32_wake(void) {
125
+ if (s_loop_task) xTaskNotifyGiveIndexed(s_loop_task, MIK__WAKE_INDEX);
126
+ }
127
+
128
+ /* IRAM: called from handlers that run while the flash cache is off (the
129
+ * GPIO and UART ISRs are installed with ESP_INTR_FLAG_IRAM). */
130
+ void IRAM_ATTR MIK_WakeFromISR(void) {
131
+ if (!s_loop_task) return;
132
+ BaseType_t woken = pdFALSE;
133
+ vTaskNotifyGiveIndexedFromISR(s_loop_task, MIK__WAKE_INDEX, &woken);
134
+ portYIELD_FROM_ISR(woken);
74
135
  }
75
136
 
76
137
  static size_t esp32_get_free_system_mem(void) {
@@ -330,6 +391,16 @@ static void esp32_log(int level, const char* tag, const char* fmt, ...) {
330
391
  mik__console_write(buf, total + 1);
331
392
  }
332
393
 
394
+ /* Start lwip's tcpip thread. esp_netif_init is idempotent (INVALID_STATE
395
+ * on a second call); the wifi and http modules run it too and share the
396
+ * result. */
397
+ static void esp32_net_init(void) {
398
+ esp_err_t err = esp_netif_init();
399
+ if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
400
+ ESP_LOGE("mikrojs", "esp_netif_init failed: %s", esp_err_to_name(err));
401
+ }
402
+ }
403
+
333
404
  static const MIKPlatform esp32_platform = {
334
405
  .get_boot_us = esp32_get_boot_us,
335
406
  .get_rtc_us = esp32_get_rtc_us,
@@ -337,6 +408,8 @@ static const MIKPlatform esp32_platform = {
337
408
  .restart = esp32_restart,
338
409
  .deep_sleep_us = esp32_deep_sleep_us,
339
410
  .yield = esp32_yield,
411
+ .wait = esp32_wait,
412
+ .wake = esp32_wake,
340
413
  #if CONFIG_ESP_TASK_WDT_EN
341
414
  .feed_watchdog = esp32_feed_watchdog,
342
415
  #else
@@ -361,6 +434,7 @@ static const MIKPlatform esp32_platform = {
361
434
  .get_device_name = esp32_get_device_name,
362
435
  .set_device_name = esp32_set_device_name,
363
436
  .get_reset_reason = esp32_get_reset_reason,
437
+ .net_init = esp32_net_init,
364
438
  };
365
439
 
366
440
  /*
package/dist/boards.d.ts CHANGED
@@ -43,6 +43,18 @@ export interface FirmwareExport {
43
43
  target: string;
44
44
  file: string;
45
45
  }
46
+ /** A board that runs one of the generic images, as its firmware.json says.
47
+ * `mikro flash` flashes that image with the board's name written into it. */
48
+ export interface GenericBoard {
49
+ name: string;
50
+ description?: string;
51
+ /** The generic board whose image it runs: `esp32-generic`. */
52
+ firmware: string;
53
+ specifier: string;
54
+ key: string;
55
+ packageName: string;
56
+ packageDir: string;
57
+ }
46
58
  /** The full image's folder in the board's, and its name for `--features`. */
47
59
  export declare const FULL_IMAGE = "full";
48
60
  /** A board's name in file and artifact names: `@acme/pi` becomes `acme-pi`. */
@@ -52,31 +64,40 @@ export declare function boardFileName(name: string): string;
52
64
  * or `mikro-fw-<chip>` for firmware without a name. The chip is left out when
53
65
  * the name already ends with it (`seeed-xiao-esp32c6`) or is `<chip>-generic`,
54
66
  * so the chip is always the last word, or the one before `generic`.
55
- * `mikro fw pack` names archives this way, and `mikro flash --from` looks for
56
- * them; the CI artifact of a build has the same name.
67
+ * `mikro fw pack` names archives this way, and the CI artifact of a build has
68
+ * the same name.
57
69
  */
58
70
  export declare function archiveName(name: string | undefined, chip: string): string;
59
- /** Whether an archive name (without `.tar.gz`) is a full image for `chip`, by
60
- * the place archiveName puts the chip. A board's other images (`+no-ble`)
61
- * are only ever asked for by name. */
62
- export declare function isArchiveForChip(archive: string, chip: string): boolean;
63
71
  /** The package's `firmware` exports, and the ones that can't name a board. */
64
72
  export declare function firmwareExports(packageDir: string): {
65
73
  entries: FirmwareExport[];
66
74
  problems: BoardProblem[];
67
75
  };
68
- /** What a firmware.json says, or why it can't describe a board. */
76
+ type ImageJson = Pick<BoardImage, 'name' | 'description' | 'chip' | 'version' | 'features'>;
77
+ type GenericJson = Pick<GenericBoard, 'name' | 'description' | 'firmware'>;
78
+ /** What an image's firmware.json says, or why it can't describe an image. */
69
79
  export declare function readFirmwareJson(file: string): {
70
80
  ok: true;
71
- value: Pick<BoardImage, 'name' | 'description' | 'chip' | 'version' | 'features'>;
81
+ value: ImageJson;
72
82
  } | {
73
83
  ok: false;
74
84
  message: string;
75
85
  };
76
- /** The boards a package declares, and the `firmware` exports that aren't one
77
- * (not built, or not a Mikro.js firmware.json). */
86
+ /** What the firmware.json of a board that runs a generic image says, or why
87
+ * it isn't one. */
88
+ export declare function readGenericBoardJson(file: string): {
89
+ ok: true;
90
+ value: GenericJson;
91
+ } | {
92
+ ok: false;
93
+ message: string;
94
+ };
95
+ /** The boards a package declares: those with an image of their own in
96
+ * `boards`, those that run a generic image in `generic`, and the `firmware`
97
+ * exports that are neither (not built, or not a Mikro.js firmware.json). */
78
98
  export declare function loadBoards(packageDir: string): {
79
99
  boards: BoardImage[];
100
+ generic: GenericBoard[];
80
101
  problems: BoardProblem[];
81
102
  };
82
103
  /**
@@ -130,6 +151,31 @@ export interface BoardConfig {
130
151
  /** Images to build besides the full one, each with features switched off
131
152
  * or on: `[{ble: false}, {ble: false, wifi: false}]`. */
132
153
  images?: readonly ImageFeatures[];
154
+ /** Only for a board on the generic firmware (GenericBoardConfig). */
155
+ firmware?: never;
156
+ }
157
+ /** The generic boards, one per chip: the firmware a board on the generic
158
+ * firmware runs. */
159
+ declare const GENERIC_FIRMWARE: ("esp32-generic" | "esp32c3-generic" | "esp32c5-generic" | "esp32c6-generic" | "esp32s3-generic")[];
160
+ export type GenericFirmware = (typeof GENERIC_FIRMWARE)[number];
161
+ /** A board in boards.config.ts that runs one of the generic images, such as a
162
+ * board whose drivers are all JavaScript. `mikro flash` writes its name into
163
+ * the image. */
164
+ export interface GenericBoardConfig {
165
+ /** The generic board whose image it runs: `esp32-generic`. */
166
+ firmware: GenericFirmware;
167
+ /** The name the device reports as sys.board.name. Default: the specifier of
168
+ * the board's export. */
169
+ name?: string;
170
+ /** Shown when `mikro flash` asks which board to flash. Default: the
171
+ * package's description. */
172
+ description?: string;
173
+ chip?: never;
174
+ sdkconfig?: never;
175
+ partitions?: never;
176
+ nativeModules?: never;
177
+ project?: never;
178
+ images?: never;
133
179
  }
134
180
  /** boards.config.ts: the boards a package builds, keyed by the export that
135
181
  * declares each, `.` or `./<board>`. */
@@ -138,7 +184,7 @@ export interface BoardsConfig {
138
184
  * board at `.`, `<dist>/<board>/full/` for the others, and each other image
139
185
  * in a folder beside `full/`. Default: `dist-fw`. */
140
186
  dist?: string;
141
- boards: Record<string, BoardConfig>;
187
+ boards: Record<string, BoardConfig | GenericBoardConfig>;
142
188
  }
143
189
  /** A board from boards.config.ts, with defaults filled in and paths resolved. */
144
190
  export interface ConfiguredBoard {
@@ -161,6 +207,19 @@ export interface ConfiguredBoard {
161
207
  /** The images besides the full one. */
162
208
  images: ConfiguredImage[];
163
209
  }
210
+ /** A board from boards.config.ts that runs a generic image. `mikro fw build`
211
+ * writes its firmware.json to `target`, which names the generic board. */
212
+ export interface ConfiguredGenericBoard {
213
+ key: string;
214
+ specifier: string;
215
+ name: string;
216
+ description?: string;
217
+ firmware: GenericFirmware;
218
+ /** The generic board's chip. */
219
+ chip: Chip;
220
+ target: string;
221
+ boardDir: string;
222
+ }
164
223
  /** An image from `images` in boards.config.ts. */
165
224
  export interface ConfiguredImage {
166
225
  /** `no-ble`: the image's folder in the board's, and its name everywhere else. */
@@ -175,10 +234,12 @@ export interface ConfiguredImage {
175
234
  * and everything wrong with it: fields that don't check out, files it names
176
235
  * that don't exist, names a device can't take, and `exports` that don't match
177
236
  * (a board without its `firmware` export, one pointing elsewhere, or a
178
- * `firmware` export the config doesn't have).
237
+ * `firmware` export the config doesn't have). Boards with an image of their own
238
+ * are in `boards`, boards that run a generic image in `generic`.
179
239
  */
180
240
  export declare function checkBoardsConfig(packageDir: string, config: unknown): {
181
241
  boards: ConfiguredBoard[];
242
+ generic: ConfiguredGenericBoard[];
182
243
  problems: BoardProblem[];
183
244
  };
184
245
  //# sourceMappingURL=boards.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"boards.d.ts","sourceRoot":"","sources":["../src/boards.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"boards.d.ts","sourceRoot":"","sources":["../src/boards.ts"],"names":[],"mappings":"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"}
package/dist/boards.js CHANGED
@@ -21,7 +21,7 @@
21
21
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
22
22
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
23
23
  import { array, enumOf, object, optional, string, validate } from '@mikrojs/schema';
24
- import { chips } from "./index.js";
24
+ import { CHIPS, chips } from "./index.js";
25
25
  /** The form of a board name: a package name, optionally with /<board>. The
26
26
  * device keeps it in a 64-byte buffer, and a registry checks the same form
27
27
  * (docs/registry-spec.md, "Board names"). */
@@ -34,6 +34,13 @@ const FirmwareJson = object({
34
34
  version: string(),
35
35
  features: optional(array(string())),
36
36
  });
37
+ /** The firmware.json of a board that runs a generic image: no image of its
38
+ * own, the generic board's name in `firmware`. */
39
+ const GenericFirmwareJson = object({
40
+ name: string(),
41
+ description: optional(string()),
42
+ firmware: string(),
43
+ });
37
44
  /** An image name: what it leaves out (`no-ble`) or adds, joined with `+`. */
38
45
  const IMAGE_NAME_RE = /^(no-)?[a-z0-9]+(\+(no-)?[a-z0-9]+)*$/;
39
46
  /** The full image's folder in the board's, and its name for `--features`. */
@@ -47,8 +54,8 @@ export function boardFileName(name) {
47
54
  * or `mikro-fw-<chip>` for firmware without a name. The chip is left out when
48
55
  * the name already ends with it (`seeed-xiao-esp32c6`) or is `<chip>-generic`,
49
56
  * so the chip is always the last word, or the one before `generic`.
50
- * `mikro fw pack` names archives this way, and `mikro flash --from` looks for
51
- * them; the CI artifact of a build has the same name.
57
+ * `mikro fw pack` names archives this way, and the CI artifact of a build has
58
+ * the same name.
52
59
  */
53
60
  export function archiveName(name, chip) {
54
61
  if (name === undefined)
@@ -58,14 +65,6 @@ export function archiveName(name, chip) {
58
65
  ? `mikro-fw-${fileName}`
59
66
  : `mikro-fw-${fileName}-${chip}`;
60
67
  }
61
- /** Whether an archive name (without `.tar.gz`) is a full image for `chip`, by
62
- * the place archiveName puts the chip. A board's other images (`+no-ble`)
63
- * are only ever asked for by name. */
64
- export function isArchiveForChip(archive, chip) {
65
- return (archive.startsWith('mikro-fw-') &&
66
- !archive.includes('+') &&
67
- (archive.endsWith(`-${chip}`) || archive === `mikro-fw-${chip}-generic`));
68
- }
69
68
  function readPackageJson(packageDir) {
70
69
  const file = join(packageDir, 'package.json');
71
70
  try {
@@ -120,18 +119,24 @@ export function firmwareExports(packageDir) {
120
119
  }
121
120
  return { entries, problems };
122
121
  }
123
- /** What a firmware.json says, or why it can't describe a board. */
124
- export function readFirmwareJson(file) {
122
+ /** A firmware.json's content, or why it can't be read. */
123
+ function readJson(file) {
125
124
  if (!existsSync(file))
126
125
  return { ok: false, message: `not built: ${file} does not exist` };
127
- let json;
128
126
  try {
129
- json = JSON.parse(readFileSync(file, 'utf8'));
127
+ return { ok: true, value: JSON.parse(readFileSync(file, 'utf8')) };
130
128
  }
131
129
  catch (e) {
132
130
  return { ok: false, message: `${file} is not valid JSON: ${e.message}` };
133
131
  }
134
- const invalid = validate(FirmwareJson, json, '');
132
+ }
133
+ /** Whether a firmware.json is a generic board's: no image, a `firmware` instead. */
134
+ function isGenericJson(json) {
135
+ return isObject(json) && Object.hasOwn(json, 'firmware');
136
+ }
137
+ /** `json` checked against `schema` and the board-name form, or why it isn't. */
138
+ function checkedJson(schema, json, file) {
139
+ const invalid = validate(schema, json, '');
135
140
  if (invalid) {
136
141
  const { message, path } = invalid.error;
137
142
  return { ok: false, message: `${file}${path ? ` (${path.slice(1)})` : ''}: ${message}` };
@@ -143,16 +148,35 @@ export function readFirmwareJson(file) {
143
148
  message: `${file}: "${value.name}" is not a board name (at most ${MAX_BOARD_NAME_LENGTH} characters, the form of a package name with an optional /<board>)`,
144
149
  };
145
150
  }
146
- return {
147
- ok: true,
148
- value: {
149
- name: value.name,
150
- description: value.description,
151
- chip: value.chip,
152
- version: value.version,
153
- features: value.features,
154
- },
155
- };
151
+ return { ok: true, value };
152
+ }
153
+ function imageJson(json, file) {
154
+ if (isGenericJson(json)) {
155
+ return { ok: false, message: `${file} is a board that runs a generic image, not an image` };
156
+ }
157
+ const read = checkedJson(FirmwareJson, json, file);
158
+ if (!read.ok)
159
+ return read;
160
+ const { name, description, chip, version, features } = read.value;
161
+ return { ok: true, value: { name, description, chip, version, features } };
162
+ }
163
+ function genericJson(json, file) {
164
+ const read = checkedJson(GenericFirmwareJson, json, file);
165
+ if (!read.ok)
166
+ return read;
167
+ const { name, description, firmware } = read.value;
168
+ return { ok: true, value: { name, description, firmware } };
169
+ }
170
+ /** What an image's firmware.json says, or why it can't describe an image. */
171
+ export function readFirmwareJson(file) {
172
+ const json = readJson(file);
173
+ return json.ok ? imageJson(json.value, file) : json;
174
+ }
175
+ /** What the firmware.json of a board that runs a generic image says, or why
176
+ * it isn't one. */
177
+ export function readGenericBoardJson(file) {
178
+ const json = readJson(file);
179
+ return json.ok ? genericJson(json.value, file) : json;
156
180
  }
157
181
  /** A board's other images: the folders beside its full image's `full/`
158
182
  * folder, named like an image, that hold an image of the same board. */
@@ -176,25 +200,40 @@ function siblingImages(dir, name, chip) {
176
200
  }
177
201
  return images.sort((a, b) => a.name.localeCompare(b.name));
178
202
  }
179
- /** The boards a package declares, and the `firmware` exports that aren't one
180
- * (not built, or not a Mikro.js firmware.json). */
203
+ /** The boards a package declares: those with an image of their own in
204
+ * `boards`, those that run a generic image in `generic`, and the `firmware`
205
+ * exports that are neither (not built, or not a Mikro.js firmware.json). */
181
206
  export function loadBoards(packageDir) {
182
207
  const { entries, problems } = firmwareExports(packageDir);
183
- const read = readPackageJson(packageDir);
184
- const packageName = (read.ok ? read.value.name : undefined) ?? packageDir;
208
+ const pkg = readPackageJson(packageDir);
209
+ const packageName = (pkg.ok ? pkg.value.name : undefined) ?? packageDir;
185
210
  const boards = [];
211
+ const generic = [];
186
212
  for (const { key, specifier, file } of entries) {
187
- const read = readFirmwareJson(file);
188
- if (read.ok) {
189
- const dir = dirname(file);
190
- const images = siblingImages(dir, read.value.name, read.value.chip);
191
- boards.push({ ...read.value, images, specifier, key, packageName, packageDir, dir });
213
+ const json = readJson(file);
214
+ if (!json.ok) {
215
+ problems.push({ specifier, message: json.message });
216
+ }
217
+ else if (isGenericJson(json.value)) {
218
+ const read = genericJson(json.value, file);
219
+ if (read.ok)
220
+ generic.push({ ...read.value, specifier, key, packageName, packageDir });
221
+ else
222
+ problems.push({ specifier, message: read.message });
192
223
  }
193
224
  else {
194
- problems.push({ specifier, message: read.message });
225
+ const read = imageJson(json.value, file);
226
+ if (read.ok) {
227
+ const dir = dirname(file);
228
+ const images = siblingImages(dir, read.value.name, read.value.chip);
229
+ boards.push({ ...read.value, images, specifier, key, packageName, packageDir, dir });
230
+ }
231
+ else {
232
+ problems.push({ specifier, message: read.message });
233
+ }
195
234
  }
196
235
  }
197
- return { boards, problems };
236
+ return { boards, generic, problems };
198
237
  }
199
238
  /** Whether npm publishes `path` (relative to the package) under this `files`
200
239
  * list. Glob entries are taken as covering it: they need npm's matcher. */
@@ -224,7 +263,7 @@ export function checkBoardPackage(packageDir) {
224
263
  return [{ specifier: packageDir, message: read.message }];
225
264
  const pkg = read.value;
226
265
  const { entries } = firmwareExports(packageDir);
227
- const { boards, problems } = loadBoards(packageDir);
266
+ const { boards, generic, problems } = loadBoards(packageDir);
228
267
  const packageName = pkg.name ?? packageDir;
229
268
  for (const { specifier, target, file } of entries) {
230
269
  const inPackage = relative(packageDir, dirname(file));
@@ -265,7 +304,7 @@ export function checkBoardPackage(packageDir) {
265
304
  }
266
305
  }
267
306
  const byName = new Map();
268
- for (const board of boards) {
307
+ for (const board of [...boards, ...generic]) {
269
308
  const other = byName.get(board.name);
270
309
  if (other) {
271
310
  problems.push({
@@ -309,6 +348,12 @@ export function sortImageName(name) {
309
348
  .sort((a, b) => feature(a).localeCompare(feature(b)))
310
349
  .join('+');
311
350
  }
351
+ /** The generic boards, one per chip: the firmware a board on the generic
352
+ * firmware runs. */
353
+ const GENERIC_FIRMWARE = CHIPS.map((chip) => `${chip}-generic`);
354
+ function isGenericFirmware(name) {
355
+ return GENERIC_FIRMWARE.includes(name);
356
+ }
312
357
  const DEFAULT_DIST = 'dist-fw';
313
358
  const BOARD_KEYS = [
314
359
  'chip',
@@ -320,6 +365,7 @@ const BOARD_KEYS = [
320
365
  'project',
321
366
  'images',
322
367
  ];
368
+ const GENERIC_BOARD_KEYS = ['firmware', 'name', 'description'];
323
369
  /** `./<board>`: one segment, in the form of a board name's last part. */
324
370
  const SUBPATH_RE = /^\.\/[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;
325
371
  function isStringArray(value) {
@@ -334,18 +380,21 @@ function exportsSnippet(boards) {
334
380
  * and everything wrong with it: fields that don't check out, files it names
335
381
  * that don't exist, names a device can't take, and `exports` that don't match
336
382
  * (a board without its `firmware` export, one pointing elsewhere, or a
337
- * `firmware` export the config doesn't have).
383
+ * `firmware` export the config doesn't have). Boards with an image of their own
384
+ * are in `boards`, boards that run a generic image in `generic`.
338
385
  */
339
386
  export function checkBoardsConfig(packageDir, config) {
340
387
  const read = readPackageJson(packageDir);
341
- if (!read.ok)
342
- return { boards: [], problems: [{ specifier: packageDir, message: read.message }] };
388
+ if (!read.ok) {
389
+ return { boards: [], generic: [], problems: [{ specifier: packageDir, message: read.message }] };
390
+ }
343
391
  const pkg = read.value;
344
392
  const packageName = pkg.name ?? packageDir;
345
393
  const problems = [];
346
394
  // Stops at a problem that leaves nothing to check, keeping the ones found so far
347
395
  const fail = (message) => ({
348
396
  boards: [],
397
+ generic: [],
349
398
  problems: [...problems, { specifier: packageName, message }],
350
399
  });
351
400
  if (pkg.name === undefined)
@@ -375,6 +424,7 @@ export function checkBoardsConfig(packageDir, config) {
375
424
  return fail('boards.config.ts: a package has one board at "." or boards at "./<board>", not both');
376
425
  }
377
426
  const boards = [];
427
+ const generic = [];
378
428
  for (const [key, board] of Object.entries(config.boards)) {
379
429
  const sub = key.slice(2);
380
430
  const specifier = key === '.' ? packageName : `${packageName}/${sub}`;
@@ -388,20 +438,55 @@ export function checkBoardsConfig(packageDir, config) {
388
438
  problem('expected an object');
389
439
  continue;
390
440
  }
441
+ const { name, description } = board;
442
+ if (name !== undefined && typeof name !== 'string')
443
+ problem('"name" must be a string');
444
+ if (description !== undefined && typeof description !== 'string') {
445
+ problem('"description" must be a string');
446
+ }
447
+ const boardName = typeof name === 'string' ? name : specifier;
448
+ if (!BOARD_NAME_RE.test(boardName) || boardName.length > MAX_BOARD_NAME_LENGTH) {
449
+ problem(`"${boardName}" is not a board name (at most ${MAX_BOARD_NAME_LENGTH} characters, the ` +
450
+ 'form of a package name with an optional /<board>)' +
451
+ (typeof name === 'string' ? '' : '; set "name"'));
452
+ }
453
+ const target = `./${distPath}/${key === '.' ? '' : `${sub}/`}${FULL_IMAGE}/firmware.json`;
454
+ const boardDir = key === '.' ? distDir : join(distDir, sub);
455
+ const boardDescription = typeof description === 'string' ? description : pkg.description;
456
+ if (Object.hasOwn(board, 'firmware')) {
457
+ const { firmware } = board;
458
+ for (const field of Object.keys(board)) {
459
+ if (GENERIC_BOARD_KEYS.includes(field))
460
+ continue;
461
+ problem(BOARD_KEYS.includes(field)
462
+ ? `a board that runs a generic image gets "${field}" from it; leave out "${field}"`
463
+ : `unknown field "${field}"`);
464
+ }
465
+ if (!isGenericFirmware(firmware)) {
466
+ problem(`"firmware" must be one of ${GENERIC_FIRMWARE.join(', ')}`);
467
+ continue;
468
+ }
469
+ generic.push({
470
+ key,
471
+ specifier,
472
+ name: boardName,
473
+ description: boardDescription,
474
+ firmware,
475
+ chip: CHIPS[GENERIC_FIRMWARE.indexOf(firmware)],
476
+ target,
477
+ boardDir,
478
+ });
479
+ continue;
480
+ }
391
481
  for (const field of Object.keys(board)) {
392
482
  if (!BOARD_KEYS.includes(field))
393
483
  problem(`unknown field "${field}"`);
394
484
  }
395
- const { chip, name, description, sdkconfig, partitions, nativeModules, project, images } = board;
485
+ const { chip, sdkconfig, partitions, nativeModules, project, images } = board;
396
486
  if (typeof chip !== 'string' || !chips.includes(chip)) {
397
487
  problem(`"chip" must be one of ${chips.join(', ')}`);
398
488
  continue;
399
489
  }
400
- if (name !== undefined && typeof name !== 'string')
401
- problem('"name" must be a string');
402
- if (description !== undefined && typeof description !== 'string') {
403
- problem('"description" must be a string');
404
- }
405
490
  if (sdkconfig !== undefined && typeof sdkconfig !== 'string' && !isStringArray(sdkconfig)) {
406
491
  problem('"sdkconfig" must be a path or a list of paths');
407
492
  }
@@ -458,19 +543,11 @@ export function checkBoardsConfig(packageDir, config) {
458
543
  if (!existsSync(resolve(packageDir, file)))
459
544
  problem(`${file} does not exist`);
460
545
  }
461
- const boardName = typeof name === 'string' ? name : specifier;
462
- if (!BOARD_NAME_RE.test(boardName) || boardName.length > MAX_BOARD_NAME_LENGTH) {
463
- problem(`"${boardName}" is not a board name (at most ${MAX_BOARD_NAME_LENGTH} characters, the ` +
464
- 'form of a package name with an optional /<board>)' +
465
- (typeof name === 'string' ? '' : '; set "name"'));
466
- }
467
- const target = `./${distPath}/${key === '.' ? '' : `${sub}/`}${FULL_IMAGE}/firmware.json`;
468
- const boardDir = key === '.' ? distDir : join(distDir, sub);
469
546
  boards.push({
470
547
  key,
471
548
  specifier,
472
549
  name: boardName,
473
- description: typeof description === 'string' ? description : pkg.description,
550
+ description: boardDescription,
474
551
  chip: chip,
475
552
  sdkconfig: (typeof sdkconfig === 'string'
476
553
  ? [sdkconfig]
@@ -486,7 +563,7 @@ export function checkBoardsConfig(packageDir, config) {
486
563
  });
487
564
  }
488
565
  const byName = new Map();
489
- for (const board of boards) {
566
+ for (const board of [...boards, ...generic]) {
490
567
  const other = byName.get(board.name);
491
568
  if (other) {
492
569
  problems.push({
@@ -499,7 +576,7 @@ export function checkBoardsConfig(packageDir, config) {
499
576
  // The exports must declare exactly the configured boards, where the config
500
577
  // puts their images.
501
578
  const targets = firmwareTargets(pkg.exports);
502
- const missing = boards.filter((board) => {
579
+ const missing = [...boards, ...generic].filter((board) => {
503
580
  const target = targets.get(board.key);
504
581
  return (typeof target !== 'string' ||
505
582
  resolve(packageDir, target) !== resolve(packageDir, board.target));
@@ -527,6 +604,6 @@ export function checkBoardsConfig(packageDir, config) {
527
604
  message: `add these to "exports" in package.json (next to any other conditions of the same export):\n${exportsSnippet(missing)}`,
528
605
  });
529
606
  }
530
- return { boards, problems };
607
+ return { boards, generic, problems };
531
608
  }
532
609
  //# sourceMappingURL=boards.js.map