语法

Meson 规范语言的语法尽可能保持简单。它是一种强类型语言,因此在幕后永远不会将一个对象转换为另一个对象。变量没有可见的类型,这使得 Meson 成为动态类型语言(也称为鸭子类型语言)。

该语言的主要构建块是变量数字布尔值字符串数组函数调用方法调用if 语句包含

通常,一条 Meson 语句只占一行。与例如C语言不同,无法在一行中包含多条语句。函数和方法调用的参数列表可以跨多行拆分。Meson 将自动检测这种情况并执行正确的操作。

在其他情况下,(0.50 版本添加) 可以通过在行尾添加 \ 来获得多行语句。除了换行符之外,空白符没有语法意义。

变量

Meson 中的变量的工作原理与其他高级编程语言中的变量类似。变量可以包含任何类型的 value,例如整数或字符串。变量不需要预先声明,只需对其进行赋值即可,它们就会出现。以下是如何将 values 赋值给两个不同的变量。

var1 = 'hello'
var2 = 102

Meson 中变量工作方式的一个重要区别在于所有对象都是不可变的。当看到一个看起来像是变异的操作时,实际上会创建一个新的对象并将其赋值给该名称。这与例如 Python 对对象的操作方式不同,但与例如 Python 字符串类似。

var1 = [1, 2, 3]
var2 = var1
var2 += [4]
# var2 is now [1, 2, 3, 4]
# var1 is still [1, 2, 3]

数字

Meson 只支持整数。它们只需通过写出来即可声明。支持基本的算术运算。

x = 1 + 2
y = 3 * 4
d = 5 % 3 # Yields 2.

从 0.45.0 版本开始支持十六进制字面量。

int_255 = 0xFF

从 0.47.0 版本开始支持八进制和二进制字面量。

int_493 = 0o755
int_1365 = 0b10101010101

字符串可以像这样转换为数字

string_var = '42'
num = string_var.to_int()

数字可以转换为字符串

int_var = 42
string_var = int_var.to_string()

布尔值

布尔值可以是 truefalse

truth = true

布尔值可以转换为字符串或数字

bool_var = true
string_var = bool_var.to_string()
int_var = bool_var.to_int()

字符串

Meson 中的字符串用单引号声明。要输入一个字面单引号,请像这样操作

single_quote = 'contains a \' character'

转义序列的完整列表如下

  • \\ 反斜杠
  • \' 单引号
  • \a 响铃
  • \b 退格键
  • \f 换页符
  • \n 换行符
  • \r 回车符
  • \t 水平制表符
  • \v 垂直制表符
  • \ooo 八进制 value 为 ooo 的字符
  • \xhh 十六进制 value 为 hh 的字符
  • \uxxxx 十六进制 value 为 xxxx 的 16 位字符
  • \Uxxxxxxxx 十六进制 value 为 xxxxxxxx 的 32 位字符
  • \N{name} Unicode 数据库中名为 name 的字符

与 Python 和 C 一样,\ooo 中最多接受三个八进制数字。

无法识别的转义序列将保留在字符串中,即反斜杠将保留在字符串中。

字符串连接

可以使用 + 符号将字符串连接起来以形成一个新的字符串。

str1 = 'abc'
str2 = 'xyz'
combined = str1 + '_' + str2 # combined is now abc_xyz

字符串路径构建

(0.49 版本添加)

可以使用 / 作为运算符来连接任意两个字符串以构建路径。这将始终使用 / 作为所有平台上的路径分隔符。

joined = '/usr/share' / 'projectname'    # => /usr/share/projectname
joined = '/usr/local' / '/etc/name'      # => /etc/name

joined = 'C:\\foo\\bar' / 'builddir'     # => C:/foo/bar/builddir
joined = 'C:\\foo\\bar' / 'D:\\builddir' # => D:/builddir

请注意,这等效于使用 join_paths(),该函数已被此运算符弃用。

跨多行的字符串

跨多行的字符串可以用三个单引号声明,如下所示

multiline_string = '''#include <foo.h>
int main (int argc, char ** argv) {
  return FOO_SUCCESS;
}'''

这些是原始字符串,不支持上面列出的转义序列。这些字符串也可以与下面描述的 .format() 结合使用以实现字符串格式化功能。

请注意,多行 f-string 支持已在 0.63 版本中添加。

