diff --git a/OAT.xml b/OAT.xml index 33d68ae9..21f1fb39 100644 --- a/OAT.xml +++ b/OAT.xml @@ -56,8 +56,8 @@ Note:If the text contains special characters, please escape them according to th - - + + @@ -65,6 +65,7 @@ Note:If the text contains special characters, please escape them according to th + diff --git a/examples/napitutorials/tool/commandLine/docs/guide/INSTRUCTION_ZH.md b/examples/napitutorials/tool/commandLine/docs/guide/INSTRUCTION_ZH.md index ea0c3ad5..a33b31c5 100644 --- a/examples/napitutorials/tool/commandLine/docs/guide/INSTRUCTION_ZH.md +++ b/examples/napitutorials/tool/commandLine/docs/guide/INSTRUCTION_ZH.md @@ -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中的测试用例: diff --git a/examples/napitutorials/tool/commandLine/src/function.json b/examples/napitutorials/tool/commandLine/src/function.json new file mode 100644 index 00000000..7aabaf2f --- /dev/null +++ b/examples/napitutorials/tool/commandLine/src/function.json @@ -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": { + } +} \ No newline at end of file diff --git a/examples/napitutorials/tool/commandLine/src/main.js b/examples/napitutorials/tool/commandLine/src/main.js index 7975bd15..a08e87a8 100644 --- a/examples/napitutorials/tool/commandLine/src/main.js +++ b/examples/napitutorials/tool/commandLine/src/main.js @@ -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); - } -} diff --git a/examples/napitutorials/tool/commandLine/src/napiGen/functionDirect.js b/examples/napitutorials/tool/commandLine/src/napiGen/functionDirect.js index 8c7158d5..810b00c1 100644 --- a/examples/napitutorials/tool/commandLine/src/napiGen/functionDirect.js +++ b/examples/napitutorials/tool/commandLine/src/napiGen/functionDirect.js @@ -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) { diff --git a/examples/napitutorials/tool/commandLine/src/napiGen/functionDirectTest.js b/examples/napitutorials/tool/commandLine/src/napiGen/functionDirectTest.js index 1ff1bef3..eb854329 100644 --- a/examples/napitutorials/tool/commandLine/src/napiGen/functionDirectTest.js +++ b/examples/napitutorials/tool/commandLine/src/napiGen/functionDirectTest.js @@ -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' diff --git a/examples/napitutorials/tool/commandLine/src/tools/Tool.js b/examples/napitutorials/tool/commandLine/src/tools/Tool.js index f6c518b4..3582e141 100644 --- a/examples/napitutorials/tool/commandLine/src/tools/Tool.js +++ b/examples/napitutorials/tool/commandLine/src/tools/Tool.js @@ -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; } +/** + * 获取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 + readFile, + writeFile, + appendWriteFile, + generateRandomInteger, + getJsonCfg } \ No newline at end of file diff --git a/examples/napitutorials/tool/commandLine/src/tools/re.js b/examples/napitutorials/tool/commandLine/src/tools/re.js index 55af6e69..61385d14 100644 --- a/examples/napitutorials/tool/commandLine/src/tools/re.js +++ b/examples/napitutorials/tool/commandLine/src/tools/re.js @@ -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; } diff --git a/examples/napitutorials/tool/commandLine/src/tsGen/tsMain.js b/examples/napitutorials/tool/commandLine/src/tsGen/tsMain.js index 59a7b5a2..ee85c383 100644 --- a/examples/napitutorials/tool/commandLine/src/tsGen/tsMain.js +++ b/examples/napitutorials/tool/commandLine/src/tsGen/tsMain.js @@ -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 } diff --git a/examples/napitutorials/tool/plugin/docs/guide/DEVELOP_ZH.md b/examples/napitutorials/tool/plugin/docs/guide/DEVELOP_ZH.md index 929fa344..1ed282a4 100644 --- a/examples/napitutorials/tool/plugin/docs/guide/DEVELOP_ZH.md +++ b/examples/napitutorials/tool/plugin/docs/guide/DEVELOP_ZH.md @@ -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插件。 ## 工具开发 diff --git a/examples/napitutorials/tool/plugin/docs/guide/INSTRUCTION_ZH.md b/examples/napitutorials/tool/plugin/docs/guide/INSTRUCTION_ZH.md index 280eaacf..0cacb3cd 100644 --- a/examples/napitutorials/tool/plugin/docs/guide/INSTRUCTION_ZH.md +++ b/examples/napitutorials/tool/plugin/docs/guide/INSTRUCTION_ZH.md @@ -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中的测试用例: diff --git a/examples/napitutorials/tool/plugin/src/com/sk/na/ng/GenDTS.java b/examples/napitutorials/tool/plugin/src/com/sk/na/ng/GenDTS.java index 8e074177..52384d93 100644 --- a/examples/napitutorials/tool/plugin/src/com/sk/na/ng/GenDTS.java +++ b/examples/napitutorials/tool/plugin/src/com/sk/na/ng/GenDTS.java @@ -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) { diff --git a/examples/napitutorials/tool/plugin/src/com/sk/na/utils/GenNotification.java b/examples/napitutorials/tool/plugin/src/com/sk/na/utils/GenNotification.java index b02c6aa0..88319030 100644 --- a/examples/napitutorials/tool/plugin/src/com/sk/na/utils/GenNotification.java +++ b/examples/napitutorials/tool/plugin/src/com/sk/na/utils/GenNotification.java @@ -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 {