当前位置: 首页 > news >正文

Android build子系统(02)Ninja语法与复杂依赖构建解读

说明:本文将解读Ninja构建系统的基础语法和应用,同时给出一些示例便于理解和学习;给出一个复杂构建的基础demo,通过这个demo的分析理解复杂构建的内在逻辑和build.ninja编写法则;最后扩展之前Android Framework中构建build.ninja的2种新方式:android中的gradle方式和cmake方式。这样对Ninja这个构建系统有一个更具体的了解。

1 Ninja的基本语法解读

build.ninja 文件是 Ninja 构建系统的配置文件,它定义了如何构建项目中的各个目标(比如可执行文件、库文件等)。以下是 build.ninja 文件的一些基本语法规则和常见关键词的含义,以及一些示例。

1.1 基本语法规则解读如下:

  • 规则(Rule):定义了如何生成一个或多个文件的模板。
  • 构建(Build):定义了具体的构建指令,告诉 Ninja 如何生成一个目标。
  • 变量(Variable):用于简化构建文件,避免重复。

1.2 常见关键词解读如下:

  • rule:定义一个构建规则。
  • build:定义一个构建语句,指定输出文件和输入文件,以及使用哪个规则。
  • variables:定义变量,可以在构建文件中重复使用。
  • default:指定默认的构建目标。
  • include:包含其他 .ninja 文件。
  • subninja:包含另一个 .ninja 文件,通常用于子项目。

1.3 示例解读

接下来我们用示例的方式来看这些规则和关键词具体如何使用

1.3.1 规则定义示例如下:

# 定义一个名为 "cc" 的规则,用于编译 C++ 文件
rule cccommand = gcc -c $in -o $outdescription = Compiling $outrestat = 1 # 总是重新检查文件状态# 定义一个名为 "link" 的规则,用于链接生成可执行文件
rule linkcommand = gcc $in -o $outdescription = Linking $out

1.3.2 构建语句示例如下:

# 使用 "cc" 规则来编译 main.o 对象文件
build main.o: cc main.c# 使用 "link" 规则将 main.o 链接成可执行文件 "my_program"
build my_program: link main.o

1.3.3 变量定义参考示例如下:

# 定义变量
cxx = g++
flags = -Wall -O2# 使用变量
rule compilecommand = $(cxx) $(flags) -c $in -o $outdescription = Compiling $outbuild main.o: compile main.cpp

1.3.4 默认目标参考示例如下:

# 指定默认目标
default my_program

1.3.5 包含其他文件参考示例如下:

# 包含另一个 .ninja 文件
include foo.ninja# 包含变量文件
include variables.ninja

1.3.6 子项目参考示例如下:

# 包含子项目的 build.ninja 文件
subninja subproject.ninja

1.3.7 完整示例解读如下:

# 定义编译和链接规则
rule cccommand = gcc -c $in -o $outdescription = Compiling $outrule linkcommand = gcc $in -o $outdescription = Linking $out# 定义变量
cxx = g++
flags = -Wall -O2# 使用变量和规则来编译和链接
build main.o: cc main.c
build foo.o: cc foo.c# 链接生成可执行文件
build my_program: link main.o foo.o# 指定默认目标
default my_program

在这个示例中,我们定义了编译和链接的规则,然后使用这些规则来编译 main.cfoo.c 文件,并最终链接它们生成 my_program 可执行文件。我们还定义了一些变量来简化规则的命令,并指定了默认的构建目标。注意:在实际项目中,build.ninja 文件通常是由构建系统(如 CMake)生成的,而不是手动编写的。但理解其语法和结构对于自定义构建过程和调试构建问题非常有用。

2 Ninja复杂依赖体系的构建

2.1 一个复杂依赖体系构建的案例解读

这里假设 A依赖于B,B依赖于C的构建,B依赖于D的构建,C也依赖于D的构建,A B C D 均有自己的 build.ninja构建文件。那么想要依赖如何编写build.gradle呢?参考如下:

项目 A 的 build.ninja 文件 (build-A.ninja):

build A: cc A.c B

项目 B 的 build.ninja 文件 (build-B.ninja):

build B: cc B.c C D

项目 C 的 build.ninja 文件 (build-C.ninja):

build C: cc C.c D

项目 D 的 build.ninja 文件 (build-D.ninja):

build D: cc D.c

然后,在顶层的 build.ninja 文件中,使用 subninja 来包含这些子项目的构建文件,并定义最终的目标和依赖关系:

顶层 build.ninja 文件:

# 定义编译规则
rule cccommand = gcc -c $in -o $outdescription = Compiling $out# 包含子项目的构建文件
subninja build-A.ninja
subninja build-B.ninja
subninja build-C.ninja
subninja build-D.ninja# 定义最终的目标,这里假设 A 是最终目标
build A: phony B C
build B: phony C D
build C: phony D# 默认目标
default A

在这个顶层的 build.ninja 文件中,我们使用 subninja 来包含每个子项目的构建文件。然后,我们定义了最终的目标 A,并指定它依赖于 B 和 C。同样,B 依赖于 C 和 D,C 依赖于 D。这里使用了 phony 目标,因为 A、B 和 C 不是实际的文件,而是其他目标的别名。

请注意,这里的 cc 规则只是一个示例,你需要根据实际的编译命令来定义它。另外,每个子项目的 build.ninja 文件应该定义如何从源文件构建相应的目标。

要执行构建,只需在命令行中运行:

$ninja -f build.ninja

Ninja 会自动处理依赖关系,并确保在构建 A 之前先构建 B、C 和 D。

接下来我们针对复杂的编译构架体系,了解下 为什么很多大型项目中不直接编辑build.ninja,而是使用cmake、gn、soong、kati等其他工具来转换生成build.ninja。

2.2 为什么大型项目中不直接编辑build.ninja

在实际项目中,build.ninja 文件通常不是直接编辑的,而是通过其他构建系统生成的,主要有以下几个原因:

  1. 复杂性管理: 大型项目可能有成百上千的源文件和复杂的依赖关系。手动编辑 build.ninja 文件来管理这些复杂性是非常困难且容易出错的。

  2. 可维护性: 如果 build.ninja 文件是手动编写的,那么每次项目结构或依赖关系发生变化时,都需要手动更新这个文件,这很难保持一致性和准确性。

  3. 自动化: 使用自动化的构建系统(如 CMake、Meson 或 Bazel)可以自动分析项目的源代码和依赖关系,并生成相应的 build.ninja 文件。这样可以确保构建文件始终是最新的,并且与项目的实际结构保持同步。

  4. 跨平台支持: 自动化构建系统可以生成跨多个平台的构建文件。例如,CMake 可以生成 Windows、Linux 和 macOS 都能使用的 Ninja 文件,而手动编写则需要为每个平台单独编写和维护构建文件。

  5. 可读性和可理解性: 构建系统(如 CMake)使用的配置文件(如 CMakeLists.txt)通常更易于理解和维护。开发者可以专注于用这些高级构建脚本来定义构建逻辑,而不是直接处理底层的构建文件。

  6. 复用性和一致性: 在大型组织或多个项目中,使用统一的构建系统可以确保所有的项目都遵循相同的构建流程和标准,这有助于保持构建过程的一致性。

  7. 高级功能: 现代构建系统提供了许多高级功能,如预编译头文件、依赖扫描、自动生成项目文件等,这些功能很难通过手动编辑 build.ninja 文件来实现。

  8. 社区和生态系统: 构建系统如 CMake 拥有庞大的社区和丰富的文档,开发者可以轻松找到帮助和资源。而直接编辑 build.ninja 文件则意味着离开了这个生态系统,减少了可获得的支持。