字符串索引

字符串支持索引 ([<num>]) 运算符。此运算符允许(只读)访问特定字符。返回值保证是长度为 1 的字符串。

foo = 'abcd'
message(foo[1])  # Will print 'b'
foo[2] = 'C'     # ERROR: Meson objects are immutable!

字符串格式化

.format()

可以使用字符串格式化功能来构建字符串。

template = 'string: @0@, number: @1@, bool: @2@'
res = template.format('text', 1, true)
# res now has value 'string: text, number: 1, bool: true'

如您所见,格式化通过将 @number@ 类型的占位符替换为相应的参数来实现。

格式字符串

(0.58 版本添加)

格式字符串可以用作上面描述的字符串格式化功能的非位置替代方法。请注意,多行 f-string 支持已在 0.63 版本中添加。

n = 10
m = 'hi'

s = f'int: @n@, string: @m@'
# s now has the value 'int: 10, string: hi'

目前,格式字符串中只支持 identity-expressions,这意味着您不能在其中使用任意的 Meson 表达式。

n = 10
m = 5

# The following is not a valid format string
s = f'result: @n + m@'

字符串方法

字符串还支持一些其他方法,这些方法将返回转换后的副本。

.replace()

从 0.58.0 版本开始,您可以替换字符串中的子字符串。

# Replaces all instances of one substring with another
s = 'semicolons;as;separators'
s = s.replace('as', 'are')
# 's' now has the value of 'semicolons;are;separators'

.strip()

# Similar to the Python str.strip(). Removes leading/ending spaces and newlines.
define = ' -Dsomedefine '
stripped_define = define.strip()
# 'stripped_define' now has the value '-Dsomedefine'

# You may also pass a string to strip, which specifies the set of characters to
# be removed instead of the default whitespace.
string = 'xyxHelloxyx'.strip('xy')
# 'string' now has the value 'Hello'

从 0.43.0 版本开始,您可以指定一个位置字符串参数,该字符串中的所有字符都将被剥离。

.to_upper(), .to_lower()

target = 'x86_FreeBSD'
upper = target.to_upper() # t now has the value 'X86_FREEBSD'
lower = target.to_lower() # t now has the value 'x86_freebsd'

.to_int()

version = '1'
# Converts the string to an int and throws an error if it can't be
ver_int = version.to_int()

.contains(), .startswith(), .endswith()

target = 'x86_FreeBSD'
is_fbsd = target.to_lower().contains('freebsd')
# is_fbsd now has the boolean value 'true'
is_x86 = target.startswith('x86') # boolean value 'true'
is_bsd = target.to_lower().endswith('bsd') # boolean value 'true'

.substring()

从 0.56.0 版本开始,您可以从字符串中提取子字符串。

# Similar to the Python str[start:end] syntax
target = 'x86_FreeBSD'
platform = target.substring(0, 3) # prefix string value 'x86'
system = target.substring(4) # suffix string value 'FreeBSD'

该方法接受负值,其中负 start 相对于字符串的末尾 len(string) - start,以及负 end

string = 'foobar'
string.substring(-5, -3) # => 'oo'
string.substring(1, -1) # => 'ooba'

.split(), .join()

# Similar to the Python str.split()
components = 'a b   c d '.split()
# components now has the value ['a', 'b', 'c', 'd']
components = 'a b   c d '.split(' ')
# components now has the value ['a', 'b', '', '', 'c', 'd', '']

# Similar to the Python str.join()
output = ' '.join(['foo', 'bar'])
# Output value is 'foo bar'
pathsep = ':'
path = pathsep.join(['/usr/bin', '/bin', '/usr/local/bin'])
# path now has the value '/usr/bin:/bin:/usr/local/bin'

# For joining path elements, you should use path1 / path2
# This has the advantage of being cross-platform
path = '/usr' / 'local' / 'bin'
# path now has the value '/usr/local/bin'

# For sources files, use files():
my_sources = files('foo.c')
...
my_sources += files('bar.c')
# This has the advantage of always calculating the correct relative path, even
# if you add files in another directory or use them in a different directory
# than they're defined in

# Example to set an API version for use in library(), install_header(), etc
project('project', 'c', version: '0.2.3')
version_array = meson.project_version().split('.')
# version_array now has the value ['0', '2', '3']
api_version = '.'.join([version_array[0], version_array[1]])
# api_version now has the value '0.2'

