首页 > 其他分享 >【转】GN Language and Operation

【转】GN Language and Operation

时间:2023-11-10 14:13:14浏览次数:34  
标签:target Language list name build file GN Operation help

原文链接:https://gn.googlesource.com/gn/+/refs/heads/main/docs/language.md

这里还有一篇:谷歌gn编译文件的使用简介

GN Language and Operation

Contents

Introduction

This page describes many of the language details and behaviors.

Use the built-in help!

GN has an extensive built-in help system which provides a reference for every function and built-in variable. This page is more high-level.

gn help

You can also see the slides from a March, 2016 introduction to GN. The speaker notes contain the full content.

Design philosophy

  • Writing build files should not be a creative endeavour. Ideally two people should produce the same buildfile given the same requirements. There should be no flexibility unless it's absolutely needed. As many things should be fatal errors as possible.

  • The definition should read more like code than rules. I don't want to write or debug Prolog. But everybody on our team can write and debug C++ and Python.

  • The build language should be opinionated as to how the build should work. It should not necessarily be easy or even possible to express arbitrary things. We should be changing source and tooling to make the build simpler rather than making everything more complicated to conform to external requirements (within reason).

  • Be like Blaze when it makes sense (see “Differences and similarities to Blaze” below).

Language

GN uses an extremely simple, dynamically typed language. The types are:

  • Boolean (truefalse).
  • 64-bit signed integers.
  • Strings.
  • Lists (of any other types).
  • Scopes (sort of like a dictionary, only for built-in stuff).

There are some built-in variables whose values depend on the current environment. See gn help for more.

There are purposefully many omissions in the language. There are no user-defined function calls, for example (templates are the closest thing). As per the above design philosophy, if you need this kind of thing you're probably doing it wrong.

The full grammar for language nerds is available in gn help grammar.

Strings

Strings are enclosed in double-quotes and use backslash as the escape character. The only escape sequences supported are:

  • \" (for literal quote)
  • \$ (for literal dollars sign)
  • \\ (for literal backslash)

Any other use of a backslash is treated as a literal backslash. So, for example, \b used in patterns does not need to be escaped, nor do most Windows paths like "C:\foo\bar.h".

Simple variable substitution is supported via $, where the word following the dollars sign is replaced with the value of the variable. You can optionally surround the name with {} if there is not a non-variable-name character to terminate the variable name. More complex expressions are not supported, only variable name substitution.

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

You can encode 8-bit characters using “$0xFF” syntax, so a string with newlines (hex 0A) would "look$0x0Alike$0x0Athis".

Lists

Aside from telling empty lists from non empty lists (a == []), there is no way to get the length of a list. If you find yourself wanting to do this kind of thing, you're trying to do too much work in the build.

Lists support appending:

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

Appending a list to another list appends the items in the second list rather than appending the list as a nested member.

You can remove items from a list:

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

The - operator on a list searches for matches and removes all matching items. Subtracting a list from another list will remove each item in the second list.

If no matching items are found, an error will be thrown, so you need to know in advance that the item is there before removing it. Given that there is no way to test for inclusion, the main use-case is to set up a master list of files or flags, and to remove ones that don't apply to the current build based on various conditions.

