#冲刺创作新星#[四]移植一个开源库 原创 精华

左翼风发
发布于 2022-9-27 10:05
浏览
1收藏

作者:王石,胡瑞涛

在**《[二]第一个OpenHarmony程序》**里提到了如何在OpenHarmony里开发一个HelloWorld程序,本节我们讨论下如果引用一个开源三方库,引入OpenHarmony编译并在设备上运行。

1. 开源库结构

我们选取比较简单的linux-logo开源库(库地址为:https://github.com/deater/linux_logo),

.
├── configure
├── contrib
├── libsysinfo-0.3.0
├── linux_logo.c
├── LINUX_LOGO.FAQ
├── linux_logo.h
├── ll_asm
├── load_logo.c
├── load_logo.h
├── load_logos.h
├── logo_config
├── logo_config.BSD
├── logo_config.Irix
├── logo_config.Solaris
├── logos
├── logo_types.h
├── parse_logos
├── parse_logos.c
├── po
├── tests
  • 在linux下的运行步骤和结果

    • 目录 ls
      #冲刺创作新星#[四]移植一个开源库-鸿蒙开发者社区

    • 配置 ./configure
      #冲刺创作新星#[四]移植一个开源库-鸿蒙开发者社区

    • 编译,运行 make&./linux_logo
      #冲刺创作新星#[四]移植一个开源库-鸿蒙开发者社区

3. 移植到OpenHarmony系统下进行编译运行

移植到OpenHarmony需要在三方库里增加文件夹和gn文件。OpenHarmony里三方库的位置在[代码目录]/third_party位置

我们可以在三方库里创建文件夹:linuxlogo,然后在linuxlogo里创建BUILD.gn文件。接下来我们需要拷贝第三方linuxlog文件到此文件夹内。然后我们需要阅读三方库的Makefile文件。以下是基本的makefile的规则:

  • makefile的规则

    • target... : prerequisites ...
                command
                ...
               -----------------------------------------------------------------
      
      • target就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个动作/伪目标/标签(Label),如clean

      • prerequisites就是,要生成那个target所需要的文件或是目标。

      • command也就是make需要执行的命令。(任意的Shell命令)

  • 这是一个文件的依赖关系,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。这就是整个make的依赖性。

  • GNU的make工作时的执行步骤如下:(想来其它的make也是类似)

    \1. 读入所有的Makefile。

    \2. 读入被include的其它Makefile。

    \3. 初始化文件中的变量。

    \4. 推导隐晦规则,并分析所有规则。

    \5. 为所有的目标文件创建依赖关系链。

    \6. 根据依赖关系,决定哪些目标要重新生成。

    \7. 执行生成命令。

    1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。规则包含两个部分,一个是依赖关系,一个是生成目标的方法。

  • 查看makefile(内部有一些关键命令的注释)

    • Makefile 会告诉 make 命令做什么(通常是如何编译和链接程序)。
    路径: /../Makefile
    
    -include Makefile.default
    
    PROGNAME = linux_logo
    
    #ifeq ($(OS),IRIX64) 
    #   LDFLAGS += -lintl
    #endif
    
    ifeq ($(OS),FreeBSD)
        LDFLAGS += -lintl
    endif
    
    #
    # Installation location
    #
    INSTALL_BINPATH = $(DESTDIR)$(PREFIX)/bin
    INSTALL_MANPATH = $(DESTDIR)$(PREFIX)/share/man
    INSTALL_DOCPATH = $(DESTDIR)$(PREFIX)/share/doc
    
    #
    # Libsysinfo location
    #
    LIBSYSINFO_INCLUDE = -I$(LIBSYSINFO)
    LIBSYSINFO_LIBRARY = -L$(LIBSYSINFO)
    
    ifneq ($(XGETTEXT),)
        TRANSLATIONS = translations
    endif
    
    all:	Makefile.default parse_logos linux_logo $(TRANSLATIONS)
    
    Makefile.default:	 
    	ifneq($(CONFIGURE_RAN),1)
    	$(error Please run configure first)
    	endif
    
    translations:
    	cd po && $(MAKE)
    
    logos-all:	logo_config
    	find ./logos -type f -a ! -name banner.logo -a ! -name classic.logo | sort >> logo_config
    	$(MAKE) all
    
    logo_config:
    	echo "./logos/banner.logo" > logo_config
    	echo "./logos/classic.logo" >> logo_config
    
    clean:	Makefile.default
    	rm -f *.o
    	rm -f linux_logo linux_logo-dyn parse_logos load_logos.h
    	rm -f *~
    	cd $(LIBSYSINFO) && $(MAKE) clean
    	cd po && $(MAKE) clean
    
    distclean: clean
    	cd $(LIBSYSINFO) && $(MAKE) distclean
    	rm -f Makefile.default logo_config
    #	rm -f po/linux_logo.pot
    
    #此处即为我们最终生成的linux_logo二进制文件,他依赖linux_logo.o,load_logo.o两个中间文件和libsysinfo.a这个库
    linux_logo:	linux_logo.o load_logo.o ./$(LIBSYSINFO)/libsysinfo.a
    	$(CROSS)$(CC) $(LDFLAGS) -o linux_logo linux_logo.o load_logo.o $(LIBSYSINFO_LIBRARY) ./$(LIBSYSINFO)/libsysinfo.a 
    
    linux_logo_shared:	linux_logo.o load_logo.o ./$(LIBSYSINFO)/libsysinfo.a
    	$(CROSS)$(CC) $(CFLAGS) -o linux_logo-dyn linux_logo.o load_logo.o $(LIBSYSINFO_LIBRARY) -lsysinfo
    
    #此处为libsysinfo.a库的编译脚本,他会进入$(LIBSYSINFO)目录再执行make命令
    ./$(LIBSYSINFO)/libsysinfo.a:
    	cd $(LIBSYSINFO) && $(MAKE)
    
    parse_logos:	parse_logos.o load_logo_native.o
    	$(CC) $(LDFLAGS) -o parse_logos parse_logos.o load_logo_native.o
    
    parse_logos.o:	parse_logos.c logo_config
    	$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c parse_logos.c
    
    load_logos.h:	logo_config parse_logos
    	./parse_logos
    
    load_logo.o:	load_logo.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c load_logo.c
    
    load_logo_native.o:	load_logo.c
    	$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -o load_logo_native.o -c load_logo.c	
    #此处即为我们最终生成的linux_logo文件的main函数
    linux_logo.o:	linux_logo.c defaults.h load_logos.h
    	@echo Compiling for $(OS)
    	@echo Edit defaults.h to change Default Values
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) \
    		      -DLOCALE_DIR=\"$(PREFIX)/share/locale\" -DUSE_I18N=$(USE_I18N) \
    		      -c linux_logo.c
    
    install:	linux_logo
    	$(INSTALL) -c -m755 $(PROGNAME) -D $(INSTALL_BINPATH)/$(PROGNAME)
    	$(INSTALL) -c -D -m644 $(PROGNAME).1.gz $(INSTALL_MANPATH)/man1/$(PROGNAME).1.gz
    	cd po && $(MAKE) install
    
    install-doc:
    	$(INSTALL) -c -d -m 755 $(INSTALL_DOCPATH)/$(PROGNAME)
    	$(INSTALL) -c -p -m 644 *[A-Z] $(INSTALL_DOCPATH)/$(PROGNAME)
    
    # The old way of installing
    install-by-copying:
    	cp linux_logo $(PREFIX)/bin
    
    • 进而查看libsysinfo.a静态库下的makefile
    路径:/../libsysinfo-0.3.0/Makefile
    
    include Makefile.default
    
    all:   libsysinfo.a
    
    clean:
    	rm -f *.o *~
    	rm -f libsysinfo.a libsysinfo.so config.h
    	cd AIX && $(MAKE) clean
    	cd Linux && $(MAKE) clean
    	cd FreeBSD && $(MAKE) clean
    	cd Irix && $(MAKE) clean
    	cd SunOS && $(MAKE) clean
    	cd w32 && $(MAKE) clean
    	cd all && $(MAKE) clean
    
    distclean: clean
    	rm -f Makefile.default
    install: 
    #选择一种系统类型,本文选用的是arm
    libsysinfo.a:	Linux/cpuinfo_alpha.c \                 
    		Linux/cpuinfo_ia64.c \
    		Linux/cpuinfo_ppc.c \
    		Linux/cpuinfo_vax.c \
    		Linux/cpuinfo_arm.c \
    		Linux/cpuinfo_m68k.c \
    		Linux/cpuinfo_s390.c \
    		Linux/cpuinfo_avr32.c \
    		Linux/cpuinfo_mips.c \
    		Linux/cpuinfo_sh3.c \
    		Linux/cpuinfo_x86.c \
    		Linux/cpuinfo_cris.c \
    		Linux/cpuinfo_parisc.c \
    	    Linux/cpuinfo_sparc.c \
    		Linux/sysinfo_linux.c
    	#进入all目录进行编译
    	cd all && $(MAKE)
    	#进入$(OS)目录进行编译
    	cd $(OS) && $(MAKE)
    	$(CROSS)$(AR) crus libsysinfo.a ./$(OS)/*.o ./all/*.o
    #此处为libsysinfo.a库的链接命令,此处也依赖了$(OS)和all文件夹里的内容
    shared:	libsysinfo.a
    	ld -shared -o libsysinfo.so ./$(OS)/*.o ./all/*.o
    
    • 查看all文件下的makefile
    路径: /../libsysinfo-0.3.0/all/Makefile
    
    include ../Makefile.default
    
    all:	bogomips.o fix_mhz.o parsing.o string_truncate.o sysinfo_common.o uname.o
    
    bogomips.o:	bogomips.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c bogomips.c
    
    fix_mhz.o:	fix_mhz.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c fix_mhz.c
    
    parsing.o:	parsing.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c parsing.c
    
    sysinfo_common.o:	sysinfo_common.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c sysinfo_common.c
    
    string_truncate.o:	string_truncate.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c string_truncate.c
    
    uname.o:	uname.c
    	$(CROSS)$(CC) $(CFLAGS) $(LIBSYSINFO_INCLUDE) -c uname.c
    
    clean:
    	rm -f *.o
    	rm -f *~
    

4. 编译开源库

  • 在BUILD.gn文件里增加以下内容。可以参考third-party/libuv/BUILD.gn里的内容
  ohos_executable("linuxlogo"){
    include_dirs=[
      "linux_logo-master",
      "linux_logo-master/libsysinfo-0.3.0",
      "linux_logo-master/libsysinfo-0.3.0/include",
    ]
    sources=[
      "linux_logo-master/linux_logo.c",
      "linux_logo-master/load_logo.c",
      "linux_logo-master/libsysinfo-0.3.0/Linux/cpuinfo_arm.c",
      "linux_logo-master/libsysinfo-0.3.0/all/bogomips.c",
      "linux_logo-master/libsysinfo-0.3.0/all/fix_mhz.c",
      "linux_logo-master/libsysinfo-0.3.0/all/parsing.c",
      "linux_logo-master/libsysinfo-0.3.0/all/string_truncate.c",
      "linux_logo-master/libsysinfo-0.3.0/all/sysinfo_common.c",
      "linux_logo-master/libsysinfo-0.3.0/all/uname.c",
      "linux_logo-master/libsysinfo-0.3.0/Linux/sysinfo_linux.c",
    ]
  }
  • 完成后,这个开源库还需要有一个入口进行加载,在此文章中我们仍然选取graphic的BULID.gn进行依赖添加
路径:test/xts/acts/grahpic/BUILD.gn
import("//build/ohos_var.gni")
group("graphic") {
  testonly = true
  if (is_standard_system) {
    deps = [
      "webGL:webGL_hap_test",
      "windowstandard:window_hap_test",
      //增加下面这行引入依赖
      "//third_party/libuv:linuxlogo",
    ]
  } else {
    deps = [
      "appaccount:appaccount_hap",
      "osaccount:osaccount_hap",
    ]
  }
}

同**《[二]编写一个Hello World文件》** 一样

在源码根目录下执行

./build.sh --product-name rk3568 --gn-args build_xts=true --build-target "acts" --gn-args is_standard_system=true

5. 执行开源库

编译完成后生成的第三方库验证程序linuxlogo使用流程

库文件会输出linuxlogo,路径为:out/rk3568/common/common

hrt@ubuntu:~/oh31/out/rk3568/common/common$ ls
glcts  libdeqp_ohos_platform.z.so  libhmicui18n.z.so  libphonenumber_standard.z.so  librosen_context.z.so  libc  libgif.z.so    libhmicuuc.z.so       linuxlogo
libprotobuf_standard.z.so     libsqlite.z.so
1. 拷贝linuxlogo到板子上
hdc_std.exe file send linuxlogo /data/local/tmp
//2. 登录板子,运行linuxlogo,如果第一次需要给权限
pc端:hdc_std.exe shell
板子:cd /data/local/tmp
板子:chmod +x runtest
板子:./linuxlogo

6. 结果显示

#冲刺创作新星#[四]移植一个开源库-鸿蒙开发者社区

补充

​ 在此,想说关于程序编译的一些规范和方法,一般来说,C、C++代码要先把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,Linux下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。

  • 编译时,是要确定编译目标所需要的源文件。编译器需要的是正确的语法,正确的函数与变量声明。对于后者,通常是需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
  • 链接时,是确定链接的时候需要依赖的额外的库。主要是链接函数和全局变量,所以,可以使用这些中间目标文件来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件,在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包生成库文件,在Windows下是Library File,也就是 .lib 文件;在UNIX下,是Archive File,即为.a文件。

​ 总结就是,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会报警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报Linker Error。

总结

本文主要介绍了在OpenHarmony下如何移植一个开源库,手写一个BUILD.gn、编译运行。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-12-8 15:03:19修改
4
收藏 1
回复
举报
4条回复
按时间正序
/
按时间倒序
离北况归
离北况归

移植完三方库后,通过napi将c、c++接口转换成js接口供应用层调用。作者有了解吗

回复
2022-9-27 14:22:04
左翼风发
左翼风发 回复了 离北况归
移植完三方库后,通过napi将c、c++接口转换成js接口供应用层调用。作者有了解吗

后面可以写一个文章

1
回复
2022-9-27 14:58:55
张三疯_
张三疯_

👍

回复
2022-9-27 16:17:13
离北况归
离北况归 回复了 左翼风发
后面可以写一个文章

期待

回复
2022-9-27 17:12:44
回复
    相关推荐