Android编译器及编译工具之编译工具

657 阅读11分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」。

上篇介绍了编译器,如果直接使用编译器管理我们的大中型项目,会相当痛苦而且根本无法维护。为了提效,于是产生了各种编译工具。所以今天介绍移动端开发对应的的编译工具。

Apache Ant

还记得那个用eclipse开发Android的年代吗?eclipse就是基于Ant工具来构建Android项目的。

Apache Ant 是由 Java 语言开发的工具,由 Apache 软件基金会所提供。Ant 是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。Apache Ant 的配置文件写成 XML 容易维护和书写,而且结构很清晰。

Ant 环境安装与配置

  1. 下载ant,mirror.esocc.com/apache//ant…
  2. 解压缩,配置ANT_HOME环境变量;
  3. 验证是否安装成功:ant -version

Ant 构建文件

一般来说,Ant 的构建文件默认为 build.xml,放在项目顶层目录中。然而,并没有限制构建文件必须命名为 bulid.xml,也并不限制放在项目顶层目录中。你可以将构建文件命名为其他名字,也可以将它放在项目的其他地方。

Ant构建脚本主要要理解projct、target、task三个概念:

project

<project name="demo" default="hello">

属性:

  • name : 项目名称

  • default:指定运行的target名称,没有指定时使用缺省的target;

  • basedir:基准路径,其他的相对路径都是基于这个基准路径的;

  • description:项目描述信息。

target

<target name="hello" depends="build,combineJavaScript" description="compile all source files into a single stand-alone script." />

属性:

  • name:target名称,这个属性是必须的;

  • depends:依赖目标,非必须

  • if:当属性设置时才执行该target,非必须;

  • unless:当属性没有设置时才执行该target,非必须;

  • description:target的描述信息,非必须;

一个target可以依赖于其他target。例如hello依赖于 build target 和 combineJaveScript target,ant 会按照 depends 中 target 出现的顺序依次执行,并且每个 target 只会被执行一次,即使有多个target依赖于他。

task

Task分为内置task和自定义task,每个task都是一段可执行的代码。

  1. 内置task

    <copy file="${xxx}/xxx.java" tofile="${zzz}/xxx.js" />
    
  2. 自定义task

    调用任务:

    <glslToJavascript minify="${build.minification}" minifystatefile="${buildDirectory}/minifyShaders.state">
        <glslfiles dir="${shadersDirectory}" includes="**/*.glsl" />
        <existingjsfiles dir="${shadersDirectory}" includes="**/*.js" excludes="*.profile.js" />
    </glslToJavascript>
    

    定义任务:

    <scriptdef name="glslToJavascript" language="javascript" src="${tasksDirectory}/glslToJavaScript.js" manager="bsf" classpathref="javascriptClassPath" loaderref="javascript.loader">
                <attribute name="minify" />
                <attribute name="minifystatefile" />
                <element name="glslfiles" type="fileset" />
                <element name="existingjsfiles" type="fileset" />
    </scriptdef>
    

构建文件配置示例:

<target name="build" description="Compile main source tree java files">
   <mkdir dir="${build.dir}"/>
   <javac destdir="${build.dir}" source="1.5" target="1.5" debug="true"
      deprecation="false" optimize="false" failonerror="true">
      <src path="${src.dir}"/>
      <classpath refid="master-classpath"/>
   </javac>
</target>

编译项目

编写好build.xml文件后,在该文件所在路径下执行ant即可完成编译。

Eclipse集成Ant

Eclipse中已经集成了Ant,我们可以直接在Eclipse中运行Ant。我们可以通过创建项目自动生成build.xml,直接编译。

具体可参考官网:ant.apache.org/ 以及中文教程:www.w3cschool.cn/ant/4xdp1hw…

maven

