18 KiB
short-description |
---|
Syntax and structure of Meson files |
Variables in Meson work just like in other high level programming languages. A variable can contain a value of any type, such as an integer or a string. Variables don't need to be predeclared, you can just assign to them and they appear. Here's how you would assign values to two different variables.
var1 = 'hello'
var2 = 102
One important difference in how variables work in Meson is that all objects are immutable. This is different from, for example, how Python works.
var1 = [1, 2, 3]
var2 = var1
var2 += [4]
# var2 is now [1, 2, 3, 4]
# var1 is still [1, 2, 3]
Numbers
Meson supports only integer numbers. They are declared simply by writing them out. Basic arithmetic operations are supported.
x = 1 + 2
y = 3 * 4
d = 5 % 3 # Yields 2.
Hexadecimal literals are supported since version 0.45.0:
int_255 = 0xFF
Octal and binary literals are supported since version 0.47.0:
int_493 = 0o755
int_1365 = 0b10101010101
Strings can be converted to a number like this:
string_var = '42'
num = string_var.to_int()
Numbers can be converted to a string:
int_var = 42
string_var = int_var.to_string()
Booleans
A boolean is either true
or false
.
truth = true
Strings
Strings in Meson are declared with single quotes. To enter a literal single quote do it like this:
single quote = 'contains a \' character'
The full list of escape sequences is:
\\
Backslash\'
Single quote\a
Bell\b
Backspace\f
Formfeed\n
Newline\r
Carriage Return\t
Horizontal Tab\v
Vertical Tab\ooo
Character with octal value ooo\xhh
Character with hex value hh\uxxxx
Character with 16-bit hex value xxxx\Uxxxxxxxx
Character with 32-bit hex value xxxxxxxx\N{name}
Character named name in Unicode database
As in python and C, up to three octal digits are accepted in \ooo
.
Unrecognized escape sequences are left in the string unchanged, i.e., the backslash is left in the string.
String concatenation
Strings can be concatenated to form a new string using the +
symbol.
str1 = 'abc'
str2 = 'xyz'
combined = str1 + '_' + str2 # combined is now abc_xyz
Strings running over multiple lines
Strings running over multiple lines can be declared with three single quotes, like this:
multiline_string = '''#include <foo.h>
int main (int argc, char ** argv) {
return FOO_SUCCESS;
}'''
These are raw strings that do not support the escape sequences listed above. These strings can also be combined with the string formatting functionality described below.
String formatting
Strings can be built using the string formatting functionality.
template = 'string: @0@, number: @1@, bool: @2@'
res = template.format('text', 1, true)
# res now has value 'string: text, number: 1, bool: true'
As can be seen, the formatting works by replacing placeholders of type
@number@
with the corresponding argument.
String methods
Strings also support a number of other methods that return transformed copies.
.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'
.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'
.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: '>', '<', '>=', '<=', '!=', '==', '='
Arrays
Arrays are delimited by brackets. An array can contain an arbitrary number of objects of any type.
my_array = [1, 2, 'string', some_obj]
Accessing elements of an array can be done via array indexing:
my_array = [1, 2, 'string', some_obj]
second_element = my_array[1]
last_element = my_array[-1]
You can add more items to an array like this:
my_array += ['foo', 3, 4, another_obj]
When adding a single item, you do not need to enclose it in an array:
my_array += ['something']
# This also works
my_array += 'else'
Note appending to an array will always create a new array object and
assign it to my_array
instead of modifying the original since all
objects in Meson are immutable.
Since 0.49.0, you can check if an array contains an element like this:
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
Array methods
The following methods are defined for all arrays:
length
, the size of the arraycontains
, returnstrue
if the array contains the object given as argument,false
otherwiseget
, returns the object at the given index, negative indices count from the back of the array, indexing out of bounds is a fatal error. Provided for backwards-compatibility, it is identical to array indexing.
Dictionaries
Dictionaries are delimited by curly braces. A dictionary can contain an arbitrary number of key value pairs. Keys are required to be strings, values can be objects of any type. Prior to 0.53.0 keys were required to be literal strings.
my_dict = {'foo': 42, 'bar': 'baz'}
Keys must be unique:
# This will fail
my_dict = {'foo': 42, 'foo': 43}
Dictionaries are immutable and do not have a guaranteed order.
Dictionaries are available since 0.47.0.
Visit the Reference Manual to read about the methods exposed by dictionaries.
Since 0.49.0, you can check if a dictionary contains a key like this:
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
Since 0.53.0 Keys can be any expression evaluating to a string value, not limited to string literals any more.
d = {'a' + 'b' : 42}
k = 'cd'
d += {k : 43}
Function calls
Meson provides a set of usable functions. The most common use case is creating build objects.
executable('progname', 'prog.c')
Most functions take only few positional arguments but several keyword arguments, which are specified like this:
executable('progname',
sources: 'prog.c',
c_args: '-DFOO=1')
Starting with version 0.49.0 keyword arguments can be specified
dynamically. This is done by passing dictionary representing the
keywords to set in the kwargs
keyword. The previous example would be
specified like this:
d = {'sources': 'prog.c',
'c_args': '-DFOO=1'}
executable('progname',
kwargs: d)
A single function can take keyword argumets both directly in the
function call and indirectly via the kwargs
keyword argument. The
only limitation is that it is a hard error to pass any particular key
both as a direct and indirect argument.
d = {'c_args': '-DFOO'}
executable('progname', 'prog.c',
c_args: '-DBAZ=1',
kwargs: d) # This is an error!
Attempting to do this causes Meson to immediately exit with an error.
Method calls
Objects can have methods, which are called with the dot operator. The exact methods it provides depends on the object.
myobj = some_function()
myobj.do_something('now')
If statements
If statements work just like in other languages.
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
Logical operations
Meson has the standard range of logical operations which can be used in
if
statements.
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
Logical operations work only on boolean values.
Foreach statements
To do an operation on all elements of an iterable, use the foreach
command.
Note that Meson variables are immutable. Trying to assign a new value to the iterated object inside a foreach loop will not affect foreach's control flow.
Foreach with an array
Here's an example of how you could define two executables with corresponding tests using arrays and 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 with a dictionary
Here's an example of you could iterate a set of components that should be compiled in according to some configuration. This uses a [dictionary][dictionaries], which is available since 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 break
and continue
Since 0.49.0 break
and continue
keywords can be used inside foreach loops.
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']
Comments
A comment starts with the #
character and extends until the end of the line.
some_function() # This is a comment
some_other_function()
Ternary operator
The ternary operator works just like in other languages.
x = condition ? true_value : false_value
The only exception is that nested ternary operators are forbidden to
improve legibility. If your branching needs are more complex than this
you need to write an if/else
construct.
Includes
Most source trees have multiple subdirectories to process. These can
be handled by Meson's subdir
command. It changes to the given
subdirectory and executes the contents of meson.build
in that
subdirectory. All state (variables etc) are passed to and from the
subdirectory. The effect is roughly the same as if the contents of the
subdirectory's Meson file would have been written where the include
command is.
test_data_dir = 'data'
subdir('tests')
User-defined functions and methods
Meson does not currently support user-defined functions or
methods. The addition of user-defined functions would make Meson
Turing-complete which would make it harder to reason about and more
difficult to integrate with tools like IDEs. More details about this
are in the
FAQ. If
because of this limitation you find yourself copying and pasting code
a lot you may be able to use a foreach
loop
instead.
Stability Promises
Meson is very actively developed and continuously improved. There is a possibility that future enhancements to the Meson build system will require changes to the syntax. Such changes might be the addition of new reserved keywords, changing the meaning of existing keywords or additions around the basic building blocks like statements and fundamental types. It is planned to stabilize the syntax with the 1.0 release.
Grammar
This is the full Meson grammar, as it is used to parse Meson build definition files:
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_expression: conditional_expression | (logical_or_expression assignment_operator assignment_expression)
assignment_operator: "=" | "*=" | "/=" | "%=" | "+=" | "-="
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: assignment_expression
expression_list: expression ("," expression)*
expression_statememt: 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) 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" | "+" | "-"