!453 增加h2sa工具的文档

Merge pull request !453 from 胡瑞涛/master
This commit is contained in:
openharmony_ci 2024-09-18 05:40:34 +00:00 committed by Gitee
commit f3db74c777
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
19 changed files with 296 additions and 130 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -0,0 +1,81 @@
## Develop Guide
### service代码生成工具使用场景
当开发人员为OpenHarmony系统框架开发某些功能时有时需要将这个功能包装成一个独立的服务进程运行在系统中为了其它应用进程能够调用此服务开发人员需要基于系统IPC通信框架编写一套远程接口调用实现。 Service代码生成工具能够帮助用户生成框架代码提升开发效率。用户只需提供一个定义远程方法的.h头文件工具会自动生成整个Service框架的代码包含Ability注册、proxy/stub类实现、MessageParcel数据包构造、Service子系统编译及开机自启动相关配置文件。用户可基于框架代码专注于业务功能的编写。
![image](../figures/service_file.png)
---
### service工具代码框架说明
~~~
napi_generator\src\cli\h2sa
h2sa
├── docs //文档
├── README_ZH.md //usage,使用说明
├── package.json //Node.js打包配置文件
└── src
├── gen
│ ├── analyze.js //包含用于分析C++头文件的逻辑。读取头文件内容;解析文件以提取类、方法、参数等信息。
│ ├── file_template.js //包含生成代码时使用的模板字符串。模板中的占位符将在代码生成过程中被实际的数据替换以生成所需的代码文件。之后换成inl或者tmpl模板文件并分出不同版本的文件夹不同类型的文件
│ ├── generate.js //包含核心的代码生成逻辑。调用analyze.js来获取分析结果使用fileTemplate.js中的模板和分析结果来生成代码。
│ ├── header_parser.py //脚本与analyze.js协同工作用于解析C++头文件
│ └── test.h //生成sa的提示文件
│ └── main.js //项目的入口。初始化日志记录和其它工具解析命令行参数以确定用户想要执行的操作调用generate.js来启动代码生成过程。
└── tools
├── common.js //包含整个项目中使用的通用函数和常量
├── file_rw.js //包含文件读写操作的JavaScript模块
├── napi_log.js //日志记录模块
├── re.js //包含正则表达式相关功能的模块
└── tool.js //包含一些辅助工具函数
~~~
### 运行逻辑
![image](../figures/service_runLogic.png)
main.js为脚本入口其中使用stdio.getopt获取参数其中,参数详情如下:
-f定义远程服务的.h文件
-l, 日志级别0-3默认为1
-o, 生成框架代码输入到指定路径下;
-s, 指定serviceID。 -
-v, 指定版本3.2和4.1默认版本为3.2
~~~
let ops = stdio.getopt({
'filename': { key: 'f', args: 1, description: '.h file', default: '' },
'out': { key: 'o', args: 1, description: 'output directory', default: '.' },
'loglevel': { key: 'l', args: 1, description: 'Log Level: 0~3', default: '1' },
'serviceId': { key: 's', args: 1, description: 'service register id: 9000~16777214', default: '9000' },
'versionTag': { key: 'v', args: 1, description: 'version tag: 4.1 / 3.2', default: '3.2' }
});
~~~
### 开发指导
#### 适配新版本
用户可对工具进行二次开发。例如当前工具适配的源码版本是4.1,需要适配其它版本时,可修改以下文件进行适配:
1.在main.js中在allowedVersion数组中加入适配的版本号如4.1统一为v4_1, 5.0统一为v5_0。
2.在file_template.js中以适配5.0源码为例参考bundleJsonTemplate41buildGnTemplate41 新增v5_0版本对应的bundle.json模板、BUILD.gn模板在module.exports中新增5.0版本的bundle.json,BUILD.gn模板对应的变量名。
3.在generate.js中在doGenerate方法、genFilesByTemplate方法、genFileNames方法中修改相应代码当rootInfo.version为v5_0时替换对应的BUILD.gn, bundle.json模板路径。
4.适配新版本需要增加其它配置可在file_template.js中增加配置模板并增加配置文件模板的路径在generate.js中生成配置文件。
#### RoadMap
| 时间点 | 预期任务 | 验收标准 | 完成情况 |
| :----- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------- |
| 9月份 | 1代码去重方案提供不同版本模板proxy-stub框架hidumper框架hitrace<br />2适配5.0release版本增加代码中hidump、hitrace等日志跟踪定位工具的使用 | 1设计文档<br />2适配5.0时,可以编译出对应版本的工具,且编译验证成功 | 预计2024.9.24 |
| 10月份 | 增加testapp调用 sa接口包括死亡监听 | 杀掉服务后,有信息 | |

