Skip to content

Commit

Permalink
switch the build system to waf
Browse files Browse the repository at this point in the history
This commit adds a new build system based on waf. configure and Makefile
are deprecated effective immediately and someday in the future they will be
removed (they are still available by running ./old-configure).

You can find how the choice for waf came to be in `DOCS/waf-buildsystem.rst`.
TL;DR: we couldn't get the same level of abstraction and customization with
other build systems we tried (CMake and autotools).

For guidance on how to build the software now, take a look at README.md
and the cross compilation guide.

CREDITS:
This is a squash of ~250 commits. Some of them are not by me, so here is the
deserved attribution:

 - @wm4 contributed some Windows fixes, renamed configure to old-configure
   and contributed to the bootstrap script. Also, GNU/Linux testing.
 - @lachs0r contributed some Windows fixes and the bootstrap script.
 - @Nikoli contributed a lot of testing and discovered many bugs.
 - @CrimsonVoid contributed changes to the bootstrap script.
  • Loading branch information
pigoz committed Nov 21, 2013
1 parent 0cb9227 commit 7e2edad
Show file tree
Hide file tree
Showing 49 changed files with 2,480 additions and 119 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*.a
*.d
*.exe
*.pyc
.depend

/config.h
Expand All @@ -25,3 +26,8 @@
/DOCS/man/*/mpv.out
/DOCS/man/*/mpv.pdf
/DOCS/man/*/mpv.toc

/waf
/build
/.waf*
/.lock-waf_*
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ branches:
- ci

before_install: ./travis-deps libass-stable $LIBAV
script: ./configure && make
script:
- ./bootstrap.py
- ./waf configure
- ./waf build

notifications-policy: &notifications-policy
on_success: change
Expand Down
6 changes: 3 additions & 3 deletions DOCS/crosscompile-mingw.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ produce both 32 bit and 64 bit executables. MinGW-w64 is available from
http://mingw-w64.sourceforge.net.

You have to run mpv's configure with these arguments:
./configure --enable-cross-compile --target=i686-w64-mingw32
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure

Using mingw-w64-cmake to setup a MinGW-w64 environment is recommended (this will
also build mpv and its dependencies): https://github.com/lachs0r/mingw-w64-cmake
Expand Down Expand Up @@ -44,8 +44,8 @@ make pthreads
git clone https://github.com/mpv-player/mpv.git
cd mpv
export PATH=/opt/mingw/usr/bin/:$PATH
./configure --enable-cross-compile --target=i686-w64-mingw32
make
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
./waf build

# This should work. Note however that MXE’s ffmpeg package might be very old
# in order to avoid breaking e.g. xine-lib, so you might want to update that
Expand Down
1 change: 0 additions & 1 deletion DOCS/man/docutils.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ latex-preamble: \usepackage[usenames,dvipsnames]{xcolor}
\makeatletter
\renewcommand{\maketitle}{
\begin{center}
\includegraphics{etc/mpv-icon-8bit-64x64.png}\\
\vspace*{-1.5em}
\begin{Huge}
\@title
Expand Down
151 changes: 151 additions & 0 deletions DOCS/waf-buildsystem.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
waf build system overview
=========================

mpv's new build system is based on waf and it should completly replace the
custom ./configure + Makefile based system inherited from MPlayer.

Goals and the choice of waf
===========================

The new system comes with some goals, which can be summed up as: be as good as
the old one at what it did well (customizability) and fix some of it's major
shortcomings:

1) The build system must be uniform in how it handles any single feature check.
Repetition and boilerplate have to be avoided.

When adding a new feature using the old configure, one had to add a fair
amount of code to the shell script to do option parsing, detection of the
feature and declaration of variables for the Makefile to pickup. The worst
part is this pieces are spread apart in the configure and copy pasted for
any single case. That brings us to..

2) --enable-feature has to override the user and help him understand that he
has libraries missing and should install them for the feature to be enabled.

3) Must be customizable, hackable, pleasant to the developer eyes and to work
with in general.

4) Must have separate configuration and build steps.

Goal 2 comes as a given on pretty much any build system, since autotools made
this behaviour very popular among users (and rightly so).

Goal 1+3 were somewhat harder to accomplish as it looks like all of the build
systems we evaluated (waf included!) had problems with them. For reference we
had proof of concept build systems with waf, CMake and autotools.

What puts waf apart from CMake and autotools, is that projects using it use
Python to program their build system. Also while the Waf Book shows really
simple API usages, you can write your own build system on top of waf that is
tailored to the project's specific needs.

mpv's custom configure step on top of waf
=========================================

To some extents mpv has a custom build system written on top of waf. This
document will not go over the standard waf behaviour as that is documented in
the ``Waf book``.

All of the configuration process is handled with a declarative approach. Lists
of dictionaries define the checks, and some custom Python code traverses these
lists and depending on the check definition it calls into the actual waf API.

A simple example using pkg-config would be::

{
'name': '--vdpau',
'desc': 'VDPAU acceleration',
'deps': [ 'x11' ],
'func': check_pkg_config('vdpau', '>= 0.2'),
}

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 succeds a ``#define HAVE_VDPAU 1`` will be added to
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added.

The defines names are automatically prepended with ``HAVE_``, capitalized and
special characters are replaced with underscores. This happens in
``waftools/inflectors.py``.

Mandatory fields:
-----------------

``name``: indicates the unique identifier used by the custom dependency code
to refer to a feature. If the unique identifier is prepended with ``--``
the build system will also generate options for ``./waf configure`` so that
the feature can be enabled and disabled.

``desc``: this is the textual representation of the feature used in the
interactions with the users.

``func``: function that will perform the check. These functions are defined in
``waftools/checks``. The reusable checks are all functions that return
functions. The return functions will then be applied using waf's configuration
context.

The source code for the reusable checks is a bit convoluted, but it should be
easy to pick up their usage from the ``wscript``. Their signature mirrors
the semantics of some of the shell functions used in mplayer.

If someone expresses some interest, I will extend this document with official
documentation for each check function.

Optional fields
---------------

``deps``: list of dependencies of this feature. It is a list of names of
other features as defined in the ``name`` field (minus the eventual leading
``--``). All of the dependencies must be satisfied. If they are not the check
will be skipped without even running ``func``.

``deps_any``: like deps but it is satisfied even if only one of the dependencies
is satisfied. You can think of ``deps`` as a 'and' condition and ``deps_any``
as a 'or' condition.

``deps_neg``: like deps but it is satisfied when none of the dependencies is
satisfied.

``req``: defaults to False. If set to True makes this feature a hard
dependency of mpv (configuration will fail if autodetection fails). If set to
True you must also provide ``fmsg``.

``fmsg``: string with the failure message in case a required dependency is not
satisfied.

``os_specific_checks``: this takes a dictionary that has ``os-`` dependencies
as keys (such as ``os-win32``), and by values has another dictionary that is
merged on top of the current feature definition only for that specific OS.
For example::

{
'name': '--pthreads',
'desc': 'POSIX threads',
'func': check_pthreads,
'os_specific_checks': {
'os-win32': {
'func': check_pthreads_w32_static.
}
}
}

will override the value of ``func`` with ``check_pthreads_w32_static`` only
if the target OS of the build is Windows.

mpv's custom build step on top of waf
=====================================

Build step is pretty much vanilla waf. The only difference being that the list
of source files can contain both strings or tuples. If a tuple is found,
the second element in the tuple will the used to match the features detected
in the configure step (the ``name`` field described above). If this feature
was not enabled during configure, the source file will not be compiled in.

All of the custom Python for this is inside the function ``filtered_sources``
contained in the file ``waftools/dependencies.py``.

Also ``dependencies_use`` and ``dependencies_includes`` collect cflags and
ldflags that were generated from the features checks in the configure step.
38 changes: 16 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@ Compilation
-----------

Compiling with full features requires development files for several
external libraries. Below is a list of some important requirements. For
more information see the output of `./configure --help` for a list of options,
or look at the list of enabled and disabled features printed after running
`./configure`. If you think you have support for some feature installed
but configure fails to detect it, the file `config.log` may contain
information about the reasons for the failure.
external libraries. Below is a list of some important requirements.

The mpv build system uses *waf* but we don't store it in your source tree. The
script './bootstrap.py' will download the latest version of waf that was tested
with the build system.

For a list of the available build options use `./waf 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.

To build the software you can use `./waf build`, and `./waf install` to install
it.

NOTE: Using the old build system (with `./old-configure`) should still work,
but will be removed in a future version of mpv.

Essential dependencies (incomplete list):

Expand All @@ -47,22 +57,6 @@ If you are running Mac OSX and using homebrew we provide [homebrew-mpv][homebrew
to date formula that compiles mpv with sensible dependencies and defaults for
OSX.

### configure `--enable-*` parameters

The `--enable-*` parameters unconditionally force options on, completely
skipping autodetection. This behavior is unlike what you may be used to from
autoconf-based configure scripts that can decide to override you. This greater
level of control comes at a price. You may have to provide the correct compiler
and linker flags yourself.

If you used one of these options and experience a compilation or
linking failure, make sure you have passed the necessary compiler/linker flags
to configure.

mpv's configure script is greedy and automatically enables features as a result
of autodetection. The cases where you may want to use `--enable-*` are very
limited.

FFmpeg vs. Libav
----------------

Expand Down
20 changes: 12 additions & 8 deletions TOOLS/osxbundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,28 @@ def user_dylib_lst(input_file):
return [lib for lib in dylib_lst(input_file).split("\n") if
is_user_lib(lib, input_file)]

def bundle_name(binary_name):
def bundle_path(binary_name):
return "%s.app" % binary_name

def bundle_name(binary_name):
return os.path.basename(bundle_path(binary_name))

def target_plist(binary_name):
return os.path.join(bundle_name(binary_name), 'Contents', 'Info.plist')
return os.path.join(bundle_path(binary_name), 'Contents', 'Info.plist')

def target_directory(binary_name):
return os.path.join(bundle_name(binary_name), 'Contents', 'MacOS')
return os.path.join(bundle_path(binary_name), 'Contents', 'MacOS')

def target_binary(binary_name):
return os.path.join(target_directory(binary_name), binary_name)
return os.path.join(target_directory(binary_name),
os.path.basename(binary_name))

def copy_bundle(binary_name):
if os.path.isdir(bundle_name(binary_name)):
shutil.rmtree(bundle_name(binary_name))
if os.path.isdir(bundle_path(binary_name)):
shutil.rmtree(bundle_path(binary_name))
shutil.copytree(
os.path.join('TOOLS', 'osxbundle', bundle_name(binary_name)),
bundle_name(binary_name))
bundle_path(binary_name))

def copy_binary(binary_name):
shutil.copy(binary_name, target_binary(binary_name))
Expand Down Expand Up @@ -91,7 +95,7 @@ def fix_dylibs_paths(target_file, dest_dir, root=True):
fix_dylibs_paths(dylib_dest_path, dest_dir, False)

def apply_plist_template(plist_file, version):
sh("sed -i -e 's/{{VERSION}}/%s/g' %s" % (version, plist_file))
sh("sed -i -e 's/${VERSION}/%s/g' %s" % (version, plist_file))

def bundle_dependencies(binary_name):
lib_bundle_directory = os.path.join(target_directory(binary_name), "lib")
Expand Down
2 changes: 1 addition & 1 deletion TOOLS/osxbundle/mpv.app/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>{{VERSION}}</string>
<string>${VERSION}</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>CFBundleURLTypes</key>
Expand Down
28 changes: 28 additions & 0 deletions bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python

# This script simply downloads waf to the current directory

from __future__ import print_function
import os, sys, stat, hashlib

try:
from urllib.request import urlopen
except:
from urllib2 import urlopen

WAFRELEASE = "waf-1.7.13"
SHA256HASH = "03cc750049350ee01cdbc584b70924e333fcc17ba4a2d04648dab1535538a873"

waf = urlopen("https://waf.googlecode.com/files/" + WAFRELEASE).read()

if SHA256HASH == hashlib.sha256(waf).hexdigest():
with open("waf", "wb") as wf:
wf.write(waf)

os.chmod("waf", os.stat("waf").st_mode | stat.S_IXUSR)
print("Checksum verified.")
else:
print("The checksum of the downloaded file does not match!")
print("Please download and verify the file manually.")

sys.exit(1)
Loading

0 comments on commit 7e2edad

Please sign in to comment.