尽管 build.ninja 文件不是直接编辑的,但理解其内容和结构对于调试构建问题和定制构建过程是非常有帮助的。构建系统提供了一个抽象层,允许开发者以更高级、更易于管理的方式定义构建逻辑,而自动生成 build.ninja 文件则确保了构建过程的准确性和高效性。

3 gradle和cmake使用Ninja构建

3.1 gradle中Ninja编译的构建

说明:从 Android Gradle Plugin (AGP) 3.0 开始,Ninja 被设置为默认的构建系统来编译原生代码,替代了之前的 make 工具。如果你的项目中包含 C 或 C++ 代码,AGP 会自动使用 Ninja 进行构建,即使你没有显式地在 build.gradle 文件中指定使用 Ninja。

这个变化是为了提高构建性能,因为 Ninja 通常比 make 更快,尤其是在处理大型项目或频繁构建时。Ninja 的并行构建能力可以显著减少构建时间。如果你的gradle版本默认不是ninja,则参考如下步骤,配置使用ninja编译的方式如下:

第一步,全局配置。在项目的根目录下的gradle.properties文件中设置以下属性,这将影响所有的项目和模块。

android.useAndroidX=true
android.enableJetifier=true
org.gradle.parallel=true
# 设置Ninja为默认构建工具
org.gradle.native.cpp.build.type=Ninja

第二步,在模块的build.gradle文件中,确保你已经应用了com.android.applicationcom.android.library插件,并配置了externalNativeBuild以使用Ninja。配置如下所示:

apply plugin: 'com.android.application' // 或 'com.android.library'android {// ...buildTypes {release {// ...externalNativeBuild {cmake {arguments "-GNinja", "-DCMAKE_BUILD_TYPE=Release"}}}debug {// ...externalNativeBuild {cmake {arguments "-GNinja", "-DCMAKE_BUILD_TYPE=Debug"}}}}
}

3.2 cmake中ninja编译的构建

这里用 CMake 将 CMakeLists.txt 文件转换成 build.ninja 文件,你需要在命令行中运行 CMake 并指定 Ninja 作为生成的构建系统。下面是一个简单的示例,展示了整个过程:

@1 创建 CMakeLists.txt

首先,创建一个包含基本构建指令的 CMakeLists.txt 文件。例如,如果你有一个简单的 C 程序,包含两个源文件 main.chello.c,并且你想将它们编译成一个可执行文件 hello,你的 CMakeLists.txt 可能如下所示:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.0)# 定义项目名称和语言
project(MyProject C)# 定义要构建的可执行文件及其源文件
add_executable(hello main.c hello.c)

@2 运行 CMake 命令

然后,在命令行中运行 CMake 命令,指定 Ninja 作为构建系统,并生成构建文件。你需要确保已经安装了 CMake 和 Ninja,并且 Ninja 的可执行文件在系统的 PATH 环境变量中。

$cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .

这个命令告诉 CMake 使用 Ninja 生成构建文件,并设置构建类型为 Release。-G Ninja 参数指定了生成器为 Ninja,-DCMAKE_BUILD_TYPE=Release 设置了构建类型,. 表示当前目录是源代码的根目录。

运行上述 CMake 命令后,CMake 会在当前目录下生成一个 build.ninja 文件,以及一个 CMakeFiles 目录,其中包含了一些中间文件。这时可以打开 build.ninja 文件查看其内容。它将定义如何使用 Ninja 构建你的项目。例如,它可能包含如下内容:

# Auto-generated by CMake - do not edit!rule CMAKE_RULEcommand = /usr/bin/cmake -E make_directory $outdescription = Creating directory: $outrule compilecommand = /usr/bin/gcc -fPIC -o $out -c $indescription = Compiling $outrule linkcommand = /usr/bin/gcc -o $out $indescription = Linking $outbuild CMakeFiles/hello.dir/main.c.o: compile CMakeFiles/hello.dir/main.c
build CMakeFiles/hello.dir/hello.c.o: compile CMakeFiles/hello.dir/hello.c
build hello: link CMakeFiles/hello.dir/main.c.o CMakeFiles/hello.dir/hello.c.o