Stylistically, prefer to only add to lists and have each source file or dependency appear once. This is the opposite of the advice Chrome-team used to give for GYP (GYP would prefer to list all files, and then remove the ones you didn't want in conditionals).

Lists support zero-based subscripting to extract values:

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

The [] operator is read-only and can not be used to mutate the list. The primary use-case of this is when an external script returns several known values and you want to extract them.

There are some cases where it's easy to overwrite a list when you mean to append to it instead. To help catch this case, it is an error to assign a nonempty list to a variable containing an existing nonempty list. If you want to get around this restriction, first assign the destination variable to the empty list.

a = [ "one" ]
a = [ "two" ]  # Error: overwriting nonempty list with a nonempty list.
a = []         # OK
a = [ "two" ]  # OK

Note that execution of the build script is done without intrinsic knowledge of the meaning of the underlying data. This means that it doesn't know that sources is a list of file names, for example. So if you remove an item, it must match the literal string rather than specifying a different name that will resolve to the same file name.

Conditionals

Conditionals look like C:

  if (is_linux || (is_win && target_cpu == "x86")) {
    sources -= [ "something.cc" ]
  } else if (...) {
    ...
  } else {
    ...
  }

You can use them in most places, even around entire targets if the target should only be declared in certain circumstances.

Looping

You can iterate over a list with foreach. This is discouraged. Most things the build should do can normally be expressed without doing this, and if you find it necessary it may be an indication you're doing too much work in the metabuild.

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

Function calls

Simple function calls look like most other languages:

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

Such functions are built-in and the user can not define new ones.

Some functions take a block of code enclosed by { } following them:

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

Most of these define targets. The user can define new functions like this with the template mechanism discussed below.

Precisely, this expression means that the block becomes an argument to the function for the function to execute. Most of the block-style functions execute the block and treat the resulting scope as a dictionary of variables to read.

Scoping and execution

Files and function calls followed by { } blocks introduce new scopes. Scopes are nested. When you read a variable, the containing scopes will be searched in reverse order until a matching name is found. Variable writes always go to the innermost scope.

There is no way to modify any enclosing scope other than the innermost one. This means that when you define a target, for example, nothing you do inside of the block will “leak out” into the rest of the file.

if/else/foreach statements, even though they use { }, do not introduce a new scope so changes will persist outside of the statement.

Naming things

File and directory names

File and directory names are strings and are interpreted as relative to the current build file's directory. There are three possible forms:

Relative names:

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

Source-tree absolute names:

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

System absolute names (rare, normally used for include directories):

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

Build configuration

Targets

A target is a node in the build graph. It usually represents some kind of executable or library file that will be generated. Targets depend on other targets. The built-in target types (see gn help <targettype> for more help) are:

  • action: Run a script to generate a file.
  • action_foreach: Run a script once for each source file.
  • bundle_data: Declare data to go into a Mac/iOS bundle.
  • create_bundle: Creates a Mac/iOS bundle.
  • executable: Generates an executable file.
  • group: A virtual dependency node that refers to one or more other targets.
  • shared_library: A .dll or .so.
  • loadable_module: A .dll or .so loadable only at runtime.
  • source_set: A lightweight virtual static library (usually preferrable over a real static library since it will build faster).
  • static_library: A .lib or .a file (normally you'll want a source_set instead).

You can extend this to make custom target types using templates (see below). In Chrome some of the more commonly-used templates are:

  • component: Either a source set or shared library, depending on the build type.
  • test: A test executable. On mobile this will create the appropriate native app type for tests.
  • app: Executable or Mac/iOS application.
  • android_apk: Make an APK. There are a lot of other Android ones, see //build/config/android/rules.gni.

Configs

Configs are named objects that specify sets of flags, include directories, and defines. They can be applied to a target and pushed to dependent targets.

To define a config:

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

To apply a config to a target:

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

It is common for the build config file to specify target defaults that set a default list of configs. Targets can add or remove to this list as needed. So in practice you would usually use configs += ":myconfig" to append to the list of defaults.

See gn help config for more information about how configs are declared and applied.

Public configs

A target can apply settings to other targets that depend on it. The most common example is a third party target that requires some defines or include directories for its headers to compile properly. You want these settings to apply both to the compile of the third party library itself, as well as all targets that use the library.

To do this, you write a config with the settings you want to apply:

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

Then this config is added to the target as a “public” config. It will apply both to the target as well as targets that directly depend on it.

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

Dependent targets can in turn forward this up the dependency tree another level by adding your target as a “public” dependency.

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

A target can forward a config to all dependents until a link boundary is reached by setting it as an all_dependent_config. This is strongly discouraged as it can spray flags and defines over more of the build than necessary. Instead, use public_deps to control which flags apply where.

In Chrome, prefer the build flag header system (build/buildflag_header.gni) for defines which prevents most screw-ups with compiler defines.

Templates

Templates are GN's primary way to re-use code. Typically, a template would expand to one or more other target types.

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

Typically your template definition would go in a .gni file and users would import that file to see the template definition:

import("//tools/idl_compiler.gni")

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

Declaring a template creates a closure around the variables in scope at that time. When the template is invoked, the magic variable invoker is used to read variables out of the invoking scope. The template would generally copy the values its interested in into its own scope:

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

The current directory when a template executes will be that of the invoking build file rather than the template source file. This is so files passed in from the template invoker will be correct (this generally accounts for most file handling in a template). However, if the template has files itself (perhaps it generates an action that runs a script), you will want to use absolute paths (“//foo/...”) to refer to these files to account for the fact that the current directory will be unpredictable during invocation. See gn help template for more information and more complete examples.

Other features

Imports

You can import .gni files into the current scope with the import function. This is not an include in the C++ sense. The imported file is executed independently and the resulting scope is copied into the current file (C++ executes the included file in the current context of when the include directive appeared). This allows the results of the import to be cached, and also prevents some of the more “creative” uses of includes like multiply-included files.

Typically, a .gni would define build arguments and templates. See gn help import for more.

Your .gni file can define temporary variables that are not exported files that include it by using a preceding underscore in the name like _this.

Path processing

Often you will want to make a file name or a list of file names relative to a different directory. This is especially common when running scripts, which are executed with the build output directory as the current directory, while build files usually refer to files relative to their containing directory.

You can use rebase_path to convert directories. See gn help rebase_path for more help and examples. Typical usage to convert a file name relative to the current directory to be relative to the root build directory would be: new_paths = rebase_path("myfile.c", root_build_dir)

Patterns

Patterns are used to generate the output file names for a given set of inputs for custom target types, and to automatically remove files from the list values (see gn help filter_include and gn help filter_exclude).

They are like simple regular expressions. See gn help label_pattern for more.

Executing scripts

There are two ways to execute scripts. All external scripts in GN are in Python. The first way is as a build step. Such a script would take some input and generate some output as part of the build. Targets that invoke scripts are declared with the “action” target type (see gn help action).

The second way to execute scripts is synchronously during build file execution. This is necessary in some cases to determine the set of files to compile, or to get certain system configurations that the build file might depend on. The build file can read the stdout of the script and act on it in different ways.

Synchronous script execution is done by the exec_script function (see gn help exec_script for details and examples). Because synchronously executing a script requires that the current buildfile execution be suspended until a Python process completes execution, relying on external scripts is slow and should be minimized.

To prevent abuse, files permitted to call exec_script can be whitelisted in the toplevel .gn file. Chrome does this to require additional code review for such additions. See gn help dotfile.

You can synchronously read and write files which is discouraged but occasionally necessary when synchronously running scripts. The typical use-case would be to pass a list of file names longer than the command-line limits of the current platform. See gn help read_file and gn help write_file for how to read and write files. These functions should be avoided if at all possible.

Actions that exceed command-line length limits can use response files to get around this limitation without synchronously writing files. See gn help response_file_contents.

Differences and similarities to Blaze

Blaze is Google's internal build system, now publicly released as Bazel. It has inspired a number of other systems such as Pants and Buck.

In Google's homogeneous environment, the need for conditionals is very low and they can get by with a few hacks (abi_deps). Chrome uses conditionals all over the place and the need to add these is the main reason for the files looking different.

GN also adds the concept of “configs” to manage some of the trickier dependency and configuration problems which likewise don't arise on the server. Blaze has a concept of a “configuration” which is like a GN toolchain, but built into the tool itself. The way that toolchains work in GN is a result of trying to separate this concept out into the build files in a clean way.

GN keeps some GYP concept like “all dependent” settings which work a bit differently in Blaze. This is partially to make conversion from the existing GYP code easier, and the GYP constructs generally offer more fine-grained control (which is either good or bad, depending on the situation).

GN also uses GYP names like “sources” instead of “srcs” since abbreviating this seems needlessly obscure, although it uses Blaze's “deps” since “dependencies” is so hard to type. Chromium also compiles multiple languages in one target so specifying the language type on the target name prefix was dropped (e.g. from cc_library).

Powered by GitilesPrivacyTerms

标签:target,Language,list,name,build,file,GN,Operation,help
From: https://www.cnblogs.com/xiululu/p/17823961.html

相关文章

  • OpenFeign 接口调用问题及解决方案
    问题描述如果在同一个工程中出现两个OpenFeign接口使用一样的服务名称会报以下错误:Description:Thebean'optimization-user.FeignClientSpecification',definedinnull,couldnotberegistered.Abeanwiththatnamehasalreadybeendefinedinnullandoverridin......
  • 【论文阅读笔记】【OCR-文本识别】 Scene Text Recognition with Permuted Autoregres
    PARSeqECCV2022读论文思考的问题论文试图解决什么问题?一些文本识别模型会对semantic信息建模,从而辅助某些困难情况下的文本识别传统的auto-regressive方式限制了语义信息的传输方向;双向的auto-regressive聚合增加了不必要的计算量和复杂度;聚合视觉模型和语言......
  • scaled sign compressor(比例符号压缩器)
    1、信号处理领域在信号处理领域,"scaledsigncompressor"(比例符号压缩器)是一种用于压缩信号幅度的算法。它通常用于减小信号的动态范围,以便更有效地表示或传输信号。"scaledsigncompressor"的工作原理如下:首先,它计算信号的绝对值,并将其与预先设定的阈值进行比较。如果信号的绝......
  • 【AntDesign】Docker部署
    docker部署是主流的部署方式,极大的方便了开发部署环境,保持了环境的统一,也是实现自动化部署的前提。1项目的目录结构dist:使用build打包命令,生成的打包目录npmrunbuild:打包项目命令docker:存放docker容器需要修改的配置目录,比如nginx配置Dockerfile:跟项......
  • 【AntDesign】Docker部署
    docker部署是主流的部署方式,极大的方便了开发部署环境,保持了环境的统一,也是实现自动化部署的前提。1项目的目录结构dist:使用build打包命令,生成的打包目录npmrunbuild:打包项目命令docker:存放docker容器需要修改的配置目录,比如nginx配置Dockerfile:跟......
  • pyqt5-designer简单使用和代码简单说明
    学习pyqt,实际上主要是逐个学习ui组件。 1、designer的简单使用(1)创建窗口主要是QWidget类窗口和QMainWindow窗口的使用。后者支持窗口菜单栏的实现。(2)拖动(3)布局和预览(4)查看组件属性(5)对象名称和类的 2、简单的代码说明(1)窗口中的所有ui组件,是作为窗口对象的......
  • 无涯教程-批处理 - Align Right函数
    这用于将文本向右对齐,通常用于提高数字列的可读性。@echooffsetx=1000sety=1sety=%y%echo%x%sety=%y:~-4%echo%y%关于上述程序,需要注意的一些关键事项是-将空格添加到y变量,在这种情况下,将9个空格添加到y变量。无涯教程使用〜-4选项表示只想show......
  • ALLEGRO导网表报错This reference has already been assigned to a different package
     (1)QUESTION(ORCAP-1589):Nethastwoormorealiases-possibleshort?原因:器件默认管脚命名(NET名称)与所连接网络的NET名称不一致导致的措施:可忽略。或关闭Tools->DesignRulesCheck->PhysicalRules->Checkpowergroundshort(2)ReportforInvalidReferencesERROR(ORCAP-......
  • 防止DOS攻击(检测nignx日志若某个IP短时间的PV过大则使用防火墙将其禁掉)
    #!/bin/bashtime=`date|awk'{print$3"\\\\/"$2"\\\\/"$6}'`awk'$4~/'"${time}"'/{print$0}'access.log|awk'{ip[$1]++}END{for(iinip)printi,ip[i]}'|sort-rnk2|head>18.t......
  • SP15637 GNYR04H - Mr Youngs Picture Permutations(线性 dp)
    题目求方案数,考虑dp——状态设计和边界——题目告诉了一个很显然的性质:每一排从左至右保证高度单调递减每一列从后往前保证高度单调递减那么可以发现,对于每一行,每一列,一定是按高度顺序插入,并且是连续插入,因为如果不连续,就无法保证单调递减的性质同时,它给出了另一个性......