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.

207 lines
7.0 KiB

---
short-description: Generation of source files before compilation
...
# Generating sources
Sometimes source files need to be preprocessed before they are passed
to the actual compiler. As an example you might want build an IDL
compiler and then run some files through that to generate actual
source files. In Meson this is done with
[[generator]] or
[[custom_target]].
## Using custom_target()
Let's say you have a build target that must be built using sources
generated by a compiler. The compiler can either be a built target:
```meson
mycomp = executable('mycompiler', 'compiler.c')
```
Or an external program provided by the system, or script inside the
source tree:
```meson
mycomp = find_program('mycompiler')
```
Custom targets can take zero or more input files and use them to
generate one or more output files. Using a custom target, you can run
this compiler at build time to generate the sources:
```meson
gen_src = custom_target('gen-output',
input : ['somefile1.c', 'file2.c'],
output : ['out.c', 'out.h'],
command : [mycomp, '@INPUT@',
'--c-out', '@OUTPUT0@',
'--h-out', '@OUTPUT1@'])
```
The `@INPUT@` there will be transformed to `'somefile1.c'
'file2.c'`. Just like the output, you can also refer to each input
file individually by index.
Then you just put that in your program and you're done.
### Generating headers
Adding a generated header to a source list will ensure that the header
is generated and that the proper include paths are created for the
target:
```meson
prog_python = import('python').find_installation('python3')
foo_c = custom_target(
'foo.c',
output : 'foo.c',
input : 'my_gen.py',
command : [prog_python, '@INPUT@', '--code', '@OUTPUT@'],
)
foo_h = custom_target(
'foo.h',
output : 'foo.h',
input : 'my_gen.py',
command : [prog_python, '@INPUT@', '--header', '@OUTPUT@'],
)
libfoo = static_library('foo', [foo_c, foo_h])
executable('myexe', ['main.c', foo_h], link_with : libfoo)
```
Each target that depends on a generated header should add that header
to its sources, as seen above with `libfoo` and `myexe`. This is
because there is no way for Meson or the backend to know that `myexe`
depends on `foo.h` just because `libfoo` does, it could be a private
header.
### Generating multiple files at a time
Sometimes it makes sense for a single generator to create two or more
files at a time, (perhaps a header and source file), Meson has this
case covered as well. `custom_target`s can be indexed like a list to
get each output file separately. The order is the same as the order of
the output argument to `custom_target`
```meson
prog_python = import('python').find_installation('python3')
foo_ch = custom_target(
'foo.[ch]',
output : ['foo.c', 'foo.h'],
input : 'my_gen.py',
command : [prog_python, '@INPUT@', '@OUTPUT@'],
)
libfoo = static_library('foo', [foo_ch])
executable('myexe', ['main.c', foo_ch[1]], link_with : libfoo)
```
In this case `libfoo` depends on both `foo.c` and `foo.h` but `myexe`
only depends on `foo.h`, the second output.
### Using dependencies to manage generated resources
In some cases it might be easier to use `declare_dependency` to
"bundle" the header and library dependency, especially if there are
many generated headers:
```meson
idep_foo = declare_dependency(
sources : [foo_h, bar_h],
link_with : [libfoo],
)
```
See [dependencies](Dependencies.md#declaring-your-own), and
[[declare_dependency]] for more
information.
## Using generator()
Generators are similar to custom targets, except that we define a
*generator*, which defines how to transform an input file into one or
more output files, and then use that on as many input files as we
want.
Note that generators should only be used for outputs that will only be
used as inputs for a build target or a custom target. When you use the
processed output of a generator in multiple targets, the generator
will be run multiple times to create outputs for each target. Each
output will be created in a target-private directory `@BUILD_DIR@`.
If you want to generate files for general purposes such as for
generating headers to be used by several sources, or data that will be
installed, and so on, use a
[[custom_target]] instead.
```meson
gen = generator(mycomp,
output : '@BASENAME@.c',
arguments : ['@INPUT@', '@OUTPUT@'])
```
The first argument is the executable file to run. The next file
specifies a name generation rule. It specifies how to build the output
file name for a given input name. `@BASENAME@` is a placeholder for
the input file name without preceding path or suffix (if any). So if
the input file name were `some/path/filename.idl`, then the output
name would be `filename.c`. You can also use `@PLAINNAME@`, which
preserves the suffix which would result in a file called
`filename.idl.c`. The last line specifies the command line arguments
to pass to the executable. `@INPUT@` and `@OUTPUT@` are placeholders
for the input and output files, respectively, and will be
automatically filled in by Meson. If your rule produces multiple
output files and you need to pass them to the command line, append the
location to the output holder like this: `@OUTPUT0@`, `@OUTPUT1@` and
so on.
With this rule specified we can generate source files and add them to
a target.
```meson
gen_src = gen.process('input1.idl', 'input2.idl')
executable('program', 'main.c', gen_src)
```
Generators can also generate multiple output files with unknown names:
```meson
gen2 = generator(someprog,
output : ['@BASENAME@.c', '@BASENAME@.h'],
arguments : ['--out_dir=@BUILD_DIR@', '@INPUT@'])
```
In this case you can not use the plain `@OUTPUT@` variable, as it
would be ambiguous. This program only needs to know the output
directory, it will generate the file names by itself.
To make passing different additional arguments to the generator
program at each use possible, you can use the `@EXTRA_ARGS@` string in
the `arguments` list. Note that this placeholder can only be present
as a whole string, and not as a substring. The main reason is that it
represents a list of strings, which may be empty, or contain multiple
elements; and in either case, interpolating it into the middle of a
single string would be troublesome. If there are no extra arguments
passed in from a `process()` invocation, the placeholder is entirely
omitted from the actual list of arguments, so an empty string won't be
passed to the generator program because of this. If there are multiple
elements in `extra_args`, they are inserted into to the actual
argument list as separate elements.
```meson
gen3 = generator(genprog,
output : '@BASENAME@.cc',
arguments : ['@INPUT@', '@EXTRA_ARGS@', '@OUTPUT@'])
gen3_src1 = gen3.process('input1.y')
gen3_src2 = gen3.process('input2.y', extra_args: '--foo')
gen3_src3 = gen3.process('input3.y', extra_args: ['--foo', '--bar'])
```