GN用于编译文件作配置定义

语法

GN使用简单动态类型语言,涉及的变量类型为:

  • Boolean (true, false)
  • 64-bit signed integers
  • Strings
  • Lists
  • Scopes (sort of like a dictionary, only for built-in stuff)
    完整的语法参考gn help grammar

字符串

字符串有双引号包裹,使用反斜杠\转义,支持的反斜杠转义包括\"\$\\
其他使用都不会进行转义
$可以用于变量替换,可以用{}包裹。

1
2
3
a = "mypath"
b = "$a/foo.cc" # b -> "mypath/foo.cc"
c = "foo${a}bar.cc" # c -> "foomypathbar.cc"

可以使用$0xFF语法表示8bit的数值。

列表

列表支持append操作:

1
2
3
4
a = ["first"]
a += ["second"] # ["first, "second"]
a += ["third", "fourth"] # ["first", "second", "third", "fourth"]
b = a + ["fifth"] # ["first", "second", "third", "fourth", "fifth"]

还可以作减法:

1
2
3
a = ["first", "second", "third", "first"]
b = a - ["first"] # ["second", "third"]
a -= ["second"] # ["first", "third", "first"]

如果被减列表没有该元素的话,会报错。
列表支持索引:

1
2
a = ["first", "second", "third"]
b = a[1] # -> "second"

当列表为空时,可以覆写为新的带元素列表。

条件语句

比较像C

1
2
3
4
5
6
7
if (is_linux || (is_win && target_cpu == "x86")) {
sources == ["something.cc"]
} else if (...) {
...
} else {
...
}

循环

使用foreach迭代列表,但是不推荐。

1
2
3
foreach(i, mylist) {
print(i) # Note: i is a copy of each element, not a reference to it.
}

函数调用

简单的函数调用,例如print,assert

1
2
print("hello, world")
assert(is_win, "This should only be executed on Windows")

一些函数需要使用{}跟随

1
2
3
static_libary("mylibrary") {
sources = ["a.cc"]
}

该语句表示sources为该函数执行的参数,大多数的block_style函数都是执行block(eg: sources)然后将剩余的scope作为字典变量读取。

执行作用域

文件和函数调用{}块会引入新的作用域。

命名

文件和目录名

文件和目录名字是字符串并且是相对于当前编译文件目录的相对路径

1
2
3
"foo.cc"
"src/foo.cc"
"../src/foo.cc"

相对于源码的绝对路径:

1
2
"//net/foo.cc"
"//base/test/foo.cc"

系统绝对路径:

1
2
"/usr/local/include/"
"/C:/Program Files/Windows Kits/Include"

编译配置

编译目标

目标是编译图中的一个节点。它一般代表要编译生成的可执行程序或者库文件。目标依赖于其他目标。定义的目标类型如下:

  • action:执行脚本生成文件
  • action_foreach:循环执行多个脚本,一个脚本对应一个文件
  • bundle_data:声明数据为Mac/iOS bundle
  • create_bundle:创建 Mac/iOS bundle
  • executable:生成可执行文件
  • group:虚拟依赖节点,指向一个或多个其他的目标
  • shared_library:生成一个dll文件,或者so文件
  • loadable_module:运行时dll或so文件
  • source_set:轻量级虚拟静态库(效率更高)
  • static_library:.lib或.a文件,source_set是更优选择
    你可以扩展其去定义目标类型,使用如下模版。
  • component:source_set / shared_library,基于其build类型
  • test:test可执行程序。在mobile环境中其会创建适合的native app类型来作为测试
  • app:Mac/iOS可执行程序
  • android_apk:制作APK。

设置

定义了flag,include directories和defines。这些可以应用于目标以及相依赖的目标
config定义:

1
2
3
4
config("myconfig") {
includes = ["src/include"]
defines = ["ENABLE_DOOM_MELOW"]
}

应用config到目标:

1
2
3
executable("doom_melon") {
configs = [":myconfig"]
}

常见情况是设置一个默认列表。目标可以根据列表做添加或删除。因此在实际中通常会使用configs += ":myconfig"来添加配置。
gn help config来查阅更多的config相关配置

Public configs

目标可以将配置应用于依赖其的其他目标。最常见的情况是第三方目标需要一些定义或者导入库目录来正常编译。一般需要这些配置不仅应用于第三方库,还要应用于引用这些第三方库的目标上。
实现方式如下

1
2
3
4
config("my_external_library_config") {
includes = "."
defines = ["DISABLE_JANK"]
}

然后该配置会加入到目标中作为”public” config,可以同时设置该config

1
2
3
4
5
shared_library("my_external_library") {
...
# Target that depend on this get this config applied
public_configs = [ ":my_external_library_config" ]
}

依赖的目标需要加入其依赖

1
2
3
4
5
static_library("intermediate_library") {
...
# Targets that depend on this also get the configs from "my external library".
public_deps = [ ":my_external_library" ]
}

目标可以将config转发到所有依赖项直到触及边界值,通过设置all_dependent_config实现。不推荐,因为会造成冗余以及错误。推荐使用public_deps来实现。

模版

模版是GN重用代码的方式。模版会包括一到多个目标类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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头文件中声明,用户可以使用该文件来查看定义

1
2
3
4
5
import("//tools/idl_compiler.gni")

idl("my_interfaces") {
sources = ["a.idl", "b.idl"]
}

声明模版后也引入了新的作用域。可以使用invoker来引用外部的变量。

1
2
3
4
5
template("idl"){
source_set(target_name) {
sources = invoker.sources
}
}

其他

Imports

可以导入.gni文件。

Path processing

创建文件名或者列表。特别是运行脚本时,以编译输出目录作为当前目录。
可以使用rebase_path来转换目录。

Patterns

用于生成输出文件的名字,并且自动化删除列表的元素
gn help label_pattern

Executing scripts

脚本为python语言。使用action来执行。
第二种方案是在编译时同步进行,使用exec_script