diff options
Diffstat (limited to 'meson/docs/markdown/Subprojects.md')
-rw-r--r-- | meson/docs/markdown/Subprojects.md | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/meson/docs/markdown/Subprojects.md b/meson/docs/markdown/Subprojects.md new file mode 100644 index 000000000..7cb04e0eb --- /dev/null +++ b/meson/docs/markdown/Subprojects.md @@ -0,0 +1,382 @@ +--- +short-description: Using Meson projects as subprojects within other Meson projects +... + +# Subprojects + +Some platforms do not provide a native packaging system. In these +cases it is common to bundle all third party libraries in your source +tree. This is usually frowned upon because it makes it hard to add +these kinds of projects into e.g. those Linux distributions that +forbid bundled libraries. + +Meson tries to solve this problem by making it extremely easy to +provide both at the same time. The way this is done is that Meson +allows you to take any other Meson project and make it a part of your +build without (in the best case) any changes to its Meson setup. It +becomes a transparent part of the project. + +It should be noted that this is only guaranteed to work for subprojects +that are built with Meson. The reason is the simple fact that there is +no possible way to do this reliably with mixed build systems. Because of +this, only Meson subprojects are described here. +[CMake based subprojects](CMake-module.md#cmake-subprojects) are also +supported but not guaranteed to work. + +## A subproject example + +Usually dependencies consist of some header files plus a library to +link against. To declare this internal dependency use +`declare_dependency` function. + +As an example, suppose we have a simple project that provides a shared +library. Its `meson.build` would look like this. + +```meson +project('libsimple', 'c') + +inc = include_directories('include') +libsimple = shared_library('simple', + 'simple.c', + include_directories : inc, + install : true) + +libsimple_dep = declare_dependency(include_directories : inc, + link_with : libsimple) +``` + +### Naming convention for dependency variables + +Ideally the dependency variable name should be of `<project_name>_dep` +form. This way one can just use it without even looking inside build +definitions of that subproject. + +In cases where there are multiple dependencies need to be declared, +the default one should be named as `<project_name>_dep` (e.g. +`gtest_dep`), and others can have `<project_name>_<other>_<name>_dep` +form (e.g. `gtest_main_dep` - gtest with main function). + +There may be exceptions to these rules where common sense should be applied. + +### Adding variables to the dependency + +*New in 0.54.0* + +In some cases a project may define special variables via pkg-config or +cmake that a caller needs to know about. Meson provides a +`dependency.get_variable` method to hide what kind of dependency is +provided, and this is available to subprojects as well. Use the +`variables` keyword to add a dict of strings: + +```meson +my_dep = declare_dependency(..., variables : {'var': 'value', 'number': '3'}) +``` + +Which another project can access via: + +```meson +var = my_dep.get_variable(internal : 'var', cmake : 'CMAKE_VAR') +``` + +The values of the dict must be strings, as pkg-config and cmake will +return variables as strings. + +### Build options in subproject + +All Meson features of the subproject, such as project options keep +working and can be set in the master project. There are a few +limitations, the most important being that global compiler arguments +must be set in the main project before calling subproject. Subprojects +must not set global arguments because there is no way to do that +reliably over multiple subprojects. To check whether you are running +as a subproject, use the `is_subproject` function. + +## Using a subproject + +All subprojects must be inside `subprojects` directory. The +`subprojects` directory must be at the top level of your project. +Subproject declaration must be in your top level `meson.build`. + +### A simple example + +Let's use `libsimple` as a subproject. + +At the top level of your project create `subprojects` directory. +Then copy `libsimple` into `subprojects` directory. + +Your project's `meson.build` should look like this. + +```meson +project('my_project', 'cpp') + +libsimple_proj = subproject('libsimple') +libsimple_dep = libsimple_proj.get_variable('libsimple_dep') + +executable('my_project', + 'my_project.cpp', + dependencies : libsimple_dep, + install : true) +``` + +Note that the subproject object is *not* used as the dependency, but +rather you need to get the declared dependency from it with +`get_variable` because a subproject may have multiple declared +dependencies. + +### Toggling between system libraries and embedded sources + +When building distro packages it is very important that you do not +embed any sources. Some distros have a rule forbidding embedded +dependencies so your project must be buildable without them or +otherwise the packager will hate you. + +Here's how you would use system libraries and fall back to embedding sources +if the dependency is not available. + +```meson +project('my_project', 'cpp') + +libsimple_dep = dependency('libsimple', required : false) + +if not libsimple_dep.found() + libsimple_proj = subproject('libsimple') + libsimple_dep = libsimple_proj.get_variable('libsimple_dep') +endif + +executable('my_project', + 'my_project.cpp', + dependencies : libsimple_dep, + install : true) +``` + +Because this is such a common operation, Meson provides a shortcut for +this use case. + +```meson +dep = dependency('foo', fallback : [subproject_name, variable_name]) +``` + +The `fallback` keyword argument takes two items, the name of the +subproject and the name of the variable that holds the dependency. If +you need to do something more complicated, such as extract several +different variables, then you need to do it yourself with the manual +method described above. + +Using this shortcut the build definition would look like this. + +```meson +project('my_project', 'cpp') + +libsimple_dep = dependency('libsimple', fallback : ['libsimple', 'libsimple_dep']) + +executable('my_project', + 'my_project.cpp', + dependencies : libsimple_dep, + install : true) +``` + +With this setup when libsimple is provided by the system, we use it. +When that is not the case we use the embedded version (the one from +subprojects). + +Note that `libsimple_dep` can point to an external or an internal +dependency but you don't have to worry about their differences. Meson +will take care of the details for you. + +### Subprojects depending on other subprojects + +Subprojects can use other subprojects, but all subprojects must reside +in the top level `subprojects` directory. Recursive use of subprojects +is not allowed, though, so you can't have subproject `a` that uses +subproject `b` and have `b` also use `a`. + +## Obtaining subprojects + +Meson ships with a dependency system to automatically obtain +dependency subprojects. It is documented in the [Wrap dependency +system manual](Wrap-dependency-system-manual.md). + +## Command-line options + +The usage of subprojects can be controlled by users and distros with +the following command-line options: + +* **--wrap-mode=nodownload** + + Meson will not use the network to download any subprojects or + fetch any wrap information. Only pre-existing sources will be used. + This is useful (mostly for distros) when you want to only use the + sources provided by a software release, and want to manually handle + or provide missing dependencies. + +* **--wrap-mode=nofallback** + + Meson will not use subproject fallbacks for any dependency + declarations in the build files, and will only look for them in the + system. Note that this does not apply to unconditional subproject() + calls, and those are meant to be used for sources that cannot be + provided by the system, such as copylibs. + + This option may be overridden by `--force-fallback-for` for specific + dependencies. + +* **--wrap-mode=forcefallback** + + Meson will not look at the system for any dependencies which have + subproject fallbacks available, and will *only* use subprojects for + them. This is useful when you want to test your fallback setup, or + want to specifically build against the library sources provided by + your subprojects. + +* **--force-fallback-for=list,of,dependencies** + + Meson will not look at the system for any dependencies listed there, + provided a fallback was supplied when the dependency was declared. + + This option takes precedence over `--wrap-mode=nofallback`, and when + used in combination with `--wrap-mode=nodownload` will only work + if the dependency has already been downloaded. + + This is useful when your project has many fallback dependencies, + but you only want to build against the library sources for a few + of them. + + **Warning**: This could lead to mixing system and subproject version of the + same library in the same process. Take this case as example: + - Libraries `glib-2.0` and `gstreamer-1.0` are installed on your system. + - `gstreamer-1.0` depends on `glib-2.0`, pkg-config file `gstreamer-1.0.pc` + has `Requires: glib-2.0`. + - In your application build definition you do: + ```meson + executable('app', ..., + dependencies: [ + dependency('glib-2.0', fallback: 'glib'), + dependency('gstreamer-1.0', fallback: 'gstreamer')], + ) + ``` + - You configure with `--force-fallback-for=glib`. + This result in linking to two different versions of library `glib-2.0` + because `dependency('glib-2.0', fallback: 'glib')` will return the + subproject dependency, but `dependency('gstreamer-1.0', fallback: 'gstreamer')` + will not fallback and return the system dependency, including `glib-2.0` + library. To avoid that situation, every dependency that itself depend on + `glib-2.0` must also be forced to fallback, in this case with + `--force-fallback-for=glib,gsteamer`. + +* **--wrap-mode=nopromote** + + *Since 0.56.0* Meson will automatically use wrap files found in subprojects + and copy them into the main project. That new behavior can be disabled by + passing `--wrap-mode=nopromote`. In that case only wraps found in the main + project will be used. + +## `meson subprojects` command + +*Since 0.49.0* + +`meson subprojects` has various subcommands to manage all subprojects. +If the subcommand fails on any subproject the execution continues with +other subprojects. All subcommands accept `--sourcedir` argument +pointing to the root source dir of the main project. + +*Since 0.56.0* all subcommands accept `--types <file|git|hg|svn>` +argument to run the subcommands only on subprojects of the given +types. Multiple types can be set as comma separated list e.g. `--types +git,file`. + +*Since 0.56.0* If the subcommand fails on any subproject an error code +is returned at the end instead of retuning success. + +### Download subprojects + +*Since 0.49.0* + +Meson will automatically download needed subprojects during configure, +unless **--wrap-mode=nodownload** option is passed. It is sometimes +preferable to download all subprojects in advance, so the Meson +configure can be performed offline. The command-line `meson +subprojects download` can be used for that, it will download all +missing subprojects, but will not update already fetched subprojects. + +### Update subprojects + +*Since 0.49.0* + +Once a subproject has been fetched, Meson will not update it automatically. +For example if the wrap file tracks a git branch, it won't pull latest commits. + +To pull latest version of all your subprojects at once, just run the command: +`meson subprojects update`. +- If the wrap file comes from wrapdb, the latest version of the wrap file will + be pulled and used next time meson reconfigure the project. This can be + triggered using `meson --reconfigure`. Previous source tree is not deleted, to + prevent from any loss of local changes. *Since 0.58.0* If `--reset` is + specified, the source tree is deleted and new source is extracted. +- If subproject is currently in detached mode, a checkout of the revision from + wrap file is performed. *Since 0.56.0* a rebase is also performed in case the + revision already existed locally but was outdated. If `--reset` is specified, + a hard reset is performed instead of rebase. +- If subproject is currently at the same branch as specified by the wrap file, + a rebase on `origin` commit is performed. *Since 0.56.0* If `--reset` is + specified, a hard reset is performed instead of rebase. +- If subproject is currently in a different branch as specified by the wrap file, + it is skipped unless `--rebase` option is passed in which case a rebase on + `origin` commit is performed. *Since 0.56.0* the `--rebase` argument is + deprecated and has no effect. Instead, a checkout of the revision from wrap file + file is performed and a rebase is also performed in case the revision already + existed locally by was outdated. If `--reset` is specified, a hard reset is + performed instead of rebase. +- *Since 0.56.0* if the `url` specified in wrap file is different to the URL set + on `origin` for a git repository it will not be updated, unless `--reset` is + specified in which case the URL of `origin` will be reset first. +- *Since 0.56.0* If the subproject directory is not a git repository but has a + `[wrap-git]` the subproject is ignored, unless `--reset` is specified in which + case the directory is deleted and the new repository is cloned. + +### Start a topic branch across all git subprojects + +*Since 0.49.0* + +The command-line `meson subprojects checkout <branch_name>` will +checkout a branch, or create one with `-b` argument, in every git +subprojects. This is useful when starting local changes across +multiple subprojects. It is still your responsibility to commit and +push in each repository where you made local changes. + +To come back to the revision set in wrap file (i.e. master), just run +`meson subprojects checkout` with no branch name. + +*Since 0.56.0* any pending changes are now stashed before checkout a new branch. + +### Execute a command on all subprojects + +*Since 0.51.0* + +The command-line `meson subprojects foreach <command> [...]` will +execute a command in each subproject directory. For example this can +be useful to check the status of subprojects (e.g. with `git status` +or `git diff`) before performing other actions on them. + +## Why must all subprojects be inside a single directory? + +There are several reasons. + +First of all, to maintain any sort of sanity, the system must prevent going +inside other subprojects with `subdir()` or variations thereof. Having the +subprojects in well defined places makes this easy. If subprojects could be +anywhere at all, it would be a lot harder. + +Second of all it is extremely important that end users can easily see what +subprojects any project has. Because they are in one, and only one, place, +reviewing them becomes easy. + +This is also a question of convention. Since all Meson projects have the same +layout w.r.t subprojects, switching between projects becomes easier. You don't +have to spend time on a new project traipsing through the source tree looking +for subprojects. They are always in the same place. + +Finally if you can have subprojects anywhere, this increases the possibility of +having many different (possibly incompatible) versions of a dependency in your +source tree. Then changing some code (such as changing the order you traverse +directories) may cause a completely different version of the subproject to be +used by accident. |