View File

@ -0,0 +1,198 @@
### Usage Guide
## 简介
h2sa工具即SERVICE框架生成工具当开发者为OpenHarmony系统框架开发某些功能时有时需要将这个功能包装成一个独立的服务进程运行在系统中为了其它应用进程能够调用此服务开发人员需要基于系统IPC通信框架编写一套远程接口调用实现。实现Service远程调用接口需要开发人员熟悉IPC通信框架了解proxy/stub的继承与实现方式掌握C++类型转为MessageParcel数据包的各种API方法有一定的学习成本。而Service代码生成工具能够帮助使用者生成框架代码提升开发效率。用户只需提供一个定义远程方法的.h头文件工具会自动生成整个Service框架的代码包含Ability注册、proxy/stub类实现、MessageParcel数据包构造、Service子系统编译及开机自启动相关配置文件。
## 约束
系统建议Ubuntu 20.04或者Windows 10
依赖版本VS Code 1.62.0
## 使用方法
#### 命令行
1. 安装python库 CppHeaderParser
~~~
pip install CppHeaderParser
~~~
2. 安装typescript在napi_generator/src/cli/h2sa/src目录下执行命令
~~~
npm i typescript
~~~
3. 安装stdio在napi_generator/src/cli/h2sa目录下执行命令
~~~
npm i stdio
~~~
4. 在napi_generator/src/cli/h2sa/src/gen目录下执行命令生成service框架代码
~~~
node main.js -f test.h
~~~
其中,参数详情如下:
-f定义远程服务的.h文件
-l可选参数日志级别0-3默认为1
-o可选参数生成框架代码输入到指定路径下
-s可选参数指定serviceID默认为19000
-v可选参数指定版本3.2和4.1默认版本为3.2
#### 生成物
1. 输出testservice文件夹其中的文件如下所示
![](../figures/h2sa_outRes.png)
~~~
├── BUILD.gn # 整个服务的编译文件包含2个内容:1)服务端程序动态库编译 2)客户端可执行程序编译
├── bundle.json # 将服务包装成一个OpenHarmoney子系统组件提供相关信息
├── etc # 服务启动配置目录,如果服务不需要开机自动启动,可以删除此目录。
│ ├── BUILD.gn
│ └── test_service.cfg # 服务自启动配置文件,编译烧录后会在/ect/init/下生成xxx_service.cfg启动文件
├── include
│ ├── test_service.h # 服务端头文件
│ ├── test_service_proxy.h # proxy 客户端头文件为开发人员封装remote请求发送的处理
│ └── test_service_stub.h # stub 服务端头文件为开发人员封装remote请求接收的处理
├── interface
│ └── i_test_service.h # 由用户提供的.h文件生成的remote接口文件stub和proxy都基于此文件实现接口。
├── sa_profile
│ ├── 19000.json # 服务配置文件
│ └── BUILD.gn
└── src
├── i_test_service.cpp # 接口实现文件
├── test_client.cpp # 客户端程序
├── test_service.cpp # 服务端程序
├── test_service_proxy.cpp # 客户端代理实现
└── test_service_stub.cpp # 服务端 stub 实现
~~~
#### 应用和验证
1. 将生成的testservice文件夹放在对应版本的源码根目录下
2. 修改服务配置文件
在foundation/systemabilitymgr/samgr/interfaces/innerkits/samgr_proxy/include/system_ability_definition.h增加以下一行
```
TEST_SERVICE_ID = {serviceID}, //保证ID与参数-s的值一致且没有重复例如默认值19000
```
3. 修改子系统配置文件
在build/subsystem_config.json中增加以下内容。
```
"testservice": {
"path":"testservice",
"name": "testservice"
}
```
4. 修改产品配置如rk3568
在vendor/hihope/rk3568/config.json中增加以下内容
```
{
"subsystem": "testservice",
"components": [
{
"component": "testservice_part",
"features": []
}
]
}
```
5. 修改权限配置
在相应的产品目录的vendor/hihope/rk3568/security_config/high_privilege_process_list.json中增加以下内容
```
{
"name": "testservice",
"uid": "system",
"gid": ["root", "system"]
}
```
6. selinux权限配置
vendor/hihope/rk3568/config.json中"build_selinux"属性若为true 即要配置selinux权限应修改以下文件若为false无需修改
> 1. testservice/etc/sample_service.cfg
>
> ```
> "secon" : "u:r:testservice:s0"
> ```
>
> 2. base/security/selinux_adapter/sepolicy/base/public/service_contexts
>
> ```
> 9016 u:object_r:sa_testservice:s0
> ```
>
> 3. base/security/selinux_adapter/sepolicy/base/public/service.te
>
> ```
> type sa_testservice, sa_service_attr;
> ```
>
> 4. base/security/selinux_adapter/sepolicy/ohos_policy/startup/init/system/init.te
>
> ```
> allow init testservice:process { getattr rlimitinh siginh transition };
> ```
>
> 5. base/security/selinux/sepolicy/base/public/type.te
>
> ```
> type testservice, sadomain, domain;
> ```
>
> 6. /base/security/selinux/sepolicy/base/te目录下增加新service的te文件新增文件名即为服务名例如testservice.te
>
> ```
> allow testservice init_param:file { map open read };
> allow testservice sa_testservice:samgr_class { add get };
> ```
7. 编码完成后,执行镜像编译命令
~~~
./build.sh --product-name 产品名
若编译rk3568开发板则执行
./build.sh --product-name rk3568
~~~
8. 烧录镜像
9. 运行验证
>验证一: shell登录开发板。 查看服务端进程是否已正常启动
>
>~~~
>ps -ef | grep testservice
>system 288 1 0 00:02:13 ? 00:00:00 testservice_sa --- 服务进程已正常运行
>~~~
>
>验证二:运行客户端
>
>~~~
>/system/bin/testclient
>~~~

