The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
572 lines
14 KiB
572 lines
14 KiB
--- |
|
short-description: Syntax and structure of Meson files |
|
... |
|
|
|
# Syntax |
|
|
|
The syntax of Meson's specification language has been kept as simple |
|
as possible. It is *strongly typed* so no object is ever converted to |
|
another under the covers. Variables have no visible type which makes |
|
Meson *dynamically typed* (also known as *duck typed*). |
|
|
|
The main building blocks of the language are *variables*, *numbers*, |
|
*booleans*, *strings*, *arrays*, *function calls*, *method calls*, *if |
|
statements* and *includes*. |
|
|
|
Usually one Meson statement takes just one line. There is no way to |
|
have multiple statements on one line as in e.g. *C*. Function and |
|
method calls' argument lists can be split over multiple lines. Meson |
|
will autodetect this case and do the right thing. In other cases you |
|
can get multi-line statements by ending the line with a `\`. Apart |
|
from line ending whitespace has no syntactic meaning. |
|
|
|
Variables |
|
-- |
|
|
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
x = 1 + 2 |
|
y = 3 * 4 |
|
d = 5 % 3 # Yields 2. |
|
``` |
|
|
|
Hexadecimal literals are supported since version 0.45.0: |
|
|
|
```meson |
|
int_255 = 0xFF |
|
``` |
|
|
|
Octal and binary literals are supported since version 0.47.0: |
|
|
|
```meson |
|
int_493 = 0o755 |
|
int_1365 = 0b10101010101 |
|
``` |
|
|
|
Strings can be converted to a number like this: |
|
|
|
```meson |
|
string_var = '42' |
|
num = string_var.to_int() |
|
``` |
|
|
|
Booleans |
|
-- |
|
|
|
A boolean is either `true` or `false`. |
|
|
|
```meson |
|
truth = true |
|
``` |
|
|
|
Strings |
|
-- |
|
|
|
Strings in Meson are declared with single quotes. To enter a literal |
|
single quote do it like this: |
|
|
|
```meson |
|
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`. |
|
|
|
#### String concatenation |
|
|
|
Strings can be concatenated to form a new string using the `+` symbol. |
|
|
|
```meson |
|
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: |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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()** |
|
|
|
```meson |
|
# 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()** |
|
|
|
```meson |
|
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()** |
|
|
|
```meson |
|
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()** |
|
|
|
```meson |
|
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()** |
|
|
|
```meson |
|
# 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 paths, you should use join_paths() |
|
# This has the advantage of being cross-platform |
|
path = join_paths(['/usr', 'local', 'bin']) |
|
# path now has the value '/usr/local/bin' |
|
|
|
# Don't use join_paths for sources files, use files for that: |
|
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()** |
|
|
|
```meson |
|
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()** |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
my_array = [1, 2, 'string', some_obj] |
|
``` |
|
|
|
Accessing elements of an array can be done via array indexing: |
|
|
|
```meson |
|
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: |
|
|
|
```meson |
|
my_array += ['foo', 3, 4, another_obj] |
|
``` |
|
|
|
When adding a single item, you do not need to enclose it in an array: |
|
|
|
```meson |
|
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: |
|
```meson |
|
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 array |
|
- `contains`, returns `true` if the array contains the object given as argument, `false` otherwise |
|
- `get`, 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 literal |
|
strings, values can be objects of any type. |
|
|
|
```meson |
|
my_dict = {'foo': 42, 'bar': 'baz'} |
|
``` |
|
|
|
Keys must be unique: |
|
|
|
```meson |
|
# This will fail |
|
my_dict = {'foo': 42, 'foo': 43} |
|
``` |
|
|
|
Dictionaries are immutable. |
|
|
|
Dictionaries are available since 0.47.0. |
|
|
|
Visit the [Reference Manual](Reference-manual.md#dictionary-object) to read |
|
about the methods exposed by dictionaries. |
|
|
|
Since 0.49.0, you can check if a dictionary contains a key like this: |
|
```meson |
|
my_dict = {'foo': 42, 'foo': 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 |
|
``` |
|
|
|
Function calls |
|
-- |
|
|
|
Meson provides a set of usable functions. The most common use case is |
|
creating build objects. |
|
|
|
```meson |
|
executable('progname', 'prog.c') |
|
``` |
|
|
|
Most functions take only few positional arguments but several keyword |
|
arguments, which are specified like this: |
|
|
|
```meson |
|
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: |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
myobj = some_function() |
|
myobj.do_something('now') |
|
``` |
|
|
|
If statements |
|
-- |
|
|
|
If statements work just like in other languages. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
some_function() # This is a comment |
|
some_other_function() |
|
``` |
|
|
|
Ternary operator |
|
-- |
|
|
|
The ternary operator works just like in other languages. |
|
|
|
```meson |
|
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. |
|
|
|
```meson |
|
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](FAQ.md#why-is-meson-not-just-a-python-module-so-i-could-code-my-build-setup-in-python). If |
|
because of this limitation you find yourself copying and pasting code |
|
a lot you may be able to use a [`foreach` loop |
|
instead](#foreach-statements).
|
|
|