| # Wrap best practices and tips |
| |
| There are several things you need to take into consideration when |
| writing a Meson build definition for a project. This is especially |
| true when the project will be used as a subproject. This page lists a |
| few things to consider when writing your definitions. |
| |
| ## Do not put config.h in external search path |
| |
| Many projects use a `config.h` header file that they use for |
| configuring their project internally. These files are never installed |
| to the system header files so there are no inclusion collisions. This |
| is not the case with subprojects, your project tree may have an |
| arbitrary number of configuration files, so we need to ensure they |
| don't clash. |
| |
| The basic problem is that the users of the subproject must be able to |
| include subproject headers without seeing its `config.h` file. The |
| most correct solution is to rename the `config.h` file into something |
| unique, such as `foobar-config.h`. This is usually not feasible unless |
| you are the maintainer of the subproject in question. |
| |
| The pragmatic solution is to put the config header in a directory that |
| has no other header files and then hide that from everyone else. One |
| way is to create a top level subdirectory called `internal` and use |
| that to build your own sources, like this: |
| |
| ```meson |
| subdir('internal') # create config.h in this subdir |
| internal_inc = include_directories('internal') |
| shared_library('foo', 'foo.c', include_directories : internal_inc) |
| ``` |
| |
| Many projects keep their `config.h` in the top level directory that |
| has no other source files in it. In that case you don't need to move |
| it but can just do this instead: |
| |
| ```meson |
| internal_inc = include_directories('.') # At top level meson.build |
| ``` |
| |
| ## Make libraries buildable both as static and shared |
| |
| Some platforms (e.g. iOS) requires linking everything in your main app |
| statically. In other cases you might want shared libraries. They are |
| also faster during development due to Meson's relinking |
| optimization. However building both library types on all builds is |
| slow and wasteful. |
| |
| Your project should provide a toggle specifying which type of library |
| it should build. As an example if you have a Meson option called |
| `shared_lib` then you could do this: |
| |
| ```meson |
| if get_option('shared_lib') |
| libtype = 'shared_library' |
| else |
| libtype = 'static_library' |
| endif |
| |
| mylib = build_target('foo', 'foo.c', |
| target_type : libtype) |
| ``` |
| |
| ## Declare generated headers explicitly |
| |
| Meson's Ninja backend works differently from Make and other |
| systems. Rather than processing things directory per directory, it |
| looks at the entire build definition at once and runs the individual |
| compile jobs in what might look to the outside as a random order. |
| |
| The reason for this is that this is much more efficient so your builds |
| finish faster. The downside is that you have to be careful with your |
| dependencies. The most common problem here is headers that are |
| generated at compile time with e.g. code generators. If these headers |
| are needed when building code that uses these libraries, the compile |
| job might be run before the code generation step. The fix is to make |
| the dependency explicit like this: |
| |
| ```meson |
| myheader = custom_target(...) |
| mylibrary = shared_library(...) |
| mydep = declare_dependency(link_with : mylibrary, |
| include_directories : include_directories(...), |
| sources : myheader) |
| ``` |
| |
| And then you can use the dependency in the usual way: |
| |
| ```meson |
| executable('dep_using_exe', 'main.c', |
| dependencies : mydep) |
| ``` |
| |
| Meson will ensure that the header file has been built before compiling `main.c`. |
| |
| ## Avoid exposing compilable source files in declare_dependency |
| |
| The main use for the `sources` argument in `declare_dependency` is to |
| construct the correct dependency graph for the backends, as |
| demonstrated in the previous section. It is extremely important to |
| note that it should *not* be used to directly expose compilable |
| sources (`.c`, `.cpp`, etc.) of dependencies, and should rather only |
| be used for header/config files. The following example will illustrate |
| what can go wrong if you accidentally expose compilable source files. |
| |
| So you've read about unity builds and how Meson natively supports |
| them. You decide to expose the sources of dependencies in order to |
| have unity builds that include their dependencies. For your support |
| library you do |
| |
| ```meson |
| my_support_sources = files(...) |
| |
| mysupportlib = shared_library( |
| ... |
| sources : my_support_sources, |
| ...) |
| mysupportlib_dep = declare_dependency( |
| ... |
| link_with : mylibrary, |
| sources : my_support_sources, |
| ...) |
| ``` |
| |
| And for your main project you do: |
| |
| ```meson |
| mylibrary = shared_library( |
| ... |
| dependencies : mysupportlib_dep, |
| ...) |
| myexe = executable( |
| ... |
| link_with : mylibrary, |
| dependencies : mysupportlib_dep, |
| ...) |
| ``` |
| |
| This is extremely dangerous. When building, `mylibrary` will build and |
| link the support sources `my_support_sources` into the resulting |
| shared library. Then, for `myexe`, these same support sources will be |
| compiled again, will be linked into the resulting executable, in |
| addition to them being already present in `mylibrary`. This can |
| quickly run afoul of the [One Definition Rule |
| (ODR)](https://en.wikipedia.org/wiki/One_Definition_Rule) in C++, as |
| you have more than one definition of a symbol, yielding undefined |
| behavior. While C does not have a strict ODR rule, there is no |
| language in the standard which guarantees such behavior to |
| work. Violations of the ODR can lead to weird idiosyncratic failures |
| such as segfaults. In the overwhelming number of cases, exposing |
| library sources via the `sources` argument in `declare_dependency` is |
| thus incorrect. If you wish to get full cross-library performance, |
| consider building `mysupportlib` as a static library instead and |
| employing LTO. |
| |
| There are exceptions to this rule. If there are some natural |
| constraints on how your library is to be used, you can expose |
| sources. For instance, the WrapDB module for GoogleTest directly |
| exposes the sources of GTest and GMock. This is valid, as GTest and |
| GMock will only ever be used in *terminal* link targets. A terminal |
| target is the final target in a dependency link chain, for instance |
| `myexe` in the last example, whereas `mylibrary` is an intermediate |
| link target. For most libraries this rule is not applicable though, as |
| you cannot in general control how others consume your library, and as |
| such should not expose sources. |