# We can do the same with .format() too:
api_version = '@0@.@1@'.format(version_array[0], version_array[1])
# api_version now (again) has the value '0.2'

.underscorify()

name = 'Meson Docs.txt#Reference-manual'
# Replaces all characters other than `a-zA-Z0-9` with `_` (underscore)
# Useful for substituting into #defines, filenames, etc.
underscored = name.underscorify()
# underscored now has the value 'Meson_Docs_txt_Reference_manual'

.version_compare()

version = '1.2.3'
# Compare version numbers semantically
is_new = version.version_compare('>=2.0')
# is_new now has the boolean value false
# Supports the following operators: '>', '<', '>=', '<=', '!=', '==', '='

Meson 版本比较约定包括

'3.6'.version_compare('>=3.6.0') == false

最好明确地指定完整的修订级别进行比较。

数组

数组用方括号分隔。数组可以包含任意数量的任何类型的对象。

my_array = [1, 2, 'string', some_obj]

可以通过数组索引访问数组元素

my_array = [1, 2, 'string', some_obj]
second_element = my_array[1]
last_element = my_array[-1]

您可以像这样向数组添加更多项目

my_array += ['foo', 3, 4, another_obj]

添加单个项目时,无需将其放在数组中

my_array += ['something']
# This also works
my_array += 'else'

请注意,由于 Meson 中的所有对象都是不可变的,因此追加到数组将始终创建一个新的数组对象并将其赋值给 my_array,而不是修改原始数组。

从 0.49.0 版本开始,您可以像这样检查数组是否包含元素

my_array = [1, 2]
if 1 in my_array
# This condition is true
endif
if 1 not in my_array
# This condition is false
endif

数组方法

以下方法为所有数组定义

  • length,数组的大小
  • contains,如果数组包含作为参数给出的对象,则返回 true,否则返回 false
  • get,返回给定索引处的对象,负索引从数组的末尾开始计数,索引超出范围是一个致命错误。为了向后兼容,它与数组索引相同。

字典

字典用大括号分隔。字典可以包含任意数量的键:value 对。键必须是字符串,但 values 可以是任何类型的对象。在0.53.0 之前,键必须是字面字符串,即您不能使用包含字符串 value 的变量作为键。

my_dict = {'foo': 42, 'bar': 'baz'}

键必须是唯一的

# This will fail
my_dict = {'foo': 42, 'foo': 43}

访问字典元素的方式与数组索引类似

my_dict = {'foo': 42, 'bar': 'baz'}
forty_two = my_dict['foo']
# This will fail
my_dict['does_not_exist']

字典是不可变的,没有保证的顺序。

字典从 0.47.0 版本开始提供。

访问参考手册中的 dict 对象页面,了解字典公开的方法。

从 0.49.0 版本开始,您可以像这样检查字典是否包含键

my_dict = {'foo': 42, 'bar': 43}
if 'foo' in my_dict
# This condition is true
endif
if 42 in my_dict
# This condition is false
endif
if 'foo' not in my_dict
# This condition is false
endif

从 0.53.0 版本开始,键可以是任何计算结果为字符串 value 的表达式,不再局限于字符串字面量。

d = {'a' + 'b' : 42}
k = 'cd'
d += {k : 43}

函数调用

Meson 提供了一组可用的函数。最常见的用例是创建构建对象。

executable('progname', 'prog.c')

大多数函数只接受少数位置参数,但接受多个关键字参数,这些参数的指定方式如下

executable('progname',
  sources: 'prog.c',
  c_args: '-DFOO=1')

从 0.49.0 版本开始,关键字参数可以动态指定。这是通过将表示要设置的关键字的字典传递给 kwargs 关键字来实现的。前面的示例的指定方式如下

d = {'sources': 'prog.c',
  'c_args': '-DFOO=1'}

executable('progname',
  kwargs: d)

单个函数可以通过函数调用直接接受关键字参数,也可以通过 kwargs 关键字参数间接接受关键字参数。唯一的限制是,将任何特定键同时作为直接参数和间接参数传递是一个硬错误。

d = {'c_args': '-DFOO'}
executable('progname', 'prog.c',
  c_args: '-DBAZ=1',
  kwargs: d) # This is an error!

尝试这样做会导致 Meson 立即退出并显示错误。

