GN语言和操作

  • GN语言和操作

    • 内容
    • 介绍
      • 使用内置的帮助
      • 设计理念
    • 语言
      • 字符串
      • 清单
      • 条件语句
      • 循环
      • 函数调用
      • 作用域和执行Scoping and execution
    • 命名事物
      • 文件和目录名称
    • 构建配置
    • 目标
    • CONFIGS
      • 公共配置
    • 模板
    • 其他特性
      • Imports
      • 路径处理
      • 模式
      • 执行脚本
  • 与Blaze的区别和相似之处

介绍

本页面描述了许多语言的细节和行为。

使用内置的帮助!

GN有一个广泛的内置帮助系统,为每个功能和内置变量提供参考。这个页面更高级。

gn help

你也可以看到2016年3月份的GNE幻灯片。演讲者笔记包含完整的内容。

设计理念

  • 编写构建文件不应该是一个创造性的努力。理想情况下,两个人应该产生相同的构建文件来实现相同的需求。除非绝对需要,否则不应有任何灵活性。做越多的事情越可能产生致命的错误。

  • 定义应该比代码更像代码。我不想编写或调试Prolog。但是我们团队的每个人都可以编写和调试C ++和Python。

  • 构建语言应该被视为构建应该如何工作。表达任意事物不一定容易甚至不可能。我们应该改变源代码和工具,使构建变得更简单,而不是把所有事情都变得更复杂以符合外部要求(在合理的范围内)。

  • 在有意义的时候就像Blaze一样(见下面的“与Blaze的区别和相似之处”)。

语言

GN使用非常简单的动态类型语言。类型是:

  • 布尔(truefalse)。
  • 64位有符号整数。
  • 字符串。
  • 列表(任何其他类型)。
  • 范围(Scopes)(有点像字典,仅是内置的东西(built-in stuff))。

有一些内置变量的值取决于当前的环境。了解gn help更多信息。

语言中故意有许多遗漏。例如没有用户定义的函数调用,(模板是最接近的)。按照上述设计理念,如果你需要这样的东西,你可能做错了。

变量sources有一个特殊的规则:赋值给它时,将应用一个排除模式列表。这被设计成自动过滤掉某些类型的文件。见gn help set_sources_assignment_filtergn help label_pattern了解更多。

语言书呆子的完整语法可以在gn help grammar获取到。

字符串

字符串用双引号括起来,并使用反斜杠作为转义字符。唯一支持的转义序列是:

  • \" (用于直接引用)
  • \$ (字面上的美元符号)
  • \\ (用于文字反斜杠)

任何其他反斜杠的使用都被视为文字反斜杠。所以,例如,\b在模式中使用不需要转义,大多数Windows路径"C:\foo\bar.h"也不需要。

使用$支持简单的变量替换,其中美元符号后的单词被替换为变量的值。如果没有非变量名字符来终止变量名称,可以选择{}包围名称。更复杂的表达式不被支持,仅支持变量名称替换。

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

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

您可以使用 “$0xFF” 语法对8位字符进行编码,因此带有换行符(十六进制0A)的字符串会如下所示,"look$0x0Alike$0x0Athis"

清单

没有办法得到一个列表的长度。如果你发现自己想要做这种事情,那么你就是想在构建中做太多的工作。

列表支持追加:

a = [ "first" ]
a += [ "second" ]  # [ "first", "second" ]
a += [ "third", "fourth" ]  # [ "first", "second", "third", "fourth" ]
b = a + [ "fifth" ]  # [ "first", "second", "third", "fourth", "fifth" ]

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", "fourth" ]

b = a - [ "first" ]  # [ "second", "third" ]
a -= [ "second" ]  # [ "first", "third", "fourth" ]

列表中的 - 运算符搜索匹配项并删除所有匹配的项目。从另一个列表中减去一个列表将删除第二个列表中的每个项目。

