Android编译系统初探

425 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

零.从HelloWorld.cpp说起

下面是一个最简单的C++程序,我们通过它来引入我们今天要讲解的Android编译系统

/*************************************************************************
@File Name : HelloWorld.cpp
@Author : SangYu
@Email : sangyu.code@gmail.com
@Description : Test for g++
************************************************************************/
#include <iostream>
int main(){
    std::cout<<"Hello World!"<<std::endl;
    return 0;
}

g++编译指令:g++ -o main HelloWorld.cpp 示例 这里我们只有一个cpp文件,只需要一条命令即可编译出我们的可执行文件main.exe

一.Make与Makefile

在大型项目开发过程中,我们会有不只一个module,同时有数以万计的cpp文件,它们的编译组织(先编译谁,后编译谁,编译的库依赖哪些静态库,动态库......)这些问题我们就不能够再通过一条条命令来解决。Make应运而生,帮助我们组织整个大型项目的编译过程,更重要地,能够识别出哪部分需要重新编译,并重新进行编译,并不需要重新编译整体项目。

The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them.

make官网:www.gnu.org/software/ma…

Makefile---描述了整个程序是如何编译链接的,Make的工作依赖于MakeFile的规则。

原书中的Makefile例子:

edit : main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
    rm edit main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o

一个简单的例子:

#ifndef _ADD_HEAD_
#define _ADD_HEAD_
int AddInt(int num1, int num2);
#endif
#include <iostream>
#include "Add.h"
int AddInt(int num1, int num2)
{
    return num1 + num2;
}

int main()
{
    int num1,num2;
    std::cout<<"Entry two number:";
    std::cin>>num1>>num2;
    std::cout<<"num1+num2:"<<num1<<"+"<<num2<<"="<<AddInt(num1,num2)<<std::endl;
    return 0;
}
exe_name=main
program_name=Add
objects_name=$(program_name).o
$(exe_name):$(objects_name)
        g++ -o $(exe_name) $(objects_name)
$(program_name).o:$(program_name).h

.PHONY: clean
clean:
        -rm $(exe_name) $(objects_name)

二.Android 编译系统

1.Android Make Build System---Android.mk

Android 基于Make做的Android.mk,目前大部分HAL层代码和JNI代码都是使用该种方式进行的编译 Android.mk的相关语法与关键字: developer.android.com/ndk/guides/…

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := libxmlrpc++
LOCAL_MODULE_HOST_OS := linux

LOCAL_RTTI_FLAG := -frtti
LOCAL_CPPFLAGS := -Wall -Werror -fexceptions
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/src

LOCAL_SRC_FILES := $(call \
    all-cpp-files-under,src)
include $(BUILD_SHARED_LIBRARY)

Android.mk方式的缺陷:

The Make build system is widely supported and used, but at Android's scale became slow, error prone, unscalable, and difficult to test. The Soong build system provides the flexibility required for Android builds.

编译变慢/易错的/无法扩展/难以测试

2.Soong build system---Android.bp

Soong 构建系统是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。

根据设计,Android.bp 文件很简单。它们不包含任何条件语句,也不包含控制流语句;所有复杂问题都由用 Go 编写的构建逻辑处理。

cc_library_shared {
    name: “libxmlrpc++”,
    
    rtti: true,
    cppflags: [
        “-Wall”,
        “-Werror”,
        “-fexceptions”,
    ],

    export_include_dirs: [“src”],
    srcs: [“src/**/*.cpp”],
    
    target: {
        darwin: {
            enabled: false,
        },
    },
}

source.android.google.cn/compatibili…

(1).Ninja

Ninja 是一个专注于速度的小型构建系统。它与其他构建系统在两个主要方面不同:

  • Ninja的输入文件被设计为由更高级的构建系统生成。
  • Ninja被设计为尽可能快地运行构建, 其他构建系统基于高级语言,而Ninja基于汇编。

Ninja基于汇编,专注于速度,不支持分支、循环等流程控制,也不支持逻辑运算,但它允许以其它语言如来维护这些复杂的编译流程和逻辑。例如,我们可以采用Makefile, go, python等等来维护编译的流程和逻辑。

(2).Blueprint

Blueprint 是一个元构建系统,该系统读取Blueprint文件来描述需要构建的模块,并生成一个Ninja清单来描述需要运行的命令及其依赖项。

Blueprint是ninja构建文件的生成器。android编译系统soong集成了Blueprint,Blueprint可将我们编写的android.bp解析生成一个ninja构建文件。我们在编译一个模块时,只需要将这个模块的android.bp文件配置好,编译系统会自动为这个模块生成ninja清单,最终使用ninja来调用gcc、clang、java、dex、aapt2等等命令来构建模块。

(3).kati

简单地说,kati就是一个转换工具,它可以将Makefile和.mk文件转换为ninja。

(4).Soong

Soong集成了Ninja, 而Ninja专注于速度,没有条件或流程控制语句,也不支持逻辑运算。但它允许以其它语言如来维护这些复杂的编译流程和逻辑。例如,我们可以继续采用makefile, 或者采用go语言来维护编译流程和逻辑。上面已经提到了Ninja,Blueprint, kati等等好几种工具,为了完整、快速的构建一个android系统,就需要一个“管家”来协调这些工具。例如,将.bp转换成ninja时使用Blueprint, 将Makefile转换成ninja时使用kati。这个选择转换工具、选择解析框架、解析维护构建逻辑的“管家”就是soong。

(5).androidmk

androidmk可以将android.mk转换成android.bp。

(6).bpfmt

用于格式化Android.bp文件的工具bpfmt,类似于gofmt。

Android.bp的规范格式包括:

  • 4个空格缩进。
  • 多元素列表的每个元素后的换行符。
  • 在lists和maps的结尾处始终包含一个逗号。

(7).ninja,Blueprint,kati,androidmk与Soong的关系和作用

  • kati可以将Android.mk文件转换成ninja文件。
  • androidmk可以将Android.mk文件转换成Android.bp文件
  • Blueprint可以将Android.bp文件转换成ninja文件。
  • Blueprint,kati,androidmk由Soong调用和协调,一起合作完成android源码的构建。