@tsplc/build 0.0.0-stage → 0.2.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.
- package/LICENSE +21 -0
- package/README.md +6 -2
- package/dist/cli.js +1696 -0
- package/dist/cli.js.map +34 -0
- package/dist/index.js +1683 -0
- package/dist/index.js.map +31 -0
- package/dist/libraries.js +128 -0
- package/dist/libraries.js.map +10 -0
- package/docker/Dockerfile +173 -0
- package/docker/Dockerfile.dockerignore +18 -0
- package/docker/Dockerfile.source +15 -0
- package/docker/build-image +38 -0
- package/docker/codesys-build +5 -0
- package/docker/config.ts +8 -0
- package/docker/control-linux-sl/Dockerfile +38 -0
- package/docker/edge-gateway/Dockerfile +17 -0
- package/docker/entrypoint.sh +10 -0
- package/docker/install/NOTICE.md +21 -0
- package/docker/install/core.sh +36 -0
- package/docker/install/decode.c +12 -0
- package/docker/install/device/Runtime.ico +0 -0
- package/docker/install/device/Runtime.png +0 -0
- package/docker/install/device/codesyscontrol.devdesc.xml +1502 -0
- package/docker/install/extract.py +43 -0
- package/docker/install/helper-launcher.c +46 -0
- package/docker/install/install.sh +94 -0
- package/docker/install/prerequisites.sh +15 -0
- package/docker/install/verify.py +133 -0
- package/docker/install/verify.sh +34 -0
- package/docker/install/wine-ncrypt/COPYING.LIB +502 -0
- package/docker/install/wine-ncrypt/README.md +28 -0
- package/docker/install/wine-ncrypt/enable-oaep.py +34 -0
- package/docker/install/wine-ncrypt/main.c +619 -0
- package/docker/install/wine-ncrypt/oaep.patch +11 -0
- package/docker/install/wine-signal/README.md +22 -0
- package/docker/install/wine-signal/build.sh +10 -0
- package/docker/install/wine-signal/check.sh +34 -0
- package/docker/install/wine-signal/noop-i386.c +2 -0
- package/docker/install/wine-signal/signal-state.c +105 -0
- package/docker/install/wine-signal/tests/avx-registers.c +53 -0
- package/docker/install/wine-signal/tests/frame-and-handler.c +192 -0
- package/docker/install/wine-signal/tests/loader-i386.c +8 -0
- package/docker/install/wine-signal/tests/signal-return.c +47 -0
- package/docker/install/wine-signal/wine +4 -0
- package/docker/runner.ts +156 -0
- package/package.json +53 -4
- package/python/build.py +660 -0
- package/python/codesys_login.py +100 -0
- package/python/codesys_online.py +327 -0
- package/python/test_build.py +444 -0
- package/python/test_codesys_login.py +120 -0
- package/python/test_codesys_online.py +184 -0
- package/python/test_installer_packages.py +216 -0
- package/python/test_installer_verification.py +307 -0
- package/python/test_worker.py +142 -0
- package/python/worker.py +102 -0
- package/runtime/install.sh +74 -0
- package/src/__tests__/fixtures/xml/fb-simple.xml +40 -0
- package/src/__tests__/fixtures/xml/fb-state-machine.xml +86 -0
- package/src/__tests__/fixtures/xml/prg-cyclic.xml +44 -0
- package/src/artifacts.ts +77 -0
- package/src/build.ts +87 -0
- package/src/cli.ts +116 -0
- package/src/config.ts +12 -0
- package/src/controller-online.ts +297 -0
- package/src/controller-session.ts +298 -0
- package/src/diagnostics.ts +68 -0
- package/src/docker-build.ts +163 -0
- package/src/docker-context.ts +35 -0
- package/src/docker-run.ts +103 -0
- package/src/host/codesys-command.ts +45 -0
- package/src/host/codesys-host.ts +145 -0
- package/src/host/codesys-worker.ts +348 -0
- package/src/host/windows-path.ts +73 -0
- package/src/index.ts +43 -0
- package/src/io.ts +127 -0
- package/src/libraries.ts +166 -0
- package/src/package-path.ts +5 -0
- package/src/run.ts +133 -0
- package/src/runtime-status.ts +21 -0
- package/src/sources.ts +248 -0
- package/src/st/declarations.ts +229 -0
- package/src/st/lexer.ts +125 -0
- package/src/st/parse.ts +202 -0
- package/src/st/reader.ts +50 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<project xmlns="http://www.plcopen.org/xml/tc6_0200" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
3
|
+
<fileHeader companyName="" productName="CODESYS" productVersion="CODESYS V3.5 SP21" creationDateTime="<NORMALIZED>" />
|
|
4
|
+
<contentHeader name="FB_StateMachine" modificationDateTime="<NORMALIZED>">
|
|
5
|
+
<coordinateInfo>
|
|
6
|
+
<fbd><scaling x="1" y="1" /></fbd>
|
|
7
|
+
<ld><scaling x="1" y="1" /></ld>
|
|
8
|
+
<sfc><scaling x="1" y="1" /></sfc>
|
|
9
|
+
</coordinateInfo>
|
|
10
|
+
</contentHeader>
|
|
11
|
+
<types>
|
|
12
|
+
<dataTypes />
|
|
13
|
+
<pous>
|
|
14
|
+
<pou name="FB_StateMachine" pouType="functionBlock">
|
|
15
|
+
<interface>
|
|
16
|
+
<inputVars>
|
|
17
|
+
<variable name="bExecute">
|
|
18
|
+
<type><BOOL /></type>
|
|
19
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Rising edge starts cycle</xhtml></documentation>
|
|
20
|
+
</variable>
|
|
21
|
+
<variable name="tTimeout">
|
|
22
|
+
<type><TIME /></type>
|
|
23
|
+
<initialValue><simpleValue value="T#5s" /></initialValue>
|
|
24
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Operation timeout</xhtml></documentation>
|
|
25
|
+
</variable>
|
|
26
|
+
</inputVars>
|
|
27
|
+
<outputVars>
|
|
28
|
+
<variable name="bDone">
|
|
29
|
+
<type><BOOL /></type>
|
|
30
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Cycle complete</xhtml></documentation>
|
|
31
|
+
</variable>
|
|
32
|
+
<variable name="bBusy">
|
|
33
|
+
<type><BOOL /></type>
|
|
34
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Cycle in progress</xhtml></documentation>
|
|
35
|
+
</variable>
|
|
36
|
+
<variable name="bErr">
|
|
37
|
+
<type><BOOL /></type>
|
|
38
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Cycle errored</xhtml></documentation>
|
|
39
|
+
</variable>
|
|
40
|
+
<variable name="nErrId">
|
|
41
|
+
<type><INT /></type>
|
|
42
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Error ID</xhtml></documentation>
|
|
43
|
+
</variable>
|
|
44
|
+
</outputVars>
|
|
45
|
+
<localVars>
|
|
46
|
+
<variable name="nState">
|
|
47
|
+
<type><INT /></type>
|
|
48
|
+
<initialValue><simpleValue value="0" /></initialValue>
|
|
49
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">State machine pointer</xhtml></documentation>
|
|
50
|
+
</variable>
|
|
51
|
+
<variable name="fbTimer">
|
|
52
|
+
<type><derived name="TON" /></type>
|
|
53
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Timeout watchdog</xhtml></documentation>
|
|
54
|
+
</variable>
|
|
55
|
+
</localVars>
|
|
56
|
+
</interface>
|
|
57
|
+
<body>
|
|
58
|
+
<ST>
|
|
59
|
+
<xhtml xmlns="http://www.w3.org/1999/xhtml"><![CDATA[CASE nState OF
|
|
60
|
+
0: (* IDLE *)
|
|
61
|
+
IF bExecute THEN
|
|
62
|
+
nState := 10;
|
|
63
|
+
END_IF
|
|
64
|
+
10: (* WORKING *)
|
|
65
|
+
bBusy := TRUE;
|
|
66
|
+
fbTimer(IN := TRUE, PT := tTimeout);
|
|
67
|
+
IF fbTimer.Q THEN
|
|
68
|
+
nState := 99;
|
|
69
|
+
nErrId := 100;
|
|
70
|
+
END_IF
|
|
71
|
+
90: (* DONE *)
|
|
72
|
+
bDone := TRUE;
|
|
73
|
+
bBusy := FALSE;
|
|
74
|
+
99: (* ERROR *)
|
|
75
|
+
bErr := TRUE;
|
|
76
|
+
bBusy := FALSE;
|
|
77
|
+
END_CASE]]></xhtml>
|
|
78
|
+
</ST>
|
|
79
|
+
</body>
|
|
80
|
+
</pou>
|
|
81
|
+
</pous>
|
|
82
|
+
</types>
|
|
83
|
+
<instances>
|
|
84
|
+
<configurations />
|
|
85
|
+
</instances>
|
|
86
|
+
</project>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<project xmlns="http://www.plcopen.org/xml/tc6_0200" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
3
|
+
<fileHeader companyName="" productName="CODESYS" productVersion="CODESYS V3.5 SP21" creationDateTime="<NORMALIZED>" />
|
|
4
|
+
<contentHeader name="PRG_Main" modificationDateTime="<NORMALIZED>">
|
|
5
|
+
<coordinateInfo>
|
|
6
|
+
<fbd><scaling x="1" y="1" /></fbd>
|
|
7
|
+
<ld><scaling x="1" y="1" /></ld>
|
|
8
|
+
<sfc><scaling x="1" y="1" /></sfc>
|
|
9
|
+
</coordinateInfo>
|
|
10
|
+
</contentHeader>
|
|
11
|
+
<types>
|
|
12
|
+
<dataTypes />
|
|
13
|
+
<pous>
|
|
14
|
+
<pou name="PRG_Main" pouType="program">
|
|
15
|
+
<interface>
|
|
16
|
+
<localVars>
|
|
17
|
+
<variable name="fbWorker">
|
|
18
|
+
<type><derived name="FB_StateMachine" /></type>
|
|
19
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Cycle worker</xhtml></documentation>
|
|
20
|
+
</variable>
|
|
21
|
+
<variable name="bStart">
|
|
22
|
+
<type><BOOL /></type>
|
|
23
|
+
<documentation><xhtml xmlns="http://www.w3.org/1999/xhtml">Operator start button</xhtml></documentation>
|
|
24
|
+
</variable>
|
|
25
|
+
</localVars>
|
|
26
|
+
</interface>
|
|
27
|
+
<body>
|
|
28
|
+
<ST>
|
|
29
|
+
<xhtml xmlns="http://www.w3.org/1999/xhtml"><![CDATA[fbWorker(
|
|
30
|
+
bExecute := bStart,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
IF fbWorker.bDone THEN
|
|
34
|
+
bStart := FALSE;
|
|
35
|
+
END_IF]]></xhtml>
|
|
36
|
+
</ST>
|
|
37
|
+
</body>
|
|
38
|
+
</pou>
|
|
39
|
+
</pous>
|
|
40
|
+
</types>
|
|
41
|
+
<instances>
|
|
42
|
+
<configurations />
|
|
43
|
+
</instances>
|
|
44
|
+
</project>
|
package/src/artifacts.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { copyFile, cp, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { cyclicTasksSchema } from "./config";
|
|
5
|
+
|
|
6
|
+
export const buildFiles = {
|
|
7
|
+
project: "Application.project",
|
|
8
|
+
output: "runtime/Application.app",
|
|
9
|
+
checksum: "runtime/Application.crc",
|
|
10
|
+
receipt: "build.json",
|
|
11
|
+
// Normalized EDS files of a generated device tree, imported by the worker.
|
|
12
|
+
devices: "eds",
|
|
13
|
+
// With a generated device tree: the project plus the descriptions an IDE needs.
|
|
14
|
+
archive: "Application.projectarchive",
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
const receiptSchema = z.object({
|
|
18
|
+
mode: z.literal("build"),
|
|
19
|
+
application: z.string().regex(/^[A-Za-z_][A-Za-z_0-9]*$/),
|
|
20
|
+
tasks: cyclicTasksSchema.optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/** A receipt is published last, after every artifact has been checked and replaced. */
|
|
24
|
+
export const readBuild = async (directory: string) => {
|
|
25
|
+
const receipt = receiptSchema.safeParse(
|
|
26
|
+
JSON.parse(await readFile(resolve(directory, buildFiles.receipt), "utf8")),
|
|
27
|
+
);
|
|
28
|
+
if (!receipt.success) {
|
|
29
|
+
throw new Error("CODESYS did not produce a valid build receipt", {
|
|
30
|
+
cause: receipt.error,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
for (const file of [buildFiles.project, buildFiles.output, buildFiles.checksum]) {
|
|
34
|
+
const artifact = await stat(resolve(directory, file));
|
|
35
|
+
if (!artifact.isFile() || artifact.size === 0) {
|
|
36
|
+
throw new Error(`CODESYS produced an empty artifact: ${file}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
...receipt.data,
|
|
41
|
+
directory,
|
|
42
|
+
project: resolve(directory, buildFiles.project),
|
|
43
|
+
output: resolve(directory, buildFiles.output),
|
|
44
|
+
checksum: resolve(directory, buildFiles.checksum),
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Publish a verified IDE build, with the completion receipt written last. */
|
|
49
|
+
export const publishBuild = async (
|
|
50
|
+
compiled: Awaited<ReturnType<typeof readBuild>>,
|
|
51
|
+
outDir: string,
|
|
52
|
+
) => {
|
|
53
|
+
await copyFile(compiled.project, resolve(outDir, buildFiles.project));
|
|
54
|
+
await rm(resolve(outDir, "runtime"), { recursive: true, force: true });
|
|
55
|
+
await rename(resolve(compiled.directory, "runtime"), resolve(outDir, "runtime"));
|
|
56
|
+
await rm(resolve(outDir, buildFiles.archive), { force: true });
|
|
57
|
+
await cp(
|
|
58
|
+
resolve(compiled.directory, buildFiles.archive),
|
|
59
|
+
resolve(outDir, buildFiles.archive),
|
|
60
|
+
).catch((cause: unknown) => {
|
|
61
|
+
if (!(cause && typeof cause === "object" && "code" in cause && cause.code === "ENOENT"))
|
|
62
|
+
throw cause;
|
|
63
|
+
});
|
|
64
|
+
await writeFile(
|
|
65
|
+
resolve(outDir, buildFiles.receipt),
|
|
66
|
+
JSON.stringify(
|
|
67
|
+
{
|
|
68
|
+
mode: "build",
|
|
69
|
+
application: compiled.application,
|
|
70
|
+
...(compiled.tasks && { tasks: compiled.tasks }),
|
|
71
|
+
},
|
|
72
|
+
null,
|
|
73
|
+
2,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
return readBuild(outDir);
|
|
77
|
+
};
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { access, copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { buildFiles, publishBuild, readBuild } from "./artifacts";
|
|
5
|
+
import { dockerImage, type ResolvedConfig } from "./config";
|
|
6
|
+
import { buildInDocker } from "./docker-build";
|
|
7
|
+
import { createCodesysWorker, withWorkerLock } from "./host/codesys-worker";
|
|
8
|
+
import { loadSources } from "./sources";
|
|
9
|
+
import { toWindowsPath } from "./host/windows-path.ts";
|
|
10
|
+
|
|
11
|
+
export const buildProject = async (config: ResolvedConfig) => {
|
|
12
|
+
if (!config.docker) throw new Error("Builds require a Docker builder image.");
|
|
13
|
+
return buildInDocker(config, dockerImage);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** The container runner owns Wine; this operation is not a public host build. */
|
|
17
|
+
export const buildInContainer = async (config: ResolvedConfig) => {
|
|
18
|
+
return withWorkerLock(config, async () => {
|
|
19
|
+
// Invalidate the previous build before validation, so run cannot deploy stale
|
|
20
|
+
// output after a failed rebuild. Keep the last project available for inspection.
|
|
21
|
+
await rm(resolve(config.outDir, buildFiles.receipt), { force: true });
|
|
22
|
+
const sources = await loadSources(config);
|
|
23
|
+
await access(config.template);
|
|
24
|
+
const worker = createCodesysWorker(config);
|
|
25
|
+
const session = await worker.launch();
|
|
26
|
+
const directory = resolve(config.outDir, ".codesys/build");
|
|
27
|
+
await rm(directory, { recursive: true, force: true });
|
|
28
|
+
await mkdir(resolve(directory, "runtime"), { recursive: true });
|
|
29
|
+
const template = resolve(directory, "Template.project");
|
|
30
|
+
await copyFile(config.template, template);
|
|
31
|
+
// CODESYS imports each adapter's EDS from the build directory.
|
|
32
|
+
const deviceTree = config.deviceTree && {
|
|
33
|
+
...config.deviceTree,
|
|
34
|
+
devices: await Promise.all(
|
|
35
|
+
config.deviceTree.devices.map(async (device) => {
|
|
36
|
+
const eds = `${buildFiles.devices}/${device.name}.eds`;
|
|
37
|
+
await mkdir(resolve(directory, buildFiles.devices), { recursive: true });
|
|
38
|
+
await writeFile(resolve(directory, eds), device.eds);
|
|
39
|
+
// The digest keys project reuse on the description's content.
|
|
40
|
+
return { ...device, eds, sha256: createHash("sha256").update(device.eds).digest("hex") };
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
const request = {
|
|
45
|
+
libraryReferences: config.libraryReferences,
|
|
46
|
+
libraries: await Promise.all(
|
|
47
|
+
(config.libraries ?? []).map(async (library) => ({
|
|
48
|
+
...library,
|
|
49
|
+
path: await toWindowsPath(library.path, config.codesys.wine),
|
|
50
|
+
})),
|
|
51
|
+
),
|
|
52
|
+
...sources,
|
|
53
|
+
task: config.task,
|
|
54
|
+
...(config.tasks && { tasks: config.tasks }),
|
|
55
|
+
removeObjects: config.removeObjects,
|
|
56
|
+
symbols: config.symbols,
|
|
57
|
+
...(deviceTree && { deviceTree }),
|
|
58
|
+
...(config.controller && { controller: config.controller }),
|
|
59
|
+
mode: "build",
|
|
60
|
+
output: buildFiles.output,
|
|
61
|
+
project: buildFiles.project,
|
|
62
|
+
archive: buildFiles.archive,
|
|
63
|
+
receipt: buildFiles.receipt,
|
|
64
|
+
template: "Template.project",
|
|
65
|
+
templateHash: createHash("sha256")
|
|
66
|
+
.update(await readFile(template))
|
|
67
|
+
.digest("hex"),
|
|
68
|
+
};
|
|
69
|
+
await writeFile(resolve(directory, "request.json"), JSON.stringify(request, null, 2));
|
|
70
|
+
console.log(`CODESYS build: ${config.outDir}`);
|
|
71
|
+
try {
|
|
72
|
+
await worker.request(session, { action: "build" }, config.timeouts.build);
|
|
73
|
+
const compiled = await readBuild(directory);
|
|
74
|
+
if (config.tasks && JSON.stringify(compiled.tasks) !== JSON.stringify(config.tasks)) {
|
|
75
|
+
throw new Error("CODESYS build receipt does not match the requested task plan");
|
|
76
|
+
}
|
|
77
|
+
// Builds and runs share the lock. Removing the receipt above keeps a failed
|
|
78
|
+
// publication from looking complete even if only some files were replaced.
|
|
79
|
+
return await publishBuild(compiled, config.outDir);
|
|
80
|
+
} catch (cause) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`CODESYS build failed. Inspect ${directory}/progress.log, error.txt and ${session.directory}/codesys.log`,
|
|
83
|
+
{ cause },
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
};
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { deviceTree } from "@tsplc/typegen";
|
|
4
|
+
import { loadHardware, resolveIo } from "@tsplc/typegen/hardware";
|
|
5
|
+
import { controllerFor, loadTsplcConfig, rawBuildConfig } from "@tsplc/config";
|
|
6
|
+
import { buildProject } from "./build.ts";
|
|
7
|
+
import { resolveConfig } from "./config.ts";
|
|
8
|
+
import { inspectController, updateController } from "./controller-online.ts";
|
|
9
|
+
import { configureDocker, configureRuntime } from "./docker-build.ts";
|
|
10
|
+
import { installLibraries } from "./libraries.ts";
|
|
11
|
+
import { runProject } from "./run.ts";
|
|
12
|
+
import { loadSources } from "./sources.ts";
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
const { values, positionals } = parseArgs({
|
|
16
|
+
allowPositionals: true,
|
|
17
|
+
options: {
|
|
18
|
+
config: { type: "string" },
|
|
19
|
+
help: { type: "boolean", short: "h" },
|
|
20
|
+
read: { type: "string", multiple: true },
|
|
21
|
+
"initialize-user": { type: "boolean" },
|
|
22
|
+
"frozen-lockfile": { type: "boolean" },
|
|
23
|
+
runtime: { type: "boolean" },
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const command = positionals[0];
|
|
27
|
+
const commands = new Set([
|
|
28
|
+
"configure",
|
|
29
|
+
"install",
|
|
30
|
+
"check",
|
|
31
|
+
"build",
|
|
32
|
+
"run",
|
|
33
|
+
"download",
|
|
34
|
+
"online-change",
|
|
35
|
+
"inspect",
|
|
36
|
+
]);
|
|
37
|
+
if (positionals.length > 1) throw new Error("Specify one command.");
|
|
38
|
+
if (!command || values.help) {
|
|
39
|
+
console.log(
|
|
40
|
+
"tsplc-build configure|install|check|build|run|download|online-change|inspect [--config tsplc.config.ts] [--read symbol] [--runtime]",
|
|
41
|
+
);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!commands.has(command)) throw new Error(`Unknown command ${command}.`);
|
|
45
|
+
if (values.runtime && command !== "configure")
|
|
46
|
+
throw new Error("--runtime applies only to configure.");
|
|
47
|
+
if (command === "configure") {
|
|
48
|
+
await (values.runtime ? configureRuntime() : configureDocker());
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const { config, configFile, root } = await loadTsplcConfig(values.config);
|
|
52
|
+
if (command === "install") {
|
|
53
|
+
const libraries = await installLibraries(config.dependencies, root, {
|
|
54
|
+
frozen: values["frozen-lockfile"],
|
|
55
|
+
});
|
|
56
|
+
console.log(`Installed ${libraries.length} CODESYS libraries.`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!config.build) throw new Error("Configure build options in tsplc.config.ts.");
|
|
60
|
+
if (command === "inspect") {
|
|
61
|
+
// Inspection only needs connection/baseline settings; it remains usable
|
|
62
|
+
// without sources, EDS descriptors or installed library payloads.
|
|
63
|
+
console.log(
|
|
64
|
+
JSON.stringify(
|
|
65
|
+
await inspectController(
|
|
66
|
+
resolveConfig(
|
|
67
|
+
{ ...config.build, controller: controllerFor(config, command) },
|
|
68
|
+
configFile,
|
|
69
|
+
),
|
|
70
|
+
values.read,
|
|
71
|
+
),
|
|
72
|
+
null,
|
|
73
|
+
2,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (config.compiler)
|
|
79
|
+
throw new Error("Use tsplc for TypeScript projects. tsplc-build accepts raw Structured Text.");
|
|
80
|
+
const build = rawBuildConfig(config);
|
|
81
|
+
const hardware = await loadHardware(config, root);
|
|
82
|
+
const io = resolveIo(config, hardware);
|
|
83
|
+
if (command === "check") {
|
|
84
|
+
const sources = await loadSources(resolveConfig({ ...build, io }, configFile));
|
|
85
|
+
console.log(`Validated ${sources.objects.length} Structured Text objects.`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const libraries = await installLibraries(config.dependencies, root, {
|
|
89
|
+
frozen: values["frozen-lockfile"],
|
|
90
|
+
});
|
|
91
|
+
const settings = resolveConfig(
|
|
92
|
+
{
|
|
93
|
+
...build,
|
|
94
|
+
libraries,
|
|
95
|
+
io,
|
|
96
|
+
deviceTree: await deviceTree(config, hardware),
|
|
97
|
+
controller: controllerFor(config, command),
|
|
98
|
+
},
|
|
99
|
+
configFile,
|
|
100
|
+
);
|
|
101
|
+
if (command === "build") await buildProject(settings);
|
|
102
|
+
else if (command === "run") {
|
|
103
|
+
await buildProject(settings);
|
|
104
|
+
await runProject(settings, config.name);
|
|
105
|
+
} else if (command === "download" || command === "online-change")
|
|
106
|
+
await updateController(settings, command, {
|
|
107
|
+
initializeUser: values["initialize-user"],
|
|
108
|
+
read: values.read,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await main();
|
|
113
|
+
} catch (cause) {
|
|
114
|
+
console.error(cause instanceof Error ? cause.message : String(cause));
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
package/src/config.ts
ADDED