如果找不到匹配的项目,将会抛出错误,因此您需要事先知道该项目在移除之前确实已经存在。鉴于没有办法测试包含,主要的用例是建立一个文件或标志的主列表,并基于各种条件删除那些不适用于当前版本的构建。

从风格上来说,最好只添加到列表,并让每个源文件或依赖项只出现一次。这与Chrome团队用于GYP的建议相反(GYP倾向于列出所有文件,然后删除条件中不需要的文件)。

列表支持从零开始的下标以提取值:

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

b = a[1]  # -> "second"

[]运算符是只读的,不能用来改变列表。这个主要的用例是当一个外部脚本返回几个已知的值,并且你想提取它们。

在某些情况下,如果您要添加到列表中,则很容易覆盖列表。为了帮助理解这种情况,将非空列表分配给包含现有非空列表的变量是错误的。如果您想避开此限制,请首先将目标变量分配给空列表。

a = [“one”]
a = [“two”]#错误:用非空列表覆盖非空列表。
a = []#OK
a = [“two”]#OK

a = [“two”]#错误:用非空列表覆盖非空列表。
a = []#OK
a = [“two”]#OK

请注意,构建脚本的执行没有内在知识的底层数据的意义。例如,这意味着它不知道sources是一个文件名列表。所以,如果你删除一个项目,它必须匹配文字字符串,而不是指定一个不同的名称,那将解析为相同的文件名称。

条件语句

条件看起来像C:

  if(is_linux ||(is_win && target_cpu ==“x86”)){sources -= [ "something.cc" ]} else if(...){...} else {...}
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(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")

assert(is_win, "This should only be executed on Windows")

这些功能是内置的,用户不能定义新的功能。

一些函数在它们下面接受一个由{ }组成的代码块:

static_library(“mylibrary”){sources = [“a.cc”]
}
sources = [“a.cc”]
}

其中大多数用来定义目标。用户可以使用下面讨论的模板机制来定义新的函数。

确切地说,这个表达式意味着该块成为函数执行的参数。大多数块式函数都会执行块,并将结果范围视为要读取的变量字典。

作用域和执行(Scoping and execution)

文件和函数调用后面跟着{ }块引入新的作用域。作用域是嵌套的。当您读取一个变量时,将会以相反的顺序搜索包含的作用域,直到找到匹配的名称。变量写入总是进入最内层的作用域。

除了最内层的作用域以外,没有办法修改任何封闭作用域。这意味着当你定义一个目标时,例如,你在块内部做的任何事情都不会泄露到文件的其余部分。

if/ else/ foreach语句,即使他们使用{ },不会引入新的范围,所以更改将持续在语句之外。

命名事物

文件和目录名称

文件和目录名称是字符串,并被解释为相对于当前构建文件的目录。有三种可能的形式:

相对名称:

"foo.cc"
"src/foo.cc"
"../src/foo.cc"

"src/foo.cc"
"../src/foo.cc"

源代码树绝对名称:

“//net/foo.cc”
“//base/test/foo.cc”

“//base/test/foo.cc”

系统绝对名称(罕见,通常用于包含目录):

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

"/C:/Program Files/Windows Kits/Include"

构建配置

目标

目标是构建图中的一个节点。它通常代表将要生成的某种类型的可执行文件或库文件。目标取决于其他目标。内置的目标类型(请参阅gn help <targettype>以获取更多帮助)是:

  • action:运行一个脚本来生成一个文件。
  • action_foreach:为每个源文件运行一次脚本。
  • bundle_data:声明数据加入到Mac / iOS包。
  • create_bundle:创建一个Mac / iOS包。
  • executable:生成一个可执行文件。
  • group:引用一个或多个其他目标的虚拟依赖关系节点。
  • shared_library:.dll或.so。
  • loadable_module:.dll或.so只能在运行时加载。
  • source_set:一个轻量级的虚拟静态库(通常比真正的静态库更可取,因为它的构建速度会更快)。
  • static_library:.lib或.a文件(通常你会想要一个source_set)。