@3 使用Ninja 构建项目

有了build.ninja文件,就可以使用 Ninja 来构建项目:

$ninja -f build.ninja

这个命令将根据 build.ninja 文件中的指令来编译和链接你的项目,生成最终的可执行文件 hello

通过上述步骤,可以使用 CMake 将 CMakeLists.txt 转换成 build.ninja 文件,并使用 Ninja 进行构建。这种方法非常适合需要精确控制构建过程和依赖关系的复杂项目。

相关文章:

Android build子系统(02)Ninja语法与复杂依赖构建解读

说明:本文将解读Ninja构建系统的基础语法和应用,同时给出一些示例便于理解和学习;给出一个复杂构建的基础demo,通过这个demo的分析理解复杂构建的内在逻辑和build.ninja编写法则;最后扩展之前Android Framework中构建b…...

JavaScript的第三天

目录 JS中的循环,使某些代码重复执行 一、for循环:重复执行某段代码,通常用于计数 1、for的语法结构 2、代码解析 3、代码尝试 4、循环重复相同的代码,可以让用户控制输出的次数(对该变量进行遍历) 5、循环…...

初识git · 有关模型

目录 前言: 有关开发模型 前言: 其实文章更新到这里的时候,我们已经学习了可以满足我们日常生活中的基本需求的指令了,但是为什么要更新本篇文章呢?是因为实际生活中我们对于开发工作,运维工作&#xff…...

基于SpringBoot+Vue+uniapp的海产品加工销售一体化管理系统的详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的视频演示 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念,提供了一套默认的配置,让开发者可以更专注于业务逻辑而不…...

解锁机器人视觉与人工智能的潜力,从“盲人机器”改造成有视觉能力的机器人(下)

机器视觉产业链全景回顾 视觉引导机器人生态系统或产业链分为三个层次。 上游(供应商) 该机器人视觉系统的上游包括使其得以运行的硬件和软件提供商。硬件提供商提供工业相机、图像采集卡、图像处理器、光源设备(LED)、镜头、光…...

CORS预检请求配置流程图 srpingboot和uniapp

首先要会判断预检请求 还是简单请求 简单请求 预检请求 #mermaid-svg-1R9nYRa7P9Pll4AK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1R9nYRa7P9Pll4AK .error-icon{fill:#552222;}#mermaid-svg-1R9nYRa7P9Pll4…...

用Spring AI 做智能客服,基于私有知识库和RAG技术

Java智能客服系统运用RAG技术提升答疑精准度 基于Spring ai 的 RAG(检索增强生成)技术,Java智能客服系统能够利用私有知识库中的信息提供更准确的答疑服务。 它的核心思路是: 首先,将客服QA以Word形式导入到系统中&…...

TemporalBench:一个专注于细粒度时间理解的多模态视频理解的新基准。

2024-10-15,由威斯康星大学麦迪逊分校、微软研究院雷德蒙德等机构联合创建了TemporalBench,它通过大约10K个视频问答对,提供了一个独特的测试平台,用以评估各种时间理解和推理能力,如动作频率、运动幅度、事件顺序等。…...

网友提问:网上申请流量卡不通过怎么办?

网上申请流量卡不通过怎么办?网上办理流量卡不通过,说明你不符合办理此套餐的要求,可以选择其他套餐,或者其他运营商的流量卡申请试试。 我们不管是在京*、淘*、拼**哪个网站申请的流量卡,提交的申请信息都是由运营商…...

JavaWeb 22.Node.js_简介和安装

有时候,后退原来是向前 —— 24.10.7 一、什么是Node.js Node.js 是一个于 Chrome V8 的 JavaScript 运行时环境,可以使 JavaScript 运行在服务器端。使用 Node.js,可以方便地开发服务器端应用程序,如 Web 应用、API、后端服务&a…...

APIJSON的使用

