1/* 2* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development 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*/ 15const { NapiLog } = require('../tools/NapiLog'); 16const { writeFile, appendWriteFile, generateRandomInteger, readFile, getJsonCfg } = require('../tools/Tool'); 17const path = require('path'); 18const re = require('../tools/re'); 19const fs = require('fs'); 20const os = require('os'); 21const util = require('util'); 22const readline = require('readline'); 23const { generateDirectFunction } = require('../napiGen/functionDirect'); 24const { generateFuncTestCase } = require('../napiGen/functionDirectTest'); 25const { generateBase } = require('../tools/commonTemplete'); 26const { InterfaceList, TypeList } = require('../tools/common'); 27 28const MIN_RANDOM = 100; 29const MAX_RANDOM = 999; 30let tsFuncName = ''; 31 32function parseFileAll(hFilePath) { 33 let execSync = require('child_process').execSync; 34 let cmd = ''; 35 36 // call exe file (for real runtime) 37 let sysInfo = os.platform(); 38 let execPath = path.dirname(process.execPath); 39 let exeFile = sysInfo === 'win32' ? path.join(execPath, 'header_parser.exe') : 40 path.join(execPath, 'header_parser'); 41 if (!fs.existsSync(exeFile)) { 42 // 若是用node执行 43 exeFile = path.join(__dirname, 'header_parser.exe'); 44 } 45 cmd = exeFile + ' ' + hFilePath; 46 47 let parseResult = null; 48 let stdout = execSync(cmd); 49 parseResult = JSON.parse(stdout.toString()).result; 50 return parseResult; 51} 52 53function createNameSpaceInfo(parseNameSpaceInfo) { 54 let nameSpaceInfo = { 55 'name': '', 56 'classes': [], 57 'functions': [] 58 }; 59 nameSpaceInfo.name = parseNameSpaceInfo; 60 return nameSpaceInfo; 61} 62 63function analyzeNameSpace(rootInfo, parseResult) { 64 let parseNameSpaces = parseResult.namespaces; 65 for (let i = 0; i < parseNameSpaces.length; ++i) { 66 let nameSpaceInfo = createNameSpaceInfo(parseNameSpaces[i]); 67 rootInfo.namespaces.push(nameSpaceInfo); 68 } 69} 70 71function isStringType(cType) { 72 switch (cType) { 73 case 'string': 74 case 'std::string': 75 case 'char': 76 case 'wchar_t': 77 case 'char16_t': 78 case 'char32_t': 79 return true; 80 default: 81 return false; 82 } 83} 84 85function isBoolType(cType) { 86 if (cType === 'bool') { 87 return true; 88 } 89 return false; 90} 91 92function isNumberType(cType) { 93 switch (cType) { 94 case 'short': 95 case 'int': 96 case 'uint32_t': 97 case 'size_t': 98 case 'long': 99 case 'long long': 100 case 'float': 101 case 'double': 102 case 'long double': 103 case 'int16_t': 104 case 'uint16_t': 105 case 'int32_t': 106 case 'int64_t': 107 case 'uint64_t': 108 case 'double_t': 109 case 'float_t': 110 return true; 111 default: 112 return false; 113 } 114} 115 116function basicC2js(cType) { 117 let jsType = ''; 118 if (isStringType(cType)) { 119 jsType = 'string'; 120 } else if (isBoolType(cType)) { 121 jsType = 'boolean'; 122 } else if (isNumberType(cType)) { 123 jsType = 'number'; 124 } else { 125 jsType = cType; 126 } 127 return jsType; 128} 129 130function getJsTypeFromC(cType, typeInfo) { 131 let basicCtype = cType; 132 let matchs = re.match('(std::)?vector<([\x21-\x7e ]+)>', basicCtype); 133 if (matchs) { 134 basicCtype = re.getReg(basicCtype, matchs.regs[2]).trim(); 135 typeInfo.array = 1; 136 } 137 138 let unsignedIdx = basicCtype.indexOf('unsigned'); 139 if (unsignedIdx >= 0) { 140 // cut off the keywords 'unsigned' 141 basicCtype = basicCtype.substring(unsignedIdx + 8, basicCtype.length).trim(); 142 } 143 let jsType = basicCtype; 144 if (typeInfo.array) { 145 jsType = util.format('Array<%s>', jsType); 146 } 147 // 去掉const 148 jsType = jsType.replaceAll('const', ''); 149 // struct cJson * 的情况 150 let matchStruct = re.match('(struct)?[A-Z_a-z0-9 *]+', basicCtype); 151 if (matchStruct) { 152 let index = basicCtype.indexOf('struct'); 153 // 去掉struct和* 154 if (index >= 0) { 155 jsType = jsType.substring(index + 6, basicCtype.length); 156 } 157 jsType = jsType.replaceAll('*', '').trim(); 158 } 159 jsType = basicC2js(jsType); 160 return jsType; 161} 162 163function createParam(parseParamInfo) { 164 let param = { 165 'name': '', 166 'type': '' 167 }; 168 param.name = parseParamInfo.name; 169 let rawType = getJsTypeFromC(parseParamInfo.type, parseParamInfo); 170 param.type = rawType; 171 172 return param; 173} 174 175function createFuncInfo(parseFuncInfo, isClassFunc) { 176 let funcInfo = { 177 'name': '', 178 'genName': '', 179 'params': [], 180 'namespace': '', 181 'retType': '', 182 'static': '' 183 }; 184 funcInfo.name = parseFuncInfo.name; 185 funcInfo.namespace = parseFuncInfo.namespace; 186 let tokenIndex = funcInfo.namespace.indexOf('::'); 187 if (tokenIndex >= 0) { 188 // delete '::' in namespace, get the pure space name. 189 funcInfo.namespace = funcInfo.namespace.substring(0, tokenIndex); 190 } 191 192 let parseParams = parseFuncInfo.parameters; 193 for (let i = 0; i < parseParams.length; ++i) { 194 let param = createParam(parseParams[i]); 195 funcInfo.params.push(param); 196 } 197 198 funcInfo.isClassFunc = isClassFunc; 199 200 if (parseFuncInfo.static && isClassFunc) { 201 funcInfo.static = 'static '; 202 } 203 let retType = parseFuncInfo.returns === '' ? parseFuncInfo.rtnType : parseFuncInfo.returns; 204 funcInfo.retType = getJsTypeFromC(retType, parseFuncInfo); 205 return funcInfo; 206} 207 208function putFuncIntoNamespace(funcInfo, namespaces) { 209 for (let i = 0; i < namespaces.length; ++i) { 210 if (namespaces[i].name === funcInfo.namespace) { 211 namespaces[i].functions.push(funcInfo); 212 return; 213 } 214 } 215} 216 217function analyzeRootTypeDef(rootInfo, parseResult) { 218 let parserTypedefs = Object.keys(parseResult.typedefs); 219 220 for (let i = 0; i < parserTypedefs.length; ++i) { 221 let objTypedefKeys = parserTypedefs[i]; 222 let objTypedefVal = parseResult.typedefs[parserTypedefs[i]]; 223 rootInfo.typedefs.push({objTypedefKeys, objTypedefVal}); 224 } 225} 226 227function analyzeRootFunction(rootInfo, parseResult) { 228 let parseFunctions = parseResult.functions; 229 for (let i = 0; i < parseFunctions.length; ++i) { 230 // 普通方法生成模板 231 let funcInfo = createFuncInfo(parseFunctions[i], false); 232 if (parseFunctions[i].namespace !== '') { 233 // function in namespace 234 putFuncIntoNamespace(funcInfo, rootInfo.namespaces); 235 } else { 236 // function without namespace, put on root 237 rootInfo.functions.push(funcInfo); 238 } 239 } 240} 241 242function createProperties(parseProperties) { 243 let propertyList = []; 244 for (let i = 0; i < parseProperties.length; ++i) { 245 let property = {}; 246 property.name = parseProperties[i].name; 247 property.type = getJsTypeFromC(parseProperties[i].type, parseProperties[i]); 248 propertyList.push(property); 249 } 250 return propertyList; 251} 252 253function createClassFunctions(parseFuncs) { 254 let funcList = []; 255 for (let i = 0; i < parseFuncs.length; ++i) { 256 let funcInfo = createFuncInfo(parseFuncs[i], true); 257 funcList.push(funcInfo); 258 } 259 return funcList; 260} 261 262function createClassInfo(parseClassInfo) { 263 let classInfo = { 264 'name': '', 265 'namespace': '', 266 'properties': [], 267 'functions': [], 268 'extends': [] 269 }; 270 classInfo.name = parseClassInfo.name; 271 classInfo.namespace = parseClassInfo.namespace; 272 classInfo.properties = createProperties(parseClassInfo.properties.public); 273 classInfo.functions = createClassFunctions(parseClassInfo.methods.public); 274 275 return classInfo; 276} 277 278function putClassIntoNamespace(classInfo, namespaces) { 279 for (let i = 0; i < namespaces.length; ++i) { 280 if (namespaces[i].name === classInfo.namespace) { 281 namespaces[i].classes.push(classInfo); 282 return; 283 } 284 } 285} 286 287function analyzeClasses(rootInfo, parseResult) { 288 let parseClasses = parseResult.classes; 289 290 for (let className in parseClasses) { 291 let classInfo = createClassInfo(parseClasses[className]); 292 if (classInfo.namespace !== '') { 293 // class in namespace 294 putClassIntoNamespace(classInfo, rootInfo.namespaces); 295 } else { 296 // class without namespace, put on root 297 rootInfo.classes.push(classInfo); 298 } 299 } 300} 301 302function getTab(tabLv) { 303 let tab = ''; 304 for (let i = 0; i < tabLv; ++i) { 305 tab += ' '; 306 } 307 return tab; 308} 309 310function genFunction(func, tabLv, dtsDeclare) { 311 let tab = getTab(tabLv); 312 let funcPrefix = func.isClassFunc ? '' : 'export const '; 313 let funcParams = ''; 314 for (let i = 0; i < func.params.length; ++i) { 315 funcParams += i > 0 ? ', ' : ''; 316 funcParams += func.params[i].name + ': ' + func.params[i].type; 317 } 318 func.genName = 'KH' + generateRandomInteger(MIN_RANDOM, MAX_RANDOM) + '_' + func.name; 319 let dtsDeclareContent = replaceAll(dtsDeclare, '[tab_replace]', tab); 320 dtsDeclareContent = replaceAll(dtsDeclareContent, '[export_replace]', funcPrefix); 321 dtsDeclareContent = replaceAll(dtsDeclareContent, '[func_name_replace]', func.genName); 322 dtsDeclareContent = replaceAll(dtsDeclareContent, '[func_param_replace]', funcParams); 323 dtsDeclareContent = replaceAll(dtsDeclareContent, '[input_introduce_replace]', funcParams === '' ? 'void' : funcParams); 324 dtsDeclareContent = replaceAll(dtsDeclareContent, '[func_return_replace]', func.retType); 325 dtsDeclareContent = replaceAll(dtsDeclareContent, '[output_introduce_replace]', func.retType); 326 dtsDeclareContent = replaceAll(dtsDeclareContent, '[func_introduce_replace]', func.name); 327 328 let classFuncTestUseTemplete = '[func_name_replace]:([func_param_replace]) => [func_return_replace]'; 329 let classFuncTestUse = replaceAll(classFuncTestUseTemplete, '[func_name_replace]', func.genName); 330 classFuncTestUse = replaceAll(classFuncTestUse, '[func_param_replace]', funcParams); 331 classFuncTestUse = replaceAll(classFuncTestUse, '[func_return_replace]', func.retType); 332 let interfaceFuncResult = null; 333 if (func.isClassFunc) { 334 interfaceFuncResult = { 335 name: func.genName, 336 type: classFuncTestUse, 337 }; 338 } 339 return [dtsDeclareContent, interfaceFuncResult]; 340} 341 342function isJsBasicType(type) { 343 if (type === 'number' || type === 'string' || type === 'boolean') { 344 return true; 345 } else { 346 return false; 347 } 348} 349 350function genClass(classInfo, tabLv, dtsDeclare, needDeclare = false) { 351 let tab = getTab(tabLv); 352 let tsClass = tab + 'export ' + 'interface ' + classInfo.name + ' {\n'; 353 let tab1 = getTab(tabLv + 1); 354 let interfaceBody = []; 355 for (let i = 0; i < classInfo.properties.length; ++i) { 356 let myType = classInfo.properties[i].type; 357 if (!isJsBasicType(myType)) { 358 myType += ' | null'; 359 } 360 tsClass += util.format('%s%s: %s;\n', tab1, classInfo.properties[i].name, myType); 361 interfaceBody.push({ 362 name: classInfo.properties[i].name, 363 type: classInfo.properties[i].type, 364 }); 365 } 366 367 // 循环加入class中的方法 368 let interfaceFunc = null; 369 for (let i = 0; i < classInfo.functions.length; ++i) { 370 let result = genFunction(classInfo.functions[i], tabLv + 1, dtsDeclare); 371 tsClass += result[0]; 372 interfaceFunc = result[1]; 373 if (interfaceFunc !== null) { 374 interfaceBody.push(interfaceFunc); 375 } 376 } 377 tsClass += tab + '}\n'; 378 379 let interfaceData = { 380 name: classInfo.name, 381 body: interfaceBody 382 }; 383 InterfaceList.push(interfaceData); 384 return tsClass; 385} 386 387function genNamespace(namespace, tabLv, dtsDeclare) { 388 let tab = getTab(tabLv); 389 let tsNamespace = tab + util.format('declare namespace %s {\n', namespace.name); 390 for (let i = 0; i < namespace.functions.length; ++i) { 391 tsNamespace += genFunction(namespace.functions[i], tabLv + 1, dtsDeclare)[0]; 392 } 393 for (let i = 0; i < namespace.classes.length; ++i) { 394 tsNamespace += genClass(namespace.classes[i], tabLv + 1, dtsDeclare); 395 } 396 tsNamespace += tab + '}\n'; 397 return tsNamespace; 398} 399 400function genType(typeInfo, tabLv) { 401 let tab = getTab(tabLv); 402 let tsTypedef = ''; 403 for (let i = 0; i < typeInfo.length; i++) { 404 if (isNumberType(typeInfo[i].objTypedefVal)) { 405 tsTypedef += tab + 'type ' + typeInfo[i].objTypedefKeys + ' = number;\n'; 406 let typeData = { 407 name: typeInfo[i].objTypedefKeys, 408 body: 'number' 409 }; 410 TypeList.push(typeData); 411 } else if (isBoolType(typeInfo[i].objTypedefVal)) { 412 tsTypedef += tab + 'type ' + typeInfo[i].objTypedefKeys + ' = boolean;\n'; 413 let typeData = { 414 name: typeInfo[i].objTypedefKeys, 415 body: 'boolean' 416 }; 417 TypeList.push(typeData); 418 } else if (isStringType(typeInfo[i].objTypedefVal)) { 419 tsTypedef += tab + 'type ' + typeInfo[i].objTypedefKeys + ' = string;\n'; 420 let typeData = { 421 name: typeInfo[i].objTypedefKeys, 422 body: 'string' 423 }; 424 TypeList.push(typeData); 425 } 426 } 427 428 return tsTypedef; 429} 430 431function genTsContent(rootInfo, dtsDeclare) { 432 let tsContent = rootInfo.needCallback ? 'import { AsyncCallback, Callback } from \'./../basic\';\n\n' : ''; 433 434 // gen typedefs 435 tsContent += genType(rootInfo.typedefs, 0); 436 for (let i = 0; i < rootInfo.classes.length; ++i) { 437 tsContent += genClass(rootInfo.classes[i], 0, dtsDeclare, true); 438 } 439 440 for (let i = 0; i < rootInfo.namespaces.length; ++i) { 441 tsContent += genNamespace(rootInfo.namespaces[i], 0, dtsDeclare); 442 } 443 444 for (let i = 0; i < rootInfo.functions.length; ++i) { 445 tsContent += genFunction(rootInfo.functions[i], 0, dtsDeclare)[0]; 446 } 447 448 return tsContent; 449} 450 451function removeMarco(hFilePath, tempFilePath, macros) { 452 // 创建读取文件的流 453 const fileStream = fs.createReadStream(hFilePath); 454 // 创建逐行读取的接口 455 const rl = readline.createInterface({ 456 input: fileStream, 457 crlfDelay: Infinity 458 }); 459 // 存储处理后的文件内容 460 let processedContent = ''; 461 // 逐行读取文件内容并处理 去除方法中的宏 462 rl.on('line', (line) => { 463 // 替换使用宏的地方,保留#define宏定义 464 if (line.indexOf('#define') < 0 && line.indexOf('#ifndef') < 0 && line.indexOf('#ifdef') && 465 line.indexOf('#elif') && line.indexOf('#if') < 0 && line.indexOf('#else')) { 466 macros.forEach(macro => { 467 // 去掉使用的宏以及括号() 468 line = getLineContent(line, macro); 469 }); 470 } 471 processedContent += line + '\n'; 472 }); 473 474 // 完成读取操作 475 rl.on('close', () => { 476 writeFile(tempFilePath, processedContent); 477 }); 478} 479 480function getLineContent(line, macro) { 481 let index = line.indexOf(macro); 482 if (index >= 0) { 483 let regexPattern = new RegExp(macro + '\\s*\\('); 484 let isMatch = regexPattern.test(line); 485 if (isMatch) { 486 let removeIndexLeft = line.indexOf('('); 487 line = line.substring(0, index) + line.substring(index + macro.length, removeIndexLeft) + 488 line.substring(removeIndexLeft + 1, line.length); 489 let removeIndexRight = line.indexOf(')'); 490 line = line.substring(0, removeIndexRight) + line.substring(removeIndexRight + 1, line.length); 491 } else { 492 let tmpLeft = line.substring(0, index); 493 let indexLeftBracket = tmpLeft.lastIndexOf('('); 494 tmpLeft = tmpLeft.substring(0, indexLeftBracket) + tmpLeft.substring(indexLeftBracket + 1, tmpLeft.length); 495 let tmpRight = line.substring(index + macro.length, line.length); 496 let indexRightBracket = tmpRight.indexOf(')'); 497 tmpRight = tmpRight.substring(0, indexRightBracket) + tmpRight.substring(indexRightBracket + 1, tmpRight.length); 498 line = tmpLeft + tmpRight; 499 } 500 } 501 return line; 502} 503 504// 读取头文件并提取所有的#define宏 505function extractMacros(headerFilePath) { 506 return new Promise((resolve, reject) => { 507 fs.readFile(headerFilePath, 'utf8', (err, data) => { 508 if (err) { 509 return reject(err); 510 } 511 512 // 匹配#define指令的正则表达式 513 const macroRegex = /^\s*#define\s+(\w+)/gm; 514 let match; 515 const macros = []; 516 517 // 使用正则表达式迭代所有匹配项 518 while ((match = macroRegex.exec(data)) !== null) { 519 // match[1]是宏的名称,match[2]是宏的定义(如果存在) 520 macros.push(match[1]); 521 } 522 523 return resolve(macros); 524 }); 525 }); 526} 527 528function sleep(ms) { 529 return new Promise(resolve => setTimeout(resolve, ms)); 530} 531 532async function doGenerate(hFilePath, testFilePath, tsFilePath, cppFilePath) { 533 // 预处理文件,读出文件中所有的宏,存在数组中 534 let macros = await extractMacros(hFilePath); 535 // 将使用宏的地方置空 536 let random = generateRandomInteger(MIN_RANDOM, MAX_RANDOM); 537 let tempFileName = '../temp_' + random + '.h'; 538 let tempFilePath = path.join(hFilePath, tempFileName); 539 removeMarco(hFilePath, tempFilePath, macros); 540 while (!fs.existsSync(tempFilePath)) { 541 await sleep(20); // 延迟 20 毫秒 542 } 543 544 let parseResult = parseFileAll(tempFilePath); 545 546 let rootInfo = { 547 'namespaces': [], 548 'classes': [], 549 'functions': [], 550 'needCallback': false, 551 'typedefs': [], 552 }; 553 554 analyzeNameSpace(rootInfo, parseResult); 555 analyzeRootTypeDef(rootInfo, parseResult); 556 analyzeRootFunction(rootInfo, parseResult); 557 analyzeClasses(rootInfo, parseResult); 558 559 // 读取Json文件 560 let funcJsonPath = path.join(__dirname, '../json/function.json'); 561 let funcJson = getJsonCfg(funcJsonPath); 562 563 // 读取dts文件模板 564 let dtsDeclarePath = path.join(__dirname, funcJson.directFunction.dtsTemplete); 565 let dtsDeclare = readFile(dtsDeclarePath); 566 567 let hFileName = hFilePath.substring(hFilePath.lastIndexOf('\\') + 1, hFilePath.length); 568 // 生成dts声明文件内容 569 let tsContent = genTsContent(rootInfo, dtsDeclare); 570 tsContent = replaceAll(tsContent, '[file_introduce_replace]', hFileName); 571 appendWriteFile(tsFilePath, '\n' + tsContent); 572 573 // 生成native侧文件 574 generateCppFile(cppFilePath, hFilePath, funcJson, rootInfo, parseResult); 575 576 // 生成测试用例 577 generateAbilityTest(funcJson, rootInfo, parseResult, testFilePath, hFileName); 578 579 // 删除生成的中间文件 580 clearTmpFile(tempFilePath); 581 582 console.info('Generate success'); 583} 584 585function checkPathType(path) { 586 try { 587 const stats = fs.statSync(path); 588 if (stats.isDirectory()) { 589 return 'directory'; 590 } else if (stats.isFile()) { 591 return 'file'; 592 } else { 593 return 'badpath'; 594 } 595 } catch (err) { 596 console.error(err); 597 } 598 return ''; 599} 600 601function checkFileIsNull(filePath) { 602 if (fs.existsSync(filePath)) { 603 try { 604 const fileContent = fs.readFileSync(filePath, 'utf-8'); 605 if (fileContent.trim() === '') { 606 return true; 607 } 608 return false; 609 } catch (err) { 610 console.error(`读取文件 ${filePath} 失败: ${err}`); 611 } 612 } 613 return false; 614} 615 616function genTestTemplete(filePath, content) { 617 try { 618 fs.writeFileSync(filePath, content); 619 } catch (err) { 620 console.error(`创建文件 ${filePath} 失败: ${err}`); 621 } 622} 623 624 625function generateAbilityTest(funcJson, rootInfo, parseResult, testFilePath, hFileName) { 626 let index = hFileName.indexOf('.h'); 627 let hFileNameReplace = hFileName.substring(0, index); 628 // 第一次生成时应生成框架 629 let abilityTestFirstGenTempletePath = funcJson.directFunction.testTempleteDetails.abilityTestFirstGenTemplete; 630 let abilityTestFirstGenAbsPath = path.join(__dirname, abilityTestFirstGenTempletePath); 631 let abilityTestFirstGenTemplete = readFile(abilityTestFirstGenAbsPath); 632 abilityTestFirstGenTemplete = replaceAll(abilityTestFirstGenTemplete, '[abilitytest_name_replace]', hFileNameReplace); 633 634 // 判断testFilePath是文件还是文件夹,若是文件夹则生成文件,同时第一次写入内容 635 let realTestFilePath = testFilePath; 636 let isDir = checkPathType(realTestFilePath); 637 if (isDir === 'directory') { 638 realTestFilePath = path.join(testFilePath, hFileNameReplace + 'Ability.test.ets'); 639 genTestTemplete(realTestFilePath, abilityTestFirstGenTemplete); 640 } else if (isDir === 'file') { 641 if (checkFileIsNull(realTestFilePath)) { 642 genTestTemplete(realTestFilePath, abilityTestFirstGenTemplete); 643 } 644 } 645 646 // 不是第一次生成则追加写入 647 let abilityTestTempletePath = funcJson.directFunction.testTempleteDetails.abilityTestTemplete; 648 let abilityTestTempleteAbsPath = path.join(__dirname, abilityTestTempletePath); 649 let abilityTestTemplete = readFile(abilityTestTempleteAbsPath); 650 651 let genTestResult = ''; 652 // 生成测试用例 同时生成多个测试用例 653 for (let i = 0; i < rootInfo.functions.length; i++) { 654 genTestResult += generateFuncTestCase(parseResult, i, rootInfo.functions[i].genName, abilityTestTemplete, hFileName); 655 } 656 const importContent = "import testNapi from 'libentry.so';"; 657 writeTestFile(realTestFilePath, importContent, genTestResult); 658} 659 660function generateCppFile(cppFilePath, hFilePath, funcJson, rootInfo, parseResult) { 661 // 写入common.h 和 common.cpp 662 generateBase(cppFilePath, '', hFilePath); 663 664 let index = hFilePath.lastIndexOf('\\'); 665 let indexH = hFilePath.lastIndexOf('.h'); 666 let napiHFileName = hFilePath.substring(index + 1, indexH).toLowerCase() + 'napi.h'; 667 let includesReplace = util.format('#include "%s"\n', napiHFileName); 668 let hFileName = hFilePath.substring(hFilePath.lastIndexOf('\\') + 1, hFilePath.length); 669 670 let funcDeclare = ''; 671 let funcInit = ''; 672 let directFuncPath = funcJson.directFunction; 673 // 调用napi转换的方法 674 for (let i = 0; i < rootInfo.functions.length; i++) { 675 let cppFileName = rootInfo.functions[i].name.toLowerCase().replace('_', '').trim(); 676 let thisFuncCppFilePath = path.join(cppFilePath, cppFileName + '.cpp'); 677 let genResult = generateDirectFunction(parseResult, i, rootInfo.functions[i].genName, directFuncPath, hFileName); 678 funcDeclare += genResult[0]; 679 funcInit += genResult[1]; 680 let funcBody = includesReplace + genResult[2]; 681 writeFile(thisFuncCppFilePath, funcBody); 682 } 683 684 // init函数内容 685 let cppInitTempletePath = funcJson.directFunction.initTempleteDetails.cppInitTemplete; 686 const cppInitTempleteAbsPath = path.join(__dirname, cppInitTempletePath); 687 let cppInitTemplete = readFile(cppInitTempleteAbsPath); 688 cppInitTemplete = replaceAll(cppInitTemplete, '[include_replace]', includesReplace); 689 cppInitTemplete = replaceAll(cppInitTemplete, '[init_replace]', funcInit); 690 let napiInitFileName = hFilePath.substring(index + 1, indexH).toLowerCase() + 'init.cpp'; 691 let initFilePath = path.join(cppFilePath, napiInitFileName); 692 writeFile(initFilePath, cppInitTemplete); 693 694 // 生成的napi方法声明文件 695 let cppDeclareTempletePath = funcJson.directFunction.cppTempleteDetails.funcHDeclare.funcHDeclareTemplete; 696 const cppDeclareTempleteAbsPath = path.join(__dirname, cppDeclareTempletePath); 697 let cppDeclareTempleteContent = readFile(cppDeclareTempleteAbsPath); 698 let napiHFileNameReplace = hFilePath.substring(index + 1, indexH).toLowerCase() + 'napi'; 699 cppDeclareTempleteContent = replaceAll(cppDeclareTempleteContent, '[h_file_name_replace]', napiHFileNameReplace.toUpperCase()); 700 cppDeclareTempleteContent = replaceAll(cppDeclareTempleteContent, '[func_declare_replace]', funcDeclare); 701 cppDeclareTempleteContent = replaceAll(cppDeclareTempleteContent, '[h_filename_replace]', 702 hFilePath.substring(index + 1, indexH).toLowerCase()); 703 let declareFilePath = path.join(cppFilePath, napiHFileName); 704 writeFile(declareFilePath, cppDeclareTempleteContent); 705} 706 707function writeTestFile(filePath, importContent, funcTestContent) { 708 // 读取原本文件内容 709 const fileContent = fs.readFileSync(filePath, 'utf8'); 710 const importPosition = fileContent.indexOf('import '); 711 let newFileContent = fileContent; 712 // 判断是否有该import语句,没有则添加 713 if (fileContent.indexOf(importContent) < 0) { 714 const newImportStatement = importContent + '\n'; 715 newFileContent = fileContent.slice(0, importPosition) + newImportStatement + fileContent.slice(importPosition); 716 } 717 // 追加写入测试用例 718 let testCasePosition = newFileContent.lastIndexOf('})'); 719 newFileContent = newFileContent.slice(0, testCasePosition) + funcTestContent + newFileContent.slice(testCasePosition); 720 721 writeFile(filePath, newFileContent); 722} 723 724function replaceAll(s, sfrom, sto) { 725 while (s.indexOf(sfrom) >= 0) { 726 s = s.replace(sfrom, sto); 727 } 728 return s; 729} 730 731function clearTmpFile(filePath) { 732 try { 733 fs.unlinkSync(filePath); 734 } catch (err) { 735 console.error(err); 736 } 737} 738 739module.exports = { 740 doGenerate 741}; 742