您可以使用模板来扩展它制作自定义目标类型(请参见下文)。在Chrome中,一些更常用的模板是:

  • component:源集或共享库,取决于构建类型。
  • test:测试可执行文件 在移动设备上,这将为测试创建适当的本机应用程序类型。
  • app:可执行文件或Mac / iOS应用程序。
  • android_apk:制作一个APK。有很多其他的Android模版,看//build/config/android/rules.gni

CONFIGS

配置文件是命名对象,用于指定标志集,包含目录和定义。他们可以被应用到一个目标,并推到相关的目标。

要定义一个配置:

config("myconfig") {includes = [ "src/include" ]defines = [ "ENABLE_DOOM_MELON" ]
}
includes = [ "src/include" ]defines = [ "ENABLE_DOOM_MELON" ]
}

要将配置应用于目标:

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

构建配置文件通常指定设置默认配置列表的目标默认值。目标可以根据需要添加或删除。所以在实践中你通常会使用configs += ":myconfig"追加到默认列表。

请参阅gn help config有关如何声明和应用配置的更多信息。

公共配置

目标可以将设置应用于依赖它的其他目标。最常见的例子是一个第三方目标,它需要一些定义或包含目录头才能正确编译。您希望这些设置既适用于第三方库本身的编译,也适用于使用该库的所有目标。

要做到这一点,你写一个你想要应用的设置的配置:

config("my_external_library_config") {includes = "."defines = [ "DISABLE_JANK" ]
}
includes = "."defines = [ "DISABLE_JANK" ]
}

然后这个配置作为“公共”配置被添加到目标。它既适用于目标,也适用于直接依赖目标的目标。

shared_library("my_external_library") {...# Targets that depend on this get this config applied.public_configs = [ ":my_external_library_config" ]
}
...# Targets that depend on this get this config applied.public_configs = [ ":my_external_library_config" ]
}

依赖目标又可以通过将目标作为“公共”依赖项添加到另一个级别,从而将依赖关系树转发到另一个级别。

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

通过把它设置成all_dependent_config一个目标可以转发一个配置给所有的依赖者,直到达到一个链接边界为止。这是强烈不鼓励的,因为它将比必要的构建配置超出更多的标志和定义。使用public_deps来控制哪些标志适用于哪里来代替它。

在Chrome中,更喜欢build/buildflag_header.gni用于定义的构建标题头文件系统,以防止大多数编译器定义的错误。

模板

模板是GN重用代码的主要方式。通常情况下,模板会扩展到一个或多个其他目标类型。

# 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.}
}

#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" ]
}
idl("my_interfaces") {sources = [ "a.idl", "b.idl" ]
}

当时声明一个模板会在范围内的变量周围创建一个闭包。当模板被调用时,魔术变量invoker被用来从调用范围中读取变量。模板通常会将感兴趣的值复制到自己的范围中:

template("idl") {source_set(target_name){sources = invoker.sources}
}
source_set(target_name){sources = invoker.sources}
}

