想了解更多关于开源的内容,请访问:
创新互联-专业网站定制、快速模板网站建设、高性价比仁布网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式仁布网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖仁布地区。费用合理售后完善,10余年实体公司更值得信赖。
开源基础软件社区
https://ost.
OpenHarmony使用gn+ninja来维护开源项目的构建。之前没有接触过gn+ninja,是时候系统性的来学习下了。边学边记录下学习过程,希望对同样需要学习gn+ninja的朋友有所帮助。
这一篇,我们来学习GN的语法和操作行为等,建议也可以阅读原版文档GN Language and Operation。
GN提供了扩展的内置帮助文档系统,提供每一个函数功能和内置变量的详细的参考引用。可以使用gn help来查看帮助,可以进一步使用gn help
GN使用机器简单的,动态类型的语言。支持的类型有:
字符串括在双引号中,并使用反斜杠作为转义字符。仅仅支持如下转义序列是:
- \"(双引号)
- \$(美元符号$)
- \\(反斜杠)
反斜杠的任何其他用法都被视为反斜杠。因此,例如,\b不需要转义,大多数 Windows 路径如 "C:\foo\bar.h")不需要转义。
通过符号$支持简单变量替换,其中美元符号$后面的单词被替换为变量的值。如果没有非变量名称字符来终止变量名称,则可以选择${}将名称括起来。不支持更复杂的表达式,仅支持变量名称替换。
a = "mypath"
b = "$a/foo.cc" # b -> "mypath/foo.cc"
c = "foo${a}bar.cc" # c -> "foomypathbar.cc"
除了把非空列表赋值给空列表(a == [])之外,没有办法获得列表的长度。如果你发现自己想做这种事情,意味着在构建中做太多的工作。— 注:说的是,列表不提供获取长度,也不应该获取长度。
列表支持追加,如下所示。将一个列表追加到另一个列表,会把每一个列表项追加为第二个列表中的项,而不是将该列表追加为嵌套成员。
a = [ "first" ]
a += [ "second" ] # [ "first", "second" ]
a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]
b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
还可以从列表中删除项目,如下。列表中的减号运算符“-”搜索匹配项并删除所有匹配项。从另一个列表中减去一个列表将删除第二个列表中的每个项目。如果未找到匹配的项目,则会引发错误,因此您需要在删除列表项之前,需要提前知道该列表项是否存在。
a = [ "first", "second", "third", "first" ]
b = a - [ "first" ] # [ "second", "third" ]
a -= [ "second" ] # [ "first", "third", "first" ]
鉴于无法测试列表项的添加引入,可以这样使用:设置一个文件或标志的主列表,然后根据各种条件删除不适用于当前版本的文件或标志。— 注:这算是推荐做法,维护一个主列表,然后只做减法,排除不适合的列表项。这个和下文的GYP提供的建议一样。这里读起来有些奇怪。
在风格上,更喜欢只添加到列表中,让每个源文件或依赖项出现一次。这与Chrome团队过去为GYP提供的建议相反(GYP更愿意列出所有文件,然后基于条件删除您不需要的文件)。
列表支持从零开始的下标来提取值:
a = [ "first", "second", "third" ]
b = a[1] # -> "second"
[] 运算符是只读的,不能用于改变列表。其主要使用场景是当外部脚本返回多个已知值,并且您想要提取它们时。
在某些情况下,覆盖一个列表比追加到一个列表更容易。为了帮助满足这种情况,将非空列表赋值给值为非空列表的变量,会产生错误。如果要绕过此限制,请首先将目标变量赋值给一个空列表。如下:
a = [ "one" ]
a = [ "two" ] # Error: overwriting nonempty list with a nonempty list.
a = [] # OK
a = [ "two" ] # OK
条件语句类似于 C语言,如下。可以在大多数情况下,使用条件语句。甚至可以把整个target目标放在条件里,如果这些target只在特定的条件下才需要声明。
if (is_linux || (is_win && target_cpu == "x86")) {
sources -= [ "something.cc" ]
} else if (...) {
...
} else {
...
}
您可以使用foreach循环访问列表。这是不鼓励的。构建应该做的大多数事情通常都可以在不这样做的情况下来完成,如果你觉得有必要,这可能表明你在元构建中做了太多的工作。
foreach(i, mylist) {
print(i) # Note: i is a copy of each element, not a reference to it.
}
简单的函数调用看起来像大多数其他语言:
print("hello, world")
assert(is_win, "This should only be executed on Windows")
这些函数是内置的,用户无法定义新的函数。一些函数采用以下代码块括起来:{ }。
static_library("mylibrary") {
sources = [ "a.cc" ]
}
大多数函数定义了目标target。用户可以使用下面讨论的template模板机制定义这样的新功能。
准确地说,上面说的代码块{}作为函数参数来执行函数的。大多数块样式的函数执行代码块,并将生成的作用域做为供读取的变量字典。
文件和函数调用后面跟的{}块引入新的作用域。作用域是嵌套的。读取变量时,将按相反的顺序搜索包含作用域,直到找到匹配的名称。变量写入始终转到最内层的作用域。
除了最里面的作用域之外,无法修改任何封闭作用域。这意味着,例如,当您定义target目标时,您在块内执行的任何操作都不会“泄漏”到文件的其余部分。
if/else/foreach语句,即使它们使用{}块,也不会引入新的作用域,因此更改将保留在语句之外。
文件名和目录名是字符串,被解释为相对于当前构建文件的目录。有三种可能的形式:
"foo.cc"
"src/foo.cc"
"../src/foo.cc"
"//net/foo.cc"
"//base/test/foo.cc"
"/usr/local/include/"
"/C:/Program Files/Windows Kits/Include"
一个目标target是构建图中的一个节点。它通常表示将生成的某种可执行文件或库文件。目标依赖于其他目标。内置目标类型如下所示。可以使用命令gn help
Configs配置是命名对象,用于指定flags、include目录和defines。它们可以应用于目标target并推送到依赖目标。
要定义配置,示例如下:
config("myconfig") {
includes = [ "src/include" ]
defines = [ "ENABLE_DOOM_MELON" ]
}
要将配置应用于目标,可以这样做:
executable("doom_melon") {
configs = [ ":myconfig" ]
}
构建配置文件通常会为target目标指定包含默认配置的列表。目标可以根据需要向此列表中添加或删除。因此,在实践中,您通常会使用configs += ":myconfig"附加到默认值列表中。有关如何声明和应用配置的详细信息,请参阅gn help config。
一个target目标可以将配置项应用于依赖于它的其他target目标上。最常见的示例是第三方目标,它需要一些定义define或包含头文件的include目录,才能正确编译。您希望这些配置项既应用于第三方库本身的编译,也应用于使用该库的所有目标。
为此,您需要使用要应用的配置项编写一个配置config:
config("my_external_library_config") {
includes = "."
defines = [ "DISABLE_JANK" ]
}
然后,此配置将作为“公共”配置添加到目标中。它将既适用于目标,也适用于直接依赖于它的目标。注:使用的配置项是public_configs。
shared_library("my_external_library") {
...
# Targets that depend on this get this config applied.
public_configs = [ ":my_external_library_config" ]
}
反过来,依赖目标可以通过将目标添加为“公共”依赖项,将其向上推进到依赖项树的另一个级别。注:使用的配置项是public_deps。
static_library("intermediate_library") {
...
# Targets that depend on this one also get the configs from "my external library".
public_deps = [ ":my_external_library" ]
}
模板是 GN 重用代码的主要方式。通常,模板会扩展一个或多个其他target目标类型。
# Declares a script that compiles IDL files to source, and then compiles those
# source files.
template("idl") {
# Always base helper targets on target_name so they're unique. Target name
# will be the string passed as the name when the template is invoked.
idl_target_name = "${target_name}_generate"
action_foreach(idl_target_name) {
...
}
# Your template should always define a target with the name target_name.
# When other targets depend on your template invocation, this will be the
# destination of that dependency.
source_set(target_name) {
...
deps = [ ":$idl_target_name" ] # Require the sources to be compiled.
}
}
通常,模板定义将放在一个.gni文件中,用户将导入该文件以查看模板定义:
import("//tools/idl_compiler.gni")
idl("my_interfaces") {
sources = [ "a.idl", "b.idl" ]
}
声明模板会在此Scope作用域内的变量周围创建一个闭包。调用模板时,魔术变量invoker用于读取Scope作用域外的变量。模板通常会将其感兴趣的值复制到自己的Scope作用域内:
template("idl") {
source_set(target_name) {
sources = invoker.sources
}
}
执行模板时的当前工作目录将是调用的构建文件的目录,而不是模板源文件的目录。因此,从模板调用程序传入的文件将是正确的(这通常占模板中的大多数文件处理)。但是,如果模板本身具有文件(也许它会生成运行脚本的操作),则需要使用绝对路径(“//foo/...”)来引用这些文件,以说明当前目录在调用期间是不可预测的。有关详细信息和更完整的示例,请参阅gn help template帮助。
您可以使用该import函数将.gni文件导入到当前作用域中。这不是C意义上的包含。导入的文件将独立执行,生成的作用域将复制到当前文件中(C当包含指令出现时,在当前上下文中执行包含的文件)。这允许缓存导入的结果,并且还阻止了一些更“创造性”的包含使用,如多次包含的文件。
通常,.gni文件将定义构建参数和模板。有关详细信息,请参阅gn help import。
您的.gni文件可以通过在名称中使用前置下划线(如_this)来表明该临时变量不会到导入文件使用。
通常,您需要相对于其他目录创建文件名或文件名列表。这在运行脚本时尤其常见,脚本是使用构建输出目录作为当前目录执行的,而构建文件通常引用相对于其包含目录的文件。
您可以使用rebase_path转换目录。有关更多帮助和示例,请参阅gn help rebase_path。将相对于当前目录的文件名转换为相对于根构建目录的典型用法是:new_paths = rebase_path("myfile.c", root_build_dir)。
模式用于为自定义target目标类型的一组给定输入生成输出文件名,并自动从列表值中删除文件(请参见gn help filter_include和gn help filter_exclude)。
它们就像简单的正则表达式。有关详细信息,请参阅gn help label_pattern。
有两种方法可以执行脚本。GN 中的所有外部脚本都是Python编写的。第一种方法是作为构建步骤。这样的脚本将接受一些输入并生成一些作为构建部分的输出。调用脚本的目标使用“action”目标类型进行声明(请参见gn help action)。
执行脚本的第二种方法是在构建文件执行期间同步执行。在某些情况下,需要确定要编译的文件集,或者获取构建文件可能依赖的某些系统配置。构建文件可以读取脚本的stdout标准输出,并据此以不同的方式对其进行操作。
同步脚本执行由函数exec_script完成(有关详细信息和示例,请参阅gn help exec_script)。由于同步执行脚本需要暂停当前构建文件的执行,直到Python进程完成执行,因此外部脚本的速度很慢,应将其最小化。
为防止滥用,允许调用exec_script的文件可以在顶级.gn文件中列入白名单。更多信息参考gn help dotfile。
您可以同步读取和写入文件,这是不鼓励的,但在同步运行脚本时偶尔是必需的。典型的用例是传递一个长度超过当前平台命令行限制的文件名列表。请参阅gn help read_file和gn help write_file了解如何读取和写入文件。如果可能的话,应避免使用这些功能。
超过命令行长度限制的操作可以使用响应文件来绕过此限制,而无需同步写入文件。查阅gn help response_file_contents了解更多内容。
本篇,我们学习了GN语言语法与脚本操作,支持的变量类型,命名、构建配置等等。
想了解更多关于开源的内容,请访问:
开源基础软件社区
https://ost.。
网站栏目:[GN+Ninja学习0x03]GN语法与操作学习
标题路径:http://www.shufengxianlan.com/qtweb/news17/97767.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联