aboutsummaryrefslogtreecommitdiffstats
path: root/meson/docs/markdown/Wrap-best-practices-and-tips.md
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /meson/docs/markdown/Wrap-best-practices-and-tips.md
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'meson/docs/markdown/Wrap-best-practices-and-tips.md')
-rw-r--r--meson/docs/markdown/Wrap-best-practices-and-tips.md158
1 files changed, 158 insertions, 0 deletions
diff --git a/meson/docs/markdown/Wrap-best-practices-and-tips.md b/meson/docs/markdown/Wrap-best-practices-and-tips.md
new file mode 100644
index 000000000..70b02250f
--- /dev/null
+++ b/meson/docs/markdown/Wrap-best-practices-and-tips.md
@@ -0,0 +1,158 @@
+# 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 use the `library` method that can be toggled
+between shared and static with the `default_library` builtin option.
+
+
+```meson
+mylib = library('foo', 'foo.c')
+```
+
+## 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.