模板执行时的当前目录将是调用的构建文件的目录,而不是模板源文件。这是因为从模板调用者传入的文件是正确的(这通常是模板中大多数文件处理的原因)。但是,如果模板本身有文件(可能会生成一个运行脚本的动作),则需要使用绝对路径(“//foo/…”)来引用这些文件,以说明当前目录在调用时将不可预知。查看gn help template更多信息和更完整的例子。

其他特性

Imports

您可以使用import函数将.gni文件导入到当前作用域。这不是 C++意义上的包含。导入的文件是独立执行的,生成的作用域被复制到当前文件中(C ++在include指令出现的当前上下文中执行包含的文件)。这样可以缓存导入的结果,还可以防止包含多个包含文件在内的一些更“创造性”的用途。

通常情况下,一个.gni会定义构建参数和模板。了解gn help import更多信息。

您的.gni文件可以定义不导出到文件临时变量,通过使用名称中的前面的下划线来包含它,就像_this

路径处理

通常情况下,您需要创建一个文件名或相对于不同目录的文件名列表。运行脚本时,这种情况尤为常见,这些脚本是以构建输出目录作为当前目录执行的,而构建文件通常是指与其包含的目录相关的文件。

您可以使用rebase_path转换目录。查看gn help rebase_path更多的帮助和例子。将相对于当前目录的文件名转换为相对于根目录的典型用法是:new_paths = rebase_path("myfile.c", root_build_dir)

模式

模式用于为自定义目标类型的给定输入集生成输出文件名,并自动从sources变量中移除文件(请参阅参考资料gn help set_sources_assignment_filter)。

他们就像简单的正则表达式。了解gn help label_pattern更多信息。

执行脚本

有两种方法来执行脚本。GN中的所有外部脚本都是Python。第一种方法是作为构建步骤。这样的脚本将需要一些输入,并生成一些输出作为构建的一部分。调用脚本的目标是使用“action”目标类型声明的(请参阅参考资料gn help action)。

执行脚本的第二种方法是在构建文件执行期间同步。这在某些情况下是必要的,以确定要编译的文件集合,或获取构建文件可能依赖的某些系统配置。构建文件可以读取脚本的标准输出(stdout)并以不同的方式对其执行操作。

同步脚本的执行由exec_script函数完成(详见gn help exec_script参考资料)。因为同步执行一个脚本需要暂停当前的构建文件执行,直到Python进程完成执行,依靠外部脚本是慢的,应该尽量减少。

为了防止滥用,允许调用的文件exec_script可以在顶层.gn文件中列入白名单。Chrome做到这一点需要额外的代码审查这样的补充。看gn help dotfile

您可以同步读取和写入在同步运行脚本时不鼓励但偶尔需要的文件。典型的用例是传递一个比当前平台的命令行限制长的文件名列表。请参阅gn help read_file以及gn help write_file如何读取和写入文件。如果可能,应该避免这些功能。

超过命令行长度限制的操作可以使用响应文件绕过此限制,而不同步写入文件。看gn help response_file_contents

与Blaze的区别和相似之处

Blaze是Google的内部构建系统,现在已经作为Bazel公开发布。它启发了一些其他系统,如Pants和Buck。

在Google的同类环境中,对条件的需求非常低,并且可以通过少量的手段(abi_deps)来获得。Chrome使用各地的条件,需要添加这些是文件看起来不同的主要原因。

GN还增加了“配置”的概念来管理一些棘手的依赖和配置问题,同样不会出现在服务器上。Blaze有一个“配置”的概念,就像一个GN工具链,但内置在工具本身。GN工具链的工作方式是试图以一种简洁的方式将这个概念分离到构建文件中的结果。

GN保留了一些GYP概念,比如“全部依赖”设置,这些设置在Blaze中有些不同。这部分是为了使现有的GYP代码更容易转换,GYP结构通常会提供更细粒度的控制(根据具体情况而定,好或坏)。

GN也使用GYP名称,比如“sources”而不是“srcs”,因为缩写似乎是不必要的,尽管它使用了Blaze的“deps”,因为“dependencies”很难打字。Chromium还在一个目标中编译多种语言,因此指定目标名称前缀的语言类型被删除(例如,从cc_library)。

google gn构建系统的介绍相关推荐

  1. chromium中的GN构建系统

    阅读最新的chromium源码,发现项目的构建系统已经从GYP全面切换到GN了.在软件开发中,经常有人忠告:不要重复造轮子.但谷歌可不管这个,造的轮子一个接一个,谁叫人家牛呢?chromiumi项目为 ...

  2. google的gn构建系统

    什么是GN? GN是一个生成Ninja构建文件的元构建系统,以便你可以用Ninja构建Chromium. 你为什么从GYP切换? 我们相信GN文件比GYP文件更具可读性和可维护性. GN很快: GN比 ...

  3. Ninja构建系统入门--GN与Ninja构建过程

    Ninja构建系统入门--GN与Ninja构建过程 开始 GN构建系统 搭建 结束 开始 GN构建系统 GN是一种元构建系统,生成Ninja构建文件(Ninja build files),相较GYP而 ...

  4. 【翻译】Google在构建静态代码分析工具方面的经验教训

    软件bug耗费开发者和软件公司大量的时间和金钱. 以2014年为例,被广泛使用的SSL协议实现中的一个("goto fail")bug导致可接受无效的SSL证书,另外一个与日期格式 ...

  5. centos7.9编译安装构建系统gn+ninja

    1 前言 环境Win10主机+VMware15.5+Centos7.9 登录用户:root Ninja 是Google推出的注重速度的构建工具,一般在Unix/Linux上的程序通过make/make ...

  6. 鸿蒙构建系统——gn官方FAQ翻译,以及gn官方文档分享

    GN FAQ 翻译 (PS:花了将近半个小时,把GN的官方FAQ翻译了一遍,有错漏之处欢迎大家指正.) GN 的文档在哪里? GN有大量的内置的帮助文档,所以你可以运行gn help命令查阅,但是你同 ...

  7. Nx 介绍: 基于插件的单一代码库(Monorepo)构建系统

    文章目录 前言 一.Nx 设计理念 二.Nx 核心概念 1. 项目图 - Project graph 2.元数据驱动 - Metadata driven 3. 任务图 - Task graph 4.受 ...

  8. android系统自动构建,[系统集成] Android 自动构建系统

    一.简介 android app 自动构建服务器用于自动下载app代码.自动打包.发布,要建立这样的服务器,关键要解决以下几个问题: 1. android app 自动化打包 android 的打包一 ...

  9. 打造一个全命令行的Android构建系统

    IDE都是给小白程序员的,大牛级别的程序员一定是命令行控,终端控,你看大牛都是使用vim,emacs 就一切搞定" 这话说的虽然有些绝对,但是也不无道理,做开发这行要想效率高,自动化还真是缺 ...

最新文章

  1. R语言文摘:Subsetting Data
  2. linux无法设置日期 不允许的操作,如何解决系统时间无法修改的问题
  3. kafka数据丢失的场景
  4. 【BC】如何将自定义的区域菜单添加到系统默认的菜单中
  5. c++学习笔记之类的应用
  6. Intel图形库Mesa的持续集成
  7. NO1:在Windows端安装SecureCRT来连接Linux
  8. python整数类型在每一台计算机上的取值范围是一样的_关于python统计一个整数列表中不同数值种类数的问题。...
  9. scala 方法调用_Scala中的方法调用
  10. 电子书包“翻转”课堂
  11. 天线下倾角示意图_《天线和下倾角.ppt
  12. 科大讯飞测试开发工程师面试
  13. 上twitter_如何在Twitter上对某人静音
  14. 蓝桥杯第十届c语言试题答案,[蓝桥杯][2019年第十届真题]空间跳跃 - C语言网
  15. 2017普实软件迎新年会报道
  16. POJ 1198 / HDU 1401 Solitaire (记忆化搜索+meet in middle)
  17. docker创建的activemq配置nio不能映射端口61618
  18. css实现文字的水平垂直居中
  19. 测试基础+性能测试+自动化测试面试题(含答案)
  20. 二级mysql选择题要对一半才能拿证书_计算机二级选择题需要达到多少分才能及格...

热门文章

  1. java devexpress_Coolite与DevExpress比较
  2. MySQL数据库实用教程考核_《MySQL数据库实用教程》郑明秋,蒙连超,赵海侠【pdf】...
  3. python统计字母空格个数_python统计字母、空格、数字等字符个数的实例
  4. java批量执行sql语句_Java中批量执行sql语句
  5. DASH流媒体MPD文件存储
  6. GitHub开源:狗屁不通文章生成器
  7. 自定义类型数组的初始化
  8. ROS系统中实现点云聚类(realsense数据源)
  9. 深度学习(4)基础4 -- 神经网络架构激活函数过拟合处理
  10. OpenCV(十四)图像阈值