summaryrefslogtreecommitdiffstats
path: root/DOCS/build-system.rst
blob: 8b8f122e6d41350349cbd9479b62b2bacaa4ff63 (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
Build system overview
=====================

mpv's new build system is based on Python and completely replaces the previous
./waf build system.

This file describes internals. See the README in the top level directory for
user help.

User help (to be moved to README.md)
====================================

Compiling with full features requires development files for several
external libraries. Below is a list of some important requirements.

For a list of the available build options use `./configure --help`. If
you think you have support for some feature installed but configure fails to
detect it, the file `build/config.log` may contain information about the
reasons for the failure.

NOTE: To avoid cluttering the output with unreadable spam, `--help` only shows
one of the many switches for each option. If the option is autodetected by
default, the `--disable-***` switch is printed; if the option is disabled by
default, the `--enable-***` switch is printed. Either way, you can use
`--enable-***` or `--disable-***` regardless of what is printed by `--help`.
By default, most features are auto-detected. You can use ``--with-***=option``
to get finer control over whether auto-detection is used for a feature.

Example:

    ./configure && make -j20

If everything goes well, the mpv binary is created in the ``build`` directory.

`make` alone can be used to rebuild parts of the player. On update, it's
recommended to run `make dist-clean` and to rerun configure.

See `./configure --help` for advanced usage.

Motivation & Requirements
=========================

It's unclear what the fuck the author of the new build system was thinking.

Big picture
===========

The configure script is written in Python. It generates config.h and config.mak
files (and possibly more). By default these are written to a newly created
"build" directory. It also writes a build.log.

The "actual" build system is based on GNU make (other make variants probably
won't work). The Makefile in the project root is manually created by the build
system "user" (i.e. the mpv developers), and is fixed and not changed by
configure. It includes the configured-generated build/config.mak file for the
variable parts. It also includes Header file dependencies are handled
automatically with the ``-MD`` option (which the compiler must support).

For out-of-tree builds, a small Makefile is generated that includes the one
from the source directory. Simply call configure from another directory.
(Note: this is broken, fails at generated files, and is also ugly.)

By default, it attempts not to write any build output to the source tree, except
to the "build" directory.

Comparison to previous waf build system
=======================================

The new configure uses the same concept as our custom layer above waf, which
made the checks generally declarative. In fact, most checks were ported
straight, changing only to the new syntax.

Some of the internal and user-visible conventions are extremely similar. For
example, the new system creates a build dir and writes to it by default.

The choice of Python as implementation language is unfortunate. Shell was
considered, but discarded for being too fragile, error prone, and PITA-ish.
Lua would be reasonable, but is too fragmented, and requires external
dependencies to do meaningful UNIX scripting. There is nothing else left that
is widely supported enough, does not require external dependencies, and which
isn't something that I would not touch without gloves. Bootstrapping a system
implemented in C was considered, but deemed too problematic.

mpv's custom configure
======================

All of the configuration process is handled with a mostly-declarative approach.
Each configure check is a call to a "check" function, which takes various named
arguments. The check function is obviously always called, even if the
corresponding feature is disabled.

A simple example using pkg-config would be::

check("-vdpau*",
      desc      = "VDPAU acceleration",
      deps      = "x11",
      fn        = lambda: check_pkg_config("vdpau >= 0.2"),
      sources   = ["video/filter/vf_vdpaupp.c",
                   "video/out/vo_vdpau.c",
                   "video/vdpau.c",
                   "video/vdpau_mixer.c"])

This defines a feature called ``vdpau`` which can be enabled or disabled by
the users with configure flags (that's the meaning of ``-``). This feature
depends on another feature whose name is ``x11``, and the autodetection check
consists of running ``pkg-config`` and looking for ``vdpau`` with version
``>= 0.2``. If the check succeeds a ``#define HAVE_VDPAU 1`` will be added to
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added (the ``*`` on the
feature name triggers emitting of such defines).

The defines names are automatically prepended with ``HAVE_``, capitalized, and
some special characters are replaced with underscores.

If the test succeeds, the listed source files are added to the build.

Read the inline-documentation on the check function in configure_common.py for
details. The following text only gives a crude overview.

Configure tests
---------------

The check function has a ``fn`` parameter. This function is called when it's
time to perform actual configure checks. Most check calls in configure make
this a lambda, so the actual code to run can be passed inline as a function
argument. (This is similar to the old waf based system, just that functions
like check_pkg_config returned a function as result, which hid the indirection.)

One central function is ``check_cc``. It's quite similar to the waf-native
function with the same name. One difference is that there is no ``mandatory``
option - instead it always returns a bool for success. On success, the passed
build flags are appended to the check's build flags. This makes it easier to
compose checks. For example::

check(desc      = "C11/C99",
      fn        = lambda: check_cc(flags = "-std=c11") or
                          check_cc(flags = "-std=c99"),
      required  = "No C11 or C99 support.")

This tries to use -std=c11, but allows a fallback to -std=c99.

If the entire check fails, none of the added build flags are added. For example,
you could chain multiple tests like this::

check("-vapoursynth*",
      fn        = lambda: check_pkg_config("vapoursynth >= 24") and
                          check_pkg_config("vapoursynth-script >= 23"))

If the second check fails, the final executable won't link to ``vapoursynth``.
(Note that this test could just make a single check_pkg_config call, and pass
each dependency as separate argument.)

Source files
------------

configure generates the list of source files and writes it to config.mak. You
can add source files at any point in configure, but normally they're added with
the ``sources`` parameter in each feature check. This is done because a larger
number of source files depend on configure options, so having it all in the same
place as the check is slightly nicer than having a separate conditional mess in
the fixed Makefile.

Configure phases, non-declarative actions
-----------------------------------------

configure was written to be as single-pass as possible. It doesn't even put the
checks in any lists or so (except for the outcome). Handling of ``--enable-...``
etc. options is done while running configure. If you pass e.g.
``--enable-doesntexist``, configure will complain about an unknown
``doesntexist`` feature only once all checks have been actually run.

Although this is slightly weird, it is done so that the ``configure`` file
itself can be a flat file with simple top-down execution. It enables you to add
arbitrary non-declarative checks and such between the ``check`` calls.

One thing you need to be aware of is that if ``--help`` was passed to configure,
it will run in "help mode". You may have to use ``is_running()`` to check
whether it's in a mode where checks are actually executed. Outside of this mode,
``dep_enabled()`` will fail.

Makefile
--------

Although most source files are added from configure, this build system still
may require you to write some make. In particular, generated files are not
handled by configure.

make is bad. It's hard to use, hard to debug, and extremely fragile. It may be
replaced by something else in the future, including the possibility of turning
configure into waf-light.

Variables:

    ``BUILD``
        The directory for build output. Can be a relative path, usually set to
        ``build``.
    ``ROOT``
        The directory that contains ``configure``. Usually the root directory
        of the repository. Source files need to be addressed relative to this
        path. Can be a relative path, usually set to ``.``.