Apache Maven 是一个潜在的基于java的apache ant的构建工具的替代者。目前大部分的java项目都基于maven来管理。二者之间的关系和特点如下:

  1. ant脚本是可以直接运行在maven中的。maven和ant最大的差别就是在于maven的编译以及所有的脚本都有一个基础,就是POM(project object model)。这个模型定义了项目的方方面面,然后各式各样的脚本在这个模型上工作,而ant完全是自己定义。
  2. Maven对所依赖的包有明确的定义,如使用那个包,版本是多少,一目了然。而ant则通常是简单的inclde所有的jar。导致的最终结果就是,你根本无法确定JBoss中的lib下的common-logging是哪个版本的,唯一的方法就是打开 META-INF目录下MANIFEST.MF。估计JBoss迟早会转向Maven的。
  3. Maven是基于中央仓库的编译,即把编译所需要的资源放在一个中央仓库里,如jar,tld,pom,等。当编译的时候,maven会自动在仓库中找到相应的包,如果本地仓库没有,则从设定好的远程仓库中下载到本地。这一切都是自动的,而ant需要自己定义了。这个好处导致的结果就是,用maven编译的项目在发布的时候只需要发布源码,小得很,而反之,ant的发布则要把所有的包一起发布,显然maven又胜了一筹。
  4. maven有大量的重用脚本可以利用,如生成网站,生成javadoc,sourcecode reference,等。而ant都需要自己去写。试试 maven site的效果。
  5. maven目前不足的地方就是没有象ant那样成熟的GUI界面,不过mavengui正在努力中。目前使用maven最好的方法还是命令行,又快又方便。

配置maven环境

  1. 下载解压(以Mac环境为例):
$ curl -O http://mirrors.hust.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
$ tar -xvf  apache-maven-3.3.9-bin.tar.gz
$ sudo mv -f apache-maven-3.3.9 /usr/local/
  1. 配置环境变量:
vim ~/.bash_profile
export MAVEN_HOME=/usr/local/apache-maven-3.3.9
export PATH=${PATH}:${MAVEN_HOME}/bin
  1. 加载配置文件:source ~/.bash_profile
  2. 验证是否生效:
$ mvn -v
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
Maven home: /usr/local/apache-maven-3.3.9
...

Maven POM配置

POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。

执行任务或目标时,Maven 会在当前目录中查找 POM。它读取 POM,获取所需的配置信息,然后执行目标。

POM 中可以指定以下配置:

  • 项目依赖
  • 插件
  • 执行目标
  • 项目构建 profile
  • 项目版本
  • 项目开发者列表
  • 相关邮件列表信息

Maven 构建Java项目

执行mvn clean package即可自动构建。执行完后,我们已经构建了自己的项目并创建了最终的 jar 文件,关于命令的解释:

  • 我们给了 maven 两个目标,首先清理目标目录(clean),然后打包项目构建的输出为 jar(package)文件。
  • 打包好的 jar 文件可以在 target 中获得,名称为 xxx-1.0-SNAPSHOT.jar。

gradle

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,也增加了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各种繁琐配置。

面向Java应用为主。当前其支持的语言C++、Java、Groovy、Kotlin、Scala和Swift,计划未来将支持更多的语言。

Maven有一些天然的缺陷:

  1. Maven的配置文件是XML格式的,假如你的项目依赖的包比较多,那么XML文件就会变得非常非常长;
  2. XML文件不太灵活,假如你需要在构建过程中添加一些自定义逻辑,搞起来非常麻烦;
  3. Maven非常的稳定,但是相对的就是对新版java支持不足,哪怕就是为了编译java11,也需要更新内置的Maven插件。

Gradle里有两个基本概念:项目(projects)和任务(tasks)。项目由多个任务组成,一个项目可以理解为提供给不同设备的构建版本,如桌面版、网页版、安卓版、iOS版等等,也可以理解为一种行为,例如部署应用到生产环境。任务相当于Ant的target,可以理解成一个构建中原子性的工作,例如编译、打包、执行等。需要注意的是,Ant中他自己的命令例如javac、copy等也叫做task,但Ant的task远没有Gradle的task那么自由。

Gradle的构建分两个阶段,第一阶段是设置阶段(configuration phase),分析构建脚本,处理依赖关系和执行顺序等,脚本本身也需要依赖来完成自身的分析。第二阶段是执行阶段(execution phase),此阶段真正构建项目并执行项目下的各个任务。

Ant与Maven对于Gradle,前者编写容易,但功能有限,需要人工操作的过程也多;后者依托于庞大的依赖仓库,因此有着强大的外部依赖管理,但添加本地依赖并不方便,且项目不能灵活修改。而Gradle能很好地结合Ant与Maven各自的优点,可以随意的编写任务并组合成项目,直接利用Maven仓库,并且能很好的支持传递依赖和内部依赖

总结类比一下:Ant是自己织布制衣服,Maven是去商店买衣服,Gradle是3D打印衣服。