参数扁平化

参数扁平化是 Meson 的一项功能,旨在简化使用方法和函数。对于启用此功能的函数,Meson 会获取参数列表并将所有嵌套列表扁平化为一个大列表。

例如,以下对 executable() 的函数调用在 Meson 中是相同的

# A normal example:
executable('exe1', ['foo.c', 'bar.c', 'foobar.c'])

# A more contrived example that also works but certainly
# isn't good Meson code:
l1 = ['bar.c']
executable('exe1', [[['foo.c', l1]], ['foobar.c']])

# How meson will treat all the previous calls internally:
executable('exe1', 'foo.c', 'bar.c', 'foobar.c')

由于内部实现细节,以下语法目前也支持,即使 executable() 的第一个参数是单个 str 而不是 list

# WARNING: This example is only valid because of an internal
#          implementation detail and not because it is intended
#
#          PLEASE DO NOT DO SOMETHING LIKE THIS!
#
executable(['exe1', 'foo.c'], 'bar.c', 'foobar.c')

此代码目前被接受,因为参数扁平化目前发生在参数求值之前。此类构造的“支持”可能会在未来的 Meson 版本中删除!

大多数但并非所有 Meson 函数和方法都支持参数扁平化。一般来说,可以假设如果精确的列表结构与函数无关,则函数或方法支持参数扁平化。

函数是否支持参数扁平化在参考手册中有所说明。

方法调用

对象可以有方法,这些方法使用点运算符调用。它提供的具体方法取决于对象。

myobj = some_function()
myobj.do_something('now')

if 语句

if 语句的工作原理与其他语言中的 if 语句相同。

var1 = 1
var2 = 2
if var1 == var2 # Evaluates to false
  something_broke()
elif var3 == var2
  something_else_broke()
else
  everything_ok()
endif

opt = get_option('someoption')
if opt != 'foo'
  do_something()
endif

逻辑运算

Meson 具有标准范围的逻辑运算,这些运算可以在 if 语句中使用。

if a and b
  # do something
endif
if c or d
  # do something
endif
if not e
  # do something
endif
if not (f or g)
  # do something
endif

逻辑运算只能对布尔值起作用。

Foreach 语句

要对可迭代对象的所有元素执行操作,请使用 foreach 命令。

请注意,Meson 变量是不可变的。尝试在 foreach 循环内部为被迭代对象分配一个新的 value 不会影响 foreach 的控制流。

使用数组的 Foreach

以下是如何使用数组和 foreach 定义两个可执行文件及其相应测试的示例。

progs = [['prog1', ['prog1.c', 'foo.c']],
         ['prog2', ['prog2.c', 'bar.c']]]

foreach p : progs
  exe = executable(p[0], p[1])
  test(p[0], exe)
endforeach

使用字典的 Foreach

以下是如何迭代应根据某些配置编译的一组组件的示例。此示例使用了一个字典,该字典从 0.47.0 版本开始提供。

components = {
  'foo': ['foo.c'],
  'bar': ['bar.c'],
  'baz': ['baz.c'],
}

# compute a configuration based on system dependencies, custom logic
conf = configuration_data()
conf.set('USE_FOO', 1)

# Determine the sources to compile
sources_to_compile = []
foreach name, sources : components
  if conf.get('USE_@0@'.format(name.to_upper()), 0) == 1
    sources_to_compile += sources
  endif
endforeach

Foreach breakcontinue

从 0.49.0 版本开始,可以在 foreach 循环中使用 breakcontinue 关键字。

items = ['a', 'continue', 'b', 'break', 'c']
result = []
foreach i : items
  if i == 'continue'
    continue
  elif i == 'break'
    break
  endif
  result += i
endforeach
# result is ['a', 'b']

注释

注释以 # 字符开头,一直延续到行尾。

some_function() # This is a comment
some_other_function()

三元运算符

三元运算符的工作原理与其他语言中的三元运算符相同。

x = condition ? true_value : false_value

唯一的例外是禁止使用嵌套的三元运算符,以提高可读性。如果您的分支需求比这更复杂,则需要编写 if/else 结构。

包含

大多数源代码树都有多个子目录需要处理。这些可以使用 Meson 的 subdir 命令来处理。它会切换到给定的子目录并在该子目录中执行 meson.build 的内容。所有状态(变量等)都会传递到子目录和从子目录传递。其效果大致相当于子目录的 Meson 文件的内容被写入包含命令的位置。

