[文章]HarmonyOS开发,从hello world开始

阅读量0
0
4
按照官方教程操作没有坑我使用的编译环境是 VM12+Ubuntu18.04并且配置了SSH和Samba。

Windows下使用VSCODE和ssh,这样的好处是只要虚拟机开机,其他的操作都可以在Windows下进行,并且可以不用在同一台电脑上,有需要可以在云上搭建。

(让人难过的是,我家里用的电脑出现了故障,在公司用的又不能安装VM,所以之后准备换成docker)

下面这个代码树就是拿到开发板后,玩板子的人操作的空间,所有你打算写的代码都在这个。
10-9微信推送.png


1、新建文件夹
2.png


2、编写业务代码
  1. #include <stdio.h>
  2. #include "ohos_init.h"
  3. #include "ohos_types.h"

  4. void HelloWorld(void)
  5. {
  6.     printf("[DEMO] Hello world.n");
  7. }
  8. SYS_RUN(HelloWorld);
复制代码

这段代码引用了三个头文件:

stdio.h:学过C语言的都知道,调用printf的时候就需要使用;
ohos_types.h:C语言的变量做了重新定义;
ohos_types.h: SYS_RUN这个宏就在这个文件里定义;

在例程的gn文件中提示了头文件的路径:

//utils/native/lite/include

3.png