gradle环境安装

  1. 官网下载:gradle.org/releases/
  2. 解压安装
  3. 配置环境变量:GRADLE_HOME
  4. 使用gradle进行命令编译打包等

参考

make

上面介绍的以编译Java项目为主,下面主要编译C/C++项目。

make构建规则都写在Makefile文件里面,要学会如何Make命令,就必须学会如何编写Makefile文件。

make语法相当复杂,我们这里展示一个最简单的示例:

.SUFFIXES:.c.o
CC=gcc
SRCS=main.c
OBJS=$(SRCS:.c=.o)
EXEC=mytime
start:$(OBJS)
	$(CC) -o $(EXEC) $(OBJS)
	@echo '----------OK--------'
.c.o:
	$(CC) -o $@ -c $<
clean:
	rm -f $(OBJS)

cmake

CMake是一种跨平台编译工具。CMake主要是编写CMakeLists.txt文件

通过cmake命令将CMakeLists.txt文件转化为make所需要的Makefile文件,最后用make命令编译源码生成可执行程序或者库文件。

ninjia

Ninja 是Google的一名程序员推出的注重速度的构建工具,一般在Unix/Linux上的程序通过make/makefile来构建编译,而Ninja通过将编译任务并行组织,大大提高了构建速度。我一般是通过cmake来生成ninja的配置,进而进行编译。

像我们Andorid的源代码AOSP以及Webrtc的编译都用到了ninjia。

Makefile是设计来给人手写的,而Ninja设计出来是给其它程序生成的。 如果说Makefile是C语言,那么Ninja就是汇编语言。 如果说Makefile是一个DSL,那么Ninja就是一种配置文件。 Makefile支持分支、循环等流程控制,而Ninja只支持一些固定形式的配置。

二者的相同点是,都是为了控制编译流程而设计。 所以,他们的核心功能,都是指定目标,以及目标之间的依赖关系,自动计算执行顺序。

与Makefile相比,由于Ninja仅仅专注于核心的功能,所以有轻巧、速度快的优点。

Makefile默认文件名为Makefilemakefile,也常用.make.mk作为文件后缀。 Ninja的默认文件名是build.ninja,其它文件也以.ninja为后缀。 执行Makefile的程序,默认是GNU make,也有一些其它的实现。 Ninja的执行程序,就是ninja命令。

在Android项目中,make需要编译主机上安装,作为环境的一部分。 而ninja命令则是Android平台代码自带。

find prebuilts/ -name ninja
prebuilts/build-tools/linux-x86/asan/bin/ninja
prebuilts/build-tools/linux-x86/bin/ninja
prebuilts/build-tools/darwin-x86/bin/ninja

参考

bazel

bazel前面文章做过较详细介绍,它是一个开源的构建和测试工具,类似于Make、Maven和Gradle。它使用人类可读的高级构建语言。Bazel支持多种语言的项目,并为多种平台构建输出。Bazel支持跨多个存储库和大量用户的大型代码库。

qmake

qmake是类似于cmake的工具,它是qt带的编译工具。Qt [1] 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。

编写好cpp文件后,分别执行命令: qmake -project、qmake、make,然后执行就可以了,其实也只最终将配置文件转换成makefile进行编译。

参考

xmake

xmake 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。

虽然,简单易用是 xmake 的一大特色,但 xmake 的功能也是非常强大的,既能够像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,还有内置的包管理系统来帮助用户解决 C/C++依赖库的集成使用问题。

目前,xmake主要用于C/C++项目的构建,但是同时也支持其他native语言的构建,可以实现跟C/C++进行混合编译,同时编译速度也是非常的快,可以跟Ninja持平。

参考

GNU AUTOCONF&AUTOMAKE

在大型C/C++项目(比如ffmpeg、openssl等)中,都会给我们提供一个Configure文件,一般都是我们执行这个文件后,再执行make,这个文件怎么生成的呢?就是通过autoconf。

800px-Autoconf-automake-process.svg.png

总结

本文主要对移动端开发中常用到的编译工具做了简要介绍,主要包括Java和C/C++,了解编译工具有助于我们自己开发效率自动化构建的效率工具,也可以帮助我们快速上手流行的大型开源项目,对这些项目进行编译、裁剪、修改等操作。后续会针对具体项目对文章中介绍的工具做详细介绍和分析。