diff options
Diffstat (limited to 'meson/docs/markdown/Generating-sources.md')
-rw-r--r-- | meson/docs/markdown/Generating-sources.md | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/meson/docs/markdown/Generating-sources.md b/meson/docs/markdown/Generating-sources.md new file mode 100644 index 000000000..c09819f60 --- /dev/null +++ b/meson/docs/markdown/Generating-sources.md @@ -0,0 +1,206 @@ +--- +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()`](Reference-manual.md#generator) or +[`custom_target()`](Reference-manual.md#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 it's 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 +[reference](Reference-manual.md#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()`](Reference-manual.md#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']) +``` |