#打卡不停更# [gn+ninja学习 0x06] gn构建文件的语言与语法 原创
[gn+ninja学习 0x06] gn构建文件的语言与语法
OpenHarmony使用gn+ninja来维护开源项目的构建。之前没有接触过gn+ninja,是时候系统性的来学习下了。边学边记录下学习过程,希望对同样需要学习gn+ninja的朋友有所帮助。
GN参考文档GN Reference包含命令、target声明、可以构建文件中使用的函数、内置预定义的变量、可以在target中使用的变量、以及其他帮助主题。由于内容非常多,学习起来非常耗费时间,可以先阅读掌握常用的命令、常用的target、常用的函数和变量等。其他内容可以在需要的时候再来查询学习。
这一篇,我们来学习下GN参考文档GN Reference中内容Language and grammar for GN build files,通过该文的学习来掌握编写GN构建文件的需要具备的知识。
1、 Tokens 标记
GN build files are read as sequences of tokens. While splitting the file
into tokens, the next token is the longest sequence of characters that form a
valid token.
GN构建文件会被读取解析为标记系列。当把构建文件分割为一系列标记时,下一个标记是形成有效标记的最长字符串。
注:Token在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。
White space and comments 空格和注释
White space is comprised of spaces (U+0020), horizontal tabs (U+0009),
carriage returns (U+000D), and newlines (U+000A).
Comments start at the character "#" and stop at the next newline.
White space and comments are ignored except that they may separate tokens
that would otherwise combine into a single token.
构建文件中的空格包含空格符、制表符、回车和换行符。 注释以符号#开头,以换行结束。空格和注释会被忽略,除非用于分割标记。
Identifiers 标记符
Identifiers name variables and functions.
identifier = letter { letter | digit } .
letter = "A" ... "Z" | "a" ... "z" | "_" .
digit = "0" ... "9" .
标记符用于命名变量和函数,标记符由字母、数字、下划线组成,必须以字母和下划线开头。
Keywords 保留字
The following keywords are reserved and may not be used as identifiers:
else false if true
GN保留字有else、false、if和true。
Integer literals 整数文本
An integer literal represents a decimal integer value.
integer = [ "-" ] digit { digit } .
Leading zeros and negative zero are disallowed.
整数文本表示十进制整数值,不能数字0开头,不能出现负0。
String literals 字符串字面量
A string literal represents a string value consisting of the quoted
characters with possible escape sequences and variable expansions.
string = `"` { char | escape | expansion } `"` .
escape = `\` ( "$" | `"` | char ) .
BracketExpansion = "{" ( identifier | ArrayAccess | ScopeAccess ) "}" .
Hex = "0x" [0-9A-Fa-f][0-9A-Fa-f]
expansion = "$" ( identifier | BracketExpansion | Hex ) .
char = /* any character except "$", `"`, or newline */ .
After a backslash, certain sequences represent special characters:
\" U+0022 quotation mark
\$ U+0024 dollar sign
\\ U+005C backslash
All other backslashes represent themselves.
To insert an arbitrary byte value, use $0xFF. For example, to insert a
newline character: "Line one$0x0ALine two".
An expansion will evaluate the variable following the '$' and insert a
stringified version of it into the result. For example, to concat two path
components with a slash separating them:
"$var_one/$var_two"
Use the "${var_one}" format to be explicitly deliniate the variable for
otherwise-ambiguous cases.
字符串字面量表示字符串值,由双引号包含的字符和可能的转义序列,变量扩展组成。
字符串 = 双引号包含字符、转义符、扩展。
转义 = 支持转义$、\ 和 "。
花括号{扩展 = {(标记符、数组访问、作用域访问)}等。
16进制hex = 0x开头,包含0-9数字和大小写字母a-f。
扩展 = $开头的,标记符、16进制、花括号扩展,
字符 = 除了$、"、换行以外的字符
在反斜线\后的一些字符转义后,表示特定的字符:
\" U+0022 双引号
\$ U+0024 美元符号
\\ U+005C 反斜线
为插入字节值,可以使用 $0xFF 这样的表示法,例如,为字符串中插入换行,可以使用:“Line one$0x0ALine two”。
变量扩展会评估解析$后的变量,然后插入字符化的版本到产生的结果字符串里。例如,连接两个使用斜线分割路径。使用“${var_one}”格式显式标识变量全名,避免混淆。
"$var_one/$var_two"
Punctuation 标点符号
The following character sequences represent punctuation:
+ += == != ( )
- -= < <= [ ]
! = > >= { }
&& || . ,
使用上述字符序列表示标点。
Grammar 语法
The input tokens form a syntax tree following a context-free grammar:
File = StatementList . // 文件
Statement = Assignment | Call | Condition . // 语句
LValue = identifier | ArrayAccess | ScopeAccess . // 左值赋值
Assignment = LValue AssignOp Expr . // 赋值
Call = identifier "(" [ ExprList ] ")" [ Block ] . // 函数调用
Condition = "if" "(" Expr ")" Block // 条件
[ "else" ( Condition | Block ) ] .
Block = "{" StatementList "}" . // 语句块
StatementList = { Statement } . // 语句列表
ArrayAccess = identifier "[" Expr "]" . // 数组下标访问
ScopeAccess = identifier "." identifier . // 作用域访问
Expr = UnaryExpr | Expr BinaryOp Expr . // 表达式
UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr . // 一元表达式
PrimaryExpr = identifier | integer | string | Call // 初等表达式、基本表达式
| ArrayAccess | ScopeAccess | Block
| "(" Expr ")"
| "[" [ ExprList [ "," ] ] "]" .
ExprList = Expr { "," Expr } . // 表达式列表
AssignOp = "=" | "+=" | "-=" . // 赋值操作
UnaryOp = "!" . // 一元操作符
BinaryOp = "+" | "-" // highest priority // 二元操作符 高优先级
| "<" | "<=" | ">" | ">="
| "==" | "!="
| "&&"
| "||" . // lowest priority // 低优先级
All binary operators are left-associative.
输入的token标记形成语义树,遵循与上下文无关的语法。所有二元运算符是左结合的。
Types 类型
The GN language is dynamically typed. The following types are used:
- Boolean: Uses the keywords "true" and "false". There is no implicit
conversion between booleans and integers.
- Integers: All numbers in GN are signed 64-bit integers.
- Strings: Strings are 8-bit with no enforced encoding. When a string is
used to interact with other systems with particular encodings (like the
Windows and Mac filesystems) it is assumed to be UTF-8. See "String
literals" above for more.
- Lists: Lists are arbitrary-length ordered lists of values. See "Lists"
below for more.
- Scopes: Scopes are like dictionaries that use variable names for keys. See
"Scopes" below for more.
GN语言是动态类型的,支持下述类型。
- Boolean布尔类型: 使用关键字 "true" and "false". 不支持布尔和整数间的隐式转换.
- 整数: GN支持的整数为有符号64位整数.
- 字符串: 8位未强制编码。当和其他系统进行字符串交互时,会假设字符串字符集编码为UTF-8。
- 列表: 任意长度的有序的值。
- Scopes作用域: 类似变量名作为键值的字典。
Lists 列表
Lists are created with [] and using commas to separate items:
mylist = [ 0, 1, 2, "some string" ]
A comma after the last item is optional. Lists are dereferenced using 0-based
indexing:
mylist[0] += 1
var = mylist[2]
Lists can be concatenated using the '+' and '+=' operators. Bare values can
not be concatenated with lists, to add a single item, it must be put into a
list of length one.
Items can be removed from lists using the '-' and '-=' operators. This will
remove all occurrences of every item in the right-hand list from the
left-hand list. It is an error to remove an item not in the list. This is to
prevent common typos and to detect dead code that is removing things that no
longer apply.
It is an error to use '=' to replace a nonempty list with another nonempty
list. This is to prevent accidentally overwriting data when in most cases
'+=' was intended. To overwrite a list on purpose, first assign it to the
empty list:
mylist = []
mylist = otherlist
列表是使用中括号封闭的使用逗号分割的一些成员。如:
mylist = [ 0, 1, 2, "some string" ]
列表支持连接操作,可以使用’+’ 和 '+='操作符。空值不能连接列表,为增加一个列表成员,列表长度至少为1。
列表还支持删除操作,可以使用’-’ 和 '-='操作符。删除操作会从左边的列表中删除右边的列表中的所有成员。删除不存在的成员时会报错,这会避免笔误,也能检测尝试删除不再使用成员的无用的代码。
使用’=’ 赋值一个非空列表给另外一个非空列表会报错,这可以避免覆盖数据,大多数场景可能打算使用的是连接操作’+='。如需专门覆写数据,首先给个空列表:
mylist = []
mylist = otherlist
Scopes 作用域
All execution happens in the context of a scope which holds the current state
(like variables). With the exception of loops and conditions, '{' introduces
a new scope.
所有的构建运行在作用域的上下文内,作用域包含当前的运行状态,类似变量。除了循环和条件,花括号'{'引入新的作用域。
Most scopes have a parent reference to the old scope. Variable reads
recursively search all parent scopes until the variable is found or there are
no more scopes. Variable writes always go into the current scope. This means
that after the closing '}' (again excepting loops and conditions), all local
variables will be restored to the previous values. This also means that "foo
= foo" can do useful work by copying a variable into the current scope that
was defined in a containing scope.
大部分作用域拥有一个父级引用到老的作用域。变量读取会递归搜索所有的父级作用域,直至发现变量或者搜索到最顶级作用域;变量写入会写入当前的作用域。意味着,在右括号'}'后,所有的本地变量会恢复到之前的值。也意味着"foo = foo"可以用于从一个包含该值定义的作业域中复制值。
Scopes can be assigned to variables. Examples of such scopes are the
implicitly-created "invoker" when invoking a template (which refers to the
variables set by the invoking code), scopes created by functions like
exec_script, and scopes explicitly created like
作用域可以赋值给变量。这样的作用域的例子有:调用模板时隐式创建的"invoker",exec_script函数创建的作用域,还有就是显式创建的作业域,如下所示:
empty_scope = {}
myvalues = {
foo = 21
bar = "something"
}
In the case of explicitly created scopes and scopes created by functions like
exec_script, there is no reference to the parent scope. Such scopes are fully
self-contained and do not "inherit" values from their defining scope.
显式创建的作用域,exec_script等函数创建的作用域,没有父级作用域。这些作用域是自包含的,不会从定义它们的作业域中继承值。
Inside an explicit scope definition can be any GN code including conditionals
and function calls. After the close of the scope, it will contain all
variables explicitly set by the code contained inside it. After this, the
values can be read, modified, or added to:
在显式作用域定义内,可以是任何GN代码,包含条件和函数调用。在作业域设置后, 它包含全部显式设置的变量,然后变量值可以读取、修改或者添加:
myvalues.foo += 2
empty_scope.new_thing = [ 1, 2, 3 ]
Scope equality is defined as single-level scopes identical within the current
scope. That is, all values in the first scope must be present and identical
within the second, and vice versa. Note that this means inherited scopes are
always unequal by definition.
作用域是否相等定义为和当前作用域同一级相等,指的是,,第一个作用域的所有值存在且与第二个作用域的值相等,反之亦然。需要注意,继承的作用域一直和定义它的作用域不相等。
6、小结
本篇,我们学习了GN构建文件的语言语法,该文的学习可以掌握编写GN构建文件的需要具备的知识。