aboutsummaryrefslogtreecommitdiff
path: root/docs/markdown/Subprojects.md
blob: 9afbe1b8e3dd4c553f1af81643233d6b3d7648c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
---
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.
- 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.