我们主要来看一下关于SYS_RUN的定义:
  1. typedef void (*InitCall)(void);
  2. #define USED_ATTR __attribute__((used))
  3. #ifdef LAYER_INIT_SHARED_LIB
  4. #define LAYER_INIT_LEVEL_0 0
  5. #define LAYER_INIT_LEVEL_1 1
  6. #define LAYER_INIT_LEVEL_2 2
  7. #define LAYER_INIT_LEVEL_3 3
  8. #define LAYER_INIT_LEVEL_4 4
  9. #define CTOR_VALUE_device 100
  10. #define CTOR_VALUE_core 110
  11. #define CTOR_VALUE_sys_service 120
  12. #define CTOR_VALUE_sys_feature 130
  13. #define CTOR_VALUE_app_service 140
  14. #define CTOR_VALUE_app_feature 150
  15. #define CTOR_VALUE_run  700
  16. #define LAYER_INITCALL(func, layer, clayer, priority)                                    
  17.     static __attribute__((constructor(CTOR_VALUE_##layer + LAYER_INIT_LEVEL_##priority)))
  18.         void BOOT_##layer##priority##func() {func();}
  19. #else
  20. #define LAYER_INITCALL(func, layer, clayer, priority)            
  21.     static const InitCall USED_ATTR __zinitcall_##layer##_##func
  22.         __attribute__((section(".zinitcall." clayer #priority ".init"))) = func
  23. #endif
  24. // Default priority is 2, priority range is [0, 4]
  25. #define LAYER_INITCALL_DEF(func, layer, clayer)
  26. LAYER_INITCALL(func, layer, clayer, 2)

  27. #define SYS_RUN(func) LAYER_INITCALL_DEF(func, run, "run")
复制代码

这段代码需要从下往上看
  1. (1)SYS_RUN(func)  
  2. func = HelloWorld
  3. (2) LAYER_INITCALL_DEF(func, run, "run")
  4. func = HelloWorld
  5. layer = run
  6. clayer = “run”
  7. (3) LAYER_INITCALL(func, layer, clayer, 2)
  8. func = HelloWorld
  9. layer = run
  10. clayer = “run”
  11. priority = 2
复制代码

LAYER_INITCALL有一个条件编译宏:

  • 定义了LAYER_INIT_SHARED_LIB
  1. #define LAYER_INITCALL(func, layer, clayer, priority)                                    
  2.     static __attribute__((constructor(CTOR_VALUE_##layer + LAYER_INIT_LEVEL_##priority)))
  3.         void BOOT_##layer##priority##func() {func();}
复制代码

整理得
  1. #define LAYER_INITCALL(func, layer, clayer, priority)                                    
  2.     static __attribute__((constructor(CTOR_VALUE_run + 2)))
  3.         void BOOT_ run2HelloWorld { HelloWorld ();}
复制代码

这里运用GUN文档中对构造属性的说明
  1. constructor
  2. destructor
  3. constructor (priority)
  4. destructor (priority)
  5. The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () completes or exit () is called. Functions with these attributes are useful for initializing data that is used implicitly during the execution of the program.
  6. You may provide an optional integer priority to control the order in which constructor and destructor functions are run. A constructor with a smaller priority number runs before a constructor with a larger priority number; the opposite relationship holds for destructors. So, if you have a constructor that allocates a resource and a destructor that deallocates the same resource, both functions typically have the same priority. The priorities for constructor and destructor functions are the same as those specified for namespace-scope C++ objects (see C++ Attributes).
复制代码

就是在这里定义了一个函数BOOT_ run2HelloWorld,在这个函数里调用应用层中的HelloWorld,且这个函数的执行顺序是在main之前,其优先级为702。

2.没定义LAYER_INIT_SHARED_LIB
  1. #define LAYER_INITCALL(func, layer, clayer, priority)            
  2.     static const InitCall USED_ATTR __zinitcall_##layer##_##func
  3.         __attribute__((section(".zinitcall." clayer #priority ".init"))) = func
复制代码

整理得
  1. #define LAYER_INITCALL(func, layer, clayer, priority)            
  2.     static const InitCall USED_ATTR __zinitcall_run_HelloWorld
  3.         __attribute__((section(".zinitcall." “run” “2”".init"))) = HelloWorld
复制代码

最终得
  1. #define LAYER_INITCALL(func, layer, clayer, priority)            
  2.     static const InitCall USED_ATTR __zinitcall_run_HelloWorld
  3.         __attribute__((section(".zinitcall.run2.init"))) = HelloWorld
复制代码
InitCall是一个没有参数没有返回值的函数指针类型,#define USED_ATTR __attribute__((used))用来修饰一个变量或者函数,目的是告诉编译器,修饰的这个变量或者函数是被使用的,即使没有使用也不要优化掉
__attribute__((section(".zinitcall.run2.init")))用来修饰一个变量或者函数,目的是告诉编译器,修饰的这个变量或者函数要被编译连接到.zinitcall.run2.init这个代码段里在系统运行后,.zinitcall.run2.init代码段的内容会被逐个执行,就会执行到我们的HelloWorld函数。


那么原宏定义就是定义了一个静态常量__zinitcall_run_HelloWorld,这个常量的类型是个InitCall的指针类型,这个常量的值为HelloWorld,对应一个函数,就是这个函数的地址。并将这个常量编译连接到.zinitcall.run2.init代码段中。

熟悉Linux的都知道,这个是Linux的常规操作,将一系列初始化函数编译链接到一起,运行时候整块取出去执行。

在官方例程的这个脚本文件下

code-1.0vendorhisihi3861hi3861buildbuild_tmpscriptslink.lds
包含下面内容:
  1. __zinitcall_run_start = .;
  2.        KEEP (*(.zinitcall.run0.init))
  3.        KEEP (*(.zinitcall.run1.init))
  4.        KEEP (*(.zinitcall.run2.init))
  5.        KEEP (*(.zinitcall.run3.init))
  6.        KEEP (*(.zinitcall.run4.init))
  7.        __zinitcall_run_end = .;
复制代码

刚刚被定义的那个函数指针,编译后就会按照声明的位置,保存在目标代码.zinitcall.run2.init 代码段中
结合连老师的链接更清晰:



补充一下关于宏定义#和##
#:字符串化
##:把宏参数连接在一起,参数在中间两边都需要##
宏定义用#和##的地方宏参数不会再展开,如果需要将传入参数的宏再次展开,需要中间增加一层转换,把所有宏的参数在这层里全部展开,在最下层使用#或者##。


3、业务构建成静态库的BUILD.gn文件,跟刚刚的.c文件在同一个目录下
  1. static_library("myapp") {
  2.     sources = [
  3.         "hello_world.c"
  4.     ]
  5.     include_dirs = [
  6.         "//utils/native/lite/include"
  7.     ]
  8. }
复制代码

BUILD.gn文件由三部分内容(目标、源文件、头文件路径)构成:

· static_library中指定业务模块的编译结果,为静态库文件libmyapp.a,开发者根据实际情况完成填写。
· sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含"//"则表示绝对路径(此处为代码根路径),若不包含"//"则表示相对路径。

· include_dirs中指定source所需要依赖的.h文件路径。

4、编写模块BUILD.gn文件,指定需参与构建的特性模块,在app这个目录下,和刚刚新建的那个文件夹(my_first_app)在同一个文件夹下
  1. <p class="MsoNormal">import("//build/lite/config/component/lite_component.gni")<o:p></o:p></p><p class="MsoNormal"><o:p> </o:p></p><p class="MsoNormal">lite_component("app") {<o:p></o:p></p><p class="MsoNormal">    features = [<o:p></o:p></p><p class="MsoNormal">        "my_first_app:myapp",<o:p></o:p></p><p class="MsoNormal">    ]<o:p></o:p></p><p class="MsoNormal">}</p>
复制代码

"my_first_app:myapp"添加到features字段中,刚刚写的那个代码就能被编译了。

· my_first_app是相对路径,指向./applications/sample/wifi-iot/app/my_first_app/BUILD.gn。
· myapp是目标,指向./applications/sample/wifi-iot/app/my_first_app/BUILD.gn中的static_library("myapp")。


编译后进行烧录
4.png


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友