View File

@ -1,117 +0,0 @@
/*
* Copyright (c) 2022 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
*
* 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,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef EXAM_H
#define EXAM_H
#include <mutex>
#include <thread>
#include <unordered_map>
#include <string>
using std::string;
namespace OHOS {
namespace Example {
class Basic {
public:
std::string basicName;
int getBasicId()
{
return this->basicId;
}
void setBasicId (int id)
{
this->basicId = id;
}
private:
int basicId;
};
class Human : public Basic {
public:
bool getOpFlag()
{
return this->opFlag;
};
void setOpFlag (bool flag)
{
this->opFlag = flag;
};
std::string getOpDesc()
{
return this->opDesc;
};
void setOpDesc(std::string desc)
{
this->opDesc = desc;
};
int getOpSeqId()
{
return this->opSeqId;
};
void setOpSeqId(int id)
{
opSeqId = id;
};
std::string opName;
int age = 0;
private:
bool opFlag;
std::string opDesc;
int opSeqId;
}
struct Book {
int getCc()
{
return this->cc;
};
void setCc(int cc)
{
this->cc = cc;
};
Basic getBasicObj()
{
return this->basicObj;
};
void setBasicObj(Basic obj)
{
this->basicObj = obj;
};
public:
int aa;
bool bb;
Basic direcObj;
private:
int cc;
Basic basicObj;
}
/**
* @brief service服务IPC调用接口
* @ServiceClass
*/
class Exam2 {
public:
Book getBook(Basic& basic);
int fun1 (Book v1);
int fun2 (Basic& basic, Human& human);
};
} // namespace Example
} // namespace OHOS
#endif // EXAM_H

