@dusted/anqst 0.1.0 → 0.1.2
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/README.md +135 -129
- package/dist/src/app.js +287 -113
- package/dist/src/bin/anqst.js +0 -0
- package/dist/src/debug-dump.js +40 -0
- package/dist/src/emit.js +488 -64
- package/dist/src/layout.js +70 -0
- package/dist/src/parser.js +16 -1
- package/dist/src/program.js +120 -0
- package/dist/src/project.js +230 -77
- package/dist/src/typegraph.js +172 -0
- package/dist/src/verify.js +9 -1
- package/index.d.ts +1 -0
- package/package.json +13 -5
- package/spec/AnQst-Spec-DSL.d.ts +2 -2
package/dist/src/emit.js
CHANGED
|
@@ -5,12 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateOutputs = generateOutputs;
|
|
7
7
|
exports.writeGeneratedOutputs = writeGeneratedOutputs;
|
|
8
|
-
exports.installTypeScriptOutputs = installTypeScriptOutputs;
|
|
9
8
|
exports.installEmbeddedWebBundle = installEmbeddedWebBundle;
|
|
10
9
|
exports.installQtIntegrationCMake = installQtIntegrationCMake;
|
|
10
|
+
exports.installQtDesignerPluginCMake = installQtDesignerPluginCMake;
|
|
11
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const typescript_1 = __importDefault(require("typescript"));
|
|
14
|
+
const pngjs_1 = require("pngjs");
|
|
15
|
+
const layout_1 = require("./layout");
|
|
14
16
|
function stripAnQstType(typeText) {
|
|
15
17
|
return typeText
|
|
16
18
|
.replace(/\bAnQst\.Type\.stringArray\b/g, "string[]")
|
|
@@ -158,6 +160,87 @@ function cppToVariantExpression(cppType, expr) {
|
|
|
158
160
|
}
|
|
159
161
|
return `QVariant::fromValue(${expr})`;
|
|
160
162
|
}
|
|
163
|
+
function splitTopLevelTemplateArgs(text) {
|
|
164
|
+
const args = [];
|
|
165
|
+
let start = 0;
|
|
166
|
+
let depth = 0;
|
|
167
|
+
for (let i = 0; i < text.length; i++) {
|
|
168
|
+
const ch = text[i];
|
|
169
|
+
if (ch === "<") {
|
|
170
|
+
depth++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (ch === ">") {
|
|
174
|
+
depth--;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (ch === "," && depth === 0) {
|
|
178
|
+
args.push(text.slice(start, i).trim());
|
|
179
|
+
start = i + 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const tail = text.slice(start).trim();
|
|
183
|
+
if (tail.length > 0) {
|
|
184
|
+
args.push(tail);
|
|
185
|
+
}
|
|
186
|
+
return args;
|
|
187
|
+
}
|
|
188
|
+
function templateTypeArgs(cppType, containerName) {
|
|
189
|
+
const trimmed = cppType.trim();
|
|
190
|
+
const prefix = `${containerName}<`;
|
|
191
|
+
if (!trimmed.startsWith(prefix) || !trimmed.endsWith(">")) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const inner = trimmed.slice(prefix.length, -1).trim();
|
|
195
|
+
if (inner.length === 0)
|
|
196
|
+
return [];
|
|
197
|
+
return splitTopLevelTemplateArgs(inner);
|
|
198
|
+
}
|
|
199
|
+
function isNumericCppType(cppType) {
|
|
200
|
+
return [
|
|
201
|
+
"double",
|
|
202
|
+
"qint64",
|
|
203
|
+
"quint64",
|
|
204
|
+
"qint32",
|
|
205
|
+
"quint32",
|
|
206
|
+
"qint16",
|
|
207
|
+
"quint16",
|
|
208
|
+
"qint8",
|
|
209
|
+
"quint8",
|
|
210
|
+
"int8_t",
|
|
211
|
+
"uint8_t",
|
|
212
|
+
"int16_t",
|
|
213
|
+
"uint16_t",
|
|
214
|
+
"int32_t",
|
|
215
|
+
"uint32_t"
|
|
216
|
+
].includes(cppType);
|
|
217
|
+
}
|
|
218
|
+
function designerPlaceholderCppExpression(cppType, memberName) {
|
|
219
|
+
const escapedMember = escapeCppStringLiteral(memberName);
|
|
220
|
+
const stringLiteral = `QStringLiteral("${escapedMember} value")`;
|
|
221
|
+
if (cppType === "QString")
|
|
222
|
+
return stringLiteral;
|
|
223
|
+
if (cppType === "bool")
|
|
224
|
+
return "true";
|
|
225
|
+
if (isNumericCppType(cppType))
|
|
226
|
+
return `static_cast<${cppType}>(1)`;
|
|
227
|
+
if (cppType === "QStringList")
|
|
228
|
+
return `QStringList{${stringLiteral}}`;
|
|
229
|
+
if (cppType === "QVariantMap") {
|
|
230
|
+
return `QVariantMap{{QStringLiteral("${escapedMember}"), QVariant(${stringLiteral})}}`;
|
|
231
|
+
}
|
|
232
|
+
const optionalArgs = templateTypeArgs(cppType, "std::optional");
|
|
233
|
+
if (optionalArgs && optionalArgs.length === 1) {
|
|
234
|
+
const inner = optionalArgs[0];
|
|
235
|
+
return `std::optional<${inner}>{${designerPlaceholderCppExpression(inner, memberName)}}`;
|
|
236
|
+
}
|
|
237
|
+
const listArgs = templateTypeArgs(cppType, "QList");
|
|
238
|
+
if (listArgs && listArgs.length === 1) {
|
|
239
|
+
const inner = listArgs[0];
|
|
240
|
+
return `QList<${inner}>{${designerPlaceholderCppExpression(inner, memberName)}}`;
|
|
241
|
+
}
|
|
242
|
+
return `${cppType}{}`;
|
|
243
|
+
}
|
|
161
244
|
function collectStructDecls(spec) {
|
|
162
245
|
const out = new Map();
|
|
163
246
|
for (const d of spec.namespaceTypeDecls)
|
|
@@ -266,7 +349,7 @@ function normalizeImportPathForGenerated(specFilePath, generatedFileRelPath, mod
|
|
|
266
349
|
return moduleSpecifier;
|
|
267
350
|
}
|
|
268
351
|
const specDir = node_path_1.default.dirname(specFilePath);
|
|
269
|
-
const generatedAbs = node_path_1.default.resolve(node_path_1.default.dirname(specFilePath), "
|
|
352
|
+
const generatedAbs = node_path_1.default.resolve(node_path_1.default.dirname(specFilePath), "generated", generatedFileRelPath);
|
|
270
353
|
const generatedDir = node_path_1.default.dirname(generatedAbs);
|
|
271
354
|
const resolvedModulePath = node_path_1.default.resolve(specDir, moduleSpecifier);
|
|
272
355
|
const relative = node_path_1.default.relative(generatedDir, resolvedModulePath);
|
|
@@ -746,7 +829,12 @@ function renderCppStub(spec, cppTypes) {
|
|
|
746
829
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
747
830
|
const pascal = pascalCase(member.name);
|
|
748
831
|
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
749
|
-
lines.push(` if (!m_${member.name}Handler)
|
|
832
|
+
lines.push(` if (!m_${member.name}Handler) {`);
|
|
833
|
+
lines.push(` if (property("anqstDesignerContext").toBool()) {`);
|
|
834
|
+
lines.push(` return ${cppToVariantExpression(cppType, designerPlaceholderCppExpression(cppType, member.name))};`);
|
|
835
|
+
lines.push(` }`);
|
|
836
|
+
lines.push(` return QVariant();`);
|
|
837
|
+
lines.push(` }`);
|
|
750
838
|
for (let i = 0; i < member.parameters.length; i++) {
|
|
751
839
|
const p = member.parameters[i];
|
|
752
840
|
const pType = cppTypes.mapTypeText(p.typeText, [service.name, member.name, p.name]);
|
|
@@ -913,6 +1001,57 @@ function escapeXml(text) {
|
|
|
913
1001
|
function normalizeSlashes(value) {
|
|
914
1002
|
return value.split(node_path_1.default.sep).join("/");
|
|
915
1003
|
}
|
|
1004
|
+
function resolveActiveBuildStamp() {
|
|
1005
|
+
const fromEnv = process.env.ANQST_BUILD_STAMP?.trim();
|
|
1006
|
+
if (fromEnv && fromEnv.length > 0) {
|
|
1007
|
+
return fromEnv;
|
|
1008
|
+
}
|
|
1009
|
+
const activePath = node_path_1.default.resolve(__dirname, "..", "..", ".anqstgen-version-active.json");
|
|
1010
|
+
if (!node_fs_1.default.existsSync(activePath)) {
|
|
1011
|
+
return "";
|
|
1012
|
+
}
|
|
1013
|
+
try {
|
|
1014
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(activePath, "utf8"));
|
|
1015
|
+
if (typeof parsed.active === "string" && parsed.active.trim().length > 0) {
|
|
1016
|
+
return parsed.active.trim();
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
catch {
|
|
1020
|
+
return "";
|
|
1021
|
+
}
|
|
1022
|
+
return "";
|
|
1023
|
+
}
|
|
1024
|
+
function withBuildStamp(relativePath, content) {
|
|
1025
|
+
const stamp = resolveActiveBuildStamp();
|
|
1026
|
+
if (!stamp) {
|
|
1027
|
+
return content;
|
|
1028
|
+
}
|
|
1029
|
+
const marker = `Built by AnQst ${stamp}`;
|
|
1030
|
+
const rel = normalizeSlashes(relativePath);
|
|
1031
|
+
const lower = rel.toLowerCase();
|
|
1032
|
+
if (lower.endsWith(".json")) {
|
|
1033
|
+
try {
|
|
1034
|
+
const parsed = JSON.parse(content);
|
|
1035
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1036
|
+
const next = { "//": marker, ...parsed };
|
|
1037
|
+
return `${JSON.stringify(next, null, 2)}\n`;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
catch {
|
|
1041
|
+
// If JSON parsing fails, fall through to plain comment prefix.
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (lower.endsWith(".qrc") || lower.endsWith(".xml") || lower.endsWith(".html")) {
|
|
1045
|
+
return `<!-- ${marker} -->\n${content}`;
|
|
1046
|
+
}
|
|
1047
|
+
if (lower.endsWith(".cmake")) {
|
|
1048
|
+
return `# ${marker}\n${content}`;
|
|
1049
|
+
}
|
|
1050
|
+
if (lower.endsWith(".h") || lower.endsWith(".cpp") || lower.endsWith(".ts") || lower.endsWith(".js") || lower.endsWith(".d.ts")) {
|
|
1051
|
+
return `// ${marker}\n${content}`;
|
|
1052
|
+
}
|
|
1053
|
+
return `# ${marker}\n${content}`;
|
|
1054
|
+
}
|
|
916
1055
|
function renderEmbeddedQrc(widgetName, embeddedWebFiles) {
|
|
917
1056
|
const files = [...embeddedWebFiles].sort();
|
|
918
1057
|
const lines = [];
|
|
@@ -1087,7 +1226,7 @@ export declare class ${serviceName} {
|
|
|
1087
1226
|
}
|
|
1088
1227
|
function renderTsServices(spec) {
|
|
1089
1228
|
const serviceClasses = spec.services.map((s) => renderTsService(spec, s.name)).join("\n");
|
|
1090
|
-
const externalTypeImports = renderRequiredTypeImports(spec,
|
|
1229
|
+
const externalTypeImports = renderRequiredTypeImports(spec, `frontend/${(0, layout_1.generatedFrontendDirName)(spec.widgetName)}/services.ts`).trim();
|
|
1091
1230
|
const localTypeImports = renderLocalTypeImports(spec).trim();
|
|
1092
1231
|
const typeImports = [externalTypeImports, localTypeImports].filter((s) => s.length > 0).join("\n");
|
|
1093
1232
|
const typeImportsBlock = typeImports.length > 0 ? `${typeImports}\n\n` : "";
|
|
@@ -1231,6 +1370,11 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
|
|
|
1231
1370
|
}
|
|
1232
1371
|
if (type === "hostError") {
|
|
1233
1372
|
console.error("AnQst host error:", message["payload"]);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
if (type === "widgetReattached") {
|
|
1376
|
+
document.body.textContent = "Widget Reattached";
|
|
1377
|
+
this.socket.close();
|
|
1234
1378
|
}
|
|
1235
1379
|
});
|
|
1236
1380
|
}
|
|
@@ -1240,12 +1384,22 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
|
|
|
1240
1384
|
if (!configResponse.ok) {
|
|
1241
1385
|
throw new Error("AnQst host bootstrap missing: unable to read /anqst-dev-config.json");
|
|
1242
1386
|
}
|
|
1243
|
-
const config = (await configResponse.json()) as { wsUrl?: string };
|
|
1244
|
-
|
|
1245
|
-
|
|
1387
|
+
const config = (await configResponse.json()) as { wsUrl?: string; wsPath?: string };
|
|
1388
|
+
let wsUrl = config.wsUrl;
|
|
1389
|
+
if (!wsUrl && config.wsPath) {
|
|
1390
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
1391
|
+
wsUrl = protocol + "//" + window.location.host + config.wsPath;
|
|
1392
|
+
}
|
|
1393
|
+
if (!wsUrl) {
|
|
1394
|
+
throw new Error("AnQst host bootstrap missing: wsUrl/wsPath is unavailable.");
|
|
1395
|
+
}
|
|
1396
|
+
if (wsUrl.startsWith("http://")) {
|
|
1397
|
+
wsUrl = "ws://" + wsUrl.slice("http://".length);
|
|
1398
|
+
} else if (wsUrl.startsWith("https://")) {
|
|
1399
|
+
wsUrl = "wss://" + wsUrl.slice("https://".length);
|
|
1246
1400
|
}
|
|
1247
1401
|
return await new Promise<WebSocketBridgeAdapter>((resolve, reject) => {
|
|
1248
|
-
const socket = new WebSocket(
|
|
1402
|
+
const socket = new WebSocket(wsUrl!);
|
|
1249
1403
|
socket.addEventListener("open", () => resolve(new WebSocketBridgeAdapter(socket)));
|
|
1250
1404
|
socket.addEventListener("error", () => reject(new Error("Failed to connect to AnQst WebSocket bridge.")));
|
|
1251
1405
|
});
|
|
@@ -1397,13 +1551,13 @@ ${serviceClasses}
|
|
|
1397
1551
|
`;
|
|
1398
1552
|
}
|
|
1399
1553
|
function renderTsTypes(spec) {
|
|
1400
|
-
const typeImports = renderRequiredTypeImports(spec,
|
|
1554
|
+
const typeImports = renderRequiredTypeImports(spec, `frontend/${(0, layout_1.generatedFrontendDirName)(spec.widgetName)}/types.ts`).trim();
|
|
1401
1555
|
const typeDecls = renderTypeDeclarations(spec, true).trim();
|
|
1402
1556
|
const sections = [typeImports, typeDecls].filter((s) => s.length > 0);
|
|
1403
1557
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
1404
1558
|
}
|
|
1405
1559
|
function renderTypeServicesDts(spec) {
|
|
1406
|
-
const externalTypeImports = renderRequiredTypeImports(spec,
|
|
1560
|
+
const externalTypeImports = renderRequiredTypeImports(spec, `frontend/${(0, layout_1.generatedFrontendDirName)(spec.widgetName)}/types/services.d.ts`).trim();
|
|
1407
1561
|
const localTypeImports = renderLocalTypeImports(spec).trim();
|
|
1408
1562
|
const serviceDecls = spec.services
|
|
1409
1563
|
.map((s) => renderTsServiceDts(spec, s.name))
|
|
@@ -1412,7 +1566,7 @@ function renderTypeServicesDts(spec) {
|
|
|
1412
1566
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
1413
1567
|
}
|
|
1414
1568
|
function renderTypeTypesDts(spec) {
|
|
1415
|
-
const typeImports = renderRequiredTypeImports(spec,
|
|
1569
|
+
const typeImports = renderRequiredTypeImports(spec, `frontend/${(0, layout_1.generatedFrontendDirName)(spec.widgetName)}/types/types.d.ts`).trim();
|
|
1416
1570
|
const typeDecls = renderTypeDeclarations(spec, true).trim();
|
|
1417
1571
|
const sections = [typeImports, typeDecls].filter((s) => s.length > 0);
|
|
1418
1572
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
@@ -1478,13 +1632,13 @@ function nodeCap(value) {
|
|
|
1478
1632
|
return value.length === 0 ? value : `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
1479
1633
|
}
|
|
1480
1634
|
function renderNodeExpressWsTypes(spec) {
|
|
1481
|
-
const typeImports = renderRequiredTypeImports(spec,
|
|
1635
|
+
const typeImports = renderRequiredTypeImports(spec, `backend/node/express/${generatedNodeExpressWsDirName(spec.widgetName)}/types/index.d.ts`).trim();
|
|
1482
1636
|
const typeDecls = renderTypeDeclarations(spec, true).trim();
|
|
1483
1637
|
const sections = [typeImports, typeDecls].filter((s) => s.length > 0);
|
|
1484
1638
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
1485
1639
|
}
|
|
1486
1640
|
function renderNodeExpressWsIndex(spec) {
|
|
1487
|
-
const typeImports = renderRequiredTypeImports(spec,
|
|
1641
|
+
const typeImports = renderRequiredTypeImports(spec, `backend/node/express/${generatedNodeExpressWsDirName(spec.widgetName)}/index.ts`);
|
|
1488
1642
|
const typeDecls = renderTypeDeclarations(spec, true);
|
|
1489
1643
|
const handlerBridgeTypeName = `${spec.widgetName}HandlerBridge`;
|
|
1490
1644
|
const sessionBridgeTypeName = `${spec.widgetName}SessionBridge`;
|
|
@@ -2183,26 +2337,27 @@ function renderTypeRootIndexDts(spec) {
|
|
|
2183
2337
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
2184
2338
|
}
|
|
2185
2339
|
function generatedCppLibraryDirName(widgetName) {
|
|
2186
|
-
return
|
|
2340
|
+
return (0, layout_1.generatedQtWidgetDirName)(widgetName);
|
|
2187
2341
|
}
|
|
2188
2342
|
function generatedNodeExpressWsDirName(widgetName) {
|
|
2189
|
-
return
|
|
2343
|
+
return (0, layout_1.generatedNodeExpressDirName)(widgetName);
|
|
2190
2344
|
}
|
|
2191
2345
|
function generateOutputs(spec, options = { emitQWidget: true, emitAngularService: true, emitNodeExpressWs: false }) {
|
|
2192
|
-
const
|
|
2193
|
-
const
|
|
2346
|
+
const frontendDir = `frontend/${(0, layout_1.generatedFrontendDirName)(spec.widgetName)}`;
|
|
2347
|
+
const cppDir = `backend/cpp/qt/${generatedCppLibraryDirName(spec.widgetName)}`;
|
|
2348
|
+
const nodeDir = `backend/node/express/${generatedNodeExpressWsDirName(spec.widgetName)}`;
|
|
2194
2349
|
const outputs = {};
|
|
2195
2350
|
if (options.emitAngularService) {
|
|
2196
|
-
outputs[
|
|
2197
|
-
outputs[
|
|
2198
|
-
outputs[
|
|
2199
|
-
outputs[
|
|
2200
|
-
outputs[
|
|
2201
|
-
outputs[
|
|
2202
|
-
outputs[
|
|
2203
|
-
outputs[
|
|
2204
|
-
outputs[
|
|
2205
|
-
outputs[
|
|
2351
|
+
outputs[`${frontendDir}/package.json`] = renderNpmPackage(spec);
|
|
2352
|
+
outputs[`${frontendDir}/index.ts`] = renderTsIndex();
|
|
2353
|
+
outputs[`${frontendDir}/services.ts`] = renderTsServices(spec);
|
|
2354
|
+
outputs[`${frontendDir}/types.ts`] = renderTsTypes(spec);
|
|
2355
|
+
outputs[`${frontendDir}/index.js`] = renderJsIndex();
|
|
2356
|
+
outputs[`${frontendDir}/services.js`] = renderJsServices();
|
|
2357
|
+
outputs[`${frontendDir}/types.js`] = renderJsTypes();
|
|
2358
|
+
outputs[`${frontendDir}/types/index.d.ts`] = renderTypeRootIndexDts(spec);
|
|
2359
|
+
outputs[`${frontendDir}/types/services.d.ts`] = renderTypeServicesDts(spec);
|
|
2360
|
+
outputs[`${frontendDir}/types/types.d.ts`] = renderTypeTypesDts(spec);
|
|
2206
2361
|
}
|
|
2207
2362
|
if (options.emitQWidget) {
|
|
2208
2363
|
const cppTypes = buildCppTypeContext(spec);
|
|
@@ -2220,36 +2375,11 @@ function generateOutputs(spec, options = { emitQWidget: true, emitAngularService
|
|
|
2220
2375
|
return outputs;
|
|
2221
2376
|
}
|
|
2222
2377
|
function writeGeneratedOutputs(cwd, outputs) {
|
|
2223
|
-
const outputRoot =
|
|
2378
|
+
const outputRoot = (0, layout_1.anqstGeneratedRootDir)(cwd);
|
|
2224
2379
|
for (const [relPath, content] of Object.entries(outputs)) {
|
|
2225
2380
|
const filePath = node_path_1.default.join(outputRoot, relPath);
|
|
2226
2381
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(filePath), { recursive: true });
|
|
2227
|
-
node_fs_1.default.writeFileSync(filePath, content, "utf8");
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
function installTypeScriptOutputs(cwd) {
|
|
2231
|
-
const sourceDir = node_path_1.default.join(cwd, "generated_output", "npmpackage");
|
|
2232
|
-
const targetDir = node_path_1.default.join(cwd, "src", "anqst-generated");
|
|
2233
|
-
if (!node_fs_1.default.existsSync(sourceDir))
|
|
2234
|
-
return;
|
|
2235
|
-
node_fs_1.default.rmSync(targetDir, { recursive: true, force: true });
|
|
2236
|
-
node_fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
2237
|
-
const queue = [sourceDir];
|
|
2238
|
-
while (queue.length > 0) {
|
|
2239
|
-
const current = queue.shift();
|
|
2240
|
-
for (const entry of node_fs_1.default.readdirSync(current, { withFileTypes: true })) {
|
|
2241
|
-
const abs = node_path_1.default.join(current, entry.name);
|
|
2242
|
-
const rel = node_path_1.default.relative(sourceDir, abs);
|
|
2243
|
-
const dst = node_path_1.default.join(targetDir, rel);
|
|
2244
|
-
if (entry.isDirectory()) {
|
|
2245
|
-
node_fs_1.default.mkdirSync(dst, { recursive: true });
|
|
2246
|
-
queue.push(abs);
|
|
2247
|
-
}
|
|
2248
|
-
else if (entry.isFile()) {
|
|
2249
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(dst), { recursive: true });
|
|
2250
|
-
node_fs_1.default.copyFileSync(abs, dst);
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2382
|
+
node_fs_1.default.writeFileSync(filePath, withBuildStamp(relPath, content), "utf8");
|
|
2253
2383
|
}
|
|
2254
2384
|
}
|
|
2255
2385
|
function listFilesRecursively(rootDir) {
|
|
@@ -2385,7 +2515,7 @@ function installEmbeddedWebBundle(cwd, widgetName) {
|
|
|
2385
2515
|
if (!node_fs_1.default.existsSync(node_path_1.default.join(distWebRoot, "index.html"))) {
|
|
2386
2516
|
return false;
|
|
2387
2517
|
}
|
|
2388
|
-
const cppLibraryRoot =
|
|
2518
|
+
const cppLibraryRoot = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, widgetName).cppQtWidgetRoot;
|
|
2389
2519
|
const cppLibraryWebRoot = node_path_1.default.join(cppLibraryRoot, "webapp");
|
|
2390
2520
|
node_fs_1.default.rmSync(cppLibraryWebRoot, { recursive: true, force: true });
|
|
2391
2521
|
node_fs_1.default.mkdirSync(cppLibraryWebRoot, { recursive: true });
|
|
@@ -2393,7 +2523,7 @@ function installEmbeddedWebBundle(cwd, widgetName) {
|
|
|
2393
2523
|
normalizeEmbeddedIndexHtml(node_path_1.default.join(cppLibraryWebRoot, "index.html"), cppLibraryWebRoot);
|
|
2394
2524
|
const embeddedFiles = listFilesRecursively(cppLibraryWebRoot);
|
|
2395
2525
|
const qrcPath = node_path_1.default.join(cppLibraryRoot, `${widgetName}.qrc`);
|
|
2396
|
-
node_fs_1.default.writeFileSync(qrcPath, renderEmbeddedQrc(widgetName, embeddedFiles), "utf8");
|
|
2526
|
+
node_fs_1.default.writeFileSync(qrcPath, withBuildStamp(`${widgetName}.qrc`, renderEmbeddedQrc(widgetName, embeddedFiles)), "utf8");
|
|
2397
2527
|
return true;
|
|
2398
2528
|
}
|
|
2399
2529
|
function normalizeEmbeddedIndexHtml(indexPath, webRoot) {
|
|
@@ -2417,15 +2547,15 @@ function normalizeEmbeddedIndexHtml(indexPath, webRoot) {
|
|
|
2417
2547
|
node_fs_1.default.writeFileSync(indexPath, html, "utf8");
|
|
2418
2548
|
}
|
|
2419
2549
|
function renderQtIntegrationCMake(widgetName) {
|
|
2420
|
-
const generatedRootVar = "
|
|
2550
|
+
const generatedRootVar = "ANQST_GENERATED_WIDGET_DIR";
|
|
2421
2551
|
const generatedIncludeVar = "ANQST_GENERATED_INCLUDE_DIR";
|
|
2422
|
-
const
|
|
2552
|
+
const projectRootVar = "ANQST_PROJECT_ROOT";
|
|
2423
2553
|
const widgetTarget = `${widgetName}Widget`;
|
|
2424
2554
|
const autogenTarget = `${widgetTarget}_anqst_codegen`;
|
|
2425
2555
|
return `cmake_minimum_required(VERSION 3.21)
|
|
2426
2556
|
|
|
2427
|
-
set(${
|
|
2428
|
-
set(${generatedRootVar} "\${
|
|
2557
|
+
set(${projectRootVar} "\${CMAKE_CURRENT_LIST_DIR}/../../../../..")
|
|
2558
|
+
set(${generatedRootVar} "\${CMAKE_CURRENT_LIST_DIR}/../qt/${generatedCppLibraryDirName(widgetName)}")
|
|
2429
2559
|
set(${generatedIncludeVar} "\${${generatedRootVar}}/include")
|
|
2430
2560
|
|
|
2431
2561
|
if(TARGET ${widgetTarget})
|
|
@@ -2433,7 +2563,7 @@ if(TARGET ${widgetTarget})
|
|
|
2433
2563
|
endif()
|
|
2434
2564
|
|
|
2435
2565
|
if(NOT TARGET anqstwebhostbase)
|
|
2436
|
-
message(FATAL_ERROR "Target 'anqstwebhostbase' must exist before including
|
|
2566
|
+
message(FATAL_ERROR "Target 'anqstwebhostbase' must exist before including generated AnQst CMake for ${widgetName}.")
|
|
2437
2567
|
endif()
|
|
2438
2568
|
|
|
2439
2569
|
find_package(Qt5 REQUIRED COMPONENTS Core Widgets)
|
|
@@ -2453,7 +2583,7 @@ add_custom_command(
|
|
|
2453
2583
|
"\${${generatedRootVar}}/webapp/index.html"
|
|
2454
2584
|
COMMAND "\${ANQST_NPM_EXECUTABLE}" install
|
|
2455
2585
|
COMMAND "\${ANQST_NPM_EXECUTABLE}" run anqst:build
|
|
2456
|
-
WORKING_DIRECTORY "\${${
|
|
2586
|
+
WORKING_DIRECTORY "\${${projectRootVar}}"
|
|
2457
2587
|
COMMENT "Generating AnQst widget library (${widgetTarget}) from Angular project"
|
|
2458
2588
|
VERBATIM
|
|
2459
2589
|
)
|
|
@@ -2494,7 +2624,301 @@ target_link_libraries(${widgetTarget}
|
|
|
2494
2624
|
`;
|
|
2495
2625
|
}
|
|
2496
2626
|
function installQtIntegrationCMake(cwd, widgetName) {
|
|
2497
|
-
const integrationDir =
|
|
2627
|
+
const integrationDir = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, widgetName).cppCmakeRoot;
|
|
2498
2628
|
node_fs_1.default.mkdirSync(integrationDir, { recursive: true });
|
|
2499
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(integrationDir, "CMakeLists.txt"), renderQtIntegrationCMake(widgetName), "utf8");
|
|
2629
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(integrationDir, "CMakeLists.txt"), withBuildStamp("backend/cpp/cmake/CMakeLists.txt", renderQtIntegrationCMake(widgetName)), "utf8");
|
|
2630
|
+
}
|
|
2631
|
+
function normalizeIcoSize(dim) {
|
|
2632
|
+
return dim === 0 ? 256 : dim;
|
|
2633
|
+
}
|
|
2634
|
+
function escapeCppStringLiteral(value) {
|
|
2635
|
+
return value
|
|
2636
|
+
.replace(/\\/g, "\\\\")
|
|
2637
|
+
.replace(/"/g, '\\"')
|
|
2638
|
+
.replace(/\r/g, "\\r")
|
|
2639
|
+
.replace(/\n/g, "\\n");
|
|
2640
|
+
}
|
|
2641
|
+
function readDistFavicon(cwd) {
|
|
2642
|
+
const distRoot = node_path_1.default.join(cwd, "dist");
|
|
2643
|
+
if (!node_fs_1.default.existsSync(distRoot) || !node_fs_1.default.statSync(distRoot).isDirectory()) {
|
|
2644
|
+
return null;
|
|
2645
|
+
}
|
|
2646
|
+
const stack = [distRoot];
|
|
2647
|
+
while (stack.length > 0) {
|
|
2648
|
+
const current = stack.shift();
|
|
2649
|
+
const entries = node_fs_1.default.readdirSync(current, { withFileTypes: true })
|
|
2650
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
2651
|
+
for (const entry of entries) {
|
|
2652
|
+
const fullPath = node_path_1.default.join(current, entry.name);
|
|
2653
|
+
if (entry.isDirectory()) {
|
|
2654
|
+
stack.push(fullPath);
|
|
2655
|
+
continue;
|
|
2656
|
+
}
|
|
2657
|
+
if (entry.isFile() && entry.name.toLowerCase() === "favicon.ico") {
|
|
2658
|
+
return node_fs_1.default.readFileSync(fullPath);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
return null;
|
|
2663
|
+
}
|
|
2664
|
+
function resolveFaviconIcoBuffer(cwd) {
|
|
2665
|
+
const distFavicon = readDistFavicon(cwd);
|
|
2666
|
+
if (distFavicon !== null) {
|
|
2667
|
+
return distFavicon;
|
|
2668
|
+
}
|
|
2669
|
+
const fallbackFiles = [
|
|
2670
|
+
node_path_1.default.join(cwd, "res", "favicon.ico"),
|
|
2671
|
+
node_path_1.default.join(cwd, "src", "favicon.ico"),
|
|
2672
|
+
node_path_1.default.join(cwd, "favicon.ico")
|
|
2673
|
+
];
|
|
2674
|
+
for (const filePath of fallbackFiles) {
|
|
2675
|
+
if (node_fs_1.default.existsSync(filePath) && node_fs_1.default.statSync(filePath).isFile()) {
|
|
2676
|
+
return node_fs_1.default.readFileSync(filePath);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
return null;
|
|
2680
|
+
}
|
|
2681
|
+
function decodeIcoBmpToPng(imageData) {
|
|
2682
|
+
if (imageData.length < 40) {
|
|
2683
|
+
throw new Error("ICO BMP frame too small.");
|
|
2684
|
+
}
|
|
2685
|
+
const headerSize = imageData.readUInt32LE(0);
|
|
2686
|
+
if (headerSize < 40 || imageData.length < headerSize) {
|
|
2687
|
+
throw new Error("ICO BMP frame has unsupported DIB header.");
|
|
2688
|
+
}
|
|
2689
|
+
const width = imageData.readInt32LE(4);
|
|
2690
|
+
const heightTotal = imageData.readInt32LE(8);
|
|
2691
|
+
const planes = imageData.readUInt16LE(12);
|
|
2692
|
+
const bitCount = imageData.readUInt16LE(14);
|
|
2693
|
+
const compression = imageData.readUInt32LE(16);
|
|
2694
|
+
if (width <= 0 || heightTotal <= 0) {
|
|
2695
|
+
throw new Error("ICO BMP frame has invalid dimensions.");
|
|
2696
|
+
}
|
|
2697
|
+
const height = Math.floor(heightTotal / 2);
|
|
2698
|
+
if (height <= 0) {
|
|
2699
|
+
throw new Error("ICO BMP frame has invalid mask height.");
|
|
2700
|
+
}
|
|
2701
|
+
if (planes !== 1 || bitCount !== 32 || compression !== 0) {
|
|
2702
|
+
throw new Error("ICO BMP frame format unsupported; expected 32-bit BI_RGB.");
|
|
2703
|
+
}
|
|
2704
|
+
const pixelOffset = headerSize;
|
|
2705
|
+
const rowBytes = width * 4;
|
|
2706
|
+
const pixelBytes = rowBytes * height;
|
|
2707
|
+
if (imageData.length < pixelOffset + pixelBytes) {
|
|
2708
|
+
throw new Error("ICO BMP frame is truncated.");
|
|
2709
|
+
}
|
|
2710
|
+
const png = new pngjs_1.PNG({ width, height });
|
|
2711
|
+
for (let y = 0; y < height; y += 1) {
|
|
2712
|
+
const srcY = height - 1 - y;
|
|
2713
|
+
const srcRow = pixelOffset + srcY * rowBytes;
|
|
2714
|
+
const dstRow = y * rowBytes;
|
|
2715
|
+
for (let x = 0; x < width; x += 1) {
|
|
2716
|
+
const src = srcRow + x * 4;
|
|
2717
|
+
const dst = dstRow + x * 4;
|
|
2718
|
+
const b = imageData[src];
|
|
2719
|
+
const g = imageData[src + 1];
|
|
2720
|
+
const r = imageData[src + 2];
|
|
2721
|
+
const a = imageData[src + 3];
|
|
2722
|
+
png.data[dst] = r;
|
|
2723
|
+
png.data[dst + 1] = g;
|
|
2724
|
+
png.data[dst + 2] = b;
|
|
2725
|
+
png.data[dst + 3] = a;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
return pngjs_1.PNG.sync.write(png);
|
|
2729
|
+
}
|
|
2730
|
+
function convertIcoToPngBuffer(icoBytes) {
|
|
2731
|
+
if (icoBytes.length < 6) {
|
|
2732
|
+
throw new Error("favicon.ico is too small.");
|
|
2733
|
+
}
|
|
2734
|
+
const reserved = icoBytes.readUInt16LE(0);
|
|
2735
|
+
const iconType = icoBytes.readUInt16LE(2);
|
|
2736
|
+
const count = icoBytes.readUInt16LE(4);
|
|
2737
|
+
if (reserved !== 0 || iconType !== 1 || count === 0) {
|
|
2738
|
+
throw new Error("favicon.ico has invalid ICO header.");
|
|
2739
|
+
}
|
|
2740
|
+
if (icoBytes.length < 6 + count * 16) {
|
|
2741
|
+
throw new Error("favicon.ico has truncated directory entries.");
|
|
2742
|
+
}
|
|
2743
|
+
const frames = [];
|
|
2744
|
+
for (let i = 0; i < count; i += 1) {
|
|
2745
|
+
const entryOffset = 6 + i * 16;
|
|
2746
|
+
const width = normalizeIcoSize(icoBytes[entryOffset]);
|
|
2747
|
+
const height = normalizeIcoSize(icoBytes[entryOffset + 1]);
|
|
2748
|
+
const bytesInRes = icoBytes.readUInt32LE(entryOffset + 8);
|
|
2749
|
+
const imageOffset = icoBytes.readUInt32LE(entryOffset + 12);
|
|
2750
|
+
if (bytesInRes === 0)
|
|
2751
|
+
continue;
|
|
2752
|
+
if (imageOffset + bytesInRes > icoBytes.length)
|
|
2753
|
+
continue;
|
|
2754
|
+
frames.push({ width, height, bytesInRes, imageOffset });
|
|
2755
|
+
}
|
|
2756
|
+
if (frames.length === 0) {
|
|
2757
|
+
throw new Error("favicon.ico contains no readable image frames.");
|
|
2758
|
+
}
|
|
2759
|
+
frames.sort((a, b) => {
|
|
2760
|
+
const areaDiff = b.width * b.height - a.width * a.height;
|
|
2761
|
+
if (areaDiff !== 0)
|
|
2762
|
+
return areaDiff;
|
|
2763
|
+
return b.bytesInRes - a.bytesInRes;
|
|
2764
|
+
});
|
|
2765
|
+
const pngSignature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
2766
|
+
for (const frame of frames) {
|
|
2767
|
+
const imageData = icoBytes.subarray(frame.imageOffset, frame.imageOffset + frame.bytesInRes);
|
|
2768
|
+
if (imageData.subarray(0, 8).equals(pngSignature)) {
|
|
2769
|
+
return Buffer.from(imageData);
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
return decodeIcoBmpToPng(icoBytes.subarray(frames[0].imageOffset, frames[0].imageOffset + frames[0].bytesInRes));
|
|
2773
|
+
}
|
|
2774
|
+
function renderDesignerPluginQrc() {
|
|
2775
|
+
return `<RCC>
|
|
2776
|
+
<qresource prefix="/anqstdesignerplugin">
|
|
2777
|
+
<file>plugin-icon.png</file>
|
|
2778
|
+
</qresource>
|
|
2779
|
+
</RCC>
|
|
2780
|
+
`;
|
|
2781
|
+
}
|
|
2782
|
+
function installDesignerPluginIconAssets(cwd, pluginDir) {
|
|
2783
|
+
const iconTargetPath = node_path_1.default.join(pluginDir, "plugin-icon.png");
|
|
2784
|
+
const qrcTargetPath = node_path_1.default.join(pluginDir, "designerplugin.qrc");
|
|
2785
|
+
const icoBytes = resolveFaviconIcoBuffer(cwd);
|
|
2786
|
+
if (icoBytes === null) {
|
|
2787
|
+
if (node_fs_1.default.existsSync(iconTargetPath))
|
|
2788
|
+
node_fs_1.default.rmSync(iconTargetPath, { force: true });
|
|
2789
|
+
if (node_fs_1.default.existsSync(qrcTargetPath))
|
|
2790
|
+
node_fs_1.default.rmSync(qrcTargetPath, { force: true });
|
|
2791
|
+
return { hasIcon: false };
|
|
2792
|
+
}
|
|
2793
|
+
const pngBytes = convertIcoToPngBuffer(icoBytes);
|
|
2794
|
+
node_fs_1.default.writeFileSync(iconTargetPath, pngBytes);
|
|
2795
|
+
node_fs_1.default.writeFileSync(qrcTargetPath, renderDesignerPluginQrc(), "utf8");
|
|
2796
|
+
return { hasIcon: true };
|
|
2797
|
+
}
|
|
2798
|
+
function renderQtDesignerPluginCpp(widgetName, widgetCategory, hasIcon) {
|
|
2799
|
+
const pluginClass = `${widgetName}DesignerPlugin`;
|
|
2800
|
+
const widgetClass = `${widgetName}::${widgetName}`;
|
|
2801
|
+
const groupName = escapeCppStringLiteral(widgetCategory);
|
|
2802
|
+
const iconExpression = hasIcon
|
|
2803
|
+
? 'QIcon(QStringLiteral(":/anqstdesignerplugin/plugin-icon.png"))'
|
|
2804
|
+
: "QIcon()";
|
|
2805
|
+
return `#include <QtUiPlugin/QDesignerCustomWidgetInterface>
|
|
2806
|
+
#include <QIcon>
|
|
2807
|
+
#include <QObject>
|
|
2808
|
+
#include <QString>
|
|
2809
|
+
#include <QWidget>
|
|
2810
|
+
#include "include/${widgetName}.h"
|
|
2811
|
+
|
|
2812
|
+
class ${pluginClass} final : public QObject, public QDesignerCustomWidgetInterface {
|
|
2813
|
+
Q_OBJECT
|
|
2814
|
+
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
|
|
2815
|
+
Q_INTERFACES(QDesignerCustomWidgetInterface)
|
|
2816
|
+
|
|
2817
|
+
public:
|
|
2818
|
+
explicit ${pluginClass}(QObject* parent = nullptr) : QObject(parent) {}
|
|
2819
|
+
|
|
2820
|
+
QString name() const override { return QStringLiteral("${widgetClass}"); }
|
|
2821
|
+
QString group() const override { return QStringLiteral("${groupName}"); }
|
|
2822
|
+
QIcon icon() const override { return ${iconExpression}; }
|
|
2823
|
+
QString toolTip() const override { return QStringLiteral("${widgetName} generated by AnQst."); }
|
|
2824
|
+
QString whatsThis() const override { return QStringLiteral("${widgetName} generated by AnQst."); }
|
|
2825
|
+
bool isContainer() const override { return false; }
|
|
2826
|
+
QString includeFile() const override { return QStringLiteral("include/${widgetName}.h"); }
|
|
2827
|
+
QWidget* createWidget(QWidget* parent) override {
|
|
2828
|
+
auto* widget = new ${widgetClass}(parent);
|
|
2829
|
+
widget->setProperty("anqstDesignerContext", true);
|
|
2830
|
+
return widget;
|
|
2831
|
+
}
|
|
2832
|
+
bool isInitialized() const override { return true; }
|
|
2833
|
+
void initialize(QDesignerFormEditorInterface*) override {}
|
|
2834
|
+
|
|
2835
|
+
QString domXml() const override {
|
|
2836
|
+
return QStringLiteral(
|
|
2837
|
+
"<ui language=\\"c++\\">\\n"
|
|
2838
|
+
" <widget class=\\"${widgetClass}\\" name=\\"${widgetName.toLowerCase()}\\">\\n"
|
|
2839
|
+
" </widget>\\n"
|
|
2840
|
+
"</ui>\\n");
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
|
|
2844
|
+
#include "${pluginClass}.moc"
|
|
2845
|
+
`;
|
|
2846
|
+
}
|
|
2847
|
+
function renderQtDesignerPluginCMake(widgetName, hasIcon) {
|
|
2848
|
+
const widgetTarget = `${widgetName}Widget`;
|
|
2849
|
+
const pluginTarget = `${widgetName}DesignerPlugin`;
|
|
2850
|
+
const resourceLine = hasIcon ? " \"${CMAKE_CURRENT_LIST_DIR}/designerplugin.qrc\"\n" : "";
|
|
2851
|
+
return `cmake_minimum_required(VERSION 3.21)
|
|
2852
|
+
project(${pluginTarget} LANGUAGES CXX)
|
|
2853
|
+
|
|
2854
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
2855
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
2856
|
+
set(CMAKE_AUTOMOC ON)
|
|
2857
|
+
set(CMAKE_AUTOUIC ON)
|
|
2858
|
+
set(CMAKE_AUTORCC ON)
|
|
2859
|
+
|
|
2860
|
+
set(ANQST_PROJECT_ROOT "\${CMAKE_CURRENT_LIST_DIR}/../../../../../../..")
|
|
2861
|
+
set(ANQST_WIDGET_DIR "\${CMAKE_CURRENT_LIST_DIR}/..")
|
|
2862
|
+
set(ANQST_WEBBASE_DIR "" CACHE PATH "Path to AnQstWebBase source directory")
|
|
2863
|
+
|
|
2864
|
+
if(NOT EXISTS "\${ANQST_WIDGET_DIR}/CMakeLists.txt")
|
|
2865
|
+
message(FATAL_ERROR "Missing generated widget CMake project at \${ANQST_WIDGET_DIR}. Run 'anqst build' first.")
|
|
2866
|
+
endif()
|
|
2867
|
+
|
|
2868
|
+
if(NOT ANQST_WEBBASE_DIR)
|
|
2869
|
+
foreach(candidate
|
|
2870
|
+
"\${ANQST_PROJECT_ROOT}/AnQstWidget/AnQstWebBase"
|
|
2871
|
+
"\${ANQST_PROJECT_ROOT}/../AnQstWidget/AnQstWebBase"
|
|
2872
|
+
"\${ANQST_PROJECT_ROOT}/../../AnQstWidget/AnQstWebBase"
|
|
2873
|
+
"\${ANQST_PROJECT_ROOT}/../../../AnQstWidget/AnQstWebBase")
|
|
2874
|
+
if(EXISTS "\${candidate}/CMakeLists.txt")
|
|
2875
|
+
set(ANQST_WEBBASE_DIR "\${candidate}")
|
|
2876
|
+
break()
|
|
2877
|
+
endif()
|
|
2878
|
+
endforeach()
|
|
2879
|
+
endif()
|
|
2880
|
+
|
|
2881
|
+
if(NOT ANQST_WEBBASE_DIR OR NOT EXISTS "\${ANQST_WEBBASE_DIR}/CMakeLists.txt")
|
|
2882
|
+
message(FATAL_ERROR "Unable to locate AnQstWebBase sources. Set -DANQST_WEBBASE_DIR=<path/to/AnQstWidget/AnQstWebBase>.")
|
|
2883
|
+
endif()
|
|
2884
|
+
|
|
2885
|
+
find_package(Qt5 REQUIRED COMPONENTS Core Widgets UiPlugin)
|
|
2886
|
+
|
|
2887
|
+
set(ANQSTWEBBASE_BUILD_TESTS OFF CACHE BOOL "Build AnQstWebBase unit tests" FORCE)
|
|
2888
|
+
if(NOT TARGET anqstwebhostbase)
|
|
2889
|
+
add_subdirectory("\${ANQST_WEBBASE_DIR}" "\${CMAKE_CURRENT_BINARY_DIR}/anqstwebbase")
|
|
2890
|
+
endif()
|
|
2891
|
+
|
|
2892
|
+
if(NOT TARGET ${widgetTarget})
|
|
2893
|
+
add_subdirectory("\${ANQST_WIDGET_DIR}" "\${CMAKE_CURRENT_BINARY_DIR}/generated-widget")
|
|
2894
|
+
endif()
|
|
2895
|
+
|
|
2896
|
+
add_library(${pluginTarget} MODULE
|
|
2897
|
+
"\${CMAKE_CURRENT_LIST_DIR}/${pluginTarget}.cpp"
|
|
2898
|
+
${resourceLine})
|
|
2899
|
+
target_include_directories(${pluginTarget}
|
|
2900
|
+
PRIVATE
|
|
2901
|
+
"\${ANQST_WIDGET_DIR}"
|
|
2902
|
+
"\${ANQST_WIDGET_DIR}/include"
|
|
2903
|
+
)
|
|
2904
|
+
target_link_libraries(${pluginTarget}
|
|
2905
|
+
PRIVATE
|
|
2906
|
+
${widgetTarget}
|
|
2907
|
+
Qt5::Core
|
|
2908
|
+
Qt5::Widgets
|
|
2909
|
+
Qt5::UiPlugin
|
|
2910
|
+
)
|
|
2911
|
+
set_target_properties(${pluginTarget} PROPERTIES
|
|
2912
|
+
PREFIX ""
|
|
2913
|
+
)
|
|
2914
|
+
`;
|
|
2915
|
+
}
|
|
2916
|
+
function installQtDesignerPluginCMake(cwd, widgetName, options = {}) {
|
|
2917
|
+
const pluginDir = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, widgetName).designerPluginRoot;
|
|
2918
|
+
node_fs_1.default.mkdirSync(pluginDir, { recursive: true });
|
|
2919
|
+
const assets = installDesignerPluginIconAssets(cwd, pluginDir);
|
|
2920
|
+
const pluginTarget = `${widgetName}DesignerPlugin`;
|
|
2921
|
+
const widgetCategory = options.widgetCategory ?? "AnQst Widgets";
|
|
2922
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(pluginDir, "CMakeLists.txt"), withBuildStamp(`backend/cpp/qt/${generatedCppLibraryDirName(widgetName)}/designerPlugin/CMakeLists.txt`, renderQtDesignerPluginCMake(widgetName, assets.hasIcon)), "utf8");
|
|
2923
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(pluginDir, `${pluginTarget}.cpp`), withBuildStamp(`backend/cpp/qt/${generatedCppLibraryDirName(widgetName)}/designerPlugin/${pluginTarget}.cpp`, renderQtDesignerPluginCpp(widgetName, widgetCategory, assets.hasIcon)), "utf8");
|
|
2500
2924
|
}
|