mirror of
https://gitee.com/openharmony/napi_generator
synced 2024-11-26 18:20:35 +00:00
add native gen tool
Signed-off-by: gou-jingjing <goujingjing@kaihong.com>
This commit is contained in:
parent
f730eed763
commit
bea45c4e7b
5
OAT.xml
5
OAT.xml
@ -56,8 +56,8 @@ Note:If the text contains special characters, please escape them according to th
|
||||
<licensefile></licensefile>
|
||||
<policylist>
|
||||
<policy name="projectPolicy" desc="">
|
||||
<policyitem type="copyright" name="Copyright (c) 2024 Shenzhen Kaihong Digital" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
|
||||
<policyitem type="copyright" name="Copyright (c) 2023 Shenzhen Kaihong Digital" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
|
||||
<policyitem type="copyright" name="Copyright (c) 2024 Shenzhen Kaihong Digital" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
|
||||
<policyitem type="copyright" name="Copyright (c) 2023 Shenzhen Kaihong Digital" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
|
||||
<policyitem type="copyright" name="Copyright (c) 2022 Shenzhen Kaihong Digital" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
|
||||
<policyitem type="copyright" name="Copyright (c) 2022 Guangzhou Digitalchina Information Technology Co., Ltd." path=".*" desc=""/>
|
||||
</policy>
|
||||
@ -65,6 +65,7 @@ Note:If the text contains special characters, please escape them according to th
|
||||
<filefilterlist>
|
||||
<filefilter name="defaultFilter" desc="Files not to check">
|
||||
<filteritem type="filename" name="*.mp4" desc="mp4 files"/>
|
||||
<filteritem type="filename" name="*.py" desc="python files"/>
|
||||
<filteritem type="filepath" name="src/generator/src/com/sk/dialog/.*." desc=""/>
|
||||
<filteritem type="filepath" name="napi_IntelliJ_plugin/generator/src/com/sk/dialog/.*." desc=""/>
|
||||
<filteritem type="filepath" name="test/storytest/test.py" desc="GPL or BSD license"/>
|
||||
|
@ -18,13 +18,27 @@ Native生成工具支持两种入口,分别是命令行、IntelliJ插件,使
|
||||
2.在命令行使用 以下命令运行脚本
|
||||
|
||||
```
|
||||
node ./tool/commandLine/src/main.js 接口文件路径
|
||||
node ./tool/commandLine/src/main.js -f 接口文件路径
|
||||
```
|
||||
|
||||
其中,参数详情如下:
|
||||
|
||||
-f, 待转换的.h文件;
|
||||
|
||||
-t, 可选参数,工程目录下测试用例文件Ability.test.ets文件路径,默认路径为.h文件所在工程目录下的
|
||||
|
||||
Ability.test.ets文件路径;
|
||||
|
||||
-i, 可选参数,工程目录下ts声明文件index.s.ts文件路径,默认路径为.h文件所在工程目录下的
|
||||
|
||||
index.d.ts文件路径;
|
||||
|
||||
-o, 可选参数,工程目录下生成的.cpp文件所在文件夹路径,若该目录下不存在.cpp文件则会创建test.cpp文件,默认路径为.h所在工程目录./src/main/cpp路径下;
|
||||
|
||||
例如:
|
||||
|
||||
```
|
||||
node ./tool/commandLine/src/main.js E:\napi_generator_aboutTest\napi_240329\napi_generator\examples\napitutorials\entry\src\main\cpp\test.h
|
||||
node ./tool/commandLine/src/main.js -f E:\napi_generator_aboutTest\napi_240329\napi_generator\examples\napitutorials\entry\src\main\cpp\test.h
|
||||
```
|
||||
|
||||
3.运行成功后命令行会打印出 Generate success,并在./entry/src/main/cpp会生成test.cpp文件,其中是接口napi层模板;在./entry/src/main/cpp/types/libentry/index.d.ts文件中会追加写入生成的ts接口;在./entrysrc/ohosTest/ets/test/Ability.test.ets生成接口测试代码模板。用户根据自身需求在test.cpp中增加业务代码,并在Ability.test.ets中增加合适断言之后,即可连接开发板并运行测试用例测试验证生成napi代码是否正确。例如:
|
||||
@ -38,7 +52,7 @@ res = value0 + value1;
|
||||
在Ability.test.ets文件中增加断言:
|
||||
|
||||
```
|
||||
expect(result).assertEqual(2.3+3.2)
|
||||
expect(result).assertEqual(2+3)
|
||||
```
|
||||
|
||||
连接开发板,运行Ability.test.ets中的测试用例:
|
||||
|
35
examples/napitutorials/tool/commandLine/src/function.json
Normal file
35
examples/napitutorials/tool/commandLine/src/function.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"directFunction": {
|
||||
"indexTemplete": "export const %s:(%s) => %s;\n",
|
||||
"cppFuncTemplete": "#include \"napi/native_api.h\"\n[include_replace]\n\n[body_replace]\n\nEXTERN_C_START\nstatic napi_value Init(napi_env env, napi_value exports)\n{\n napi_property_descriptor desc[] = {\n [init_replace]\n};\nnapi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);\nreturn exports;\n}\nEXTERN_C_END\n\nstatic napi_module demoModule = {\n .nm_version = 1,\n .nm_flags = 0,\n .nm_filename = nullptr,\n .nm_register_func = Init,\n .nm_modname = \"entry\",\n .nm_priv = ((void*)0),\n .reserved = { 0 },\n};\n\nextern \"C\" __attribute__((constructor)) void RegisterEntryModule(void)\n{\n napi_module_register(&demoModule);\n}",
|
||||
"cppFuncDetails": {
|
||||
"funcInitTemplete": "{ \"%s\" , nullptr, %s, nullptr, nullptr, nullptr, napi_default, nullptr },",
|
||||
"funcBodyTemplete": "static napi_value [funcName](napi_env env, napi_callback_info info)\n{\n[func_getParam_replace]\n [func_return_replace]\n }\n",
|
||||
"funcGetParamTemplete" : "size_t requireArgc = [param_length];\n size_t argc = [param_length];\n napi_value args[[param_length]] = {nullptr};\n napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);\n [getParam_replace]\n ",
|
||||
"funcReturnTemplete" : "[return_type_define]\n // Todo\n // eg. res = value0 + value1;\n napi_value result;\n [return_replace]\n return result;\n",
|
||||
"paramGenTemplete": " napi_valuetype valuetype%s;\n napi_typeof(env, args[%s], &valuetype%s);\n %s value%s;\n [getParam_replace]",
|
||||
"funcParamType": {
|
||||
"int32_t": "napi_get_value_int32(env, args[%s], &value%s);\n",
|
||||
"int64_t": "napi_get_value_int64(env, args[%s], &value%s);\n",
|
||||
"uint32_t": "napi_get_value_uint32(env, args[%s], &value%s);\n",
|
||||
"int": "",
|
||||
"double": "napi_get_value_double(env, args[%s], &value%s);\n",
|
||||
"bool": "napi_get_value_bool(env, args[%s], &value%s);\n",
|
||||
"string": "char buf[1024];\n size_t results;\n napi_get_value_string_utf8(env, args[%s], buf, 1024, &results);\n value%s = buf;\n"
|
||||
},
|
||||
"funcReturnType": {
|
||||
"int32_t": "napi_create_int32(env, res, &result);\n",
|
||||
"int64_t": "napi_create_int64(env, res, &result);\n",
|
||||
"uint32_t": "napi_create_uint32(env, res, &result);\n",
|
||||
"int": "",
|
||||
"size_t": "",
|
||||
"double": "napi_create_double(env, res, &result);\n",
|
||||
"bool": "napi_get_boolean(env, res, &result);\n",
|
||||
"string": "napi_create_string_utf8(env, res, NAPI_AUTO_LENGTH, &result);\n"
|
||||
}
|
||||
},
|
||||
"abilityTestTemplete": "it('assertContain_[random_number]', 0, () => {\n // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.\n hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');\n\n [func_direct_testCase]\n\n // Defines a variety of assertion methods, which are used to declare expected boolean conditions.\n // 断言 如:expect(result).assertEqual(2+3)\n })\n"
|
||||
},
|
||||
"asyncFunction": {
|
||||
}
|
||||
}
|
@ -14,11 +14,47 @@
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const stdio = require("stdio");
|
||||
const main = require("./TsGen/tsMain");
|
||||
|
||||
let ops = stdio.getopt({
|
||||
// 输入的.h文件路径,必填
|
||||
'filename': { key: 'f', args: 1, description: ".h file", default: "" },
|
||||
// 可选参数,可设置默认值
|
||||
'testFilename': { key: 't', args: 1, description: "Ability.test.ets file", default: "" },
|
||||
'indexFilename': { key: 'i', args: 1, description: "index.d.ts file", default: "" },
|
||||
'outCppPath': { key: 'o', args: 1, description: ".cpp dir path", default: "" },
|
||||
});
|
||||
|
||||
// 获取命令行参数 .h文件路径
|
||||
const filePath = process.argv[2] || '';
|
||||
let out = './entry/src/main/cpp/types/libentry/'
|
||||
let filePath = ops.filename
|
||||
let testFilePath = ops.testFilename
|
||||
let tsFilePath = ops.indexFilename
|
||||
let cppFilePath = ops.outCppPath
|
||||
// 读取文件内容 判断参数是否为空
|
||||
if (filePath !== '') {
|
||||
let fileDir = path.resolve(filePath, '..');
|
||||
let indexFile = findIndexDTS(fileDir);
|
||||
// 若用户没有提供路径 则程序提供默认路径
|
||||
if (!tsFilePath) {
|
||||
tsFilePath = indexFile
|
||||
}
|
||||
if (!testFilePath) {
|
||||
let rootPath = path.resolve(indexFile, '..', '..', '..', '..', '..');
|
||||
testFilePath = path.join(rootPath, 'ohosTest/ets/test/Ability.test.ets');
|
||||
}
|
||||
if(!cppFilePath) {
|
||||
let rootPath = path.resolve(indexFile, '..', '..', '..');
|
||||
cppFilePath = path.join(rootPath, 'test.cpp');
|
||||
}
|
||||
|
||||
console.info("filePath: " + filePath)
|
||||
console.info("testFilePath: " + testFilePath)
|
||||
console.info("tsFilePath: " + tsFilePath)
|
||||
console.info("cppFilePath: " + cppFilePath)
|
||||
main.doGenerate(filePath, testFilePath, tsFilePath, cppFilePath);
|
||||
}
|
||||
|
||||
|
||||
// 这个函数接收一个目录的绝对路径作为参数
|
||||
function findIndexDTS(currentDir) {
|
||||
@ -47,12 +83,3 @@ function findIndexDTS(currentDir) {
|
||||
console.log('index.d.ts not found in any checked directory.');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
if (filePath !== '') {
|
||||
let fileDir = path.resolve(filePath, '..');
|
||||
let out = findIndexDTS(fileDir);
|
||||
if (out !== null) {
|
||||
main.doGenerate(filePath, out);
|
||||
}
|
||||
}
|
||||
|
@ -12,66 +12,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// const { paramGenerate } = require("./param_generate");
|
||||
// const { returnGenerate } = require("./return_generate");
|
||||
|
||||
const { NapiLog } = require("../tools/NapiLog");
|
||||
const util = require('util');
|
||||
const path = require('path')
|
||||
const path = require('path');
|
||||
const fs = require("fs");
|
||||
const { writeFile } = require("../tools/tool");
|
||||
const re = require("../tools/re");
|
||||
const LENGTH = 10;
|
||||
const TWO_DECIMAL = 2;
|
||||
|
||||
let cppTemplete = `
|
||||
#include "napi/native_api.h"
|
||||
[include_replace]
|
||||
static napi_value [funcName](napi_env env, napi_callback_info info)
|
||||
{
|
||||
[body_replace]
|
||||
}
|
||||
|
||||
EXTERN_C_START
|
||||
static napi_value Init(napi_env env, napi_value exports)
|
||||
{
|
||||
napi_property_descriptor desc[] = {
|
||||
[init_replace]
|
||||
};
|
||||
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
|
||||
return exports;
|
||||
}
|
||||
EXTERN_C_END
|
||||
|
||||
static napi_module demoModule = {
|
||||
.nm_version = 1,
|
||||
.nm_flags = 0,
|
||||
.nm_filename = nullptr,
|
||||
.nm_register_func = Init,
|
||||
.nm_modname = "entry",
|
||||
.nm_priv = ((void*)0),
|
||||
.reserved = { 0 },
|
||||
};
|
||||
|
||||
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
|
||||
{
|
||||
napi_module_register(&demoModule);
|
||||
}`
|
||||
|
||||
let bodyTemplete = `
|
||||
size_t requireArgc = [param_length];
|
||||
size_t argc = [param_length];
|
||||
napi_value args[[param_length]] = {nullptr};
|
||||
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
|
||||
[getParme_replace]
|
||||
[return_type_define]
|
||||
// Todo
|
||||
// eg. res = value0 + value1;
|
||||
napi_value result;
|
||||
[return_replace]
|
||||
return result;
|
||||
`
|
||||
|
||||
// 暂不考虑interface情况
|
||||
function generateDirectFunction(params, tsFuncName, indexPath) {
|
||||
function generateDirectFunction(params, tsFuncName, cppFilePath, directFuncJson) {
|
||||
let funcInfo = {
|
||||
"name": "",
|
||||
"params": [],
|
||||
@ -83,16 +34,15 @@ function generateDirectFunction(params, tsFuncName, indexPath) {
|
||||
for(let i in includes) {
|
||||
includes_replace += util.format('#include %s\n', includes[i])
|
||||
}
|
||||
// console.info("includes_replace: " + includes_replace)
|
||||
|
||||
// 获取注册的方法名字 (只读取了一个方法 当前只支持一个方法的转换)
|
||||
funcInfo.name = params.functions[0].name
|
||||
let funcName_replace = funcInfo.name.substring(0,1).toUpperCase() + funcInfo.name.substring(1,funcInfo.name.length)
|
||||
// console.info("funcName_replace: " + funcName_replace)
|
||||
let serialNum = tsFuncName.substring(0,6)
|
||||
let funcName_replace = serialNum + funcInfo.name.substring(0,1).toUpperCase() + funcInfo.name.substring(1,funcInfo.name.length)
|
||||
|
||||
// 方法的注册
|
||||
let init_replace = util.format('{ "%s" , nullptr, %s, nullptr, nullptr, nullptr, napi_default, nullptr }', tsFuncName, funcName_replace)
|
||||
// console.info("init_replace: " + init_replace)
|
||||
let initTemplete = directFuncJson.cppFuncDetails.funcInitTemplete
|
||||
let init_replace = util.format(initTemplete, tsFuncName, funcName_replace)
|
||||
|
||||
// 分析方法
|
||||
funcInfo.retType = params.functions[0].rtnType
|
||||
@ -101,76 +51,115 @@ function generateDirectFunction(params, tsFuncName, indexPath) {
|
||||
let param = createParam(parseParams[i])
|
||||
funcInfo.params.push(param)
|
||||
}
|
||||
|
||||
// 生成
|
||||
let paramGenTemplete = `
|
||||
napi_valuetype valuetype%s;
|
||||
napi_typeof(env, args[%s], &valuetype%s);
|
||||
%s value%s;
|
||||
[getParam_replace]
|
||||
`
|
||||
let paramGenTemplete = directFuncJson.cppFuncDetails.paramGenTemplete
|
||||
let funcParamType = directFuncJson.cppFuncDetails.funcParamType
|
||||
let paramGenResult = ''
|
||||
// napi 获取参数
|
||||
for(let i = 0; i < funcInfo.params.length; i++) {
|
||||
let paramGen = util.format(paramGenTemplete, i, i, i, funcInfo.params[i].type, i)
|
||||
if (funcInfo.params[i].type === 'double' || funcInfo.params[i].type === 'double_t' || funcInfo.params[i].type === 'float') {
|
||||
let getParam = util.format('napi_get_value_double(env, args[%s], &value%s);', i, i)
|
||||
console.info("funcInfo.params[i].type.substring(0,10): " + funcInfo.params[i].type.substring(0,10))
|
||||
let paramType = funcInfo.params[i].type === 'size_t'? 'int64_t': funcInfo.params[i].type
|
||||
let paramGen = util.format(paramGenTemplete, i, i, i, paramType, i)
|
||||
if (funcInfo.params[i].type === 'double') {
|
||||
let getParam = util.format(funcParamType.double, i, i)
|
||||
paramGen = replaceAll(paramGen, '[getParam_replace]', getParam);
|
||||
paramGenResult += paramGen;
|
||||
} else if (funcInfo.params[i].type === 'uint32_t') {
|
||||
let getParam = util.format('napi_get_value_uint32(env, args[%s], &value%s);', i, i)
|
||||
let getParam = util.format(funcParamType.uint32_t, i, i)
|
||||
paramGen = replaceAll(paramGen, '[getParam_replace]', getParam);
|
||||
paramGenResult += paramGen;
|
||||
} else if (funcInfo.params[i].type === 'int32_t') {
|
||||
let getParam = util.format('napi_get_value_int32(env, args[%s], &value%s);', i, i)
|
||||
let getParam = util.format(funcParamType.int32_t, i, i)
|
||||
paramGen = replaceAll(paramGen, '[getParam_replace]', getParam);
|
||||
paramGenResult += paramGen;
|
||||
} else if (funcInfo.params[i].type === 'int64_t') {
|
||||
let getParam = util.format('napi_get_value_int64_t(env, args[%s], &value%s);', i, i)
|
||||
} else if (funcInfo.params[i].type === 'int64_t' || funcInfo.params[i].type === 'size_t') {
|
||||
let getParam = util.format(funcParamType.int64_t, i, i)
|
||||
paramGen = replaceAll(paramGen, '[getParam_replace]', getParam);
|
||||
paramGenResult += paramGen;
|
||||
} else if (funcInfo.params[i].type === 'bool') {
|
||||
let getParam = util.format('napi_get_value_bool(env, args[%s], &value%s);', i, i)
|
||||
let getParam = util.format(funcParamType.bool, i, i)
|
||||
paramGen = replaceAll(paramGen, '[getParam_replace]', getParam);
|
||||
paramGenResult += paramGen;
|
||||
} else if (funcInfo.params[i].type === 'std::string' || funcInfo.params[i].type.substring(0,10) === 'const char') {
|
||||
let getParam = util.format(funcParamType.string, i, i)
|
||||
paramGen = replaceAll(paramGen, '[getParam_replace]', getParam);
|
||||
paramGenResult += paramGen;
|
||||
}
|
||||
}
|
||||
// console.info("paramGenResult: " + paramGenResult)
|
||||
// 返回值处理
|
||||
let retGenResult = ''
|
||||
let funcReturnType = directFuncJson.cppFuncDetails.funcReturnType
|
||||
if (funcInfo.retType === 'uint32_t') {
|
||||
retGenResult = 'napi_create_uint32(env, res, &result);'
|
||||
} else if (funcInfo.retType === 'double' || funcInfo.retType === 'double_t' || funcInfo.retType === 'float') {
|
||||
retGenResult = 'napi_create_double(env, res, &result);'
|
||||
retGenResult = funcReturnType.uint32_t
|
||||
} else if (funcInfo.retType === 'double') {
|
||||
retGenResult = funcReturnType.double
|
||||
} else if (funcInfo.retType === 'int32_t') {
|
||||
retGenResult = 'napi_create_int32(env, res, &result);'
|
||||
} else if (funcInfo.retType === 'int64_t') {
|
||||
retGenResult = 'napi_create_int64(env, res, &result);'
|
||||
retGenResult = funcReturnType.int32_t
|
||||
} else if (funcInfo.retType === 'int64_t' || funcInfo.retType === 'size_t') {
|
||||
retGenResult = funcReturnType.int64_t
|
||||
} else if (funcInfo.retType === 'bool') {
|
||||
retGenResult = 'napi_get_boolean(env, res, &result);'
|
||||
retGenResult = funcReturnType.bool
|
||||
} else if (funcInfo.retType === 'std::string' || funcInfo.retType.substring(0,10) === 'const char') {
|
||||
retGenResult = funcReturnType.string
|
||||
}
|
||||
// console.info("retGenResult: " + retGenResult)
|
||||
|
||||
let body_replace = replaceAll(bodyTemplete, '[param_length]', funcInfo.params.length)
|
||||
body_replace = replaceAll(body_replace, '[getParme_replace]', paramGenResult)
|
||||
if(funcInfo.retType !== 'void') {
|
||||
body_replace = replaceAll(body_replace, '[return_type_define]', funcInfo.retType + ' res;')
|
||||
let bodyTemplete = directFuncJson.cppFuncDetails.funcBodyTemplete
|
||||
let body_replace = replaceAll(bodyTemplete, '[funcName]', funcName_replace)
|
||||
let funcGetParamTemplete = directFuncJson.cppFuncDetails.funcGetParamTemplete
|
||||
let genParam_replace = replaceAll(funcGetParamTemplete, '[param_length]', funcInfo.params.length)
|
||||
genParam_replace = replaceAll(genParam_replace, '[getParam_replace]', paramGenResult)
|
||||
if (funcInfo.params.length !== 0) {
|
||||
body_replace = replaceAll(body_replace, '[func_getParam_replace]', genParam_replace)
|
||||
} else {
|
||||
body_replace = replaceAll(body_replace, '[return_type_define]', '')
|
||||
body_replace = replaceAll(body_replace, '[func_getParam_replace]', '')
|
||||
}
|
||||
if(funcInfo.retType !== 'void') {
|
||||
let returnType = funcInfo.retType === 'std::string'? 'const char *': funcInfo.retType
|
||||
returnType = returnType === 'size_t'? 'int64_t': returnType
|
||||
let funcReturnTemplete = directFuncJson.cppFuncDetails.funcReturnTemplete
|
||||
let func_return_replace = replaceAll(funcReturnTemplete, '[return_type_define]', returnType + ' res;')
|
||||
func_return_replace = replaceAll(func_return_replace, '[return_replace]', retGenResult)
|
||||
body_replace = replaceAll(body_replace, '[func_return_replace]', func_return_replace)
|
||||
} else {
|
||||
body_replace = replaceAll(body_replace, '[func_return_replace]', '')
|
||||
}
|
||||
body_replace = replaceAll(body_replace, '[return_replace]', retGenResult)
|
||||
// console.info("body_replace: " + body_replace)
|
||||
|
||||
let cppContent = replaceAll(cppTemplete, '[include_replace]', includes_replace)
|
||||
cppContent = replaceAll(cppContent, '[funcName]', funcName_replace)
|
||||
cppContent = replaceAll(cppContent, '[body_replace]', body_replace)
|
||||
cppContent = replaceAll(cppContent, '[init_replace]', init_replace)
|
||||
// console.info("Last cppContent: " + cppContent)
|
||||
|
||||
// 将内容写入cpp文件
|
||||
let rootPath = path.resolve(indexPath, '..', '..', '..');
|
||||
let cppFilePath = path.join(rootPath, 'test.cpp');
|
||||
// console.info("cppFilePath: " + cppFilePath)
|
||||
writeFile(cppFilePath, cppContent)
|
||||
// 先判断cppFilePath是否存在,若存在则追加写入内容
|
||||
if (fs.existsSync(cppFilePath)) {
|
||||
// 读取cpp文件内容
|
||||
const cppFileContent = fs.readFileSync(cppFilePath, 'utf8');
|
||||
let includePosition = cppFileContent.indexOf('#include');
|
||||
let includes = includes_replace.split('\n')
|
||||
let newIncludes = ""
|
||||
for (let i = 0; i < includes.length; i++) {
|
||||
if (cppFileContent.indexOf(includes[i]) < 0) {
|
||||
newIncludes += includes[i] + '\n'
|
||||
}
|
||||
}
|
||||
let newCppFileContent = cppFileContent
|
||||
if (newIncludes !== "") {
|
||||
// 追加写入#include
|
||||
newCppFileContent = newCppFileContent.slice(0, includePosition) + newIncludes + newCppFileContent.slice(includePosition);
|
||||
}
|
||||
// 追加写入方法体
|
||||
let funcPosition = newCppFileContent.indexOf('EXTERN_C_START')
|
||||
newCppFileContent = newCppFileContent.slice(0, funcPosition) + body_replace + newCppFileContent.slice(funcPosition);
|
||||
|
||||
// 追加写入 方法的初始化
|
||||
let initPosition = newCppFileContent.indexOf('napi_property_descriptor desc[] = {') + 'napi_property_descriptor desc[] = {'.length;
|
||||
newCppFileContent = newCppFileContent.slice(0, initPosition) + '\n ' + init_replace + newCppFileContent.slice(initPosition);
|
||||
writeFile(cppFilePath, newCppFileContent)
|
||||
} else {
|
||||
let cppTemplete = directFuncJson.cppFuncTemplete
|
||||
let cppContent = replaceAll(cppTemplete, '[include_replace]', includes_replace)
|
||||
cppContent = replaceAll(cppContent, '[body_replace]', body_replace)
|
||||
cppContent = replaceAll(cppContent, '[init_replace]', init_replace)
|
||||
// 第一次生成
|
||||
writeFile(cppFilePath, cppContent)
|
||||
}
|
||||
}
|
||||
|
||||
function replaceAll(s, sfrom, sto) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
|
||||
* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
@ -16,51 +16,11 @@ const { NapiLog } = require("../tools/NapiLog");
|
||||
const util = require('util');
|
||||
const { writeFile, generateRandomInteger } = require("../tools/tool");
|
||||
const path = require('path')
|
||||
const fs = require("fs");
|
||||
const LENGTH = 10;
|
||||
const TWO_DECIMAL = 2;
|
||||
|
||||
let abilityTestTemplete = `
|
||||
import hilog from '@ohos.hilog';
|
||||
import testNapi from 'libentry.so';
|
||||
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
|
||||
|
||||
export default function abilityTest() {
|
||||
describe('ActsAbilityTest', () => {
|
||||
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
|
||||
beforeAll(() => {
|
||||
// Presets an action, which is performed only once before all test cases of the test suite start.
|
||||
// This API supports only one parameter: preset action function.
|
||||
})
|
||||
beforeEach(() => {
|
||||
// Presets an action, which is performed before each unit test case starts.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: preset action function.
|
||||
})
|
||||
afterEach(() => {
|
||||
// Presets a clear action, which is performed after each unit test case ends.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: clear action function.
|
||||
})
|
||||
afterAll(() => {
|
||||
// Presets a clear action, which is performed after all test cases of the test suite end.
|
||||
// This API supports only one parameter: clear action function.
|
||||
})
|
||||
it('assertContain', 0, () => {
|
||||
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
|
||||
let a = 'abc';
|
||||
let b = 'b';
|
||||
|
||||
[func_direct_testCase]
|
||||
|
||||
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
|
||||
expect(a).assertContain(b);
|
||||
expect(a).assertEqual(a);
|
||||
// 断言 如:expect(result).assertEqual(2+3)
|
||||
})
|
||||
})
|
||||
}
|
||||
`
|
||||
const SERIAL = 5;
|
||||
const MODTWO = 2;
|
||||
|
||||
// 随机生成浮点数值
|
||||
function generateRandomArbitrary(min, max, fixed) {
|
||||
@ -77,7 +37,20 @@ function generateRandomBoolValue() {
|
||||
return randomBool;
|
||||
}
|
||||
|
||||
function generateFuncTestCase(params, tsFuncName, indexPath) {
|
||||
// 随机生成字符串
|
||||
function generateRandomString(length) {
|
||||
let result = '';
|
||||
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let charactersLength = characters.length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function generateFuncTestCase(params, tsFuncName, testFilePath, directFuncJson) {
|
||||
let funcInfo = {
|
||||
"name": "",
|
||||
"params": [],
|
||||
@ -92,7 +65,6 @@ function generateFuncTestCase(params, tsFuncName, indexPath) {
|
||||
funcInfo.retType = params.functions[0].rtnType
|
||||
let funcParamDefine = ''
|
||||
let funcParamUse = ''
|
||||
// let funcParamExpect = ''
|
||||
// 判断函数有几个参数,依次给参数赋值
|
||||
for(let i = 0; i < funcInfo.params.length; i++) {
|
||||
if (getTestType(funcInfo.params[i].type) === 'int') {
|
||||
@ -104,6 +76,9 @@ function generateFuncTestCase(params, tsFuncName, indexPath) {
|
||||
} else if (getTestType(funcInfo.params[i].type) === 'bool') {
|
||||
funcParamDefine += util.format('let %s = %s\n', funcInfo.params[i].name, generateRandomBoolValue())
|
||||
funcParamUse += funcInfo.params[i].name + ', '
|
||||
} else if (getTestType(funcInfo.params[i].type) === 'string') {
|
||||
funcParamDefine += util.format('let %s = "%s"\n', funcInfo.params[i].name, generateRandomString(LENGTH))
|
||||
funcParamUse += funcInfo.params[i].name + ', '
|
||||
}
|
||||
}
|
||||
// 去除调用参数的最后一个','
|
||||
@ -114,17 +89,39 @@ function generateFuncTestCase(params, tsFuncName, indexPath) {
|
||||
// 加 hilog 打印
|
||||
let hilogContent = util.format('hilog.info(0x0000, "testTag", "Test NAPI %s: ", result);', tsFuncName)
|
||||
let func_test_replace = funcParamDefine + callFunc + hilogContent
|
||||
let abilityTestTemplete = directFuncJson.abilityTestTemplete
|
||||
// 替换random_number
|
||||
let serialNum = tsFuncName.substring(0,SERIAL)
|
||||
console.info("serialNum: " + serialNum)
|
||||
let funcTestContent = replaceAll(abilityTestTemplete,'[func_direct_testCase]', func_test_replace)
|
||||
|
||||
// let filePath = './entry/src/ohosTest/ets/test/Ability.test.ets'
|
||||
funcTestContent = replaceAll(funcTestContent, '[random_number]', serialNum)
|
||||
//console.info("funcTestContent: " + funcTestContent)
|
||||
// 将内容写入Ability.test.ets文件
|
||||
// __dirname //当前文件的绝对路径
|
||||
// ../
|
||||
// let relativeFilePath = './entry/src/ohosTest/ets/test/Ability.test.ets'
|
||||
let rootPath = path.resolve(indexPath, '..', '..', '..', '..', '..');
|
||||
let filePath = path.join(rootPath, 'ohosTest/ets/test/Ability.test.ets');
|
||||
// console.info("filePath: " + filePath)
|
||||
writeFile(filePath, funcTestContent)
|
||||
// 1.追加写入import模块 写在第一个import之前
|
||||
// 2.追加写入测试用例
|
||||
|
||||
// writeFile(testFilePath, funcTestContent)
|
||||
|
||||
const importContent = "import testNapi from 'libentry.so';"
|
||||
writeTestFile(testFilePath, importContent, funcTestContent)
|
||||
}
|
||||
|
||||
function writeTestFile(filePath, importContent, funcTestContent) {
|
||||
// 读取原本文件内容
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
const importPosition = fileContent.indexOf('import ');
|
||||
let newFileContent = fileContent;
|
||||
// 判断是否有该import语句,没有则添加
|
||||
if (fileContent.indexOf(importContent) < 0) {
|
||||
const newImportStatement = importContent + '\n';
|
||||
newFileContent = fileContent.slice(0, importPosition) + newImportStatement + fileContent.slice(importPosition);
|
||||
}
|
||||
// 追加写入测试用例
|
||||
let testCasePosition = newFileContent.lastIndexOf('})\n}')
|
||||
//console.info("testCasePosition: " + testCasePosition)
|
||||
newFileContent = newFileContent.slice(0, testCasePosition) + funcTestContent + newFileContent.slice(testCasePosition);
|
||||
|
||||
writeFile(filePath, newFileContent)
|
||||
}
|
||||
|
||||
function replaceAll(s, sfrom, sto) {
|
||||
@ -135,20 +132,23 @@ function replaceAll(s, sfrom, sto) {
|
||||
}
|
||||
|
||||
function getTestType(type) {
|
||||
if (type === 'uint32_t' || type === 'int32_t' || type === 'int16_t' || type === 'int64_t' || type === 'int') {
|
||||
if (type === 'uint32_t' || type === 'int32_t' || type === 'int16_t' ||
|
||||
type === 'int64_t' || type === 'int' || type === 'size_t') {
|
||||
return 'int'
|
||||
} else if (type === 'double_t' || type === 'double' || type === 'float') {
|
||||
return 'float'
|
||||
} else if (type === 'bool') {
|
||||
return 'bool'
|
||||
} else if (type === 'std::string' || type.substring(0,10) === 'const char') {
|
||||
return 'string'
|
||||
}
|
||||
}
|
||||
|
||||
function getJsType(type) {
|
||||
if (type === 'uint32_t' || type === 'int32_t' || type === 'int16_t' || type === 'int64_t' ||
|
||||
type === 'int' || type === 'double_t' || type === 'double' || type === 'float') {
|
||||
type === 'int' || type === 'double_t' || type === 'double' || type === 'float' || type === 'size_t') {
|
||||
return 'number'
|
||||
} else if (type === 'const char *' || type === 'std::string') {
|
||||
} else if (type.substring(0,10) === 'const char' || type === 'std::string') {
|
||||
return 'string'
|
||||
} else if (type === 'bool') {
|
||||
return 'boolean'
|
||||
|
@ -13,58 +13,63 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const MOVE_EIGHTEEN = 18;
|
||||
const MOVE_TWELVE = 12;
|
||||
const MOVE_SIX = 6;
|
||||
|
||||
function utf8ArrayToStr(array) {
|
||||
var out, i, len, c;
|
||||
var char2, char3;
|
||||
let char2, char3;
|
||||
|
||||
out = "";
|
||||
len = array.length;
|
||||
i = 0;
|
||||
let outStr = "";
|
||||
let len = array.length;
|
||||
let i = 0;
|
||||
while (i < len) {
|
||||
c = array[i++];
|
||||
switch (c >> 4) {
|
||||
let ch = array[i++];
|
||||
switch (ch >> 4) {
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||
// 0xxxxxxx
|
||||
out += String.fromCharCode(c);
|
||||
break;
|
||||
// 0xxxxxxx
|
||||
outStr += String.fromCharCode(ch);
|
||||
break;
|
||||
case 12: case 13:
|
||||
// 110x xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
|
||||
break;
|
||||
// 110x xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
outStr += String.fromCharCode(((ch & 0x1F) << 6) | (char2 & 0x3F));
|
||||
break;
|
||||
case 14:
|
||||
// 1110 xxxx 10xx xxxx 10xx xxxx
|
||||
// 1110 xxxx 10xx xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
char3 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x0F) << 12) |
|
||||
outStr += String.fromCharCode(((ch & 0x0F) << 12) |
|
||||
((char2 & 0x3F) << 6) |
|
||||
((char3 & 0x3F) << 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
return outStr;
|
||||
}
|
||||
|
||||
function stringToUint8Array(string, options = { stream: false }) {
|
||||
if (options.stream) {
|
||||
throw new Error(`Failed to encode: the 'stream' option is unsupported.`);
|
||||
function stringToUint8Array(string, option = { streamBool: false }) {
|
||||
if (option.streamBool) {
|
||||
throw new Error(`Failed to encode: the 'streamBool' option is unsupported.`);
|
||||
}
|
||||
let pos = 0;
|
||||
const len = string.length;
|
||||
let at = 0; // output position
|
||||
let tlen = Math.max(32, len + (len >> 1) + 7); // 1.5x size
|
||||
let target = new Uint8Array((tlen >> 3) << 3); // ... but at 8 byte offset
|
||||
let position = 0;
|
||||
// output position
|
||||
let atPos = 0;
|
||||
// 1.5x size
|
||||
let tlength = Math.max(32, len + (len >> 1) + 7);
|
||||
// ... but atPos 8 byte offset
|
||||
let target = new Uint8Array((tlength >> 3) << 3);
|
||||
|
||||
while (pos < len) {
|
||||
let value = string.charCodeAt(pos++);
|
||||
while (position < len) {
|
||||
let value = string.charCodeAt(position++);
|
||||
let isContinue = false;
|
||||
if (value >= 0xd800 && value <= 0xdbff) {
|
||||
if (pos < len) {// high surrogate
|
||||
const extra = string.charCodeAt(pos);
|
||||
if (position < len) {// high surrogate
|
||||
const extra = string.charCodeAt(position);
|
||||
if ((extra & 0xfc00) === 0xdc00) {
|
||||
++pos;
|
||||
++position;
|
||||
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
||||
}
|
||||
}
|
||||
@ -75,42 +80,42 @@ function stringToUint8Array(string, options = { stream: false }) {
|
||||
|
||||
if (!isContinue) {
|
||||
// expand the buffer if we couldn't write 4 bytes
|
||||
if (at + 4 > target.length) {
|
||||
tlen += 8; // minimum extra
|
||||
tlen *= (1.0 + (pos / string.length) * 2); // take 2x the remaining
|
||||
tlen = (tlen >> 3) << 3; // 8 byte offset
|
||||
if (atPos + 4 > target.length) {
|
||||
tlength += 8; // minimum extra
|
||||
tlength *= (1.0 + (position / string.length) * 2); // take 2x the remaining
|
||||
tlength = (tlength >> 3) << 3; // 8 byte offset
|
||||
|
||||
target = uint8Array(tlen, target);
|
||||
target = uint8Array(tlength, target);
|
||||
}
|
||||
|
||||
let calculateResult = calculate(value, target, at)
|
||||
let calculateResult = calculate(value, target, atPos)
|
||||
isContinue = calculateResult[0]
|
||||
target = calculateResult[1]
|
||||
at = calculateResult[2]
|
||||
atPos = calculateResult[2]
|
||||
}
|
||||
}
|
||||
return target.slice(0, at);
|
||||
return target.slice(0, atPos);
|
||||
}
|
||||
|
||||
function calculate(value, target, at) {
|
||||
function calculate(val, target, at) {
|
||||
let isContinue = false
|
||||
if ((value & 0xffffff80) === 0) { // 1-byte
|
||||
target[at++] = value; // ASCII
|
||||
if ((val & 0xffffff80) === 0) { // 1-byte
|
||||
target[at++] = val; // ASCII
|
||||
isContinue = true;
|
||||
} else if ((value & 0xfffff800) === 0) { // 2-byte
|
||||
target[at++] = ((value >> 6) & 0x1f) | 0xc0;
|
||||
} else if ((value & 0xffff0000) === 0) { // 3-byte
|
||||
target[at++] = ((value >> 12) & 0x0f) | 0xe0;
|
||||
target[at++] = ((value >> 6) & 0x3f) | 0x80;
|
||||
} else if ((value & 0xffe00000) === 0) { // 4-byte
|
||||
target[at++] = ((value >> 18) & 0x07) | 0xf0;
|
||||
target[at++] = ((value >> 12) & 0x3f) | 0x80;
|
||||
target[at++] = ((value >> 6) & 0x3f) | 0x80;
|
||||
} else if ((val & 0xffe00000) === 0) { // 4-byte
|
||||
target[at++] = ((val >> MOVE_EIGHTEEN) & 0x07) | 0xf0;
|
||||
target[at++] = ((val >> MOVE_TWELVE) & 0x3f) | 0x80;
|
||||
target[at++] = ((val >> MOVE_SIX) & 0x3f) | 0x80;
|
||||
} else if ((val & 0xffff0000) === 0) { // 3-byte
|
||||
target[at++] = ((val >> MOVE_TWELVE) & 0x0f) | 0xe0;
|
||||
target[at++] = ((val >> MOVE_SIX) & 0x3f) | 0x80;
|
||||
} else if ((val & 0xfffff800) === 0) { // 2-byte
|
||||
target[at++] = ((val >> MOVE_SIX) & 0x1f) | 0xc0;
|
||||
} else {
|
||||
isContinue = true;
|
||||
}
|
||||
if (!isContinue) {
|
||||
target[at++] = (value & 0x3f) | 0x80;
|
||||
target[at++] = (val & 0x3f) | 0x80;
|
||||
}
|
||||
return [isContinue, target, at]
|
||||
}
|
||||
@ -135,7 +140,7 @@ function writeFile(fn, str) {
|
||||
fs.writeFileSync(fn, data);
|
||||
}
|
||||
|
||||
function appendWriteFile(fn, str) {
|
||||
function appendWriteFile(fn, str) {
|
||||
fs.appendFile(fn, str, 'utf8', err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
@ -151,9 +156,21 @@ function generateRandomInteger(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFile,
|
||||
writeFile,
|
||||
appendWriteFile,
|
||||
generateRandomInteger
|
||||
/**
|
||||
* 获取Json文件内容
|
||||
* @returns
|
||||
*/
|
||||
function getJsonCfg(jsonFilePath) {
|
||||
let jsonCfg = null; // json 配置文件
|
||||
let jsonFile = fs.readFileSync(jsonFilePath, { encoding: "utf8" });
|
||||
jsonCfg = JSON.parse(jsonFile);
|
||||
return jsonCfg;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFile,
|
||||
writeFile,
|
||||
appendWriteFile,
|
||||
generateRandomInteger,
|
||||
getJsonCfg
|
||||
}
|
@ -18,15 +18,15 @@ function search(ss, data) {
|
||||
ss = replaceAll(ss, "\\.", "\\.")
|
||||
let reg = new RegExp(ss);
|
||||
let tt = reg.exec(data);
|
||||
if (tt == null) return null;
|
||||
if (tt === null || tt === undefined) return null;
|
||||
let ret = { "regs": [] }
|
||||
for (let i = 0; i < tt.length; i++) {
|
||||
let p = data.indexOf(tt[i]);
|
||||
if (tt[i] == null) {
|
||||
ret["regs"].push([-1, -1])
|
||||
if (tt[i] === null || tt[i] === undefined) {
|
||||
ret.regs.push([-1, -1])
|
||||
}
|
||||
else {
|
||||
ret["regs"].push([p, p + tt[i].length])
|
||||
ret.regs.push([p, p + tt[i].length])
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ function search(ss, data) {
|
||||
|
||||
function match(ss, data) {
|
||||
let tt = search(ss, data)
|
||||
if (tt != null && tt.regs[0][0] == 0) return tt;
|
||||
if ((tt !== null && tt !== undefined) && tt.regs[0][0] === 0) return tt;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
const { NapiLog } = require("../tools/NapiLog");
|
||||
const { writeFile, appendWriteFile, generateRandomInteger } = require("../tools/Tool");
|
||||
const { writeFile, appendWriteFile, generateRandomInteger, getJsonCfg } = require("../tools/Tool");
|
||||
const path = require('path')
|
||||
const re = require("../tools/re");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const util = require('util');
|
||||
const readline = require('readline');
|
||||
const { generateDirectFunction } = require('../napiGen/functionDirect')
|
||||
const { generateFuncTestCase } = require('../napiGen/functionDirectTest')
|
||||
|
||||
@ -62,7 +63,7 @@ function isStringType(cType) {
|
||||
}
|
||||
|
||||
function isBoolType(cType) {
|
||||
if (cType == 'bool') {
|
||||
if (cType === 'bool') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -121,7 +122,6 @@ function getJsTypeFromC(cType, typeInfo) {
|
||||
}
|
||||
let jsType = basicC2js(basicCtype)
|
||||
if (typeInfo.array) {
|
||||
// 替换原先的无用format
|
||||
jsType = util.format("Array<%s>", jsType)
|
||||
}
|
||||
return jsType
|
||||
@ -176,8 +176,8 @@ function analyzeRootFunction(rootInfo, parseResult) {
|
||||
let parseFunctions = parseResult.functions
|
||||
// console.info("parseFunctions: " + JSON.stringify(parseFunctions))
|
||||
for(var i = 0; i < parseFunctions.length; ++i) {
|
||||
// 普通方法生成模板
|
||||
let funcInfo = createFuncInfo(parseFunctions[i], false)
|
||||
|
||||
rootInfo.functions.push(funcInfo)
|
||||
}
|
||||
}
|
||||
@ -190,52 +190,121 @@ function getTab(tabLv) {
|
||||
return tab
|
||||
}
|
||||
|
||||
function genFunction(func, tabLv, needDeclare = false) {
|
||||
let tab = getTab(tabLv)
|
||||
function genFunction(func, funcJson) {
|
||||
let funcPrefix = func.isClassFunc ? "" : "function "
|
||||
let funcParams = ""
|
||||
for (var i = 0; i < func.params.length; ++i) {
|
||||
funcParams += i > 0 ? ", " : ""
|
||||
funcParams += func.params[i].name + ": " + func.params[i].type
|
||||
}
|
||||
let declareStr = needDeclare ? "declare " : ""
|
||||
|
||||
let indexTemplete = funcJson.directFunction.indexTemplete
|
||||
tsFuncName = 'KH' + generateRandomInteger(MIN_RANDOM, MAX_RANDOM) + '_' + func.name
|
||||
return util.format("export const %s:(%s) => %s;\n", tsFuncName, funcParams, func.retType)
|
||||
return util.format(indexTemplete, tsFuncName, funcParams, func.retType)
|
||||
}
|
||||
|
||||
function genTsContent(rootInfo) {
|
||||
function genTsContent(rootInfo, funcJson) {
|
||||
let tsContent = rootInfo.needCallback ? "import { AsyncCallback, Callback } from './../basic';\n\n" : ""
|
||||
|
||||
for(var i = 0; i < rootInfo.functions.length; ++i) {
|
||||
tsContent += genFunction(rootInfo.functions[i], 0, true)
|
||||
tsContent += genFunction(rootInfo.functions[i], funcJson)
|
||||
}
|
||||
|
||||
return tsContent
|
||||
}
|
||||
|
||||
function doGenerate(hFilePath, destDir) {
|
||||
let parseResult = parseFileAll(hFilePath)
|
||||
// console.info("parseResult: " + JSON.stringify(parseResult))
|
||||
function removeMarco(hFilePath, tempFilePath) {
|
||||
// 创建读取文件的流
|
||||
const fileStream = fs.createReadStream(hFilePath);
|
||||
// 创建逐行读取的接口
|
||||
const rl = readline.createInterface({
|
||||
input: fileStream,
|
||||
crlfDelay: Infinity
|
||||
});
|
||||
// 存储处理后的文件内容
|
||||
let processedContent = '';
|
||||
// 逐行读取文件内容并处理
|
||||
rl.on('line', (line) => {
|
||||
let tt = re.match('[A-Z_]+\([A-Za-z_ *]+\)', line)
|
||||
console.info("tt: " + JSON.stringify(tt))
|
||||
if (tt) {
|
||||
console.info("before line: " + line)
|
||||
let removeContent = re.getReg(line, tt.regs[0])
|
||||
line = line.substring(removeContent.length + 1, line.length)
|
||||
let index = line.indexOf(') ')
|
||||
console.info("index: " + index)
|
||||
if (index >= 0) {
|
||||
line = line.substring(0, index) + line.substring(index + 1, line.length)
|
||||
}
|
||||
}
|
||||
processedContent += line + '\n';
|
||||
});
|
||||
|
||||
// 完成读取操作
|
||||
rl.on('close', () => {
|
||||
console.log("processedContent: " + processedContent);
|
||||
writeFile(tempFilePath, processedContent)
|
||||
});
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function doGenerate(hFilePath, testFilePath, tsFilePath, cppFilePath) {
|
||||
let random = generateRandomInteger(MIN_RANDOM, MAX_RANDOM)
|
||||
let tempFileName = '../temp_' + random + '.h'
|
||||
let tempFilePath = path.join(hFilePath, tempFileName)
|
||||
removeMarco(hFilePath, tempFilePath)
|
||||
|
||||
while(!fs.existsSync(tempFilePath)) {
|
||||
await sleep(20); // 延迟 20 毫秒
|
||||
}
|
||||
const fileContent = fs.readFileSync(tempFilePath, 'utf8');
|
||||
console.info("fileContent: " + fileContent)
|
||||
|
||||
let parseResult = parseFileAll(tempFilePath)
|
||||
console.info("parseResult.functions: " + JSON.stringify(parseResult.functions))
|
||||
|
||||
let rootInfo = {
|
||||
"functions": [],
|
||||
"needCallback": false
|
||||
}
|
||||
// 普通方法生成一个模板模板
|
||||
analyzeRootFunction(rootInfo, parseResult)
|
||||
// 如果是class生成一个模板
|
||||
// analyzeRootClass()
|
||||
|
||||
// 读取Json文件
|
||||
let funcJsonPath = path.join(__dirname, '../function.json');
|
||||
console.info("funcJsonPath: " + funcJsonPath)
|
||||
let funcJson = getJsonCfg(funcJsonPath);
|
||||
|
||||
let hfileName = path.basename(hFilePath, ".h")
|
||||
let tsFilePath = destDir
|
||||
let tsContent = genTsContent(rootInfo)
|
||||
let tsContent = genTsContent(rootInfo, funcJson)
|
||||
console.info("tsContent: " + tsContent)
|
||||
appendWriteFile(tsFilePath, '\n' + tsContent)
|
||||
|
||||
// 调用napi转换的方法
|
||||
generateDirectFunction(parseResult, tsFuncName, destDir)
|
||||
generateDirectFunction(parseResult, tsFuncName, cppFilePath, funcJson.directFunction)
|
||||
|
||||
// 生成测试用例
|
||||
generateFuncTestCase(parseResult, tsFuncName, destDir)
|
||||
generateFuncTestCase(parseResult, tsFuncName, testFilePath, funcJson.directFunction)
|
||||
|
||||
// 删除生成的中间文件
|
||||
clearTmpFile(tempFilePath)
|
||||
|
||||
console.info('Generate success')
|
||||
}
|
||||
|
||||
function clearTmpFile(filePath) {
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
doGenerate
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## 工具代码框架介绍
|
||||
|
||||
native生成工具由由C++语法解释器和代码生成器两部分组成。C++语法解释器解析用户输入的.h文件内容,通过C++语法解析,将文件内容分解为类、方法、入参、成员属性等元素;代码生成器根据从语法解析器得到的这些元素,转换为对应的typescript语法的接口、方法、参数代码,生成.ts文件内容;同时通过语法解析器得到的元素,生成.h文件对应的napi框架代码和接口调用测试代码。
|
||||
native生成工具由由C++语法解释器和代码生成器两部分组成。C++语法解释器解析用户输入的.h文件内容,通过C++语法解析,将文件内容分解为类、方法、入参、成员属性等元素;代码生成器根据从语法解析器得到的这些元素,转换为对应的typescript语法的接口、方法、参数代码,生成.ts文件内容;同时通过语法解析器得到的元素,生成.h文件对应的napi框架代码和接口调用测试代码。native生成工具支持命令行和IntelliJ插件,本文主要介绍IntellIJ插件。
|
||||
|
||||
## 工具开发
|
||||
|
||||
|
@ -50,7 +50,7 @@ res = value0 + value1;
|
||||
在Ability.test.ets文件中增加断言:
|
||||
|
||||
```
|
||||
expect(result).assertEqual(2.3+3.2)
|
||||
expect(result).assertEqual(2+3)
|
||||
```
|
||||
|
||||
连接开发板,运行Ability.test.ets中的测试用例:
|
||||
|
@ -31,6 +31,8 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.regex.Pattern;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 项目文件入口
|
||||
@ -40,8 +42,9 @@ import java.io.InputStreamReader;
|
||||
* @version: v1.0.0
|
||||
* @since 2024-03-29
|
||||
*/
|
||||
public class GenDTS extends AnAction {
|
||||
private static final Logger LOG = Logger.getInstance(GenDTS.class);
|
||||
public class GenDts extends AnAction {
|
||||
private static final Logger LOG = Logger.getInstance(GenDts.class);
|
||||
|
||||
private boolean generateSuccess = true;
|
||||
private String sErrorMessage = "";
|
||||
|
||||
@ -58,10 +61,135 @@ public class GenDTS extends AnAction {
|
||||
return;
|
||||
}
|
||||
String destPath = file.getPath();
|
||||
// 异步执行
|
||||
runFun(destPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent event) {
|
||||
// 根据所选文件名,判断是否显示生成菜单项
|
||||
VirtualFile file = event.getData(PlatformDataKeys.VIRTUAL_FILE);
|
||||
if (file == null) {
|
||||
event.getPresentation().setEnabledAndVisible(false);
|
||||
} else {
|
||||
event.getPresentation().setEnabledAndVisible(patternFileName(file.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则匹配所选文件名是否符合规范
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return boolean 是否匹配
|
||||
*/
|
||||
public static boolean patternFileName(String fileName) {
|
||||
String pattern = "(([a-z_A-Z0-9]+).h)";
|
||||
return Pattern.matches(pattern, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成成功结果文件。
|
||||
*
|
||||
* @param process 进程ID
|
||||
*/
|
||||
private void genResultLog(Process process) {
|
||||
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getErrorStream(),
|
||||
StandardCharsets.UTF_8));
|
||||
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream(),
|
||||
StandardCharsets.UTF_8));
|
||||
String sErr = getErrorResult(stdError);
|
||||
String sOut;
|
||||
if (TextUtils.isEmpty(sErr)) {
|
||||
sOut = genInputLog(stdInput);
|
||||
if (!generateIsSuccess(sOut)) {
|
||||
sErrorMessage = sOut;
|
||||
}
|
||||
return;
|
||||
}
|
||||
generateSuccess = false;
|
||||
sErrorMessage = sErr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成文本内容。
|
||||
*
|
||||
* @param stdInput input buff
|
||||
* @return 返回当前输入框内容
|
||||
*/
|
||||
private String genInputLog(BufferedReader stdInput) {
|
||||
StringBuilder sOut = new StringBuilder();
|
||||
while (true) {
|
||||
String sTmp;
|
||||
try {
|
||||
if ((sTmp = stdInput.readLine()) == null) {
|
||||
break;
|
||||
}
|
||||
sOut.append(sTmp).append(getNewline());
|
||||
} catch (IOException ioException) {
|
||||
LOG.error(" genResultLog stdInput error" + ioException);
|
||||
}
|
||||
}
|
||||
return sOut.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成失败结果文件。
|
||||
*
|
||||
* @param stdError error buff
|
||||
* @return ErrorResult
|
||||
*/
|
||||
private String getErrorResult(BufferedReader stdError) {
|
||||
StringBuilder sErr = new StringBuilder();
|
||||
while (true) {
|
||||
String sTmp;
|
||||
try {
|
||||
if ((sTmp = stdError.readLine()) == null) {
|
||||
break;
|
||||
}
|
||||
sErr.append(sTmp).append(getNewline());
|
||||
} catch (IOException ioException) {
|
||||
LOG.error(" genResultLog stdInput error" + ioException);
|
||||
}
|
||||
}
|
||||
return sErr.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取换行符
|
||||
*
|
||||
* @return 换行符
|
||||
*/
|
||||
public static String getNewline() {
|
||||
return System.getProperty("line.separator");
|
||||
}
|
||||
|
||||
private boolean generateIsSuccess(String sOut) {
|
||||
generateSuccess = sOut.contains("success") || TextUtils.isEmpty(sOut);
|
||||
return generateSuccess;
|
||||
}
|
||||
|
||||
static class StreamConsumer extends Thread {
|
||||
InputStream is;
|
||||
|
||||
StreamConsumer(InputStream is) {
|
||||
super.setName("StreamConsumer");
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
LOG.error("StreamConsumer" + line);
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
LOG.error("StreamConsumer io error" + ioException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean callExtProcess(String command) throws IOException, InterruptedException {
|
||||
|
||||
if (TextUtils.isEmpty(command)) {
|
||||
@ -69,40 +197,20 @@ public class GenDTS extends AnAction {
|
||||
return false;
|
||||
}
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
// 读取输出流(正常输出)
|
||||
new Thread(() -> {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
|
||||
// 读取错误流(错误输出)
|
||||
new Thread(() -> {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
System.err.println(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
|
||||
// 等待进程结束
|
||||
int exitCode = process.waitFor();
|
||||
System.out.println("Process exited with code: " + exitCode);
|
||||
genResultLog(process);
|
||||
StreamConsumer errConsumer = new StreamConsumer(process.getErrorStream());
|
||||
StreamConsumer outputConsumer = new StreamConsumer(process.getInputStream());
|
||||
errConsumer.start();
|
||||
outputConsumer.start();
|
||||
|
||||
if (!generateSuccess) {
|
||||
GenNotification.notifyMessage(null, sErrorMessage, "提示", NotificationType.ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
errConsumer.join();
|
||||
outputConsumer.join();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -129,7 +237,7 @@ public class GenDTS extends AnAction {
|
||||
if (!file.exists()) {
|
||||
boolean isNewFile = file.createNewFile();
|
||||
if (!isNewFile) {
|
||||
LOG.info("writeTmpFile createNewFile error");
|
||||
LOG.info("writeTmpFile createNewFile error");
|
||||
}
|
||||
}
|
||||
FileOutputStream fw = new FileOutputStream(file);
|
||||
@ -175,6 +283,7 @@ public class GenDTS extends AnAction {
|
||||
/**
|
||||
* 生成命令行指令
|
||||
*
|
||||
* @param hFilePath .h文件路径
|
||||
* @return 返回命令行执行内容
|
||||
*/
|
||||
private String genCommand(String hFilePath) {
|
||||
@ -193,13 +302,15 @@ public class GenDTS extends AnAction {
|
||||
|
||||
File file = new File(tmpDirFile);
|
||||
String command = file.toString();
|
||||
command += " " + hFilePath;
|
||||
command += " -f " + hFilePath;
|
||||
// 判断用户是否输入了 "-o"+cpp文件路径, "-i"+dts文件路径,"-t"+test文件路径 从界面获取
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行主程序入口
|
||||
*
|
||||
* @param hFilePath .h文件路径
|
||||
* @return 执行状态
|
||||
*/
|
||||
public boolean runFun(String hFilePath) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@ -13,6 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.sk.na.utils;
|
||||
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationGroupManager;
|
||||
@ -20,13 +21,14 @@ import com.intellij.notification.NotificationGroup;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
/**
|
||||
* 通知框
|
||||
*
|
||||
* @author: liulongc digitalchina.com
|
||||
* @author: goujingjing
|
||||
* @see: tool conversion plug-in
|
||||
* @version: v1.0.0
|
||||
* @since 2022-05-27
|
||||
* @since 2024-04-02
|
||||
*/
|
||||
public class GenNotification {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user