nodalis-compiler 1.0.22 → 1.0.23
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.
- package/CHANGELOG.md +2 -1
- package/README.md +34 -10
- package/package.json +1 -1
- package/src/compilers/ArduinoCompiler.js +16 -11
- package/src/compilers/CPPCompiler.js +30 -37
- package/src/compilers/arduinoDefaults.js +5 -0
- package/src/compilers/support/arduino/nodalis.h +3 -10
- package/src/nodalis.js +22 -1
- package/src/programmers/ArduinoProgrammer.js +6 -1
- package/src/toolchains.js +417 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [1.0.
|
|
3
|
+
## [1.0.23] 2026-03-03
|
|
4
4
|
- Changed IEC parser to interpret Function Blocks that are actually standard functions to a formal function call.
|
|
5
5
|
- Fixed nodejs/jint compiles to put executables in a bin folder.
|
|
6
6
|
- Fixed syntax errors with repeat.
|
|
7
7
|
- Prevent stale files in ST bundle compile.
|
|
8
|
+
- Added a new "action" for "get-toolchains", which will download the necessary toolchains for CPP and Arduino.
|
|
8
9
|
|
|
9
10
|
## [1.0.17] 2026-02-25
|
|
10
11
|
- Added support for compilation of multiple ST files as a single project.
|
package/README.md
CHANGED
|
@@ -56,6 +56,7 @@ Usage:
|
|
|
56
56
|
Actions:
|
|
57
57
|
--action list-compilers
|
|
58
58
|
--action compile
|
|
59
|
+
--action get-toolchains
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
---
|
|
@@ -68,6 +69,18 @@ Actions:
|
|
|
68
69
|
nodalis --action list-compilers
|
|
69
70
|
```
|
|
70
71
|
|
|
72
|
+
### ✔ Install managed C/C++ toolchains
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
nodalis --action get-toolchains
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This installs:
|
|
79
|
+
- Managed Zig-based C/C++ cross-compilers
|
|
80
|
+
- Managed `arduino-cli`
|
|
81
|
+
- Default Arduino board cores for the built-in Nodalis FQBN targets
|
|
82
|
+
- Required Arduino libraries for the built-in Nodalis Arduino runtime, including `ArduinoModbus`
|
|
83
|
+
|
|
71
84
|
---
|
|
72
85
|
|
|
73
86
|
### ✔ Compile a Structured Text program
|
|
@@ -113,23 +126,34 @@ await app.compile({
|
|
|
113
126
|
|
|
114
127
|
#### Dependencies
|
|
115
128
|
|
|
116
|
-
- Uses
|
|
129
|
+
- Uses managed Zig-based toolchain wrappers under `~/.nodalis/toolchains` when no overrides are provided.
|
|
130
|
+
- Install those toolchains with `nodalis --action get-toolchains`.
|
|
131
|
+
- `get-toolchains` also installs a managed `arduino-cli` plus the default Nodalis Arduino board cores.
|
|
132
|
+
- `get-toolchains` also installs the default Arduino libraries required by the built-in runtime support.
|
|
133
|
+
- On Windows hosts, the managed toolchain installs wrappers for all Linux and Windows targets.
|
|
134
|
+
- On macOS hosts, the managed toolchain installs wrappers for all Linux, Windows, and macOS targets.
|
|
117
135
|
- Supply a `toolchain.json` file beside your source to describe a custom toolchain. Example:
|
|
118
136
|
|
|
119
137
|
```json
|
|
120
138
|
{
|
|
121
|
-
"linux-arm": "
|
|
122
|
-
"linux-arm64": "
|
|
123
|
-
"linux-x64": "
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
"
|
|
139
|
+
"linux-arm": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-linux-arm-c++",
|
|
140
|
+
"linux-arm64": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-linux-arm64-c++",
|
|
141
|
+
"linux-x64": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-linux-x64-c++",
|
|
142
|
+
"windows-x64": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-windows-x64-c++",
|
|
143
|
+
"windows-arm64": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-windows-arm64-c++",
|
|
144
|
+
"macos-x64": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-macos-x64-c++",
|
|
145
|
+
"macos-arm64": "/Users/you/.nodalis/toolchains/zig/0.15.2/wrappers/zig-macos-arm64-c++"
|
|
128
146
|
}
|
|
129
147
|
```
|
|
130
148
|
|
|
131
|
-
-
|
|
132
|
-
-
|
|
149
|
+
- The managed installer skips macOS targets on non-macOS hosts.
|
|
150
|
+
- `toolchain.json` remains the escape hatch for unsupported or custom compiler setups.
|
|
151
|
+
|
|
152
|
+
### Arduino
|
|
153
|
+
|
|
154
|
+
Nodalis uses a managed `arduino-cli` by default when present under `~/.nodalis/toolchains/arduino-cli`.
|
|
155
|
+
Running `nodalis --action get-toolchains` also installs the default board cores required by the built-in Arduino FQBN targets, currently including `arduino:mbed_opta:opta`.
|
|
156
|
+
It also installs the default Arduino libraries required by the shipped support code, currently including `ArduinoModbus`.
|
|
133
157
|
|
|
134
158
|
#### Variations
|
|
135
159
|
|
package/package.json
CHANGED
|
@@ -23,14 +23,12 @@ import { Compiler, IECLanguage, OutputType, CommunicationProtocol } from './Comp
|
|
|
23
23
|
import * as iec from './iec-parser/parser.js';
|
|
24
24
|
import { parseStructuredText } from './st-parser/parser.js';
|
|
25
25
|
import { transpile } from './st-parser/gcctranspiler.js';
|
|
26
|
+
import { DEFAULT_ARDUINO_FQBN } from './arduinoDefaults.js';
|
|
27
|
+
import { getManagedArduinoCliPath, getManagedArduinoCliExecOptions } from '../toolchains.js';
|
|
26
28
|
|
|
27
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
30
|
const __dirname = dirname(__filename);
|
|
29
31
|
|
|
30
|
-
const DEFAULT_ARDUINO_FQBN = {
|
|
31
|
-
'arduino-opta': 'arduino:mbed_opta:opta'
|
|
32
|
-
};
|
|
33
|
-
|
|
34
32
|
const MODBUS_ARDUINO_CORE_IDS = new Set([
|
|
35
33
|
'arduino:megaavr',
|
|
36
34
|
'arduino:samd',
|
|
@@ -257,7 +255,7 @@ void loop() {
|
|
|
257
255
|
fs.cpSync(path.join(supportDir, 'json.hpp'), path.join(outputPath, 'json.hpp'), { force: true });
|
|
258
256
|
|
|
259
257
|
if (this.isExecutableOutput()) {
|
|
260
|
-
const arduinoCli = compilerConfig.arduino_cli || compilerConfig.arduinoCli ||
|
|
258
|
+
const arduinoCli = compilerConfig.arduino_cli || compilerConfig.arduinoCli || getManagedArduinoCliPath();
|
|
261
259
|
const arduinoFqbn = this.resolveArduinoFqbn(target, compilerConfig);
|
|
262
260
|
if (!arduinoFqbn) {
|
|
263
261
|
throw new Error(`No Arduino FQBN configured for target "${target}". Use an explicit FQBN target (e.g. arduino:mbed_opta:opta) or add "arduino_fqbn" to toolchain.json.`);
|
|
@@ -272,14 +270,18 @@ void loop() {
|
|
|
272
270
|
fs.mkdirSync(binDir, { recursive: true });
|
|
273
271
|
const arduinoCompileCmd = `${arduinoCli} compile --fqbn "${arduinoFqbn}" "${outputPath}" --build-path "${buildDir}" --output-dir "${binDir}" --export-binaries`;
|
|
274
272
|
try {
|
|
275
|
-
execSync(arduinoCompileCmd, { stdio: 'pipe' });
|
|
273
|
+
execSync(arduinoCompileCmd, getManagedArduinoCliExecOptions({ stdio: 'pipe' }));
|
|
276
274
|
} catch (err) {
|
|
277
275
|
const stderrText = getExecOutputText(err?.stderr);
|
|
278
276
|
const stdoutText = getExecOutputText(err?.stdout);
|
|
279
277
|
const compilerOutput = [stderrText, stdoutText].filter(Boolean).join('\n');
|
|
278
|
+
const missingArduinoLibrary = compilerOutput.includes('ArduinoModbus.h') || compilerOutput.includes('No such file or directory');
|
|
280
279
|
const details = compilerOutput || err.message;
|
|
281
280
|
throw new Error(
|
|
282
|
-
`Arduino CLI build failed for "${arduinoFqbn}".
|
|
281
|
+
`Arduino CLI build failed for "${arduinoFqbn}". ` +
|
|
282
|
+
`${missingArduinoLibrary
|
|
283
|
+
? 'Run "nodalis --action get-toolchains" to install the managed Arduino cores and libraries.'
|
|
284
|
+
: 'Verify arduino-cli, board core, and library availability.'}\n${details}`
|
|
283
285
|
);
|
|
284
286
|
}
|
|
285
287
|
}
|
|
@@ -353,7 +355,7 @@ void loop() {
|
|
|
353
355
|
|
|
354
356
|
let coreList = '';
|
|
355
357
|
try {
|
|
356
|
-
coreList = execSync(`${arduinoCli} core list`, { encoding: 'utf8' });
|
|
358
|
+
coreList = execSync(`${arduinoCli} core list`, getManagedArduinoCliExecOptions({ encoding: 'utf8' }));
|
|
357
359
|
} catch (err) {
|
|
358
360
|
throw new Error(`Failed to query installed Arduino cores using "${arduinoCli}". ${err.message}`);
|
|
359
361
|
}
|
|
@@ -362,8 +364,8 @@ void loop() {
|
|
|
362
364
|
}
|
|
363
365
|
|
|
364
366
|
try {
|
|
365
|
-
execSync(`${arduinoCli} core update-index`, { stdio: 'inherit' });
|
|
366
|
-
execSync(`${arduinoCli} core install ${coreId}`, { stdio: 'inherit' });
|
|
367
|
+
execSync(`${arduinoCli} core update-index`, getManagedArduinoCliExecOptions({ stdio: 'inherit' }));
|
|
368
|
+
execSync(`${arduinoCli} core install ${coreId}`, getManagedArduinoCliExecOptions({ stdio: 'inherit' }));
|
|
367
369
|
} catch (err) {
|
|
368
370
|
throw new Error(`Failed to install Arduino core "${coreId}" required for ${arduinoFqbn}. ${err.message}`);
|
|
369
371
|
}
|
|
@@ -387,7 +389,10 @@ void loop() {
|
|
|
387
389
|
const targets = Object.keys(DEFAULT_ARDUINO_FQBN);
|
|
388
390
|
|
|
389
391
|
try {
|
|
390
|
-
const boardListRaw = execSync(
|
|
392
|
+
const boardListRaw = execSync(
|
|
393
|
+
`${getManagedArduinoCliPath()} board listall --format json`,
|
|
394
|
+
getManagedArduinoCliExecOptions({ encoding: 'utf8' })
|
|
395
|
+
);
|
|
391
396
|
const boardList = JSON.parse(boardListRaw);
|
|
392
397
|
const boards = Array.isArray(boardList.boards) ? boardList.boards : [];
|
|
393
398
|
|
|
@@ -24,19 +24,12 @@ import { parseStructuredText } from './st-parser/parser.js';
|
|
|
24
24
|
import { transpile } from './st-parser/gcctranspiler.js';
|
|
25
25
|
import { fileURLToPath } from 'node:url';
|
|
26
26
|
import { dirname } from 'node:path';
|
|
27
|
+
import { getDefaultToolchain, getToolchainRoot, isManagedZigCompiler } from '../toolchains.js';
|
|
27
28
|
|
|
28
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
30
|
const __dirname = dirname(__filename);
|
|
30
31
|
|
|
31
|
-
const DEFAULT_TOOLCHAIN =
|
|
32
|
-
"linux-arm": "arm-linux-gnueabi-g++",
|
|
33
|
-
"linux-arm64": "aarch64-linux-gnu-g++",
|
|
34
|
-
"linux-x64": "x86_64-linux-gnu-g++",
|
|
35
|
-
"macos-arm64": "clang++",
|
|
36
|
-
"macos-x64": "clang++",
|
|
37
|
-
"windows-x64": "x86_64-w64-mingw32-g++",
|
|
38
|
-
"windows-arm64": "/opt/llvm-mingw/bin/aarch64-w64-mingw32-g++"
|
|
39
|
-
};
|
|
32
|
+
const DEFAULT_TOOLCHAIN = getDefaultToolchain();
|
|
40
33
|
|
|
41
34
|
let ToolChain = { ...DEFAULT_TOOLCHAIN };
|
|
42
35
|
|
|
@@ -79,7 +72,7 @@ export class CPPCompiler extends Compiler {
|
|
|
79
72
|
const isStructuredTextLanguage = String(language || '').toUpperCase() === IECLanguage.STRUCTURED_TEXT;
|
|
80
73
|
const directoryBundleMode = sourceIsDirectory && isStructuredTextLanguage && typeof resourceName === 'string' && resourceName.trim().length > 0;
|
|
81
74
|
|
|
82
|
-
ToolChain = { ...
|
|
75
|
+
ToolChain = { ...getDefaultToolchain() };
|
|
83
76
|
const sourceDir = sourceIsDirectory ? sourcePath : path.dirname(sourcePath);
|
|
84
77
|
const toolchainConfigPath = path.join(sourceDir, "toolchain.json");
|
|
85
78
|
if (fs.existsSync(toolchainConfigPath)) {
|
|
@@ -369,7 +362,12 @@ int main() {
|
|
|
369
362
|
.forEach((dir) => searchDirs.add(dir));
|
|
370
363
|
}
|
|
371
364
|
|
|
372
|
-
const compilerPath =
|
|
365
|
+
const compilerPath = path.isAbsolute(compiler)
|
|
366
|
+
? compiler
|
|
367
|
+
: execSync(process.platform === 'win32' ? `where "${compiler}"` : `which "${compiler}"`, { encoding: 'utf8' })
|
|
368
|
+
.split(/\r?\n/)
|
|
369
|
+
.map((line) => line.trim())
|
|
370
|
+
.find(Boolean);
|
|
373
371
|
if (compilerPath) {
|
|
374
372
|
const compilerDir = path.dirname(compilerPath);
|
|
375
373
|
searchDirs.add(compilerDir);
|
|
@@ -500,44 +498,28 @@ int main() {
|
|
|
500
498
|
}
|
|
501
499
|
|
|
502
500
|
detectCompiler(hostOs, hostArch, targetOs, targetArch) {
|
|
503
|
-
const hostDefaults = {
|
|
504
|
-
linux: "g++",
|
|
505
|
-
macos: "clang++",
|
|
506
|
-
windows: "cl.exe"
|
|
507
|
-
};
|
|
508
|
-
const hostKey = `${hostOs}-${hostArch}`;
|
|
509
501
|
const targetKey = `${targetOs}-${targetArch}`;
|
|
510
502
|
|
|
511
503
|
const ensureCompilerAvailable = (compilerName, message) => {
|
|
512
|
-
const versionCommand = compilerName === "cl.exe" ? compilerName : `${compilerName} --version`;
|
|
513
504
|
try {
|
|
514
|
-
execSync(
|
|
505
|
+
execSync(`"${compilerName}" --version`, { stdio: 'ignore' });
|
|
515
506
|
} catch {
|
|
516
507
|
throw new Error(message);
|
|
517
508
|
}
|
|
518
509
|
};
|
|
519
510
|
|
|
520
|
-
if (targetKey === hostKey) {
|
|
521
|
-
const defaultCompiler = hostDefaults[hostOs];
|
|
522
|
-
if (!defaultCompiler) {
|
|
523
|
-
throw new Error(`No default compiler configured for host platform ${hostOs}.`);
|
|
524
|
-
}
|
|
525
|
-
ensureCompilerAvailable(
|
|
526
|
-
defaultCompiler,
|
|
527
|
-
`The default compiler "${defaultCompiler}" is not available. Install it using your package manager (e.g., brew install ${defaultCompiler} or apt install ${defaultCompiler}).
|
|
528
|
-
You can also create a file called "toolchain.json" in your source directory which will supply the path to the gnu c compiler for each platform. See the README file for more details.`
|
|
529
|
-
);
|
|
530
|
-
return defaultCompiler;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
511
|
const configuredCompiler = ToolChain[targetKey];
|
|
534
512
|
if (!configuredCompiler) {
|
|
535
|
-
throw new Error(
|
|
513
|
+
throw new Error(
|
|
514
|
+
`No toolchain is configured for target ${targetKey} on host ${hostOs}-${hostArch}. ` +
|
|
515
|
+
`Run "nodalis --action get-toolchains" or add an override in toolchain.json.`
|
|
516
|
+
);
|
|
536
517
|
}
|
|
537
518
|
ensureCompilerAvailable(
|
|
538
519
|
configuredCompiler,
|
|
539
|
-
`
|
|
540
|
-
|
|
520
|
+
`Toolchain "${configuredCompiler}" for target ${targetKey} is not available. ` +
|
|
521
|
+
`Run "nodalis --action get-toolchains" to install managed toolchains under ${getToolchainRoot()}, ` +
|
|
522
|
+
`or add a custom compiler path in toolchain.json.`
|
|
541
523
|
);
|
|
542
524
|
return configuredCompiler;
|
|
543
525
|
}
|
|
@@ -561,7 +543,7 @@ int main() {
|
|
|
561
543
|
if (compiler === 'cl.exe') {
|
|
562
544
|
return { c: [], cpp: [] };
|
|
563
545
|
}
|
|
564
|
-
let flags = { //default flags are for
|
|
546
|
+
let flags = { // default flags are for managed/native clang wrappers
|
|
565
547
|
'linux-x64': [],
|
|
566
548
|
'linux-arm64': [],
|
|
567
549
|
'linux-arm': [],
|
|
@@ -581,7 +563,18 @@ int main() {
|
|
|
581
563
|
"macos-arm64": "",
|
|
582
564
|
}
|
|
583
565
|
|
|
584
|
-
if (
|
|
566
|
+
if (isManagedZigCompiler(compiler)) {
|
|
567
|
+
flags = {
|
|
568
|
+
'linux-x64': [],
|
|
569
|
+
'linux-arm64': [],
|
|
570
|
+
'linux-arm': [],
|
|
571
|
+
'macos-x64': [],
|
|
572
|
+
'macos-arm64': [],
|
|
573
|
+
'windows-x64': [],
|
|
574
|
+
'windows-arm64': []
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
else if (!compiler.includes("clang")) {
|
|
585
578
|
flags = {
|
|
586
579
|
'linux-x64': ["-D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE -pthread"],
|
|
587
580
|
'linux-arm64': ["-D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE -pthread"],
|
|
@@ -671,19 +671,12 @@ template <typename T, typename... Ts>
|
|
|
671
671
|
inline T MUX(std::size_t K, T in0, Ts... rest)
|
|
672
672
|
{
|
|
673
673
|
constexpr std::size_t N = 1 + sizeof...(Ts);
|
|
674
|
+
T values[N] = {in0, static_cast<T>(rest)...};
|
|
674
675
|
if (K >= N)
|
|
675
676
|
{
|
|
676
|
-
|
|
677
|
+
return values[0];
|
|
677
678
|
}
|
|
678
|
-
|
|
679
|
-
auto values = std::tuple<T, Ts...>(in0, rest...);
|
|
680
|
-
return std::apply(
|
|
681
|
-
[K](auto... elems) -> T
|
|
682
|
-
{
|
|
683
|
-
T arr[] = {elems...};
|
|
684
|
-
return arr[K];
|
|
685
|
-
},
|
|
686
|
-
values);
|
|
679
|
+
return values[K];
|
|
687
680
|
};
|
|
688
681
|
|
|
689
682
|
// ============================================================
|
package/src/nodalis.js
CHANGED
|
@@ -30,6 +30,7 @@ import { FileProgrammer } from './programmers/FileProgrammer.js';
|
|
|
30
30
|
import { SSHProgrammer } from './programmers/SSHProgrammer.js';
|
|
31
31
|
import { ArduinoProgrammer } from './programmers/ArduinoProgrammer.js';
|
|
32
32
|
import { CompileList } from "mticp-npm"
|
|
33
|
+
import { getToolchainRoot, installDefaultToolchains } from './toolchains.js';
|
|
33
34
|
|
|
34
35
|
const __filename = fileURLToPath(import.meta.url);
|
|
35
36
|
const __dirname = path.dirname(__filename);
|
|
@@ -172,6 +173,10 @@ export class Nodalis {
|
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
176
|
+
async getToolchains() {
|
|
177
|
+
return installDefaultToolchains();
|
|
178
|
+
}
|
|
179
|
+
|
|
175
180
|
}
|
|
176
181
|
|
|
177
182
|
function isCliEntryPoint() {
|
|
@@ -221,6 +226,11 @@ Actions:
|
|
|
221
226
|
--sshPort Optional SSH port for SSH deployment.
|
|
222
227
|
--arduinoFqbn Required for Arduino target if --target Arduino.
|
|
223
228
|
|
|
229
|
+
--action get-toolchains
|
|
230
|
+
Detects the host OS/arch and installs managed C/C++ toolchains,
|
|
231
|
+
arduino-cli, and default Arduino board cores under:
|
|
232
|
+
${getToolchainRoot()}
|
|
233
|
+
|
|
224
234
|
Examples:
|
|
225
235
|
node nodalis.js --action list-compilers
|
|
226
236
|
|
|
@@ -231,6 +241,8 @@ Examples:
|
|
|
231
241
|
--resourceName MyPLC \\
|
|
232
242
|
--sourcePath ./examples/pump.iec \\
|
|
233
243
|
--language st
|
|
244
|
+
|
|
245
|
+
node nodalis.js --action get-toolchains
|
|
234
246
|
`);
|
|
235
247
|
process.exit(0);
|
|
236
248
|
}
|
|
@@ -296,9 +308,18 @@ Examples:
|
|
|
296
308
|
break;
|
|
297
309
|
}
|
|
298
310
|
|
|
311
|
+
case 'get-toolchains': {
|
|
312
|
+
app.getToolchains().then((result) => {
|
|
313
|
+
console.log(JSON.stringify(result, null, 2));
|
|
314
|
+
}).catch(err => {
|
|
315
|
+
console.error(`Toolchain installation failed: ${err.message}`);
|
|
316
|
+
});
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
299
320
|
default: {
|
|
300
321
|
console.error(`Unknown or missing action: ${argMap.action}`);
|
|
301
|
-
console.error(`Valid actions: list-compilers, list-programmers, compile, deploy`);
|
|
322
|
+
console.error(`Valid actions: list-compilers, list-programmers, compile, deploy, get-toolchains`);
|
|
302
323
|
break;
|
|
303
324
|
}
|
|
304
325
|
}
|
|
@@ -16,6 +16,7 @@ import fs from 'fs/promises';
|
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import { Programmer } from './Programmer.js';
|
|
18
18
|
import { runCommand } from './utils.js';
|
|
19
|
+
import { getManagedArduinoCliExecOptions, getManagedArduinoCliPath } from '../toolchains.js';
|
|
19
20
|
|
|
20
21
|
export class ArduinoProgrammer extends Programmer {
|
|
21
22
|
constructor(options) {
|
|
@@ -58,7 +59,11 @@ export class ArduinoProgrammer extends Programmer {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
const { stdout, stderr } = await runCommand(
|
|
62
|
+
const { stdout, stderr } = await runCommand(
|
|
63
|
+
getManagedArduinoCliPath(),
|
|
64
|
+
args,
|
|
65
|
+
getManagedArduinoCliExecOptions()
|
|
66
|
+
);
|
|
62
67
|
if (stdout) {
|
|
63
68
|
console.log(stdout.trim());
|
|
64
69
|
}
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import https from 'https';
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
|
+
import { pipeline } from 'stream/promises';
|
|
7
|
+
import { DEFAULT_ARDUINO_FQBNS } from './compilers/arduinoDefaults.js';
|
|
8
|
+
|
|
9
|
+
const ZIG_VERSION = '0.15.2';
|
|
10
|
+
const ARDUINO_CLI_VERSION = '1.3.1';
|
|
11
|
+
const TOOLCHAIN_ROOT = path.join(os.homedir(), '.nodalis', 'toolchains');
|
|
12
|
+
const WRAPPER_EXTENSION = process.platform === 'win32' ? '.cmd' : '';
|
|
13
|
+
const DEFAULT_ARDUINO_LIBRARIES = ['ArduinoModbus'];
|
|
14
|
+
|
|
15
|
+
const TARGET_TRIPLES = {
|
|
16
|
+
'linux-arm': 'arm-linux-gnueabihf',
|
|
17
|
+
'linux-arm64': 'aarch64-linux-gnu',
|
|
18
|
+
'linux-x64': 'x86_64-linux-gnu',
|
|
19
|
+
'macos-arm64': 'aarch64-macos',
|
|
20
|
+
'macos-x64': 'x86_64-macos',
|
|
21
|
+
'windows-arm64': 'aarch64-windows-gnu',
|
|
22
|
+
'windows-x64': 'x86_64-windows-gnu'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const normalizeHostOS = (value) => {
|
|
26
|
+
if (value === 'win32') return 'windows';
|
|
27
|
+
if (value === 'darwin') return 'macos';
|
|
28
|
+
if (value === 'linux') return 'linux';
|
|
29
|
+
throw new Error(`Unsupported host operating system: ${value}`);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const normalizeHostArch = (value) => {
|
|
33
|
+
if (value === 'x64') return 'x64';
|
|
34
|
+
if (value === 'arm64') return 'arm64';
|
|
35
|
+
if (value.startsWith('arm')) return 'arm';
|
|
36
|
+
throw new Error(`Unsupported host architecture: ${value}`);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const stripArchiveExtension = (archiveName) => {
|
|
40
|
+
if (archiveName.endsWith('.tar.gz')) return archiveName.slice(0, -7);
|
|
41
|
+
if (archiveName.endsWith('.tar.xz')) return archiveName.slice(0, -7);
|
|
42
|
+
if (archiveName.endsWith('.zip')) return archiveName.slice(0, -4);
|
|
43
|
+
return archiveName;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getWrapperDir = () => path.join(TOOLCHAIN_ROOT, 'zig', ZIG_VERSION, 'wrappers');
|
|
47
|
+
|
|
48
|
+
const getWrapperPath = (target) => path.join(getWrapperDir(), `zig-${target}-c++${WRAPPER_EXTENSION}`);
|
|
49
|
+
|
|
50
|
+
const getArduinoCliRootDir = () => path.join(TOOLCHAIN_ROOT, 'arduino-cli', ARDUINO_CLI_VERSION);
|
|
51
|
+
|
|
52
|
+
const getArduinoCliDataDir = () => path.join(getArduinoCliRootDir(), 'data');
|
|
53
|
+
|
|
54
|
+
const getArduinoCliDownloadsDir = () => path.join(getArduinoCliRootDir(), 'downloads');
|
|
55
|
+
|
|
56
|
+
const getArduinoCliUserDir = () => path.join(getArduinoCliRootDir(), 'user');
|
|
57
|
+
|
|
58
|
+
const getZigInstallDir = (host = detectHostPlatform()) => {
|
|
59
|
+
const archiveName = getZigArchiveName(host);
|
|
60
|
+
return path.join(TOOLCHAIN_ROOT, 'zig', ZIG_VERSION, stripArchiveExtension(archiveName));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const getZigBinaryPath = (host = detectHostPlatform()) => {
|
|
64
|
+
const installDir = getZigInstallDir(host);
|
|
65
|
+
return path.join(installDir, process.platform === 'win32' ? 'zig.exe' : 'zig');
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const getArduinoCliArchiveName = (host = detectHostPlatform()) => {
|
|
69
|
+
if (host.os === 'windows') {
|
|
70
|
+
const archSegment = host.arch === 'arm64' ? 'Windows_ARM64' : host.arch === 'x64' ? 'Windows_64bit' : null;
|
|
71
|
+
if (!archSegment) {
|
|
72
|
+
throw new Error(`Unsupported Windows host architecture for arduino-cli: ${host.arch}`);
|
|
73
|
+
}
|
|
74
|
+
return `arduino-cli_${ARDUINO_CLI_VERSION}_${archSegment}.zip`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (host.os === 'macos') {
|
|
78
|
+
const archSegment = host.arch === 'arm64' ? 'macOS_ARM64' : host.arch === 'x64' ? 'macOS_64bit' : null;
|
|
79
|
+
if (!archSegment) {
|
|
80
|
+
throw new Error(`Unsupported macOS host architecture for arduino-cli: ${host.arch}`);
|
|
81
|
+
}
|
|
82
|
+
return `arduino-cli_${ARDUINO_CLI_VERSION}_${archSegment}.tar.gz`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (host.os === 'linux') {
|
|
86
|
+
if (host.arch === 'x64') return `arduino-cli_${ARDUINO_CLI_VERSION}_Linux_64bit.tar.gz`;
|
|
87
|
+
if (host.arch === 'arm64') return `arduino-cli_${ARDUINO_CLI_VERSION}_Linux_ARM64.tar.gz`;
|
|
88
|
+
if (host.arch === 'arm') return `arduino-cli_${ARDUINO_CLI_VERSION}_Linux_ARMv7.tar.gz`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
throw new Error(`Unsupported arduino-cli host platform ${host.os}-${host.arch}`);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const getArduinoCliInstallDir = (host = detectHostPlatform()) => {
|
|
95
|
+
return path.join(getArduinoCliRootDir(), `${host.os}-${host.arch}`);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const getManagedArduinoCliPath = (host = detectHostPlatform()) => {
|
|
99
|
+
const installDir = getArduinoCliInstallDir(host);
|
|
100
|
+
return path.join(installDir, host.os === 'windows' ? 'arduino-cli.exe' : 'arduino-cli');
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const getSupportedTargetsForHost = (host = detectHostPlatform()) => {
|
|
104
|
+
const targets = ['linux-arm', 'linux-arm64', 'linux-x64', 'windows-arm64', 'windows-x64'];
|
|
105
|
+
if (host.os === 'macos') {
|
|
106
|
+
targets.push('macos-arm64', 'macos-x64');
|
|
107
|
+
}
|
|
108
|
+
return targets;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const getZigArchiveName = (host = detectHostPlatform()) => {
|
|
112
|
+
if (host.os === 'windows') {
|
|
113
|
+
const archSegment = host.arch === 'arm64' ? 'aarch64' : host.arch === 'x64' ? 'x86_64' : null;
|
|
114
|
+
if (!archSegment) {
|
|
115
|
+
throw new Error(`Unsupported Windows host architecture for Zig: ${host.arch}`);
|
|
116
|
+
}
|
|
117
|
+
return `zig-${archSegment}-windows-${ZIG_VERSION}.zip`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (host.os === 'macos') {
|
|
121
|
+
const archSegment = host.arch === 'arm64' ? 'aarch64' : host.arch === 'x64' ? 'x86_64' : null;
|
|
122
|
+
if (!archSegment) {
|
|
123
|
+
throw new Error(`Unsupported macOS host architecture for Zig: ${host.arch}`);
|
|
124
|
+
}
|
|
125
|
+
return `zig-${archSegment}-macos-${ZIG_VERSION}.tar.xz`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (host.os === 'linux') {
|
|
129
|
+
if (host.arch === 'x64') return `zig-x86_64-linux-${ZIG_VERSION}.tar.xz`;
|
|
130
|
+
if (host.arch === 'arm64') return `zig-aarch64-linux-${ZIG_VERSION}.tar.xz`;
|
|
131
|
+
if (host.arch === 'arm') return `zig-arm-linux-${ZIG_VERSION}.tar.xz`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw new Error(`Unsupported Zig host platform ${host.os}-${host.arch}`);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const writeWrapper = (wrapperPath, zigBinaryPath, targetTriple) => {
|
|
138
|
+
fs.mkdirSync(path.dirname(wrapperPath), { recursive: true });
|
|
139
|
+
|
|
140
|
+
if (process.platform === 'win32') {
|
|
141
|
+
const windowsScript = [
|
|
142
|
+
'@echo off',
|
|
143
|
+
`"%~dp0..\\${path.basename(path.dirname(zigBinaryPath))}\\zig.exe" c++ -target ${targetTriple} %*`
|
|
144
|
+
].join('\r\n');
|
|
145
|
+
fs.writeFileSync(wrapperPath, windowsScript);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const posixScript = [
|
|
150
|
+
'#!/bin/sh',
|
|
151
|
+
`exec "${zigBinaryPath}" c++ -target ${targetTriple} "$@"`
|
|
152
|
+
].join('\n');
|
|
153
|
+
fs.writeFileSync(wrapperPath, posixScript, { mode: 0o755 });
|
|
154
|
+
fs.chmodSync(wrapperPath, 0o755);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const ensureWrappers = (host = detectHostPlatform()) => {
|
|
158
|
+
const zigBinaryPath = getZigBinaryPath(host);
|
|
159
|
+
for (const target of getSupportedTargetsForHost(host)) {
|
|
160
|
+
const targetTriple = TARGET_TRIPLES[target];
|
|
161
|
+
if (!targetTriple) continue;
|
|
162
|
+
writeWrapper(getWrapperPath(target), zigBinaryPath, targetTriple);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const detectHostPlatform = () => ({
|
|
167
|
+
os: normalizeHostOS(os.platform()),
|
|
168
|
+
arch: normalizeHostArch(os.arch())
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const getToolchainRoot = () => TOOLCHAIN_ROOT;
|
|
172
|
+
|
|
173
|
+
export const getManagedArduinoCliExecOptions = (overrides = {}) => ({
|
|
174
|
+
...overrides,
|
|
175
|
+
env: {
|
|
176
|
+
...process.env,
|
|
177
|
+
ARDUINO_DIRECTORIES_DATA: getArduinoCliDataDir(),
|
|
178
|
+
ARDUINO_DIRECTORIES_DOWNLOADS: getArduinoCliDownloadsDir(),
|
|
179
|
+
ARDUINO_DIRECTORIES_USER: getArduinoCliUserDir(),
|
|
180
|
+
...(overrides.env || {})
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
export const isManagedZigCompiler = (compilerPath) => String(compilerPath || '').includes(`${path.sep}.nodalis${path.sep}toolchains${path.sep}zig${path.sep}`);
|
|
185
|
+
|
|
186
|
+
export const getDefaultToolchain = (host = detectHostPlatform()) => {
|
|
187
|
+
return getSupportedTargetsForHost(host).reduce((acc, target) => {
|
|
188
|
+
acc[target] = getWrapperPath(target);
|
|
189
|
+
return acc;
|
|
190
|
+
}, {});
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const downloadFile = async (url, destinationPath) => {
|
|
194
|
+
await new Promise((resolve, reject) => {
|
|
195
|
+
const request = https.get(url, (response) => {
|
|
196
|
+
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
197
|
+
response.resume();
|
|
198
|
+
downloadFile(response.headers.location, destinationPath).then(resolve).catch(reject);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (response.statusCode !== 200) {
|
|
203
|
+
response.resume();
|
|
204
|
+
reject(new Error(`Download failed with HTTP ${response.statusCode} for ${url}`));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const fileStream = fs.createWriteStream(destinationPath);
|
|
209
|
+
pipeline(response, fileStream).then(resolve).catch(reject);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
request.on('error', reject);
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const extractArchive = (archivePath, destinationDir) => {
|
|
217
|
+
fs.mkdirSync(destinationDir, { recursive: true });
|
|
218
|
+
|
|
219
|
+
if (archivePath.endsWith('.zip')) {
|
|
220
|
+
if (process.platform !== 'win32') {
|
|
221
|
+
throw new Error(`ZIP extraction is only configured for Windows hosts: ${archivePath}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
execFileSync(
|
|
225
|
+
'powershell.exe',
|
|
226
|
+
[
|
|
227
|
+
'-NoProfile',
|
|
228
|
+
'-NonInteractive',
|
|
229
|
+
'-Command',
|
|
230
|
+
`Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${destinationDir.replace(/'/g, "''")}' -Force`
|
|
231
|
+
],
|
|
232
|
+
{ stdio: 'inherit' }
|
|
233
|
+
);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
execFileSync('tar', ['-xf', archivePath, '-C', destinationDir], { stdio: 'inherit' });
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const ensureArduinoCliDirs = () => {
|
|
241
|
+
fs.mkdirSync(getArduinoCliRootDir(), { recursive: true });
|
|
242
|
+
fs.mkdirSync(getArduinoCliDataDir(), { recursive: true });
|
|
243
|
+
fs.mkdirSync(getArduinoCliDownloadsDir(), { recursive: true });
|
|
244
|
+
fs.mkdirSync(getArduinoCliUserDir(), { recursive: true });
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const installDefaultArduinoCores = (arduinoCliPath) => {
|
|
248
|
+
execFileSync(arduinoCliPath, ['core', 'update-index'], getManagedArduinoCliExecOptions({ stdio: 'inherit' }));
|
|
249
|
+
|
|
250
|
+
const coreIds = [...new Set(
|
|
251
|
+
DEFAULT_ARDUINO_FQBNS.map((fqbn) => {
|
|
252
|
+
const parts = String(fqbn).split(':');
|
|
253
|
+
return parts.length >= 2 ? `${parts[0]}:${parts[1]}` : null;
|
|
254
|
+
}).filter(Boolean)
|
|
255
|
+
)];
|
|
256
|
+
|
|
257
|
+
for (const coreId of coreIds) {
|
|
258
|
+
execFileSync(arduinoCliPath, ['core', 'install', coreId], getManagedArduinoCliExecOptions({ stdio: 'inherit' }));
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const installDefaultArduinoLibraries = (arduinoCliPath) => {
|
|
263
|
+
for (const libraryName of DEFAULT_ARDUINO_LIBRARIES) {
|
|
264
|
+
execFileSync(
|
|
265
|
+
arduinoCliPath,
|
|
266
|
+
['lib', 'install', libraryName],
|
|
267
|
+
getManagedArduinoCliExecOptions({ stdio: 'inherit' })
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export const installDefaultToolchains = async () => {
|
|
273
|
+
const host = detectHostPlatform();
|
|
274
|
+
const archiveName = getZigArchiveName(host);
|
|
275
|
+
const rootDir = path.join(TOOLCHAIN_ROOT, 'zig', ZIG_VERSION);
|
|
276
|
+
const archivePath = path.join(rootDir, archiveName);
|
|
277
|
+
const installDir = getZigInstallDir(host);
|
|
278
|
+
const zigBinaryPath = getZigBinaryPath(host);
|
|
279
|
+
|
|
280
|
+
fs.mkdirSync(rootDir, { recursive: true });
|
|
281
|
+
|
|
282
|
+
const results = [];
|
|
283
|
+
if (!fs.existsSync(zigBinaryPath)) {
|
|
284
|
+
const downloadUrl = `https://ziglang.org/download/${ZIG_VERSION}/${archiveName}`;
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
await downloadFile(downloadUrl, archivePath);
|
|
288
|
+
extractArchive(archivePath, rootDir);
|
|
289
|
+
fs.rmSync(archivePath, { force: true });
|
|
290
|
+
results.push({
|
|
291
|
+
id: 'zig',
|
|
292
|
+
status: 'installed',
|
|
293
|
+
installDir,
|
|
294
|
+
downloadUrl
|
|
295
|
+
});
|
|
296
|
+
} catch (err) {
|
|
297
|
+
fs.rmSync(archivePath, { force: true });
|
|
298
|
+
results.push({
|
|
299
|
+
id: 'zig',
|
|
300
|
+
status: 'failed',
|
|
301
|
+
downloadUrl,
|
|
302
|
+
error: err.message
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
host,
|
|
306
|
+
rootDir: TOOLCHAIN_ROOT,
|
|
307
|
+
toolchains: results,
|
|
308
|
+
defaultToolchain: getDefaultToolchain(host)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
results.push({
|
|
313
|
+
id: 'zig',
|
|
314
|
+
status: 'already-installed',
|
|
315
|
+
installDir
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
ensureWrappers(host);
|
|
320
|
+
|
|
321
|
+
const arduinoArchiveName = getArduinoCliArchiveName(host);
|
|
322
|
+
const arduinoRootDir = getArduinoCliRootDir();
|
|
323
|
+
const arduinoArchivePath = path.join(arduinoRootDir, arduinoArchiveName);
|
|
324
|
+
const arduinoInstallDir = getArduinoCliInstallDir(host);
|
|
325
|
+
const arduinoCliPath = getManagedArduinoCliPath(host);
|
|
326
|
+
|
|
327
|
+
ensureArduinoCliDirs();
|
|
328
|
+
|
|
329
|
+
if (!fs.existsSync(arduinoCliPath)) {
|
|
330
|
+
const downloadUrl = `https://downloads.arduino.cc/arduino-cli/${arduinoArchiveName}`;
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
await downloadFile(downloadUrl, arduinoArchivePath);
|
|
334
|
+
fs.mkdirSync(arduinoInstallDir, { recursive: true });
|
|
335
|
+
extractArchive(arduinoArchivePath, arduinoInstallDir);
|
|
336
|
+
fs.rmSync(arduinoArchivePath, { force: true });
|
|
337
|
+
results.push({
|
|
338
|
+
id: 'arduino-cli',
|
|
339
|
+
status: 'installed',
|
|
340
|
+
installDir: arduinoInstallDir,
|
|
341
|
+
downloadUrl
|
|
342
|
+
});
|
|
343
|
+
} catch (err) {
|
|
344
|
+
fs.rmSync(arduinoArchivePath, { force: true });
|
|
345
|
+
results.push({
|
|
346
|
+
id: 'arduino-cli',
|
|
347
|
+
status: 'failed',
|
|
348
|
+
downloadUrl,
|
|
349
|
+
error: err.message
|
|
350
|
+
});
|
|
351
|
+
return {
|
|
352
|
+
host,
|
|
353
|
+
rootDir: TOOLCHAIN_ROOT,
|
|
354
|
+
toolchains: results,
|
|
355
|
+
defaultToolchain: getDefaultToolchain(host),
|
|
356
|
+
arduinoCli: arduinoCliPath,
|
|
357
|
+
defaultArduinoFqbns: DEFAULT_ARDUINO_FQBNS
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
results.push({
|
|
362
|
+
id: 'arduino-cli',
|
|
363
|
+
status: 'already-installed',
|
|
364
|
+
installDir: arduinoInstallDir
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
installDefaultArduinoCores(arduinoCliPath);
|
|
370
|
+
results.push({
|
|
371
|
+
id: 'arduino-default-cores',
|
|
372
|
+
status: 'installed',
|
|
373
|
+
fqbns: DEFAULT_ARDUINO_FQBNS
|
|
374
|
+
});
|
|
375
|
+
} catch (err) {
|
|
376
|
+
results.push({
|
|
377
|
+
id: 'arduino-default-cores',
|
|
378
|
+
status: 'failed',
|
|
379
|
+
fqbns: DEFAULT_ARDUINO_FQBNS,
|
|
380
|
+
error: err.message
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
installDefaultArduinoLibraries(arduinoCliPath);
|
|
386
|
+
results.push({
|
|
387
|
+
id: 'arduino-default-libraries',
|
|
388
|
+
status: 'installed',
|
|
389
|
+
libraries: DEFAULT_ARDUINO_LIBRARIES
|
|
390
|
+
});
|
|
391
|
+
} catch (err) {
|
|
392
|
+
results.push({
|
|
393
|
+
id: 'arduino-default-libraries',
|
|
394
|
+
status: 'failed',
|
|
395
|
+
libraries: DEFAULT_ARDUINO_LIBRARIES,
|
|
396
|
+
error: err.message
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
host,
|
|
402
|
+
rootDir: TOOLCHAIN_ROOT,
|
|
403
|
+
toolchains: [
|
|
404
|
+
...results,
|
|
405
|
+
...getSupportedTargetsForHost(host).map((target) => ({
|
|
406
|
+
id: target,
|
|
407
|
+
status: 'ready',
|
|
408
|
+
compiler: getWrapperPath(target),
|
|
409
|
+
targetTriple: TARGET_TRIPLES[target]
|
|
410
|
+
}))
|
|
411
|
+
],
|
|
412
|
+
defaultToolchain: getDefaultToolchain(host),
|
|
413
|
+
arduinoCli: arduinoCliPath,
|
|
414
|
+
defaultArduinoFqbns: DEFAULT_ARDUINO_FQBNS,
|
|
415
|
+
defaultArduinoLibraries: DEFAULT_ARDUINO_LIBRARIES
|
|
416
|
+
};
|
|
417
|
+
};
|