test_data_dir = 'data'
subdir('tests')

用户定义的函数和方法

Meson 目前不支持用户定义的函数或方法。添加用户定义的函数将使 Meson 成为图灵完备的,这将使其更难推理和更难与 IDE 等工具集成。有关此方面的更多详细信息,请参阅 常见问题解答。如果您因为这种限制而发现自己经常复制和粘贴代码,您可以使用 foreach 循环

稳定性承诺

Meson 正在非常积极地开发和不断改进。Meson 构建系统的未来增强可能会要求对语法进行更改。此类更改可能是添加新的保留关键字、更改现有关键字的含义或围绕基本构建块(如语句和基本类型)的添加。计划在 1.0 版本中稳定语法。

语法

这是 Meson 语法的完整描述,用于解析 Meson 构建定义文件。

additive_expression: multiplicative_expression | (additive_expression additive_operator multiplicative_expression)
additive_operator: "+" | "-"
argument_list: positional_arguments ["," keyword_arguments] | keyword_arguments
array_literal: "[" [expression_list] "]"
assignment_statement: expression assignment_operator expression
assignment_operator: "=" | "+="
binary_literal: "0b" BINARY_NUMBER
BINARY_NUMBER: /[01]+/
boolean_literal: "true" | "false"
build_definition: (NEWLINE | statement)*
condition: expression
conditional_expression: logical_or_expression | (logical_or_expression "?" expression ":" assignment_expression
decimal_literal: DECIMAL_NUMBER
DECIMAL_NUMBER: /[1-9][0-9]*/
dictionary_literal: "{" [key_value_list] "}"
equality_expression: relational_expression | (equality_expression equality_operator relational_expression)
equality_operator: "==" | "!="
expression: conditional_expression | logical_or_expression
expression_list: expression ("," expression)*
expression_statement: expression
function_expression: id_expression "(" [argument_list] ")"
hex_literal: "0x" HEX_NUMBER
HEX_NUMBER: /[a-fA-F0-9]+/
id_expression: IDENTIFIER
IDENTIFIER: /[a-zA-Z_][a-zA-Z_0-9]*/
identifier_list: id_expression ("," id_expression)*
integer_literal: decimal_literal | octal_literal | hex_literal
iteration_statement: "foreach" identifier_list ":" id_expression NEWLINE (statement | jump_statement)* "endforeach"
jump_statement: ("break" | "continue") NEWLINE
key_value_item: expression ":" expression
key_value_list: key_value_item ("," key_value_item)*
keyword_item: id_expression ":" expression
keyword_arguments: keyword_item ("," keyword_item)*
literal: integer_literal | string_literal | boolean_literal | array_literal | dictionary_literal
logical_and_expression: equality_expression | (logical_and_expression "and" equality_expression)
logical_or_expression: logical_and_expression | (logical_or_expression "or" logical_and_expression)
method_expression: postfix_expression "." function_expression
multiplicative_expression: unary_expression | (multiplicative_expression multiplicative_operator unary_expression)
multiplicative_operator: "*" | "/" | "%"
octal_literal: "0o" OCTAL_NUMBER
OCTAL_NUMBER: /[0-7]+/
positional_arguments: expression ("," expression)*
postfix_expression: primary_expression | subscript_expression | function_expression | method_expression
primary_expression: literal | ("(" expression ")") | id_expression
relational_expression: additive_expression | (relational_expression relational_operator additive_expression)
relational_operator: ">" | "<" | ">=" | "<=" | "in" | ("not" "in")
selection_statement: "if" condition NEWLINE (statement)* ("elif" condition NEWLINE (statement)*)* ["else" (statement)*] "endif"
statement: (expression_statement | selection_statement | iteration_statement | assignment_statement) NEWLINE
string_literal: ("'" STRING_SIMPLE_VALUE "'") | ("'''" STRING_MULTILINE_VALUE "'''")
STRING_MULTILINE_VALUE: \.*?(''')\
STRING_SIMPLE_VALUE: \.*?(?<!\\)(\\\\)*?'\
subscript_expression: postfix_expression "[" expression "]"
unary_expression: postfix_expression | (unary_operator unary_expression)
unary_operator: "not" | "-"

搜索结果为