語法

Meson 規格語言的語法盡可能保持簡單。它是強型別的,因此任何物件都不會在底層轉換為其他物件。變數沒有可見的型別,這使得 Meson 成為動態型別(也稱為鴨子型別)。

該語言的主要建構區塊是變數數字布林值字串陣列函式呼叫方法呼叫if 語句包含

通常,一個 Meson 語句只佔一行。與例如 C 語言不同,沒有辦法在一行上有多個語句。函式和方法呼叫的引數列表可以跨多行。Meson 會自動偵測這種情況並執行正確的操作。

在其他情況下,(0.50 版新增)您可以使用以 \ 結尾的行來取得多行語句。除了行尾,空格沒有任何語法意義。

變數

Meson 中的變數就像其他高階程式設計語言一樣運作。變數可以包含任何型別的值,例如整數或字串。變數不需要預先宣告,您可以直接賦值給它們,它們就會出現。以下是如何為兩個不同的變數賦值的範例。

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 水平 Tab
  • \v 垂直 Tab
  • \ooo 八進位值為 ooo 的字元
  • \xhh 十六進位值為 hh 的字元
  • \uxxxx 16 位元十六進位值為 xxxx 的字元
  • \Uxxxxxxxx 32 位元十六進位值為 xxxxxxxx 的字元
  • \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-字串支援已在 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-字串支援已在 0.63 版中新增。

n = 10
m = 'hi'

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

目前,格式字串內僅支援身分運算式,表示您不能在其中使用任意 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'

請注意,附加到陣列將始終建立一個新的陣列物件並將其賦值給 my_array,而不是修改原始陣列,因為 Meson 中的所有物件都是不可變的。

從 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,傳回給定索引處的物件,負索引從陣列末端開始計數,超出界限的索引是嚴重錯誤。提供用於向後相容性,它與陣列索引相同。

字典

字典以大括號分隔。字典可以包含任意數量的鍵:值對。鍵必須是字串,但值可以是任何型別的物件。在 0.53.0 版之前,鍵必須是文字字串,也就是說,您不能使用包含字串值的變數作為鍵。

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 版開始,鍵可以是任何評估為字串值的運算式,不再限於字串文字。

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 語句的工作方式與其他語言相同。

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 迴圈內為迭代物件賦予新值不會影響 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 版本起,breakcontinue 關鍵字可以在 foreach 迴圈中使用。

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 結構。

包含 (Includes)

大多數原始碼樹狀結構都有多個子目錄需要處理。這些可以使用 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" | "-"

搜尋結果如下