APIJSON是一个用于简化后端接口开发的工具&#xff0c;在Java中可以按照以下步骤使用&#xff1a; 1. 引入依赖 在Java项目中&#xff0c;需要引入APIJSON的相关依赖。如果使用Maven&#xff0c;可以在pom.xml文件中添加以下依赖&#xff1a; <dependency><groupId…...

简单三步完成 Telegram 生态的 Web3 冷启动

在竞争激烈的 Web3 领域&#xff0c;强有力的启动往往能决定成败。Telegram 无疑当下最火热的流量池&#xff0c;是很多 Web3 项目冷启动阶段的必选项。 但眼看着好多项目在 Telegram 生态火速获取百万级甚至千万级别的用户&#xff0c;自己的项目要怎么开始做增长&#xff0c;…...

Go Wails 学习笔记:创建第一个项目

文章目录 1. 安装 Wails2. 创建 Wails 项目3. 项目结构4. 运行项目5. 构建项目6. 部署和发布总结 Wails 是一个用于构建跨平台桌面应用程序的框架&#xff0c;允许开发者使用前端技术&#xff08;如 HTML、CSS、JavaScript&#xff09;以及 Go 语言来开发桌面应用。本文基于官方…...

Postman使用-基础篇

前言 本教程将结合业界广为推崇和使用的RestAPI设计典范Github API&#xff0c;详细介绍Postman接口测试工具的使用方法和实战技巧。 在开始这个教程之前&#xff0c;先聊一下为什么接口测试在现软件行业如此重要&#xff1f; 为什么我们要学习Postman&#xff1f; 现代软件…...

LeetCode 202.快乐数

LeetCode 202.快乐数 C 思路&#xff1a; 用快慢指针来进行解答&#xff0c;可以将其看做一个回环链表&#xff0c;慢指针完成一次平方和操作&#xff0c;快指针完成两次平方和操作&#xff0c;当快慢指针相遇时&#xff0c;判断快慢指针是否为1(为1以后无论怎么取平方和都会为…...

Redis-03 持久化(RDB, AOF,混合持久化)及原理

1&#xff0c;持久化 Redis的持久化是必须的&#xff0c;当Redis服务宕机后&#xff0c;如果没有持久化&#xff0c;重启服务后redis中的数据都将丢失&#xff0c;所有的数据操作都将直连数据库&#xff0c;系统性能会大幅降低&#xff0c;所以在使用Redis做缓存服务时必须持久…...

TikTok账号策略:IP和网络环境的要求分析

在当今社交媒体迅猛发展的时代&#xff0c;TikTok作为一款短视频平台&#xff0c;凭借其独特的算法和庞大的用户基础&#xff0c;吸引了越来越多的内容创作者和营销人员。成功地运营一个TikTok账号&#xff0c;除了优质的内容创作外&#xff0c;良好的IP和网络环境也至关重要。…...

vue后台管理系统从0到1(5)

文章目录 vue后台管理系统从0到1&#xff08;5&#xff09;完善侧边栏修改bug渲染header导航栏 vue后台管理系统从0到1&#xff08;5&#xff09; 接上一期&#xff0c;我们需要完善我们的侧边狼 完善侧边栏 我们在 element 组件中可以看见&#xff0c;这一个侧边栏是符合我们…...

OpenAI的新功能Canvas,效果还不错

时隔两年&#xff0c;ChatGPT终迎来界面全新升级&#xff01; 这一次&#xff0c;OpenAI官宣推出类似 Anthropic 的 Artifacts 的界面交互功能 canvas&#xff0c;并称这是一种使用 ChatGPT 写作和编程的新方式。不论是写作&#xff0c;还是编码&#xff0c;都可以开启全新的交…...

了解一些常用的Javascript对象方法

javascript 的对象包含许多有用的方法&#xff0c;可以帮助开发人员轻松操作对象。让我们通过简短的解释和示例来了解一些最重要的内容 object.create()object.assign()object.keys()object.values()object.entries()object.freeze()object.seal()object.preventextensions()o…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...