View File

@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { NapiLog } = require('../tools/NapiLog');
const { NapiLog } = require('../tools/napi_log');
const fs = require('fs');
const os = require('os');
const { AllParseFileList } = require('../tools/common');

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { NapiLog } = require('../tools/NapiLog');
const { NapiLog } = require('../tools/napi_log');
const { replaceAll, getTab } = require('../tools/tool');
const re = require('../tools/re');
const { iServiceHTemplate, proxyHTemplate, stubHTemplate, serviceHTemplate, proxyCppTemplate, proxyFuncTemplate,
stubCppTemplate, stubInnerFuncTemplate, serviceCppTemplate, serviceFuncImplTemplate, clientCppTemplate, buildGnTemplate,
buildGnTemplate41, bundleJsonTemplate, bundleJsonTemplate41, profileGnTemplate, profileGnTemplate41, profileJsonTemplate,
profileXmlTemplate, serviceCfgTemplate, serviceCfgTemplate41, serviceCfgGnTemplate, iServiceCppTemplate } = require('./fileTemplate');
profileXmlTemplate, serviceCfgTemplate, serviceCfgTemplate41, serviceCfgGnTemplate, iServiceCppTemplate } = require('./file_template');
const { DATA_W_MAP, DATA_R_MAP, VECTOR_W_MAP, VECTOR_R_MAP, getParcelType, AllParseFileList, MarshallInfo,
ProcessingClassList } = require('../tools/common');
const numericTypes = ['short', 'int', 'long', 'long long', 'float', 'double'];

View File

@ -17,17 +17,17 @@ const stdio = require('stdio');
const fs = require('fs');
const re = require('../tools/re');
const { NapiLog } = require('../tools/NapiLog');
const { NapiLog } = require('../tools/napi_log');
const { print } = require('../tools/tool');
const analyze = require('./analyze');
const gen = require('./generate');
const { writeFile, createFolder } = require('../tools/FileRW');
const { writeFile, createFolder } = require('../tools/file_rw');
let ops = stdio.getopt({
'filename': { key: 'f', args: 1, description: '.h file', default: '' },
'out': { key: 'o', args: 1, description: 'output directory', default: '.' },
'loglevel': { key: 'l', args: 1, description: 'Log Level: 0~3', default: '1' },
'serviceId': { key: 's', args: 1, description: 'service register id: 9000~16777214', default: '9000' },
'serviceId': { key: 's', args: 1, description: 'service register id: 9000~16777214', default: '19000' },
'versionTag': { key: 'v', args: 1, description: 'version tag: 4.1 / 3.2', default: '3.2' }
});

View File

@ -15,8 +15,12 @@
const fs = require('fs');
function utf8ArrayToStr(array) {
var out, i, len, c;
var char2, char3;
let out;
let i;
let len;
let c;
let char2;
let char3;
out = '';
len = array.length;

View File

@ -32,9 +32,9 @@ NapiLog.LEV_DEBUG = 2;
NapiLog.LEV_INFO = 3;
const LEV_STR = ['[NON]', '[ERR]', '[DBG]', '[INF]'];
var logLevel = NapiLog.LEV_ERROR;
var logFileName = null;
var logResultMessage = [true, ''];
let logLevel = NapiLog.LEV_ERROR;
let logFileName = null;
let logResultMessage = [true, ''];
function getDateString() {
let nowDate = new Date();

View File

@ -28,7 +28,7 @@ function print(...args) {
}
String.prototype.format = function (...args) {
var result = this;
let result = this;
let reg = new RegExp('%[sd]{1}');
for (let i = 0; i < args.length; i++) {
let p = result.search(reg);
@ -57,7 +57,7 @@ function replaceAll(s, sfrom, sto) {
function getTab(tabLv) {
let tab = '';
for (var i = 0; i < tabLv; ++i) {
for (let i = 0; i < tabLv; ++i) {
tab += ' ';
}
return tab;