idlize
619 строк · 21.0 Кб
1/*
2* Copyright (c) 2024 Huawei Device Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15import * as fs from "fs"
16import * as path from "path"
17import { IndentedPrinter } from "../IndentedPrinter"
18import { PrimitiveType } from "./DeclarationTable"
19import { Language } from "../util"
20import { CppLanguageWriter, createLanguageWriter, LanguageWriter, Method, MethodSignature, NamedMethodSignature, PrinterLike, Type } from "./LanguageWriters"
21import { PeerGeneratorConfig } from "./PeerGeneratorConfig";
22import { PeerEventKind } from "./printers/EventsPrinter"
23import { writeDeserializer, writeSerializer } from "./printers/SerializerPrinter"
24import { writeConvertors } from "./printers/ConvertorsPrinter"
25import { PeerLibrary } from "./PeerLibrary"
26import { ArkoalaInstall, LibaceInstall } from "../Install"
27
28export const warning = "WARNING! THIS FILE IS AUTO-GENERATED, DO NOT MAKE CHANGES, THEY WILL BE LOST ON NEXT GENERATION!"
29
30function dateChunk(): string {
31const currentYear = (new Date()).getFullYear()
32if (currentYear > 2024) return `2024-${currentYear}`
33return `${currentYear}`
34}
35
36export const cStyleCopyright =
37`/*
38* Copyright (c) ${dateChunk()} Huawei Device Co., Ltd.
39* Licensed under the Apache License, Version 2.0 (the "License");
40* you may not use this file except in compliance with the License.
41* You may obtain a copy of the License at
42*
43* http://www.apache.org/licenses/LICENSE-2.0
44*
45* Unless required by applicable law or agreed to in writing, software
46* distributed under the License is distributed on an "AS IS" BASIS,
47* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48* See the License for the specific language governing permissions and
49* limitations under the License.
50*/
51`
52
53export const sharpCopyright =
54`# Copyright (c) ${dateChunk()} Huawei Device Co., Ltd.
55# Licensed under the Apache License, Version 2.0 (the "License");
56# you may not use this file except in compliance with the License.
57# You may obtain a copy of the License at
58#
59# http://www.apache.org/licenses/LICENSE-2.0
60#
61# Unless required by applicable law or agreed to in writing, software
62# distributed under the License is distributed on an "AS IS" BASIS,
63# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64# See the License for the specific language governing permissions and
65# limitations under the License.
66`
67
68const importTsInteropTypes = `
69import {
70int32,
71float32
72} from "@koalaui/common"
73import {
74KInt,
75KBoolean,
76KFloat,
77KUInt,
78KStringPtr,
79KPointer,
80KNativePointer,
81KInt32ArrayPtr,
82KUint8ArrayPtr,
83KFloat32ArrayPtr,
84pointer
85} from "@koalaui/interop"
86`.trim()
87
88export function nativeModuleDeclaration(methods: LanguageWriter, nativeBridgePath: string, useEmpty: boolean, language: Language): string {
89return `
90${language == Language.TS ? importTsInteropTypes : ""}
91
92${readLangTemplate("NativeModule_template", language)
93.replace("%NATIVE_BRIDGE_PATH%", nativeBridgePath)
94.replace("%USE_EMPTY%", useEmpty.toString())
95.replaceAll("%GENERATED_METHODS%", methods.getOutput().join('\n'))}
96`
97}
98
99export function nativeModuleEmptyDeclaration(methods: string[]): string {
100return `
101${importTsInteropTypes}
102import { NativeModule, NativeModuleIntegrated, NodePointer, PipelineContext } from "./NativeModule"
103import { nullptr } from "@koalaui/interop"
104
105${readTemplate('NativeModuleEmpty_template.ts')
106.replaceAll("%GENERATED_EMPTY_METHODS%", methods.join('\n'))}
107`
108}
109
110export function libraryCcDeclaration(): string {
111return readTemplate('library_template.cc')
112.replaceAll(`%CPP_PREFIX%`, PeerGeneratorConfig.cppPrefix)
113}
114
115export function bridgeCcGeneratedDeclaration(generatedApi: string[]): string {
116let prologue = readTemplate('bridge_generated_prologue.cc')
117.replaceAll(`%CPP_PREFIX%`, PeerGeneratorConfig.cppPrefix)
118
119return prologue.concat("\n")
120.concat(generatedApi.join("\n"))
121}
122
123export function bridgeCcCustomDeclaration(customApi: string[]): string {
124let prologue = readTemplate('bridge_custom_prologue.cc')
125.replaceAll(`%CPP_PREFIX%`, PeerGeneratorConfig.cppPrefix)
126
127return prologue.concat("\n")
128.concat(customApi.join("\n"))
129}
130
131export function appendModifiersCommonPrologue(): LanguageWriter {
132let result = createLanguageWriter(Language.CPP)
133let body = readTemplate('impl_prologue.cc')
134
135body = body.replaceAll("%CPP_PREFIX%", PeerGeneratorConfig.cppPrefix)
136
137result.writeLines(body)
138return result
139}
140
141export function appendViewModelBridge(): LanguageWriter {
142let result = createLanguageWriter(Language.CPP)
143let body = readTemplate('view_model_bridge.cc')
144
145body = body.replaceAll("%CPP_PREFIX%", PeerGeneratorConfig.cppPrefix)
146
147result.writeLines(body)
148return result
149}
150
151export function completeModifiersContent(content: PrinterLike, basicVersion: number, fullVersion: number, extendedVersion: number): LanguageWriter {
152let result = createLanguageWriter(Language.CPP)
153let epilogue = readTemplate('dummy_impl_epilogue.cc')
154
155epilogue = epilogue
156.replaceAll("%CPP_PREFIX%", PeerGeneratorConfig.cppPrefix)
157.replaceAll(`%ARKUI_BASIC_NODE_API_VERSION_VALUE%`, basicVersion.toString())
158.replaceAll(`%ARKUI_FULL_API_VERSION_VALUE%`, fullVersion.toString())
159.replaceAll(`%ARKUI_EXTENDED_NODE_API_VERSION_VALUE%`, extendedVersion.toString())
160result.writeLines(`
161void SetAppendGroupedLog(void* pFunc) {}
162`)
163result.concat(content)
164result.writeLines(epilogue)
165return result
166}
167
168export function completeDelegatesImpl(lines: string): string {
169return `
170#include "delegates.h"
171
172${lines}
173`
174}
175
176export function dummyImplementations(modifiers: LanguageWriter, accessors: LanguageWriter, basicVersion: number, fullVersion: number, extendedVersion: number): LanguageWriter {
177let prologue = readTemplate('dummy_impl_prologue.cc')
178let epilogue = readTemplate('dummy_impl_epilogue.cc')
179
180prologue = prologue
181.replaceAll(`%CPP_PREFIX%`, PeerGeneratorConfig.cppPrefix)
182epilogue = epilogue
183.replaceAll("%CPP_PREFIX%", PeerGeneratorConfig.cppPrefix)
184.replaceAll(`%ARKUI_BASIC_NODE_API_VERSION_VALUE%`, basicVersion.toString())
185.replaceAll(`%ARKUI_FULL_API_VERSION_VALUE%`, fullVersion.toString())
186.replaceAll(`%ARKUI_EXTENDED_NODE_API_VERSION_VALUE%`, extendedVersion.toString())
187
188let result = createLanguageWriter(Language.CPP)
189result.writeLines(prologue)
190result.print("namespace OHOS::Ace::NG::GeneratedModifier {")
191result.pushIndent()
192result.concat(modifiers).concat(accessors)
193result.writeLines(epilogue)
194result.popIndent()
195result.print("}")
196
197return result
198}
199
200export function modifierStructList(lines: LanguageWriter): LanguageWriter {
201let result = createLanguageWriter(Language.CPP)
202result.print(`const ${PeerGeneratorConfig.cppPrefix}ArkUINodeModifiers* ${PeerGeneratorConfig.cppPrefix}GetArkUINodeModifiers() {`)
203result.pushIndent()
204
205result.print(`static const ${PeerGeneratorConfig.cppPrefix}ArkUINodeModifiers modifiersImpl = {`)
206result.pushIndent()
207result.concat(lines)
208result.popIndent()
209result.print(`};`)
210
211result.print(`return &modifiersImpl;`)
212result.popIndent()
213result.print(`}`)
214return result
215}
216
217export function accessorStructList(lines: LanguageWriter): LanguageWriter {
218let result = createLanguageWriter(Language.CPP)
219result.print(`const ${PeerGeneratorConfig.cppPrefix}ArkUIAccessors* ${PeerGeneratorConfig.cppPrefix}GetArkUIAccessors() {`)
220result.pushIndent()
221
222result.print(`static const ${PeerGeneratorConfig.cppPrefix}ArkUIAccessors accessorsImpl = {`)
223result.pushIndent()
224result.concat(lines)
225result.popIndent()
226result.print(`};`)
227
228result.print(`return &accessorsImpl;`)
229result.popIndent()
230result.print('}')
231
232return result
233}
234
235export function makeTSSerializer(library: PeerLibrary): string {
236let printer = createLanguageWriter(library.declarationTable.language)
237const builderClassImports = Array.from(library.builderClasses.keys())
238.map(it => `import { ${it} } from "@arkoala/arkui/Ark${it}Builder"`)
239writeSerializer(library, printer)
240//TODO: need to determine imports when generating serializer
241const extraImports = library.declarationTable.language === Language.ARKTS ?
242'import { AdaptiveColor, BlurStyle, BorderStyle, DismissReason, DragPreviewMode, GradientDirection, ShadowStyle, ShadowType, SheetMode, SheetSize, SheetType, ThemeColorMode } from "./ArkCommonInterfaces"' : ''
243return `
244${extraImports}
245import { SerializerBase, Tags, RuntimeType, runtimeType, isPixelMap, isResource, isInstanceOf } from "./SerializerBase"
246import { int32 } from "@koalaui/common"
247import { unsafeCast } from "./generated-utils"
248
249${builderClassImports.join("\n")}
250
251${printer.getOutput().join("\n")}
252
253export function createSerializer(): Serializer { return new Serializer() }
254`
255}
256
257export function makeJavaSerializerWriter(library: PeerLibrary): LanguageWriter {
258let result = createLanguageWriter(library.declarationTable.language)
259result.print(`package org.koalaui.arkoala;\n`)
260writeSerializer(library, result)
261return result
262}
263
264export function makeConverterHeader(path: string, namespace: string, library: PeerLibrary): LanguageWriter {
265const converter = createLanguageWriter(Language.CPP) as CppLanguageWriter
266converter.writeLines(cStyleCopyright)
267converter.writeLines(`/*
268* ${warning}
269*/
270`)
271const includeGuardDefine = makeIncludeGuardDefine(path)
272converter.print(`#ifndef ${includeGuardDefine}`)
273converter.print(`#define ${includeGuardDefine}`)
274converter.print("")
275
276converter.writeGlobalInclude('optional')
277converter.writeGlobalInclude('cstdlib')
278converter.writeInclude('arkoala_api_generated.h')
279converter.writeInclude('base/log/log_wrapper.h')
280converter.print("")
281
282converter.pushNamespace(namespace)
283writeConvertors(library, converter)
284converter.popNamespace()
285converter.print(`\n#endif // ${includeGuardDefine}`)
286converter.print("")
287return converter
288}
289
290export function makeCSerializers(library: PeerLibrary, structs: IndentedPrinter, typedefs: IndentedPrinter): string {
291
292const serializers = createLanguageWriter(Language.CPP)
293const writeToString = createLanguageWriter(Language.CPP)
294serializers.print("\n// Serializers\n")
295writeSerializer(library, serializers)
296serializers.print("\n// Deserializers\n")
297writeDeserializer(library, serializers)
298library.declarationTable.generateStructs(structs, typedefs, writeToString)
299
300return `
301#include "SerializerBase.h"
302#include "DeserializerBase.h"
303#include "arkoala_api_generated.h"
304#include <string>
305
306${writeToString.getOutput().join("\n")}
307
308${serializers.getOutput().join("\n")}
309`
310}
311
312export function makeTSDeserializer(library: PeerLibrary): string {
313const deserializer = createLanguageWriter(Language.TS)
314writeDeserializer(library, deserializer)
315return `
316import { runtimeType, Tags, RuntimeType } from "./SerializerBase"
317import { DeserializerBase } from "./DeserializerBase"
318import { int32 } from "@koalaui/common"
319import { unsafeCast } from "./generated-utils"
320
321${deserializer.getOutput().join("\n")}
322`
323}
324
325export function makeApiModifiers(modifiers: string[], accessors: string[], events: string[]): string {
326
327let node_api = readTemplate('arkoala_node_api.h')
328.replaceAll(`%CPP_PREFIX%`, PeerGeneratorConfig.cppPrefix)
329
330
331return `
332/**
333* An API to control an implementation. When making changes modifying binary
334* layout, i.e. adding new events - increase ARKUI_API_VERSION above for binary
335* layout checks.
336*/
337typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUINodeModifiers {
338${modifiers.join("\n")}
339} ${PeerGeneratorConfig.cppPrefix}ArkUINodeModifiers;
340
341typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUIAccessors {
342${accessors.join("\n")}
343} ${PeerGeneratorConfig.cppPrefix}ArkUIAccessors;
344
345typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUIAnimation {
346} ${PeerGeneratorConfig.cppPrefix}ArkUIAnimation;
347
348typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUINavigation {
349} ${PeerGeneratorConfig.cppPrefix}ArkUINavigation;
350
351typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUIGraphicsAPI {
352${PrimitiveType.Int32.getText()} version;
353} ${PeerGeneratorConfig.cppPrefix}ArkUIGraphicsAPI;
354
355typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI {
356${events.join("\n")}
357} ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI;
358
359${node_api}
360
361/**
362* An API to control an implementation. When making changes modifying binary
363* layout, i.e. adding new events - increase ARKUI_NODE_API_VERSION above for binary
364* layout checks.
365*/
366typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUIFullNodeAPI {
367${PrimitiveType.Int32.getText()} version;
368const ${PeerGeneratorConfig.cppPrefix}ArkUINodeModifiers* (*getNodeModifiers)();
369const ${PeerGeneratorConfig.cppPrefix}ArkUIAccessors* (*getAccessors)();
370const ${PeerGeneratorConfig.cppPrefix}ArkUIAnimation* (*getAnimation)();
371const ${PeerGeneratorConfig.cppPrefix}ArkUINavigation* (*getNavigation)();
372const ${PeerGeneratorConfig.cppPrefix}ArkUIGraphicsAPI* (*getGraphicsAPI)();
373const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI* (*getEventsAPI)();
374const ${PeerGeneratorConfig.cppPrefix}ArkUIExtendedNodeAPI* (*getExtendedAPI)();
375void (*setArkUIEventsAPI)(const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI* api);
376} ${PeerGeneratorConfig.cppPrefix}ArkUIFullNodeAPI;
377
378typedef struct ${PeerGeneratorConfig.cppPrefix}ArkUIAnyAPI {
379${PrimitiveType.Int32.getText()} version;
380} ${PeerGeneratorConfig.cppPrefix}ArkUIAnyAPI;
381`
382}
383
384export function makeApiHeaders(lines: string[]): string {
385return `
386
387${lines.join("\n")}
388`
389}
390
391const TEMPLATES_CACHE = new Map<string, string>()
392
393function readTemplate(name: string): string {
394let template = TEMPLATES_CACHE.get(name);
395if (template == undefined) {
396template = fs.readFileSync(path.join(__dirname, `../templates/${name}`), 'utf8')
397TEMPLATES_CACHE.set(name, template)
398}
399return template
400}
401
402function readLangTemplate(name: string, lang: Language): string {
403return fs.readFileSync(path.join(__dirname, `../templates/${name + lang.extension}`), 'utf8')
404}
405
406
407export function makeAPI(
408apiVersion: string,
409headers: string[], modifiers: string[], accessors: string[], events: string[],
410structs: IndentedPrinter, typedefs: IndentedPrinter
411): string {
412
413let prologue = readTemplate('arkoala_api_prologue.h')
414let epilogue = readTemplate('arkoala_api_epilogue.h')
415
416prologue = prologue
417.replaceAll(`%ARKUI_FULL_API_VERSION_VALUE%`, apiVersion)
418.replaceAll(`%CPP_PREFIX%`, PeerGeneratorConfig.cppPrefix)
419epilogue = epilogue
420.replaceAll("%CPP_PREFIX%", PeerGeneratorConfig.cppPrefix)
421
422return `
423${prologue}
424
425${structs.getOutput().join("\n")}
426
427${typedefs.getOutput().join("\n")}
428
429${makeApiHeaders(headers)}
430
431${makeApiModifiers(modifiers, accessors, events)}
432
433${epilogue}
434`
435}
436
437export function copyToArkoala(from: string, arkoala: ArkoalaInstall, filters?: string[]) {
438filters = filters?.map(it => path.join(from, it))
439copyDir(path.join(from, 'koala-ui'), arkoala.koala, true, filters)
440const macros = path.join(from, 'shared', 'arkoala-macros.h')
441copyFile(macros, path.join(arkoala.nativeDir, 'arkoala-macros.h'), filters)
442}
443
444export function copyToLibace(from: string, libace: LibaceInstall) {
445const macros = path.join(from, 'shared', 'arkoala-macros.h')
446fs.copyFileSync(macros, libace.arkoalaMacros)
447}
448
449function copyDir(from: string, to: string, recursive: boolean, filters?: string[]) {
450fs.readdirSync(from).forEach(it => {
451const sourcePath = path.join(from, it)
452const targetPath = path.join(to, it)
453const statInfo = fs.statSync(sourcePath)
454if (statInfo.isFile()) {
455copyFile(sourcePath, targetPath, filters)
456}
457else if (recursive && statInfo.isDirectory()) {
458if (!fs.existsSync(targetPath)) {
459fs.mkdirSync(targetPath)
460}
461copyDir(sourcePath, targetPath, recursive, filters)
462}
463})
464}
465function copyFile(from: string, to: string, filters?: string[]) {
466if (filters && !filters.includes(from))
467return
468fs.copyFileSync(from, to)
469}
470export function makeNodeTypes(types: string[]): string {
471const enumValues = types.map(it => ` ${it},`).join("\n")
472return `
473export enum ArkUINodeType {
474${enumValues}
475}
476`.trim()
477}
478
479export function makeArkuiModule(componentsFiles: string[]): string {
480return componentsFiles.map(file => {
481const basename = path.basename(file)
482const basenameNoExt = basename.replaceAll(path.extname(basename), "")
483return `export * from "./${basenameNoExt}"`
484}).join("\n")
485}
486
487export function makeMaterializedPrologue(lang: Language): string {
488let prologue = readLangTemplate('materialized_class_prologue', lang)
489return `
490${prologue}
491
492${importTsInteropTypes}
493
494`
495}
496
497export function tsCopyrightAndWarning(content: string): string {
498return `${cStyleCopyright}
499
500// ${warning}
501
502${content}
503`
504}
505
506
507export function peerFileTemplate(content: string): string {
508return tsCopyrightAndWarning(content)
509}
510
511export function componentFileTemplate(content: string): string {
512return tsCopyrightAndWarning(content)
513}
514
515export function makePeerEvents(data: string): string {
516return `
517import { Deserializer } from './Deserializer'
518import { RuntimeType } from "./SerializerBase"
519import { int32 } from "@koalaui/common"
520
521interface PeerEvent {
522readonly kind: ${PeerEventKind}
523readonly nodeId: number
524}
525
526${data}
527`
528}
529
530export function makeCEventsArkoalaImpl(implData: LanguageWriter, receiversList: LanguageWriter): string {
531const writer = new CppLanguageWriter(new IndentedPrinter())
532writer.print(cStyleCopyright)
533writer.writeInclude("arkoala_api_generated.h")
534writer.writeInclude("events.h")
535writer.writeInclude("Serializers.h")
536writer.print("")
537
538writer.pushNamespace("Generated")
539writer.concat(implData)
540writer.writeMethodImplementation(new Method(
541`GetArkUiEventsAPI`,
542new MethodSignature(new Type(`const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI*`), []),
543), (writer) => {
544writer.print(`static const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI eventsImpl = {`)
545writer.pushIndent()
546writer.concat(receiversList)
547writer.popIndent()
548writer.print(`};`)
549writer.writeStatement(writer.makeReturn(writer.makeString(`&eventsImpl`)))
550})
551writer.popNamespace()
552return writer.getOutput().join('\n')
553}
554
555export function makeCEventsLibaceImpl(implData: PrinterLike, receiversList: PrinterLike, namespace: string): string {
556const writer = new CppLanguageWriter(new IndentedPrinter())
557writer.writeLines(cStyleCopyright)
558writer.print("")
559writer.writeInclude(`arkoala_api_generated.h`)
560writer.print("")
561writer.pushNamespace(namespace)
562
563writer.concat(implData)
564
565writer.print(`const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI* g_OverriddenEventsImpl = nullptr;`)
566writer.writeMethodImplementation(new Method(
567`${PeerGeneratorConfig.cppPrefix}SetArkUiEventsAPI`,
568new NamedMethodSignature(Type.Void, [new Type(`const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI*`)], [`api`]),
569), (writer) => {
570writer.writeStatement(writer.makeAssign(`g_OverriddenEventsImpl`, undefined, writer.makeString(`api`), false))
571})
572
573writer.writeMethodImplementation(new Method(
574`${PeerGeneratorConfig.cppPrefix}GetArkUiEventsAPI`,
575new MethodSignature(new Type(`const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI*`), []),
576), (writer) => {
577writer.print(`static const ${PeerGeneratorConfig.cppPrefix}ArkUIEventsAPI eventsImpl = {`)
578writer.pushIndent()
579writer.concat(receiversList)
580writer.popIndent()
581writer.print(`};`)
582writer.writeStatement(writer.makeCondition(
583writer.makeNaryOp("!=", [writer.makeString(`g_OverriddenEventsImpl`), writer.makeString(`nullptr`)]),
584writer.makeReturn(writer.makeString(`g_OverriddenEventsImpl`)),
585))
586writer.writeStatement(writer.makeReturn(writer.makeString(`&eventsImpl`)))
587})
588
589writer.popNamespace()
590return writer.getOutput().join('\n')
591}
592
593export function gniFile(gniSources: string): string {
594return `${sharpCopyright}
595
596# ${warning}
597
598${gniSources}
599`
600}
601
602export function mesonBuildFile(content: string): string {
603return `${sharpCopyright}
604
605# ${warning}
606
607${content}
608`
609}
610
611export function makeIncludeGuardDefine(filePath: string) {
612let basename = path.basename(filePath);
613return basename.replace(/[.\- ]/g, "_").toUpperCase()
614}
615
616export function makeFileNameFromClassName(className: string) {
617// transfroms camel-case name to snake-case
618return className.split(/(?=[A-Z][a-z])/g).join("_").toLowerCase()
619}