16
import { program } from "commander"
17
import * as fs from "fs"
18
import * as path from "path"
19
import { fromIDL, scanIDL } from "./from-idl/common"
20
import { idlToString } from "./from-idl/DtsPrinter"
21
import { generate } from "./idlize"
22
import { IDLEntry, forEachChild, toIDLString } from "./idl"
23
import { printHeader, toHeaderString, wrapWithPrologueAndEpilogue } from "./idl2h"
24
import { LinterMessage, LinterVisitor, toLinterString } from "./linter"
25
import { CompileContext, IDLVisitor } from "./IDLVisitor"
26
import { TestGeneratorVisitor } from "./TestGeneratorVisitor"
27
import { ArkoalaInstall, LibaceInstall } from "./Install"
33
makeJavaSerializerWriter,
39
} from "./peer-generation/FileGenerators"
43
} from "./peer-generation/PeerGeneratorVisitor"
44
import { defaultCompilerOptions, isDefined, toSet, Language } from "./util"
45
import { TypeChecker } from "./typecheck"
46
import { initRNG } from "./rand_utils"
47
import { DeclarationTable } from "./peer-generation/DeclarationTable"
48
import { printRealAndDummyAccessors, printRealModifiersAsMultipleFiles } from "./peer-generation/printers/ModifierPrinter"
49
import { printRealAndDummyModifiers } from "./peer-generation/printers/ModifierPrinter"
50
import { PeerLibrary } from "./peer-generation/PeerLibrary"
51
import { printComponents } from "./peer-generation/printers/ComponentsPrinter"
52
import { printPeers } from "./peer-generation/printers/PeersPrinter"
53
import { printMaterialized } from "./peer-generation/printers/MaterializedPrinter"
54
import { printSerializers, printUserConverter } from "./peer-generation/printers/HeaderPrinter"
55
import { printNodeTypes } from "./peer-generation/printers/NodeTypesPrinter"
56
import { printNativeModule, printNativeModuleEmpty } from "./peer-generation/printers/NativeModulePrinter"
57
import { PeerGeneratorConfig } from "./peer-generation/PeerGeneratorConfig";
58
import { printEvents, printEventsCArkoalaImpl, printEventsCLibaceImpl } from "./peer-generation/printers/EventsPrinter"
59
import { printGniSources } from "./peer-generation/printers/GniPrinter"
60
import { printMesonBuild } from "./peer-generation/printers/MesonPrinter"
61
import { printInterfaces } from "./peer-generation/printers/InterfacePrinter"
62
import { printConflictedDeclarations } from "./peer-generation/printers/ConflictedDeclarationsPrinter"
63
import { printFakeDeclarations } from "./peer-generation/printers/FakeDeclarationsPrinter"
64
import { printBuilderClasses } from "./peer-generation/printers/BuilderClassPrinter"
65
import { ARKOALA_PACKAGE_PATH, INTEROP_PACKAGE_PATH } from "./lang/java"
66
import { TargetFile } from "./peer-generation/printers/TargetFile"
67
import { printBridgeCcCustom, printBridgeCcGenerated } from "./peer-generation/printers/BridgeCcPrinter"
69
const options = program
70
.option('--dts2idl', 'Convert .d.ts to IDL definitions')
71
.option('--dts2h', 'Convert .d.ts to .h definitions')
72
.option('--dts2test', 'Generate tests from .d.ts to .h')
73
.option('--dts2peer', 'Convert .d.ts to peer drafts')
74
.option('--ets2ts', 'Convert .ets to .ts')
75
.option('--input-dir <path>', 'Path to input dir')
76
.option('--output-dir <path>', 'Path to output dir')
77
.option('--input-file <name>', 'Name of file to convert, all files in input-dir if none')
78
.option('--idl2dts', 'Convert IDL to .d.ts definitions')
79
.option('--idl2h', 'Convert IDL to .h definitions')
80
.option('--linter', 'Run linter')
81
.option('--linter-suppress-errors <suppress>', 'Error codes to suppress, comma separated, no space')
82
.option('--linter-whitelist <whitelist.json>', 'Whitelist for linter')
83
.option('--verbose', 'Verbose processing')
84
.option('--verify-idl', 'Verify produced IDL')
85
.option('--common-to-attributes', 'Transform common attributes as IDL attributes')
86
.option('--test-interface <name>', 'Interfaces to test (comma separated)')
87
.option('--test-method <name>', 'Methods to test (comma separated)')
88
.option('--test-property <name>', 'Properties to test (comma separated)')
89
.option('--generate-interface <name>', 'Interfaces to generate (comma separated)')
90
.option('--disable-enum-initializers', "Don't include enum member initializers in the interface")
91
.option('--native-bridge-path <name>', "Path to native bridge")
92
.option('--api-version <version>', "API version for generated peers")
93
.option('--dump-serialized', "Dump serialized data")
94
.option('--call-log', "Call log")
95
.option('--docs [all|opt|none]', 'How to handle documentation: include, optimize, or skip')
96
.option('--language [ts|sts|java]', 'Output language')
97
.option('--api-prefix <string>', 'Cpp prefix to be compatible with manual arkoala implementation')
98
.option('--need-interfaces', 'Generate interfaces to resolve all .d.ts dependencies', false)
99
.option('--only-integrated', 'Generate only thoose files that can be integrated to target', false)
101
.option('--generator-target <all|arkoala|libace|none>', 'Copy peers to arkoala or libace (use with --dts2peer)', "all")
102
.option('--arkoala-destination <path>', 'Location of arkoala repository')
103
.option('--libace-destination <path>', 'Location of libace repository')
104
.option('--copy-peers-components <name...>', 'List of components to copy (omit to copy all)')
108
function findVersion() {
109
if (process.env.npm_package_version) return process.env.npm_package_version
110
let packageJson = path.join(__dirname, '..', 'package.json')
112
let json = fs.readFileSync(packageJson).toString()
113
return json ? JSON.parse(json).version : undefined
119
if (process.env.npm_package_version) {
120
console.log(`IDLize version ${findVersion()}`)
125
if (options.dts2idl) {
126
const tsCompileContext = new CompileContext()
130
options.outputDir ?? "./idl",
131
(sourceFile, typeChecker) => new IDLVisitor(sourceFile, typeChecker, tsCompileContext, options),
133
compilerOptions: defaultCompilerOptions,
134
onSingleFile: (entries: IDLEntry[], outputDir, sourceFile) => {
135
const outFile = path.join(outputDir,
136
path.basename(sourceFile.fileName).replace(".d.ts", ".idl"))
137
console.log("producing", outFile)
138
if (options.skipDocs) {
139
entries.forEach(it => forEachChild(
140
it, (it) => it.documentation = undefined))
142
let generated = toIDLString(entries, {
143
verifyIdl: options.verifyIdl ?? false,
144
disableEnumInitializers: options.disableEnumInitializers ?? false
146
if (options.verbose) console.log(generated)
147
fs.writeFileSync(outFile, generated)
155
const allEntries = new Array<IDLEntry[]>()
156
const tsCompileContext = new CompileContext()
160
options.outputDir ?? "./headers",
161
(sourceFile, typeChecker) => new IDLVisitor(sourceFile, typeChecker, tsCompileContext, options.commonToAttributes ?? false),
163
compilerOptions: defaultCompilerOptions,
164
onSingleFile: (entries: IDLEntry[]) => allEntries.push(entries),
167
const outFile = path.join(options.outputDir ?? "./headers", "arkoala_api_generated.h")
168
console.log("producing", outFile)
170
const generated = toHeaderString(new TypeChecker(allEntries.flat()), allEntries, options.generateInterface)
171
if (options.verbose) console.log(generated)
172
fs.writeFileSync(outFile, generated)
177
const allEntries = new Array<LinterMessage[]>()
182
(sourceFile, typeChecker) => new LinterVisitor(sourceFile, typeChecker),
184
compilerOptions: defaultCompilerOptions,
185
onSingleFile: (entries: LinterMessage[]) => allEntries.push(entries),
187
onEnd: (outputDir) => {
188
const outFile = options.outputDir ? path.join(outputDir, "linter.txt") : undefined
189
let [generated, exitCode, histogram] = toLinterString(allEntries, options.linterSuppressErrors, options.linterWhitelist)
190
console.log(histogram)
191
if (!outFile || options.verbose) console.log(generated)
192
if (outFile) fs.writeFileSync(outFile, generated)
193
process.exit(exitCode)
200
if (options.dts2test) {
202
let testInterfaces = options.testInterface
203
if (testInterfaces === undefined) {
204
function fileNameToClass(name: string): string {
207
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
211
let inDir = path.resolve(options.inputDir)
212
testInterfaces = fs.readdirSync(inDir)
213
.filter(file => file.endsWith("d.ts"))
214
.map(file => file.substring(0, file.length - 5))
215
.map(fileNameToClass)
219
let lines: string[] = []
223
options.outputDir ?? "./generated/tests",
224
(sourceFile, typeChecker) => new TestGeneratorVisitor(sourceFile, typeChecker, testInterfaces, options.testMethod, options.testProperties),
226
compilerOptions: defaultCompilerOptions,
227
onBegin: (outDir: string) => {
228
lines.push(`import { ArkUINodeType } from "@arkoala/arkui/ArkUINodeType"`)
229
lines.push(`import {checkResult, checkTestFailures} from "../subset/test_utils"`)
232
onSingleFile: (entries: string[], outputDir, sourceFile) => {
233
lines = lines.concat(entries)
235
onEnd: (outDir: string) => {
237
lines.push(`checkTestFailures()`)
239
let generated = lines.join("\n")
240
const outFile = path.join(outDir, "index.ts")
241
if (options.verbose) {
242
console.log(generated)
244
console.log(`Write fuzzing peers to file ${outFile}`)
245
fs.writeFileSync(outFile, lines.join("\n"))
252
if (options.idl2dts) {
256
options.outputDir ?? "./generated/dts/",
258
options.verbose ?? false,
265
const idlFiles = scanIDL(
269
const typeChecker = new TypeChecker(idlFiles.flat())
270
const body = idlFiles
271
.flatMap(it => printHeader(typeChecker, it, toSet(options.generateInterface)))
273
.filter(it => it.length > 0)
275
const generatedHeader = wrapWithPrologueAndEpilogue(body)
276
if (options.verbose) {
279
const outputDir = options.outputDir ?? "./generated/headers"
280
if (!fs.existsSync(outputDir)) {
281
fs.mkdirSync(outputDir, { recursive: true })
283
const outFile = path.join(outputDir, "arkoala_api_generated.h")
284
console.log("producing", outFile)
285
fs.writeFileSync(outFile, generatedHeader)
289
if (options.dts2peer) {
290
if (options.apiPrefix !== undefined) {
291
PeerGeneratorConfig.cppPrefix = options.apiPrefix
293
PeerGeneratorConfig.needInterfaces = options.needInterfaces
294
const declarationTable = new DeclarationTable(options.language ?? "ts")
295
const peerLibrary = new PeerLibrary(declarationTable, toSet(options.generateInterface))
296
const generatedPeersDir = options.outputDir ?? "./generated/peers"
302
(sourceFile, typeChecker) => new PeerGeneratorVisitor({
303
sourceFile: sourceFile,
304
typeChecker: typeChecker,
309
compilerOptions: defaultCompilerOptions,
310
onBegin(outDir, typeChecker) {
311
declarationTable.typeChecker = typeChecker
313
onEnd(outDir: string) {
314
let lang = declarationTable.language
315
const peerProcessor = new PeerProcessor(peerLibrary)
316
peerProcessor.process()
317
declarationTable.analyze(peerLibrary)
319
if (options.generatorTarget == "arkoala" ||
320
options.generatorTarget == "all") {
322
generateArkoala(outDir, peerLibrary, lang)
325
if (options.generatorTarget == "libace" ||
326
options.generatorTarget == "all") {
328
generateLibace(outDir, peerLibrary)
340
function generateLibace(outDir: string, peerLibrary: PeerLibrary) {
341
const libace = options.libaceDestination ?
342
new LibaceInstall(options.libaceDestination, false) :
343
new LibaceInstall(outDir, true)
345
const gniSources = printGniSources(peerLibrary)
346
fs.writeFileSync(libace.gniComponents, gniFile(gniSources))
349
printRealModifiersAsMultipleFiles(peerLibrary, libace, {
351
base: "OHOS::Ace::NG",
352
generated: "OHOS::Ace::NG::GeneratedModifier"
355
fullVersion: options.apiVersion,
359
const converterNamespace = "OHOS::Ace::NG::Converter"
360
const { api, converterHeader } = printUserConverter(libace.userConverterHeader, converterNamespace, options.apiVersion, peerLibrary)
361
fs.writeFileSync(libace.generatedArkoalaApi, api)
362
fs.writeFileSync(libace.userConverterHeader, converterHeader)
363
const events = printEventsCLibaceImpl(peerLibrary, {namespace: "OHOS::Ace::NG::GeneratedEvents"})
364
fs.writeFileSync(libace.allEvents, events)
366
if (!options.libaceDestination) {
367
const mesonBuild = printMesonBuild(peerLibrary)
368
fs.writeFileSync(libace.mesonBuild, mesonBuildFile(mesonBuild))
371
copyToLibace(path.join(__dirname, '..', 'peer_lib'), libace)
374
function writeFile(filename: string, content: string, integrated: boolean = false) {
375
if (integrated || !options.onlyIntegrated)
376
fs.writeFileSync(filename, content)
379
function generateArkoala(outDir: string, peerLibrary: PeerLibrary, lang: Language) {
380
const arkoala = options.arkoalaDestination ?
381
new ArkoalaInstall(options.arkoalaDestination, lang, false) :
382
new ArkoalaInstall(outDir, lang, true)
383
arkoala.createDirs([ARKOALA_PACKAGE_PATH, INTEROP_PACKAGE_PATH].map(dir => path.join(arkoala.javaDir, dir)))
385
const arkuiComponentsFiles: string[] = []
388
const peers = printPeers(peerLibrary, options.dumpSerialized ?? false)
389
for (const [targetBasename, peer] of peers) {
390
const outPeerFile = arkoala.peer(new TargetFile(targetBasename))
391
console.log("producing", outPeerFile)
392
writeFile(outPeerFile, peer, true)
395
const components = printComponents(peerLibrary)
396
for (const [targetBasename, component] of components) {
397
const outComponentFile = arkoala.component(new TargetFile(targetBasename))
398
console.log("producing", outComponentFile)
399
if (options.verbose) console.log(component)
400
writeFile(outComponentFile, component, true)
401
arkuiComponentsFiles.push(outComponentFile)
404
const builderClasses = printBuilderClasses(peerLibrary, options.dumpSerialized ?? false)
405
for (const [targetBasename, builderClass] of builderClasses) {
406
const outBuilderFile = arkoala.builderClass(new TargetFile(targetBasename))
407
fs.writeFileSync(outBuilderFile, builderClass)
410
const materialized = printMaterialized(peerLibrary, options.dumpSerialized ?? false)
411
for (const [targetBasename, materializedClass] of materialized) {
412
const outMaterializedFile = arkoala.materialized(new TargetFile(targetBasename))
413
writeFile(outMaterializedFile, materializedClass)
417
if (lang === Language.TS) {
419
arkoala.tsArkoalaLib(new TargetFile('NativeModuleEmpty')),
420
printNativeModuleEmpty(peerLibrary),
424
arkoala.tsArkoalaLib(new TargetFile('NativeModule')),
425
printNativeModule(peerLibrary, options.nativeBridgeDir ?? "../../../../../../../native/NativeBridgeNapi"),
429
else if (lang === Language.JAVA) {
431
arkoala.javaLib(new TargetFile('NativeModule', ARKOALA_PACKAGE_PATH)),
432
printNativeModule(peerLibrary, options.nativeBridgeDir ?? "../../../../../../../native/NativeBridgeNapi")
436
arkoala.langLib(new TargetFile('NativeModule')),
437
printNativeModule(peerLibrary, options.nativeBridgeDir ?? "../../../../../../../native/NativeBridgeNapi")
441
if (lang == Language.TS) {
443
const interfaces = printInterfaces(peerLibrary, lang)
444
for (const [targetFile, data] of interfaces) {
445
const outComponentFile = arkoala.interface(targetFile)
446
console.log("producing", outComponentFile)
447
if (options.verbose) console.log(data)
448
writeFile(outComponentFile, data)
449
arkuiComponentsFiles.push(outComponentFile)
452
const fakeDeclarations = printFakeDeclarations(peerLibrary)
453
for (const [filename, data] of fakeDeclarations) {
454
const outComponentFile = arkoala.interface(new TargetFile(filename))
455
console.log("producing", outComponentFile)
456
if (options.verbose) console.log(data)
457
writeFile(outComponentFile, data, true)
458
arkuiComponentsFiles.push(outComponentFile)
462
arkoala.tsLib(new TargetFile('ConflictedDeclarations')),
463
printConflictedDeclarations(peerLibrary),
466
arkoala.tsLib(new TargetFile('ArkUINodeType')),
467
printNodeTypes(peerLibrary),
470
arkoala.tsLib(new TargetFile('index')),
471
makeArkuiModule(arkuiComponentsFiles),
474
arkoala.tsLib(new TargetFile("peer_events")),
475
printEvents(peerLibrary),
478
writeFile(arkoala.tsLib(new TargetFile('Serializer')),
479
makeTSSerializer(peerLibrary),
482
writeFile(arkoala.tsLib(new TargetFile('Deserializer')),
483
makeTSDeserializer(peerLibrary),
487
if (lang == Language.ARKTS) {
488
const interfaces = printInterfaces(peerLibrary, lang)
489
for (const [targetBasename, data] of interfaces) {
490
const outComponentFile = arkoala.interface(targetBasename)
491
console.log("producing", outComponentFile)
492
if (options.verbose) console.log(data)
493
writeFile(outComponentFile, data)
494
arkuiComponentsFiles.push(outComponentFile)
497
arkoala.arktsLib(new TargetFile('ArkUINodeType')),
498
printNodeTypes(peerLibrary),
500
writeFile(arkoala.arktsLib(new TargetFile('Serializer')),
501
makeTSSerializer(peerLibrary)
504
if (lang == Language.JAVA) {
505
const interfaces = printInterfaces(peerLibrary, lang)
506
for (const [targetFile, data] of interfaces) {
507
const outComponentFile = arkoala.javaLib(targetFile)
508
console.log("producing", outComponentFile)
509
if (options.verbose) console.log(data)
510
fs.writeFileSync(outComponentFile, data)
513
const writer = makeJavaSerializerWriter(peerLibrary)
514
writer.printTo(arkoala.javaLib(new TargetFile('Serializer', ARKOALA_PACKAGE_PATH)))
516
writeFile(arkoala.native(new TargetFile('bridge_generated.cc')), printBridgeCcGenerated(peerLibrary, options.callLog ?? false), true)
517
writeFile(arkoala.native(new TargetFile('bridge_custom.cc')), printBridgeCcCustom(peerLibrary, options.callLog ?? false))
519
const { api, serializers } = printSerializers(options.apiVersion, peerLibrary)
520
writeFile(arkoala.native(new TargetFile('Serializers.h')), serializers, true)
521
writeFile(arkoala.native(new TargetFile('arkoala_api_generated.h')), api, true)
523
const modifiers = printRealAndDummyModifiers(peerLibrary)
524
const accessors = printRealAndDummyAccessors(peerLibrary)
526
arkoala.native(new TargetFile('dummy_impl.cc')),
527
dummyImplementations(modifiers.dummy, accessors.dummy, 1, options.apiVersion, 6).getOutput().join('\n'),
530
arkoala.native(new TargetFile('real_impl.cc')),
531
dummyImplementations(modifiers.real, accessors.real, 1, options.apiVersion, 6).getOutput().join('\n'),
534
writeFile(arkoala.native(new TargetFile('all_events.cc'),), printEventsCArkoalaImpl(peerLibrary), true)
535
writeFile(arkoala.native(new TargetFile('library.cc')), libraryCcDeclaration())
537
copyToArkoala(path.join(__dirname, '..', 'peer_lib'), arkoala, !options.onlyIntegrated ? undefined : [
538
'koala-ui/arkoala/native/src/generated/SerializerBase.h',
539
'koala-ui/arkoala/native/src/generated/DeserializerBase.h',
540
'koala-ui/arkoala/native/src/generated/Interop.h',
541
'koala-ui/arkoala/native/src/generated/arkoala-macros.h',
542
'koala-ui/arkoala-arkui/src/SerializerBase.ts',
543
'koala-ui/arkoala-arkui/src/DeserializerBase.ts',
544
'koala-ui/arkoala-arkui/src/callback_registry.ts'