• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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