summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-06-21 02:13:48 +0200
committerwm4 <wm4@nowhere>2020-06-10 11:25:10 +0200
commit6551ea5bd3e9781e3740e99a00c56a49917d5fc9 (patch)
tree9ac8722755d86ed6e744955441582ef5185842af
parentf3864638404284b7d3a54d22412ab9e81d0a6787 (diff)
downloadmpv-6551ea5bd3e9781e3740e99a00c56a49917d5fc9.tar.bz2
mpv-6551ea5bd3e9781e3740e99a00c56a49917d5fc9.tar.xz
new build systembliss
Further changes by the following people: James Ross-Gowan <rossy@jrg.systems>: win32 fixes
-rw-r--r--DOCS/build-system.rst199
-rw-r--r--Makefile.new93
-rw-r--r--TOOLS/configure_common.py740
-rw-r--r--TOOLS/makefile_common.mak55
-rwxr-xr-xconfigure1019
5 files changed, 2106 insertions, 0 deletions
diff --git a/DOCS/build-system.rst b/DOCS/build-system.rst
new file mode 100644
index 0000000000..8b8f122e6d
--- /dev/null
+++ b/DOCS/build-system.rst
@@ -0,0 +1,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 ``.``.
diff --git a/Makefile.new b/Makefile.new
new file mode 100644
index 0000000000..58ce1a0319
--- /dev/null
+++ b/Makefile.new
@@ -0,0 +1,93 @@
+BUILDDIR = build
+
+include $(BUILDDIR)/config.mak
+include $(ROOT)/TOOLS/makefile_common.mak
+
+PROJNAME = mpv
+
+.PHONY: .force
+
+$(BUILD)/generated/version.h: $(ROOT)/version.sh .force
+ $(LOG) "VERSION" $@
+ $(Q) mkdir -p $(@D)
+ $(Q) $(ROOT)/version.sh --versionh=$@
+
+$(BUILD)/generated/ebml_types.h $(BUILD)/generated/ebml_defs.c: $(ROOT)/TOOLS/matroska.py
+ $(LOG) "EBML" "$(BUILD)/generated/ebml_types.h $(BUILD)/generated/ebml_defs.c"
+ $(Q) mkdir -p $(@D)
+ $(Q) $< --generate-header > $(BUILD)/generated/ebml_types.h
+ $(Q) $< --generate-definitions > $(BUILD)/generated/ebml_defs.c
+
+$(BUILD)/generated/%.inc: $(ROOT)/TOOLS/file2string.py $(ROOT)/%
+ $(LOG) "INC" $@
+ $(Q) mkdir -p $(@D)
+ $(Q) $^ > $@
+
+# Dependencies for generated files unfortunately need to be declared manually.
+# This is because dependency scanning is a gross shitty hack by concept, and
+# requires that the compiler successfully compiles a file to get its
+# dependencies. This results in a chicken-and-egg problem, and in conclusion
+# it works for static header files only.
+# If any headers include generated headers, you need to manually set
+# dependencies on all source files that include these headers!
+# And because make is fucking shit, you actually need to set these on all files
+# that are generated from these sources, instead of the source files. Make rules
+# specify recipes, not dependencies.
+# (Possible counter measures: always generate them with an order dependency, or
+# introduce separate dependency scanner step for creating .d files.)
+
+$(BUILD)/common/version.o: $(BUILD)/generated/version.h
+
+$(BUILD)/osdep/mpv.o: $(BUILD)/generated/version.h
+
+$(BUILD)/demux/demux_mkv.o $(BUILD)/demux/ebml.o: \
+ $(BUILD)/generated/ebml_types.h $(BUILD)/generated/ebml_defs.c
+
+$(BUILD)/video/out/x11_common.o: $(BUILD)/generated/etc/mpv-icon-8bit-16x16.png.inc \
+ $(BUILD)/generated/etc/mpv-icon-8bit-32x32.png.inc \
+ $(BUILD)/generated/etc/mpv-icon-8bit-64x64.png.inc \
+ $(BUILD)/generated/etc/mpv-icon-8bit-128x128.png.inc
+
+$(BUILD)/input/input.o: $(BUILD)/generated/etc/input.conf.inc
+
+$(BUILD)/player/main.o: $(BUILD)/generated/etc/builtin.conf.inc
+
+$(BUILD)/sub/osd_libass.o: $(BUILD)/generated/sub/osd_font.otf.inc
+
+$(BUILD)/player/lua.o: $(BUILD)/generated/player/lua/defaults.lua.inc \
+ $(BUILD)/generated/player/lua/assdraw.lua.inc \
+ $(BUILD)/generated/player/lua/options.lua.inc \
+ $(BUILD)/generated/player/lua/osc.lua.inc \
+ $(BUILD)/generated/player/lua/ytdl_hook.lua.inc \
+ $(BUILD)/generated/player/lua/stats.lua.inc \
+ $(BUILD)/generated/player/lua/console.lua.inc \
+
+$(BUILD)/player/javascript.o: $(BUILD)/generated/player/javascript/defaults.js.inc
+
+$(BUILD)/osdep/macosx_application.m $(BUILD)/video/out/cocoa_common.m: \
+ $(BUILD)/generated/TOOLS/osxbundle/mpv.app/Contents/Resources/icon.icns.inc
+
+# Why doesn't wayland just provide fucking libraries like anyone else, instead
+# of overly complex XML generation bullshit?
+# And fuck make too.
+
+# $(1): path prefix to the protocol, $(1)/$(2).xml is the full path.
+# $(2): the name of the protocol, without path or extension
+define generate_trash =
+$$(BUILD)/video/out/wayland_common.o \
+$$(BUILD)/video/out/opengl/context_wayland.o \
+: $$(BUILD)/generated/wayland/$(2).c $$(BUILD)/generated/wayland/$(2).h
+$$(BUILD)/generated/wayland/$(2).c: $(1)/$(2).xml
+ $$(LOG) "WAYSHC" $$@
+ $$(Q) mkdir -p $$(@D)
+ $$(Q) $$(WAYSCAN) private-code $$< $$@
+$$(BUILD)/generated/wayland/$(2).h: $(1)/$(2).xml
+ $$(LOG) "WAYSHH" $$@
+ $$(Q) mkdir -p $$(@D)
+ $$(Q) $$(WAYSCAN) client-header $$< $$@
+endef
+
+$(eval $(call generate_trash,$(WL_PROTO_DIR)/unstable/idle-inhibit/,idle-inhibit-unstable-v1))
+$(eval $(call generate_trash,$(WL_PROTO_DIR)/stable/presentation-time/,presentation-time))
+$(eval $(call generate_trash,$(WL_PROTO_DIR)/stable/xdg-shell/,xdg-shell))
+$(eval $(call generate_trash,$(WL_PROTO_DIR)/unstable/xdg-decoration/,xdg-decoration-unstable-v1))
diff --git a/TOOLS/configure_common.py b/TOOLS/configure_common.py
new file mode 100644
index 0000000000..ea2f32ea1a
--- /dev/null
+++ b/TOOLS/configure_common.py
@@ -0,0 +1,740 @@
+import atexit
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+# ...the fuck?
+NoneType = type(None)
+function = type(lambda: 0)
+
+programs_info = [
+ # env. name default
+ ("CC", "cc"),
+ ("PKG_CONFIG", "pkg-config"),
+ ("WINDRES", "windres"),
+ ("WAYSCAN", "wayland-scanner"),
+]
+
+install_paths_info = [
+ # env/opt default
+ ("PREFIX", "/usr/local"),
+ ("BINDIR", "$(PREFIX)/bin"),
+ ("LIBDIR", "$(PREFIX)/lib"),
+ ("CONFDIR", "$(PREFIX)/etc/$(PROJNAME)"),
+ ("INCDIR", "$(PREFIX)/include"),
+ ("DATADIR", "$(PREFIX)/share"),
+ ("MANDIR", "$(DATADIR)/man"),
+ ("DOCDIR", "$(DATADIR)/doc/$(PROJNAME)"),
+ ("HTMLDIR", "$(DOCDIR)"),
+ ("ZSHDIR", "$(DATADIR)/zsh"),
+ ("CONFLOADDIR", "$(CONFDIR)"),
+]
+
+# for help output only; code grabs them manually
+other_env_vars = [
+ # env # help text
+ ("CFLAGS", "User C compiler flags to append."),
+ ("CPPFLAGS", "Also treated as C compiler flags."),
+ ("LDFLAGS", "C compiler flags for link command."),
+ ("TARGET", "Prefix for default build tools (for cross compilation)"),
+ ("CROSS_COMPILE", "Same as TARGET."),
+]
+
+class _G:
+ help_mode = False # set if --help is specified on the command line
+
+ log_file = None # opened log file
+
+ temp_path = None # set to a private, writable temporary directory
+ build_dir = None
+ root_dir = None
+ out_of_tree = False
+
+ install_paths = {} # var name to path, see install_paths_info
+
+ programs = {} # key is symbolic name, like CC, value is string of
+ # executable name - only set if check_program was called
+
+ exe_format = "elf"
+
+ cflags = []
+ ldflags = []
+
+ config_h = "" # new contents of config.h (written at the end)
+ config_mak = "" # new contents of config.mak (written at the end)
+
+ sources = []
+
+ state_stack = []
+
+ feature_opts = {} # keyed by option name, values are:
+ # "yes": force enable, like --enable-<feature>
+ # "no": force disable, like: --disable-<feature>
+ # "auto": force auto detection, like --with-<feature>=auto
+ # "default": default (same as option not given)
+
+ dep_enabled = {} # keyed by dependency identifier; value is a bool
+ # missing key means the check was not run yet
+
+
+# Convert a string to a C string literal. Adds the required "".
+def _c_quote_string(s):
+ s = s.replace("\\", "\\\\")
+ s = s.replace("\"", "\\\"")
+ return "\"%s\"" % s
+
+# Convert a string to a make variable. Escaping is annoying: sometimes, you add
+# e..g arbitrary paths (=> everything escaped), but sometimes you want to keep
+# make variable use like $(...) unescaped.
+def _c_quote_makefile_var(s):
+ s = s.replace("\\", "\\\\")
+ s = s.replace("\"", "\\\"")
+ s = s.replace(" ", "\ ") # probably
+ return s
+
+def die(msg):
+ sys.stderr.write("Fatal error: %s\n" % msg)
+ sys.stderr.write("Not updating build files.\n")
+ if _G.log_file:
+ _G.log_file.write("--- Stopping due to error: %s\n" % msg)
+ sys.exit(1)
+
+# To be called before any user checks are performed.
+def begin():
+ _G.root_dir = "."
+ _G.build_dir = "build"
+
+ for var, val in install_paths_info:
+ _G.install_paths[var] = val
+
+ for arg in sys.argv[1:]:
+ if arg.startswith("-"):
+ name = arg[1:]
+ if name.startswith("-"):
+ name = name[1:]
+ opt = name.split("=", 1)
+ name = opt[0]
+ val = opt[1] if len(opt) > 1 else ""
+ def noval():
+ if val:
+ die("Option --%s does not take a value." % name)
+ if name == "help":
+ noval()
+ _G.help_mode = True
+ continue
+ elif name.startswith("enable-"):
+ noval()
+ _G.feature_opts[name[7:]] = "yes"
+ continue
+ elif name.startswith("disable-"):
+ noval()
+ _G.feature_opts[name[8:]] = "no"
+ continue
+ elif name.startswith("with-"):
+ if val not in ["yes", "no", "auto", "default"]:
+ die("Option --%s requires 'yes', 'no', 'auto', or 'default'."
+ % name)
+ _G.feature_opts[name[5:]] = val
+ continue
+ uname = name.upper()
+ setval = None
+ if uname in _G.install_paths:
+ def set_install_path(name, val):
+ _G.install_paths[name] = val
+ setval = set_install_path
+ elif uname == "BUILDDIR":
+ def set_build_path(name, val):
+ _G.build_dir = val
+ setval = set_build_path
+ if not setval:
+ die("Unknown option: %s" % arg)
+ if not val:
+ die("Option --%s requires a value." % name)
+ setval(uname, val)
+ continue
+
+ if _G.help_mode:
+ print("Environment variables controlling choice of build tools:")
+ for name, default in programs_info:
+ print(" %-30s %s" % (name, default))
+
+ print("")
+ print("Environment variables/options controlling install paths:")
+ for name, default in install_paths_info:
+ print(" %-30s '%s' (also --%s)" % (name, default, name.lower()))
+
+ print("")
+ print("Other environment variables:")
+ for name, help in other_env_vars:
+ print(" %-30s %s" % (name, help))
+ print("In addition, pkg-config queries PKG_CONFIG_PATH.")
+ print("")
+ print("General build options:")
+ print(" %-30s %s" % ("--builddir=PATH", "Build directory (default: build)"))
+ print(" %-30s %s" % ("", "(Requires using 'make BUILDDIR=PATH')"))
+ print("")
+ print("Specific build configuration:")
+ # check() invocations will print the options they understand.
+ return
+
+ _G.temp_path = tempfile.mkdtemp(prefix = "mpv-configure-")
+ def _cleanup():
+ shutil.rmtree(_G.temp_path)
+ atexit.register(_cleanup)
+
+ # (os.path.samefile() is "UNIX only")
+ if os.path.realpath(sys.path[0]) != os.path.realpath(os.getcwd()):
+ print("This looks like an out of tree build.")
+ print("This doesn't actually work.")
+ # Keep the build dir; this makes it less likely to accidentally trash
+ # an existing dir, especially if dist-clean (wipes build dir) is used.
+ # Also, this will work even if the same-directory check above was wrong.
+ _G.build_dir = os.path.join(os.getcwd(), _G.build_dir)
+ _G.root_dir = sys.path[0]
+ _G.out_of_tree = True
+
+ os.makedirs(_G.build_dir, exist_ok = True)
+ _G.log_file = open(os.path.join(_G.build_dir, "config.log"), "w")
+
+ _G.config_h += "// Generated by configure.\n" + \
+ "#pragma once\n\n"
+
+
+# Check whether the first argument is the same type of any in the following
+# arguments. This _always_ returns val, but throws an exception if type checking
+# fails.
+# This is not very pythonic, but I'm trying to prevent bugs, so bugger off.
+def typecheck(val, *types):
+ vt = type(val)
+ for t in types:
+ if vt == t:
+ return val
+ raise Exception("Value '%s' of type %s not any of %s" % (val, type(val), types))
+
+# If val is None, return []
+# If val is a list, return val.
+# Otherwise, return [val]
+def normalize_list_arg(val):
+ if val is None:
+ return []
+ if type(val) == list:
+ return val
+ return [val]
+
+def push_build_flags():
+ _G.state_stack.append(
+ (_G.cflags[:], _G.ldflags[:], _G.config_h, _G.config_mak,
+ _G.programs.copy()))
+
+def pop_build_flags_discard():
+ top = _G.state_stack[-1]
+ _G.state_stack = _G.state_stack[:-1]
+
+ (_G.cflags[:], _G.ldflags[:], _G.config_h, _G.config_mak,
+ _G.programs) = top
+
+def pop_build_flags_merge():
+ top = _G.state_stack[-1]
+ _G.state_stack = _G.state_stack[:-1]
+
+# Return build dir.
+def get_build_dir():
+ assert _G.build_dir is not None # too early?
+ return _G.build_dir
+
+# Root directory, i.e. top level source directory, or where configure/Makefile
+# are located.
+def get_root_dir():
+ assert _G.root_dir is not None # too early?
+ return _G.root_dir
+
+# Set which type of executable format the target uses.
+# Used for conventions which refuse to abstract properly.
+def set_exe_format(fmt):
+ assert fmt in ["elf", "pe", "macho"]
+ _G.exe_format = fmt
+
+# A check is a check, dependency, or anything else that adds source files,
+# preprocessor symbols, libraries, include paths, or simply serves as
+# dependency check for other checks.
+# Always call this function with named arguments.
+# Arguments:
+# name: String or None. Symbolic name of the check. The name can be used as
+# dependency identifier by other checks. This is the first argument, and
+# usually passed directly, instead of as named argument.
+# If this starts with a "-" flag, options with names derived from this
+# are generated:
+# --enable-$option
+# --disable-$option
+# --with-$option=<yes|no|auto|default>
+# Where "$option" is the name without flag characters, and occurrences
+# of "_" are replaced with "-".
+# If this ends with a "*" flag, the result of this check is emitted as
+# preprocessor symbol to config.h. It will have the name "HAVE_$DEF",
+# and will be either set to 0 (check failed) or 1 (check succeeded),
+# and $DEF is the name without flag characters and all uppercase.
+# desc: String or None. If specified, "Checking for <desc>..." is printed
+# while running configure. If not specified, desc is auto-generated from
+# the name.
+# default: Boolean or None. If True or None, the check is soft-enabled (that
+# means it can still be disabled by options, dependency checks, or
+# the check function). If False, the check is disabled by default,
+# but can be enabled by an option.
+# deps, deps_any, deps_neg: String, array of strings, or None. If a check is
+# enabled by default/command line options, these checks are performed in
+# the following order: deps_neg, deps_any, deps
+# deps requires all dependencies in the list to be enabled.
+# deps_any requires 1 or more dependencies to be enabled.
+# deps_neg requires that all dependencies are disabled.
+# fn: Function or None. The function is run after dependency checks. If it
+# returns True, the check is enabled, if it's False, it will be disabled.
+# Typically, your function for example check for the existence of
+# libraries, and add them to the final list of CFLAGS/LDFLAGS.
+# None behaves like "lambda: True".
+# Note that this needs to be a function. If not, it'd be run before the
+# check() function is even called. That would mean the function runs even
+# if the check was disabled, and could add unneeded things to CFLAGS.
+# If this function returns False, all added build flags are removed again,
+# which makes it easy to compose checks.
+# You're not supposed to call check() itself from fn.
+# sources: String, Array of Strings, or None.
+# If the check is enabled, add these sources to the build.
+# Duplicate sources are removed at end of configuration.
+# required: String or None. If this is a string, the check is required, and
+# if it's not enabled, the string is printed as error message.
+def check(name = None, option = None, desc = None, deps = None, deps_any = None,
+ deps_neg = None, sources = None, fn = None, required = None,
+ default = None):
+
+ deps = normalize_list_arg(deps)
+ deps_any = normalize_list_arg(deps_any)
+ deps_neg = normalize_list_arg(deps_neg)
+ sources = normalize_list_arg(sources)
+
+ typecheck(name, str, NoneType)
+ typecheck(option, str, NoneType)
+ typecheck(desc, str, NoneType)
+ typecheck(deps, NoneType, list)
+ typecheck(deps_any, NoneType, list)
+ typecheck(deps_neg, NoneType, list)
+ typecheck(sources, NoneType, list)
+ typecheck(fn, NoneType, function)
+ typecheck(required, str, NoneType)
+ typecheck(default, bool, NoneType)
+
+ option_name = None
+ define_name = None
+ if name is not None:
+ opt_flag = name.startswith("-")
+ if opt_flag:
+ name = name[1:]
+ def_flag = name.endswith("*")
+ if def_flag:
+ name = name[:-1]
+ if opt_flag:
+ option_name = name.replace("_", "-")
+ if def_flag:
+ define_name = "HAVE_" + name.replace("-", "_").upper()
+
+ if desc is None and name is not None:
+ desc = name
+
+ if _G.help_mode:
+ if not option_name:
+ return
+
+ defaction = "enable"
+ if required is not None:
+ # If they are required, but also have option set, these are just
+ # "strongly required" options.
+ defaction = "enable"
+ elif default == False:
+ defaction = "disable"
+ elif deps or deps_any or deps_neg or fn:
+ defaction = "autodetect"
+ act = "enable" if defaction == "disable" else "disable"
+ opt = "--%s-%s" % (act, option_name)
+ print(" %-30s %s %s [%s]" % (opt, act, desc, defaction))
+ return
+
+ _G.log_file.write("\n--- Test: %s\n" % (name if name else "(unnnamed)"))
+
+ if desc:
+ sys.stdout.write("Checking for %s... " % desc)
+ outcome = "yes"
+
+ force_opt = required is not None
+ use_dep = True if default is None else default
+
+ # Option handling.
+ if option_name:
+ # (The option gets removed, so we can determine whether all options were
+ # applied in the end.)
+ val = _G.feature_opts.pop(option_name, "default")
+ if val == "yes":
+ use_dep = True
+ force_opt = True
+ elif val == "no":
+ use_dep = False
+ force_opt = False
+ elif val == "auto":
+ use_dep = True
+ elif val == "default":
+ pass
+ else:
+ assert False
+
+ if not use_dep:
+ outcome = "disabled"
+
+ # Dependency resolution.
+ # But first, check whether all dependency identifiers really exist.
+ for d in deps_neg + deps_any + deps:
+ dep_enabled(d) # discard result
+ if use_dep:
+ for d in deps_neg:
+ if dep_enabled(d):
+ use_dep = False
+ outcome = "conflicts with %s" % d
+ break
+ if use_dep:
+ any_found = False
+ for d in deps_any:
+ if dep_enabled(d):
+ any_found = True
+ break
+ if len(deps_any) > 0 and not any_found:
+ use_dep = False
+ outcome = "not any of %s found" % (", ".join(deps_any))
+ if use_dep:
+ for d in deps:
+ if not dep_enabled(d):
+ use_dep = False
+ outcome = "%s not found" % d
+ break
+
+ # Running actual checks.
+ if use_dep and fn:
+ push_build_flags()
+ if fn():
+ pop_build_flags_merge()
+ else:
+ pop_build_flags_discard()
+ use_dep = False
+ outcome = "no"
+
+ # Outcome reporting and terminating if dependency not found.
+ if name:
+ _G.dep_enabled[name] = use_dep
+ if define_name:
+ add_config_h_define(define_name, 1 if use_dep else 0)
+ if use_dep:
+ _G.sources += sources
+ if desc:
+ sys.stdout.write("%s\n" % outcome)
+ _G.log_file.write("--- Outcome: %s (%s=%d)\n" %
+ (outcome, name if name else "(unnnamed)", use_dep))
+
+ if required is not None and not use_dep:
+ print("Warning: %s" % required)
+
+ if force_opt and not use_dep:
+ die("This feature is required.")
+
+
+# Runs the process like with execv() (just that args[0] is used for both command
+# and first arg. passed to the process).
+# Returns the process stdout output on success, or None on non-0 exit status.
+# In particular, this logs the command and its output/exit status to the log
+# file.
+def _run_process(args):
+ p = subprocess.Popen(args, stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ stdin = -1)
+ (p_out, p_err) = p.communicate()
+ # We don't really want this. But Python 3 in particular makes it too much of
+ # a PITA (think power drill in anus) to consistently use byte strings, so
+ # we need to use "unicode" strings. Yes, a bad program could just blow up
+ # our butt here by outputting invalid UTF-8.
+ # Weakly support Python 2 too (gcc outputs UTF-8, which crashes Python 2).
+ if type(b"") != str:
+ p_out = p_out.decode("utf-8")
+ p_err = p_err.decode("utf-8")
+ status = p.wait()
+ _G.log_file.write("--- Command: %s\n" % " ".join(args))
+ if p_out:
+ _G.log_file.write("--- stdout:\n%s" % p_out)
+ if p_err:
+ _G.log_file.write("--- stderr:\n%s" % p_err)
+ _G.log_file.write("--- Exit status: %s\n" % status)
+ return p_out if status == 0 else None
+
+# Run the C compiler, possibly including linking. Return whether the compiler
+# exited with success status (0 exit code) as boolean. What exactly it does
+# depends on the arguments. Generally, it constructs a source file and tries
+# to compile it. With no arguments, it compiles, but doesn't link, a source
+# file that contains a dummy main function.
+# Note: these tests are cumulative.
+# Arguments:
+# include: String, array of strings, or None. For each string
+# "#include <$value>" is added to the top of the source file.
+# decl: String, array of strings, or None. Added to the top of the source
+# file, global scope, separated by newlines.
+# expr: String or None. Added to the body of the main function. Despite the
+# name, needs to be a full statement, needs to end with ";".
+# defined: String or None. Adds code that fails if "#ifdef $value" fails.
+# flags: String, array of strings, or None. Each string is added to the
+# compiler command line.
+# Also, if the test succeeds, all arguments are added to the CFLAGS
+# (if language==c) written to config.mak.
+# link: String, array of strings, or None. Each string is added to the
+# compiler command line, and the compiler is made to link (not passing
+# "-c").
+# A value of [] triggers linking without further libraries.
+# A value of None disables the linking step.
+# Also, if the test succeeds, all link strings are added to the LDFLAGS
+# written to config.mak.
+# language: "c" for C, "m" for Objective-C.
+def check_cc(include = None, decl = None, expr = None, defined = None,
+ flags = None, link = None, language = "c"):
+ assert language in ["c", "m"]
+
+ use_linking = link is not None
+
+ contents = ""
+ for inc in normalize_list_arg(include):
+ contents += "#include <%s>\n" % inc
+ for dec in normalize_list_arg(decl):
+ contents += "%s\n" % dec
+ for define in normalize_list_arg(defined):
+ contents += ("#ifndef %s\n" % define) + \
+ "#error failed\n" + \
+ "#endif\n"
+ if expr or use_linking:
+ contents += "int main(int argc, char **argv) {\n";
+ if expr:
+ contents += expr + "\n"
+ contents += "return 0; }\n"
+ source = os.path.join(_G.temp_path, "test." + language)
+ _G.log_file.write("--- Test file %s:\n%s" % (source, contents))
+ with open(source, "w") as f:
+ f.write(contents)
+
+ flags = normalize_list_arg(flags)
+ link = normalize_list_arg(link)
+
+ outfile = os.path.join(_G.temp_path, "test")
+ args = [get_program("CC"), source]
+ args += _G.cflags + flags
+ if use_linking:
+ args += _G.ldflags + link
+ args += ["-o%s" % outfile]
+ else:
+ args += ["-c", "-o%s.o" % outfile]
+ if _run_process(args) is None:
+ return False
+
+ _G.cflags += flags
+ _G.ldflags += link
+ return True
+
+# Run pkg-config with function arguments passed as command arguments. Typically,
+# you specify pkg-config version expressions, like "libass >= 0.14". Returns
+# success as boolean.
+# If this succeeds, the --cflags and --libs are added to CFLAGS and LDFLAGS.
+def check_pkg_config(*args):
+ args = list(args)
+ pkg_config_cmd = [get_program("PKG_CONFIG")]
+
+ cflags = _run_process(pkg_config_cmd + ["--cflags"] + args)
+ if cflags is None:
+ return False
+ ldflags = _run_process(pkg_config_cmd + ["--libs"] + args)
+ if ldflags is None:
+ return False
+
+ _G.cflags += cflags.split()
+ _G.ldflags += ldflags.split()
+ return True
+
+def get_pkg_config_variable(arg, varname):
+ typecheck(arg, str)
+ pkg_config_cmd = [get_program("PKG_CONFIG")]
+
+ res = _run_process(pkg_config_cmd + ["--variable=" + varname] + [arg])
+ if res is not None:
+ res = res.strip()
+ return res
+
+# Check for a specific build tool. You pass in a symbolic name (e.g. "CC"),
+# which is then resolved to a full name and added as variable to config.mak.
+# The function returns a bool for success. You're not supposed to use the
+# program from configure; instead you're supposed to have rules in the makefile
+# using the generated variables.
+# (Some configure checks use the program directly anyway with get_program().)
+def check_program(env_name):
+ for name, default in programs_info:
+ if name == env_name:
+ val = os.environ.get(env_name, None)
+ if val is None:
+ prefix = os.environ.get("TARGET", None)
+ if prefix is None:
+ prefix = os.environ.get("CROSS_COMPILE", "")
+ # Shitty hack: default to gcc if a prefix is given, as binutils
+ # toolchains generally provide only a -gcc wrapper.
+ if prefix and default == "cc":
+ default = "gcc"
+ val = prefix + default
+ # Interleave with output. Sort of unkosher, but dare to stop me.
+ sys.stdout.write("(%s) " % val)
+ _G.log_file.write("--- Trying '%s' for '%s'...\n" % (val, env_name))
+ try:
+ _run_process([val])
+ except OSError as err:
+ _G.log_file.write("%s\n" % err)
+ return False
+ _G.programs[env_name] = val
+ add_config_mak_var(env_name, val)
+ return True
+ assert False, "Unknown program name '%s'" % env_name
+
+# Get the resolved value for a program. Explodes in your face if there wasn't
+# a successful and merged check_program() call before.
+def get_program(env_name):
+ val = _G.programs.get(env_name, None)
+ assert val is not None, "Called get_program(%s) without successful check." % env_name
+ return val
+
+# Return whether all passed dependency identifiers are fulfilled.
+def dep_enabled(*deps):
+ for d in deps:
+ val = _G.dep_enabled.get(d, None)
+ assert val is not None, "Internal error: unknown dependency %s" % d
+ if not val:
+ return False
+ return True
+
+# Add all of the passed strings to CFLAGS.
+def add_cflags(*fl):
+ _G.cflags += list(fl)
+
+# Add a preprocessor symbol of the given name to config.h.
+# If val is a string, it's quoted as string literal.
+# If val is None, it's defined without value.
+def add_config_h_define(name, val):
+ if type(val) == type("") or type(val) == type(b""):
+ val = _c_quote_string(val)
+ if val is None:
+ val = ""
+ _G.config_h += "#define %s %s\n" % (name, val)
+
+# Add a makefile variable of the given name to config.mak.
+# If val is a string, it's quoted as string literal.
+def add_config_mak_var(name, val):
+ if type(val) == type("") or type(val) == type(b""):
+ val = _c_quote_makefile_var(val)
+ _G.config_mak += "%s = %s\n" % (name, val)
+
+# Add these source files to the build.
+def add_sources(*sources):
+ _G.sources += list(sources)
+
+# Get an environment variable and parse it as flags array.
+def _get_env_flags(name):
+ res = os.environ.get(name, "").split()
+ if len(res) == 1 and len(res[0]) == 0:
+ res = []
+ return res
+
+# To be called at the end of user checks.
+def finish():
+ if not is_running():
+ return
+
+ is_fatal = False
+ for key, val in _G.feature_opts.items():
+ print("Unknown feature set on command line: %s" % key)
+ if val == "yes":
+ is_fatal = True
+ if is_fatal:
+ die("Unknown feature was force-enabled.")
+
+ _G.config_h += "\n"
+ add_config_h_define("CONFIGURATION", " ".join(sys.argv))
+ add_config_h_define("MPV_CONFDIR", "$(CONFLOADDIR)")
+ enabled_features = [x[0] for x in filter(lambda x: x[1], _G.dep_enabled.items())]
+ add_config_h_define("FULLCONFIG", " ".join(sorted(enabled_features)))
+
+ with open(os.path.join(_G.build_dir, "config.h"), "w") as f:
+ f.write(_G.config_h)
+
+ add_config_mak_var("BUILD", _G.build_dir)
+ add_config_mak_var("ROOT", _G.root_dir)
+ _G.config_mak += "\n"
+
+ add_config_mak_var("EXESUF", ".exe" if _G.exe_format == "pe" else "")
+
+ for name, _ in install_paths_info:
+ add_config_mak_var(name, _G.install_paths[name])
+ _G.config_mak += "\n"
+
+ _G.config_mak += "CFLAGS = %s %s %s\n" % (" ".join(_G.cflags),
+ os.environ.get("CPPFLAGS", ""),
+ os.environ.get("CFLAGS", ""))
+ _G.config_mak += "\n"
+ _G.config_mak += "LDFLAGS = %s %s\n" % (" ".join(_G.ldflags),
+ os.environ.get("LDFLAGS", ""))
+ _G.config_mak += "\n"
+
+ sources = []
+ for s in _G.sources:
+ # Prefix all source files with "$(ROOT)/". This is important for out of
+ # tree builds, where configure/make is run from "somewhere else", and
+ # not the source directory.
+ # Generated sources need to be prefixed with "$(BUILD)/" (for the same
+ # reason). Since we do not know whether a source file is generated, the
+ # convention is that the user takes care of prefixing it.
+ if not s.startswith("$(BUILD)"):
+ assert not s.startswith("$") # no other variables which make sense
+ assert not s.startswith("generated/") # requires $(BUILD) prefix
+ s = "$(ROOT)/%s" % s
+ sources.append(s)
+
+ _G.config_mak += "SOURCES = \\\n"
+ for s in sorted(list(set(sources))):
+ _G.config_mak += " %s \\\n" % s
+
+ _G.config_mak += "\n"
+
+ with open(os.path.join(_G.build_dir, "config.mak"), "w") as f:
+ f.write("# Generated by configure.\n\n" + _G.config_mak)
+
+ if _G.out_of_tree:
+ try:
+ os.symlink(os.path.join(_G.root_dir, "Makefile.new"), "Makefile")
+ except FileExistsError:
+ print("Not overwriting existing Makefile.")
+
+ _G.log_file.write("--- Finishing successfully.\n")
+ print("Done. You can run 'make' now.")
+
+# Return whether to actually run configure tests, and whether results of those
+# tests are available.
+def is_running():
+ return not _G.help_mode
+
+# Each argument is an array or tuple, with the first element giving the
+# dependency identifier, or "_" to match always fulfilled. The elements after
+# this are added as source files if the dependency matches. This stops after
+# the first matching argument.
+def pick_first_matching_dep(*deps):
+ winner = None
+ for e in deps:
+ if (e[0] == "_" or dep_enabled(e[0])) and (winner is None):
+ # (the odd indirection though winner is so that all dependency
+ # identifiers are checked for existence)
+ winner = e[1:]
+ if winner is not None:
+ add_sources(*winner)
diff --git a/TOOLS/makefile_common.mak b/TOOLS/makefile_common.mak
new file mode 100644
index 0000000000..6d5c462cb2
--- /dev/null
+++ b/TOOLS/makefile_common.mak
@@ -0,0 +1,55 @@
+ifdef V
+Q =
+else
+Q = @
+endif
+
+CFLAGS := -I$(ROOT) -I$(BUILD) $(CFLAGS)
+
+OBJECTS = $(SOURCES:.c=.o)
+OBJECTS := $(OBJECTS:.rc=.o)
+
+TARGET = mpv
+
+# The /./ -> / is for cosmetic reasons.
+BUILD_OBJECTS = $(subst /./,/,$(addprefix $(BUILD)/, $(OBJECTS)))
+
+BUILD_TARGET = $(addprefix $(BUILD)/, $(TARGET))$(EXESUF)
+BUILD_DEPS = $(BUILD_OBJECTS:.o=.d)
+CLEAN_FILES += $(BUILD_OBJECTS) $(BUILD_DEPS) $(BUILD_TARGET)
+
+LOG = $(Q) printf "%s\t%s\n"
+
+# Special rules.
+
+all: $(BUILD_TARGET)
+
+clean:
+ $(LOG) "CLEAN"
+ $(Q) rm -f $(CLEAN_FILES)
+ $(Q) rm -rf $(BUILD)/generated/
+ $(Q) (rmdir $(BUILD)/*/*/* $(BUILD)/*/* $(BUILD)/*) 2> /dev/null || true
+
+dist-clean:
+ $(LOG) "DIST-CLEAN"
+ $(Q) rm -rf $(BUILD)
+
+# Generic pattern rules (used for most source files).
+
+$(BUILD)/%.o: %.c
+ $(LOG) "CC" "$@"
+ $(Q) mkdir -p $(@D)
+ $(Q) $(CC) $(CFLAGS) $< -c -o $@
+
+$(BUILD)/%.o: %.rc
+ $(LOG) "WINRC" "$@"
+ $(Q) mkdir -p $(@D)
+ $(Q) $(WINDRES) -I$(ROOT) -I$(BUILD) $< $@
+
+$(BUILD_TARGET): $(BUILD_OBJECTS)
+ $(LOG) "LINK" "$@"
+ $(Q) $(CC) $(BUILD_OBJECTS) $(CFLAGS) $(LDFLAGS) -o $@
+
+.PHONY: all clean .pregen
+
+-include $(BUILD_DEPS)
diff --git a/configure b/configure
new file mode 100755
index 0000000000..456cc8c6d3
--- /dev/null
+++ b/configure
@@ -0,0 +1,1019 @@
+#!/usr/bin/env python3
+
+#missing:
+#- actually support out of tree builds
+#- libmpv
+#- doc generation
+#- windows console wrapper thing (?)
+#- osx testing
+#- swift stuff (impossible, crapple wants you to stick a dagger up your ass?)
+#- vaapi interops (?)
+#- RPI stuff
+#- newer BSD changes
+#- it's weird how the wayland trash is compiled to BUILDDIR/BUILDDIR/
+
+import os
+from TOOLS.configure_common import *
+
+begin()
+
+# (workaround for insufficient Python lambda syntax)
+def chain(*a):
+ return a[-1]
+
+check("-lgpl",
+ desc = "LGPL (version 2.1 or later) build",
+ default = False)
+check("gpl*",
+ desc = "GPL (version 2 or later) build",
+ deps_neg = "lgpl")
+check("-build-date*",
+ desc = "whether to include binary compile time",
+ fn = lambda: chain(add_cflags("-DNO_BUILD_TIMESTAMPS"), True))
+check(desc = "whether compiler works",
+ required = "C compiler missing or broken",
+ fn = lambda: check_program("CC") and check_cc(link = []))
+check(desc = "pkg-config",
+ required = "pkg-config missing or broken",
+ fn = lambda: check_program("PKG_CONFIG"))
+
+check("-cplayer",
+ desc = "mpv CLI player binary")
+check("-libmpv-shared",
+ desc = "libmpv shared library",
+ fn = lambda: check_cc(flags = "-fPIC"),
+ #'-Wl,-version-script', '-Wl,mpv.def
+ default = False)
+check("-libmpv-static",
+ desc = "libmpv static library",
+ default = False,
+ deps_neg = "libmpv-shared")
+
+add_cflags("-MD", "-MP", "-D_ISOC99_SOURCE", "-D_GNU_SOURCE",
+ "-D_LARGEFILE_SOURCE", "-D_FILE_OFFSET_BITS=64",
+ "-D_LARGEFILE64_SOURCE",
+ "-Wall")
+check(desc = "C11/C99",
+ fn = lambda: check_cc(flags = "-std=c11") or
+ check_cc(flags = "-std=c99"),
+ required = "No C11 or C99 support.")
+check("-optimize",
+ fn = lambda: chain(add_cflags("-O2"), True),
+ desc = "whether to optimize")
+check("-debug-build",
+ desc = "whether to compile-in debugging information",
+ fn = lambda: chain(add_cflags("-g"),
+ check_cc(flags = ["-g3", "-ggdb"]),
+ True))
+check(desc = "warning cflags",
+ fn = lambda: check_cc(flags = [
+ "-Werror=implicit-function-declaration",
+ "-Wno-error=deprecated-declarations",
+ "-Wno-error=unused-function",
+ "-Wempty-body",
+ "-Wdisabled-optimization",
+ "-Wstrict-prototypes",
+ "-Wno-format-zero-length",
+ "-Werror=format-security",
+ "-Wno-redundant-decls",
+ "-Wvla",
+ "-Wno-format-truncation"]))
+check(desc = "-fno-math-errno",
+ fn = lambda: check_cc(flags = "-fno-math-errno"))
+
+check("gnuc",
+ desc = "GNU C",
+ fn = lambda: check_cc(defined = "__GNUC__"))
+check("clang",
+ fn = lambda: check_cc(defined = "__clang__"))
+
+# Note that an important reason to try different set of warning flags is the
+# fact that both compilers may have different bogus behavior wrt. certain
+# warning options. What is needed on one compiler may be annoying or dangerous
+# on the other.
+check(desc = "extra gcc warnings",
+ deps = "gnuc",
+ deps_neg = "clang",
+ fn = lambda: check_cc(flags = [
+ "-Wall", "-Wundef", "-Wmissing-prototypes", "-Wshadow",
+ "-Wno-switch", "-Wparentheses", "-Wpointer-arith",
+ "-Wno-pointer-sign",
+ # GCC bug 66425
+ "-Wno-unused-result"]))
+check(desc = "extra clang warnings",
+ deps = "clang",
+ fn = lambda: check_cc(flags = [
+ "-Wno-logical-op-parentheses", "-fcolor-diagnostics",
+ "-Wno-tautological-compare",
+ "-Wno-tautological-constant-out-of-range-compare"]))
+
+check("-usan",
+ desc = "undefined sanitizer",
+ fn = lambda: check_cc(flags = "-fsanitize=undefined", link = []))
+
+# Reminder: normally always built, but enabled by MPV_LEAK_REPORT.
+# Building it can be disabled only by defining NDEBUG through CFLAGS.
+check("-ta-leak-report*",
+ desc = "enable ta leak report by default (development only)",
+ default = False)
+
+check("libdl*",
+ fn = lambda: check_cc(link = "-ldl", include = "dlfcn.h",
+ expr = 'dlopen("", 0);'))
+check("libm",
+ fn = lambda: check_cc(link = "-lm"))
+check("win32",
+ fn = lambda: check_cc(defined = "_WIN32",
+ flags = ["-D_WIN32_WINNT=0x0602", "-DUNICODE", "-DCOBJMACROS",
+ "-DINITGUID", "-U__STRICT_ANSI__",
+ "-D__USE_MINGW_ANSI_STDIO=1"],
+ include = "windows.h",
+ link = ["-Wl,--major-os-version=6,--minor-os-version=0",
+ "-Wl,--major-subsystem-version=6,--minor-subsystem-version=0",
+ "-mwindows"]) and
+ check_program("WINDRES") and
+ chain(set_exe_format("pe"), True),
+ sources = ["osdep/mpv.rc",
+ "osdep/w32_keyboard.c",
+ "osdep/windows_utils.c"])
+
+check("osx",
+ fn = lambda: check_cc(defined = "__APPLE__") and
+ chain(set_exe_format("macho"), True))
+
+check("mingw",
+ fn = lambda: check_cc(include = "stdlib.h",
+ defined = ["__MINGW32__", "__MINGW64_VERSION_MAJOR"]))
+check("posix*",
+ fn = lambda: check_cc(include = "unistd.h",
+ defined = "_POSIX_VERSION"),
+ sources = ["osdep/polldev.c",
+ "sub/filter_regex.c"])
+check("development environment",
+ deps_any = ["posix", "mingw"],
+ required = "Unable to find either POSIX or MinGW-w64 environment.")
+
+check("-cplugins*",
+ desc = "C plugins",
+ deps = "libdl",
+ deps_neg = "win32",
+ fn = lambda: check_cc(link = "-rdynamic"))
+
+check("noexecstack",
+ fn = lambda: check_cc(link = "-Wl,-z,noexecstack"))
+
+check("win-dep",
+ deps = "win32",
+ fn = lambda: check_cc(link = ["-Wl,--nxcompat", "-Wl,--no-seh", "-Wl,--dynamicbase"]))
+
+check("-android*",
+ fn = lambda: check_cc(include = "android/api-level.h",
+ expr = "(void)__ANDROID__;",
+ link = ["-landroid", "-lEGL"]),
+ sources = ["osdep/android/strnlen.c",
+ "video/out/opengl/context_android.c",
+ "video/out/vo_mediacodec_embed.c"])
+
+check("-uwp*",
+ desc = "Universal Windows Platform",
+ deps = "mingw",
+ default = False,
+ fn = lambda: check_cc(link = "-lwindowsapp"),
+ sources = "osdep/path-uwp.c")
+check("win32-desktop*",
+ desc = "win32 desktop APIs",
+ deps = "win32",
+ deps_neg = "uwp",
+ fn = lambda: check_cc(link = ["-lwinmm", "-lgdi32", "-lole32",
+ "-luuid", "-lavrt", "-ldwmapi",
+ "-lversion"]),
+ sources = ["osdep/path-win.c",
+ "video/out/w32_common.c",
+ "video/out/win32/displayconfig.c",
+ "video/out/win32/droptarget.c"])
+def check_vista_pthreads():
+ path = os.path.abspath(os.path.join(get_root_dir(), "osdep/win32/include"))
+ add_cflags("-I%s" % path)
+ add_cflags("-isystem%s" % path)
+ # define IN_WINPTHREAD to workaround mingw stupidity (we never want it
+ # to define features specific to its own pthread stuff)
+ add_cflags("-DIN_WINPTHREAD")
+ return True
+check("-win32-internal-pthreads*",
+ deps = "win32",
+ deps_neg = "posix",
+ fn = lambda: check_vista_pthreads(),
+ sources = "osdep/win32/pthread.c")
+check("pthreads",
+ deps_neg = "win32-internal-pthreads",
+ fn = lambda: check_cc(link = "-pthread", flags = "-pthread",
+ include = "pthread.h",
+ expr = "pthread_self();"))
+check(desc = "any pthread support",
+ deps_any = ["pthreads", "win32-internal-pthreads"],
+ required = "Unable to find pthreads support.")
+check("stdatomic*",
+ fn = lambda: check_cc(include = "stdatomic.h",
+ expr =
+ "atomic_int_least64_t test = ATOMIC_VAR_INIT(123);"
+ "atomic_fetch_add(&test, 1);"))
+check("atomics",
+ desc = "stdatomic.h support or slow emulation",
+ deps_any = ["stdatomic", "gnuc"],
+ required = "Required.")
+check("librt",
+ fn = lambda: check_cc(link = "-lrt"))
+check("iconv*",
+ fn = lambda: check_cc(include = "iconv.h", link = [],
+ expr = "iconv_open(0, 0);") or
+ check_cc(include = "iconv.h", link = "-liconv",
+ expr = "iconv_open(0, 0);"),
+ required = "Unable to find iconv which should be part of a standard \
+compilation environment. Aborting. If you really mean to compile without \
+iconv support use --disable-iconv.")
+check("dos-paths*",
+ deps = "win32")
+check("glob-posix*",
+ desc = "glob() POSIX support",
+ deps = "posix",
+ deps_neg = "win32",
+ fn = lambda: check_cc(include = "glob.h",
+ expr = 'glob("filename", 0, 0, 0);'))
+check("glob-win32",
+ desc = "glob() win32 replacement",
+ deps_neg = "glob-posix",
+ deps = "win32",
+ sources = "osdep/glob-win.c"),
+check("glob*",
+ desc = "any glob() support",
+ deps_any = ["glob-posix", "glob-win32"])
+check("fchmod*",
+ fn = lambda: check_cc(include = "sys/stat.h", expr = "fchmod(0, 0);"))
+check("glibc-thread-name*",
+ deps = "pthreads",
+ fn = lambda: check_cc(include = "pthread.h",
+ expr = 'pthread_setname_np(pthread_self(), "ducks");'))
+check("osx-thread-name*",
+ deps = "pthreads",
+ fn = lambda: check_cc(include = "pthread.h",
+ expr = 'pthread_setname_np("ducks");'))
+check("bsd-thread-name*",
+ deps = "pthreads",
+ fn = lambda: check_cc(include = ["pthread.h", "pthread_np.h"],
+ expr = 'pthread_set_name_np(pthread_self(), "ducks");'))
+check("bsd-fstatfs*",
+ fn = lambda: check_cc(include = ["sys/param.h", "sys/mount.h"],
+ expr = "struct statfs fs; fstatfs(0, &fs); fs.f_fstypename;"))
+check("linux-fstatfs*",
+ fn = lambda: check_cc(include = "sys/vfs.h",
+ expr = "struct statfs fs; fstatfs(0, &fs); fs.f_namelen;"))
+
+check("-lua*",
+ fn = lambda:
+ check_pkg_config("lua >= 5.1.0 lua < 5.2.0") or
+ check_pkg_config("lua51 >= 5.1.0") or # OpenBSD
+ check_pkg_config("lua5.1 >= 5.1.0") or # debian
+ check_pkg_config("lua-5.1 >= 5.1.0") or # FreeBSD
+ check_pkg_config("lua >= 5.2.0 lua < 5.3.0" ) or
+ check_pkg_config("lua52 >= 5.2.0") or # Arch
+ check_pkg_config("lua5.2 >= 5.2.0") or # debian
+ check_pkg_config("lua-5.2 >= 5.2.0") or # FreeBSD
+ check_pkg_config("luajit >= 2.0.0"),
+ sources = "player/lua.c")
+check("-javascript*",
+ fn = lambda: check_pkg_config("mujs", ">= 1.0.0"),
+ sources = "player/javascript.c")
+check("-libass*",
+ desc = "libass subtitle/OSD renderer",
+ fn = lambda: check_pkg_config("libass >= 0.12.1"),
+ required = "Unable to find development files for libass, or the version " +
+ "found is too old. Aborting. You can use --disable-libass " +
+ "to ignore this warning.",
+ sources = ["sub/ass_mp.c",
+ "sub/osd_libass.c",
+ "sub/sd_ass.c"])
+check(deps_neg = "libass",
+ sources = "sub/osd_dummy.c")
+check("-zlib*",
+ fn = lambda: check_cc(link = "-lz", include = "zlib.h",
+ expr = "inflate(0, Z_NO_FLUSH);"),
+ required = "Unable to find development files for zlib.")
+check("-uchardet*",
+ fn = lambda: check_pkg_config("uchardet"))
+check("-cocoa*",
+ deps = "osx",
+ fn = lambda: check_cc(decl = "#import <Cocoa/Cocoa.h>",
+ language = "m"),
+ sources = ["osdep/macosx_application.m",
+ "osdep/macosx_events.m",
+ "osdep/macosx_menubar.m",
+ "osdep/path-macosx.m",
+ "video/out/cocoa_common.m",
+ "video/out/cocoa/events_view.m",
+ "video/out/cocoa/video_view.m",
+ "video/out/cocoa/window.m"])
+check("-rubberband*",
+ fn = lambda: check_pkg_config("rubberband >= 1.8.0"),
+ sources = "audio/filter/af_rubberband.c")
+check("-lcms2*",
+ fn = lambda: check_pkg_config("lcms2 >= 2.6"))
+check("-vapoursynth*",
+ fn = lambda: check_pkg_config("vapoursynth >= 24") and
+ check_pkg_config("vapoursynth-script >= 23"))
+check("-vapoursynth-lazy*",
+ desc = "VapourSynth filter bridge (Lazy Lua)",
+ deps = "lua",
+ fn = lambda: check_pkg_config("vapoursynth >= 24"))
+check("vapoursynth-core*",
+ deps = ["vapoursynth", "vapoursynth-lazy"],
+ sources = "video/filter/vf_vapoursynth.c")
+check("-libarchive*",
+ desc = "libarchive wrapper for reading zip files and more",
+ fn = lambda: check_pkg_config("libarchive >= 3.0.0"),
+ sources = ["demux/demux_libarchive.c",
+ "stream/stream_libarchive.c"])
+
+check(desc = "FFmpeg",
+ fn = lambda: check_pkg_config(
+ "libavutil >= 56.12.100",
+ "libavcodec >= 58.16.100",
+ "libavformat >= 58.9.100",
+ "libswscale >= 5.0.101",
+ "libavfilter >= 7.14.100",
+ "libswresample >= 3.0.100"),
+ required = "Unable to find development files for some of the required \
+FFmpeg libraries.")
+check("-ffmpeg-strict-abi*",
+ desc = "Disable all known FFmpeg ABI violations'",
+ default = False)
+
+check("-zimg*",
+ desc = "libzimg support (high quality software scaler)",
+ fn = lambda: check_pkg_config("zimg >= 2.9"),
+ sources = ["video/filter/vf_fingerprint.c",
+ "video/zimg.c"]),
+
+check("-libavdevice*",
+ fn = lambda: check_pkg_config("libavdevice >= 57.0.0"))
+
+check("-sdl2",
+ default = False,
+ fn = lambda: check_pkg_config('sdl2'))
+check("-sdl2-audio*",
+ deps = "sdl2",
+ sources = "audio/out/ao_sdl.c")
+check("-sdl2-video*",
+ deps = "sdl2",
+ sources = "video/out/vo_sdl.c")
+check("-sdl2-gamepad*",
+ desc = "SDL2 gamepad input",
+ deps = "sdl2",
+ default = False,
+ sources = "input/sdl_gamepad.c")
+
+check("-pulse*",
+ fn = lambda: check_pkg_config("libpulse >= 1.0"),
+ sources = "audio/out/ao_pulse.c")
+check("-jack*",
+ deps = "gpl",
+ fn = lambda: check_pkg_config("jack"),
+ sources = "audio/out/ao_jack.c")
+check("-openal*",
+ default = False,
+ fn = lambda: check_pkg_config("openal >= 1.13"),
+ sources = "audio/out/ao_openal.c")
+check("-opensles*",
+ fn = lambda: check_cc(include = "SLES/OpenSLES.h",
+ link = "-lOpenSLES",
+ expr = "slCreateEngine;"),
+ sources = "audio/out/ao_opensles.c")
+check("-alsa*",
+ fn = lambda: check_pkg_config("alsa >= 1.0.18"),
+ sources = "audio/out/ao_alsa.c")
+check("-coreaudio*",
+ # TODO: missing frameworks: "CoreFoundation", "CoreAudio", "AudioUnit", "AudioToolbox"
+ deps = "osx",
+ sources = ["audio/out/ao_coreaudio.c",
+ "audio/out/ao_coreaudio_chmap.c",
+ "audio/out/ao_coreaudio_exclusive.c",
+ "audio/out/ao_coreaudio_properties.c",
+ "audio/out/ao_coreaudio_utils.c"])
+check("-audiounit*",
+ desc = "AudioUnit output for iOS",
+ # TODO: missing frameworks: "Foundation", "AudioToolbox"
+ deps = "osx",
+ sources = ["audio/out/ao_audiounit.m",
+ "audio/out/ao_coreaudio_chmap.c",
+ "audio/out/ao_coreaudio_utils.c"])
+check("-wasapi*",
+ deps = "win32",
+ sources = ["audio/out/ao_wasapi.c",
+ "audio/out/ao_wasapi_changenotify.c",
+ "audio/out/ao_wasapi_utils.c"])
+
+check("vt_h*",
+ fn = lambda: check_cc(include = ["sys/vt.h", "sys/ioctl.h"],
+ expr = "int m; ioctl(0, VT_GETMODE, &m);"))
+check("consio_h*",
+ deps_neg = "vt_h",
+ fn = lambda: check_cc(include = ["sys/consio.h", "sys/ioctl.h"],
+ expr = "int m; ioctl(0, VT_GETMODE, &m);"))
+check("-drm*",
+ deps_any = ["vt_h", "consio_h"],
+ fn = lambda: check_pkg_config("libdrm"),
+ sources = ["video/out/drm_atomic.c",
+ "video/out/drm_common.c",
+ "video/out/vo_drm.c"])
+check("-drmprime*",
+ fn = lambda: check_cc(include = "libavutil/pixfmt.h",
+ expr = "int i = AV_PIX_FMT_DRM_PRIME;"))
+check(deps = ["drm", "drmprime"],
+ sources = ["video/out/drm_prime.c",
+ "video/out/opengl/hwdec_drmprime_drm.c"])
+
+check("gbm",
+ fn = lambda: check_pkg_config("gbm"))
+
+def check_wayland_protos():
+ data = get_pkg_config_variable("wayland-protocols", "pkgdatadir")
+ if data is None:
+ return False
+ add_config_mak_var("WL_PROTO_DIR", data)
+ return True
+
+check("-wayland*",
+ # TODO: where does this check whether the protocol files are available?
+ fn = lambda: check_wayland_protos() and
+ check_program("WAYSCAN") and
+ check_pkg_config("wayland-client >= 1.6.0",
+ "wayland-cursor >= 1.6.0",
+ "xkbcommon >= 0.3.0"),
+ sources = ["video/out/wayland_common.c",
+ "$(BUILD)/generated/wayland/idle-inhibit-unstable-v1.c",
+ "$(BUILD)/generated/wayland/presentation-time.c",
+ "$(BUILD)/generated/wayland/xdg-shell.c",
+ "$(BUILD)/generated/wayland/xdg-decoration-unstable-v1.c"])
+check("memfd_create*",
+ desc = "Linux's memfd_create()",
+ deps = "wayland",
+ fn = lambda: check_cc(include = "sys/mman.h", link = [],
+ expr = "memfd_create(0, MFD_CLOEXEC | MFD_ALLOW_SEALING);"),
+ sources = "video/out/vo_wlshm.c")
+
+check("-x11*",
+ deps = "gpl",
+ fn = lambda: check_pkg_config("x11 >= 1.0.0",
+ "xscrnsaver >= 1.0.0",
+ "xext >= 1.0.0",
+ "xinerama >= 1.0.0",
+ "xrandr >= 1.2.0"),
+ sources = ["video/out/vo_x11.c",
+ "video/out/x11_common.c"])
+check("-xv*",
+ deps = "x11",
+ fn = lambda: check_pkg_config("xv"),
+ sources = "video/out/vo_xv.c")
+
+check("-libplacebo*",
+ desc = "libplacebo support",
+ fn = lambda: check_pkg_config("libplacebo >= 1.18.0"),
+ sources = ["video/out/placebo/ra_pl.c",
+ "video/out/placebo/utils.c"])
+
+check("-vulkan*",
+ desc = "Vulkan context support",
+ deps = "libplacebo",
+ fn = lambda: check_pkg_config("vulkan"),
+ sources = ["video/out/vulkan/context.c",
+ "video/out/vulkan/utils.c"])
+check(deps = ["vulkan", "x11"],
+ sources = "video/out/vulkan/context_xlib.c")
+check(deps = ["vulkan", "android"],
+ sources = "video/out/vulkan/context_android.c")
+check(deps = ["vulkan", "wayland"],
+ sources = "video/out/vulkan/context_wayland.c")
+check(deps = ["vulkan", "win32-desktop"],
+ sources = "video/out/vulkan/context_win.c")
+
+# TODO: the waf check is much more complicated
+check("-egl*",
+ desc = "EGL 1.4",
+ fn = lambda: check_pkg_config("egl"))
+
+check("-gl-cocoa*",
+ # TODO
+ fn = lambda: False,
+ sources = "video/out/opengl/context_cocoa.c")
+
+check("-gl-x11*",
+ desc = "OpenGL X11 Backend",
+ deps = "x11",
+ fn = lambda: check_cc(link = "-lGL",
+ include = "GL/glx.h",
+ expr = "glXGetCurrentDisplay();"),
+ sources = "video/out/opengl/context_glx.c")
+check("-egl-x11*",
+ desc = "OpenGL X11 EGL Backend",
+ deps = ["x11", "egl"],
+ sources = "video/out/opengl/context_x11egl.c")
+check("-egl-drm*",
+ desc = "OpenGL DRM EGL Backend",
+ deps = ["drm", "gbm", "egl"],
+ sources = "video/out/opengl/context_drm_egl.c")
+check("-gl-wayland*",
+ desc = "OpenGL Wayland Backend",
+ deps = ["wayland", "egl"],
+ fn = lambda: check_pkg_config("wayland-egl >= 9.0.0"),
+ sources = "video/out/opengl/context_wayland.c")
+check("-gl-win32*",
+ desc = "OpenGL Win32 Backend",
+ deps = "win32-desktop",
+ fn = lambda: check_cc(link = "-lopengl32",
+ include = "windows.h",
+ expr = "wglCreateContext(0);"),
+ sources = "video/out/opengl/context_win.c")
+check("-gl-dxinterop*",
+ desc = "OpenGL/DirectX Interop Backend",
+ deps = "gl-win32",
+ fn = lambda: check_cc(include = ["GL/gl.h", "GL/wglext.h", "d3d9.h"],
+ expr = "int i = WGL_ACCESS_WRITE_DISCARD_NV;"
+ "IDirect3D9Ex *d;"),
+ sources = "video/out/opengl/context_dxinterop.c")
+check("-egl-angle*",
+ desc = "OpenGL ANGLE headers",
+ deps = "win32",
+ fn = lambda: check_cc(include = ["EGL/egl.h", "EGL/eglext.h"],
+ expr = "int x = EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE;"),
+ sources = "video/out/opengl/angle_dynamic.c")
+check("-egl-angle-lib*",
+ desc = "OpenGL Win32 ANGLE Library",
+ deps = "egl-angle",
+ fn = lambda: check_cc(include = ["EGL/egl.h"],
+ expr = "eglCreateWindowSurface(0, 0, 0, 0);",
+ flags = ["-DGL_APICALL=", "-DEGLAPI=",
+ "-DANGLE_NO_ALIASES", "-DANGLE_EXPORT="],
+ link = ["-lEGL", "-lGLESv2", "-ldxguid", "-ld3d9",
+ "-lgdi32", "-lstdc++"]))
+check("-egl-angle-win32*",
+ desc = "OpenGL Win32 ANGLE Backend",
+ deps = ["egl-angle", "win32-desktop"],
+ sources = ["video/out/gpu/d3d11_helpers.c",
+ "video/out/opengl/context_angle.c"])
+
+check("-vdpau*",
+ 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"])
+check("-vdpau-gl-x11*",
+ desc = "VDPAU with OpenGL/X11",
+ deps = ["vdpau", "gl-x11"],
+ sources = "video/out/opengl/hwdec_vdpau.c")
+check("-vaapi*",
+ desc = "VAAPI acceleration",
+ fn = lambda: check_pkg_config("libva >= 0.36.0"),
+ sources = ["video/vaapi.c",
+ "video/filter/vf_vavpp.c"])
+check("-vaapi-x11*",
+ desc = "VAAPI (X11 support)",
+ deps = ["vaapi", "x11"],
+ fn = lambda: check_pkg_config("libva-x11 >= 0.36.0"))
+check(deps = ["vaapi-x11", "gpl"],
+ sources = "video/out/vo_vaapi.c")
+check("-vaapi-wayland*",
+ desc = "VAAPI (Wayland support)",
+ deps = ["vaapi", "gl-wayland"],
+ fn = lambda: check_pkg_config("libva-wayland >= 0.36.0"))
+check("-vaapi-drm*",
+ desc = "VAAPI (DRM/EGL support)",
+ deps = ["vaapi", "egl-drm"],
+ fn = lambda: check_pkg_config("libva-drm >= 0.36.0"))
+check("-vaapi-glx*",
+ desc = "VAAPI GLX",
+ deps = ["gpl", "vaapi-x11", "gl-x11"])
+check("-vaapi-x-egl*",
+ desc = "VAAPI EGL on X11",
+ deps = ["vaapi-x11", "egl-x11"])
+
+check("-vaapi-vulkan*",
+ desc = "VAAPI Vulkan",
+ deps = ["vaapi", "vulkan"],
+ sources = ["video/out/hwdec/hwdec_vaapi.c",
+ "video/out/hwdec/hwdec_vaapi_vk.c"])
+
+check("-vaapi-egl*",
+ desc = "VAAPI EGL",
+ deps_any = ["vaapi-x-egl", "vaapi-wayland"],
+ sources = ["video/out/hwdec/hwdec_vaapi.c",
+ "video/out/hwdec/hwdec_vaapi_gl.c"])
+
+check("-caca*",
+ deps = "gpl",
+ fn = lambda: check_pkg_config("caca >= 0.99.beta18"),
+ sources = "video/out/vo_caca.c")
+check("-jpeg*",
+ desc = "JPEG support",
+ fn = lambda: check_cc(include = ["stdio.h", "jpeglib.h"],
+ link = "-ljpeg"))
+check("-direct3d*",
+ desc = "Ancient D3D9 VO",
+ deps = ["win32-desktop", "gpl"],
+ sources = "video/out/vo_direct3d.c")
+check("-shaderc-shared",
+ desc = "libshaderc SPIR-V compiler (shared library)",
+ fn = lambda: check_cc(include = "shaderc/shaderc.h",
+ link = "-lshaderc_shared"))
+check("-shaderc-static",
+ desc = "libshaderc SPIR-V compiler (static library)",
+ deps_neg = "shaderc-shared",
+ fn = lambda: check_cc(include = "shaderc/shaderc.h",
+ link = ["-lshaderc_combined", "-lstdc++"]))
+check("shaderc*",
+ desc = "libshaderc SPIR-V compiler",
+ deps_any = ["shaderc-shared", "shaderc-static"],
+ sources = "video/out/gpu/spirv_shaderc.c")
+check("-spirv-cross-shared",
+ desc = "SPIRV-Cross SPIR-V shader converter (shared library)",
+ fn = lambda: check_pkg_config("spirv-cross-c-shared"))
+check("-spirv-cross-static",
+ desc = "SPIRV-Cross SPIR-V shader converter (static library)",
+ deps_neg = "spirv-cross-shared",
+ fn = lambda: check_pkg_config("spirv-cross"))
+check("spirv-cross*",
+ desc = "SPIRV-Cross SPIR-V shader converter",
+ deps_any = ["spirv-cross-shared", "spirv-cross-static"])
+check("-d3d11*",
+ desc = "Direct3D 11 video output",
+ deps = ["win32-desktop", "shaderc", "spirv-cross",],
+ fn = lambda: check_cc(include = ["d3d11_1.h", "dxgi1_2.h"]),
+ sources = ["video/out/d3d11/context.c",
+ "video/out/d3d11/ra_d3d11.c",
+ "video/out/gpu/d3d11_helpers.c"])
+
+check("-rpi*",
+ # TODO: or tell them to fuck off
+ fn = lambda: False,
+ sources = ["video/out/opengl/context_rpi.c",
+ "video/out/opengl/hwdec_rpi.c",
+ "video/out/vo_rpi.c"])
+check("-ios-gl*",
+ desc = "iOS OpenGL ES hardware decoding interop support",
+ fn = lambda: check_cc(include = "OpenGLES/ES3/glext.h",
+ expr = "(void)GL_RGB32F;"), # arbitrary OpenGL ES 3.0 symbol
+ sources = "video/out/opengl/hwdec_ios.m")
+check("-egl-android*",
+ desc = "Android EGL support",
+ deps = "android",
+ fn = lambda: check_cc(link = ["-landroid", "-lEGL"]),
+ sources = "video/out/opengl/context_android.c")
+
+check("-plain-gl*",
+ desc = "OpenGL without platform-specific code (e.g. for libmpv)",
+ deps = ["libmpv-shared", "libmpv-static"])
+
+check("-gl*",
+ desc = "OpenGL context support",
+ deps_any = ["gl-cocoa", "gl-x11", "egl-x11", "egl-drm", "egl-android",
+ "gl-win32", "gl-wayland", "rpi", "plain-gl"],
+ required = "No OpenGL video output found or enabled. " +
+ "Aborting. If you really mean to compile without OpenGL " +
+ "video outputs use --disable-gl.",
+ sources = ["video/out/opengl/common.c",
+ "video/out/opengl/context.c",
+ "video/out/opengl/formats.c",
+ "video/out/opengl/libmpv_gl.c",
+ "video/out/opengl/ra_gl.c",
+ "video/out/opengl/utils.c"])
+
+check("egl-helpers*",
+ desc = "EGL helper functions",
+ deps_any = ["egl-x11", "rpi", "gl-wayland", "egl-drm",
+ "egl-angle-win32", "egl-android"],
+ sources = ["video/filter/vf_gpu.c", # doesn't really belong here
+ "video/out/opengl/egl_helpers.c"]),
+
+check("videotoolbox-hwaccel*",
+ desc = "libavcodec videotoolbox hwaccel",
+ deps_any = ["gl-cocoa", "ios-gl"])
+check("-videotoolbox-gl*",
+ desc = "Videotoolbox with OpenGL",
+ deps = ["gl-cocoa", "videotoolbox-hwaccel"],
+ sources = "video/out/opengl/hwdec_osx.c")
+check("-d3d-hwaccel*",
+ desc = "D3D11VA hwaccel",
+ deps = "win32",
+ sources = ["video/d3d.c",
+ "video/filter/vf_d3d11vpp.c"])
+check("-d3d9-hwaccel*",
+ desc = "DXVA2 hwaccel",
+ deps = "d3d-hwaccel")
+check("-gl-dxinterop-d3d9*",
+ desc = "OpenGL/DirectX Interop Backend DXVA2 interop",
+ deps = ["gl-dxinterop", "d3d9-hwaccel"],
+ sources = "video/out/opengl/hwdec_dxva2gldx.c")
+
+check("-cuda-hwaccel*",
+ desc = "CUDA acceleration base dependencies",
+ fn = lambda: check_pkg_config("ffnvcodec >= 8.2.15.7"),
+ sources = "video/cuda.c")
+check("-cuda-interop*",
+ deps = "cuda-hwaccel",
+ desc = "CUDA with graphics base interop",
+ sources = "video/out/hwdec/hwdec_cuda.c")
+check("-cuda-interop-gl",
+ desc = "CUDA GL interop",
+ deps = ["cuda-interop", "gl"],
+ sources = "video/out/hwdec/hwdec_cuda_gl.c")
+check("-cuda-interop-vulkan",
+ desc = "CUDA Vulkan interop",
+ deps = ["cuda-interop", "vulkan"],
+ sources = "video/out/hwdec/hwdec_cuda_vk.c")
+
+check("-rpi-mmal*",
+ desc = "Raspberry Pi MMAL hwaccel",
+ deps = "rpi",
+ fn = lambda: check_pkg_config("mmal") or
+ check_pkg_config("/opt/vc/lib/pkgconfig/mmal.pc"),
+ sources = ["video/out/opengl/hwdec_rpi.c",
+ "video/out/vo_rpi.c"])
+
+check(deps_any = ["gl-x11", "egl-x11"],
+ sources = ["video/out/opengl/oml_sync.c"])
+
+check(deps = ["d3d-hwaccel", "egl-angle"],
+ sources = ["video/out/opengl/hwdec_d3d11egl.c"])
+
+check(deps = ["d3d-hwaccel", "d3d11"],
+ sources = "video/out/d3d11/hwdec_d3d11va.c")
+
+check(deps = ["d3d9-hwaccel", "d3d11"],
+ sources = "video/out/d3d11/hwdec_dxva2dxgi.c")
+
+check(deps = ["d3d9-hwaccel", "egl-angle"],
+ sources = "video/out/opengl/hwdec_dxva2egl.c")
+
+check(deps = ["vulkan", "wayland"],
+ sources = "video/out/vulkan/context_wayland.c")
+check(deps = ["vulkan", "win32-desktop"],
+ sources = "video/out/vulkan/context_win.c")
+check(deps = ["vulkan", "x11"],
+ sources = "video/out/vulkan/context_xlib.c")
+
+check("-libbluray*",
+ desc = "Bluray support",
+ default = False,
+ fn = lambda: check_pkg_config("libbluray >= 0.3.0"),
+ sources = "stream/stream_bluray.c")
+check("-dvdnav*",
+ desc = "dvdnav support",
+ default = False,
+ deps = "gpl",
+ fn = lambda: check_pkg_config("dvdnav >= 4.2.0") and
+ check_pkg_config("dvdread >= 4.1.0"),
+ sources = "stream/stream_dvdnav.c"),
+check("-cdda*",
+ desc = "cdda support (libcdio)",
+ deps = "gpl",
+ default = False,
+ fn = lambda: check_pkg_config("libcdio_paranoia"),
+ sources = "stream/stream_cdda.c")
+check("-dvbin*",
+ desc = "DVB input module",
+ deps = "gpl",
+ default = False,
+ sources = ["stream/dvb_tune.c", "stream/stream_dvb.c"])
+
+check("-apple-remote*",
+ desc = "Apple Remote support",
+ deps = "cocoa",
+ sources = "osdep/ar/HIDRemote.m")
+check("-macos-touchbar*",
+ desc = "macOS Touch Bar support",
+ deps = "cocoa",
+ # TODO: all that framework stuff
+ fn = lambda: False,
+ sources = "osdep/macosx_touchbar.m")
+check("-macos-cocoa-cb*",
+ desc = "macOS opengl-cb backend",
+ deps = "cocoa")
+
+check("-tests*",
+ desc = "unit tests (development only)",
+ default = False,
+ sources = ["test/chmap.c",
+ "test/gl_video.c",
+ "test/img_format.c",
+ "test/json.c",
+ "test/linked_list.c",
+ "test/paths.c",
+ "test/scale_sws.c",
+ "test/scale_test.c",
+ "test/tests.c"])
+check("tests-zimg",
+ deps = ["tests", "zimg"],
+ sources = ["test/repack.c",
+ "test/scale_zimg.c"])
+
+add_sources(
+ "audio/aframe.c",
+ "audio/audio_buffer.c",
+ "audio/chmap.c",
+ "audio/chmap_sel.c",
+ "audio/decode/ad_lavc.c",
+ "audio/decode/ad_spdif.c",
+ "audio/filter/af_drop.c",
+ "audio/filter/af_format.c",
+ "audio/filter/af_lavcac3enc.c",
+ "audio/filter/af_scaletempo.c",
+ "audio/fmt-conversion.c",
+ "audio/format.c",
+ "audio/out/ao.c",
+ "audio/out/ao_lavc.c",
+ "audio/out/ao_null.c",
+ "audio/out/ao_pcm.c",
+ "audio/out/buffer.c",
+ "common/av_common.c",
+ "common/av_log.c",
+ "common/codecs.c",
+ "common/common.c",
+ "common/encode_lavc.c",
+ "common/msg.c",
+ "common/playlist.c",
+ "common/recorder.c",
+ "common/stats.c",
+ "common/tags.c",
+ "common/version.c",
+ "demux/cache.c",
+ "demux/codec_tags.c",
+ "demux/cue.c",
+ "demux/demux.c",
+ "demux/demux_cue.c",
+ "demux/demux_disc.c",
+ "demux/demux_edl.c",
+ "demux/demux_lavf.c",
+ "demux/demux_mf.c",
+ "demux/demux_mkv.c",
+ "demux/demux_mkv_timeline.c",
+ "demux/demux_null.c",
+ "demux/demux_playlist.c",
+ "demux/demux_raw.c",
+ "demux/demux_timeline.c",
+ "demux/ebml.c",
+ "demux/packet.c",
+ "demux/timeline.c",
+ "filters/filter.c",
+ "filters/f_async_queue.c",
+ "filters/f_auto_filters.c",
+ "filters/f_autoconvert.c",
+ "filters/f_decoder_wrapper.c",
+ "filters/f_demux_in.c",
+ "filters/f_hwtransfer.c",
+ "filters/f_lavfi.c",
+ "filters/f_output_chain.c",
+ "filters/f_swresample.c",
+ "filters/f_swscale.c",
+ "filters/f_utils.c",
+ "filters/frame.c",
+ "filters/user_filters.c",
+ "input/cmd.c",
+ "input/event.c",
+ "input/input.c",
+ "input/ipc.c",
+ "input/keycodes.c",
+ "misc/bstr.c",
+ "misc/charset_conv.c",
+ "misc/dispatch.c",
+ "misc/json.c",
+ "misc/natural_sort.c",
+ "misc/node.c",
+ "misc/rendezvous.c",
+ "misc/ring.c",
+ "misc/thread_pool.c",
+ "misc/thread_tools.c",
+ "options/m_config_core.c",
+ "options/m_config_frontend.c",
+ "options/m_option.c",
+ "options/m_property.c",
+ "options/options.c",
+ "options/parse_commandline.c",
+ "options/parse_configfile.c",
+ "options/path.c",
+ "osdep/io.c",
+ "osdep/path-unix.c",
+ "osdep/semaphore_osx.c",
+ "osdep/subprocess.c",
+ "osdep/threads.c",
+ "osdep/timer.c",
+ "player/audio.c",
+ "player/client.c",
+ "player/command.c",
+ "player/configfiles.c",
+ "player/external_files.c",
+ "player/loadfile.c",
+ "player/main.c",
+ "player/misc.c",
+ "player/osd.c",
+ "player/playloop.c",
+ "player/screenshot.c",
+ "player/scripting.c",
+ "player/sub.c",
+ "player/video.c",
+ "stream/cookies.c",
+ "stream/stream.c",
+ "stream/stream_avdevice.c",
+ "stream/stream_cb.c",
+ "stream/stream_concat.c",
+ "stream/stream_edl.c",
+ "stream/stream_file.c",
+ "stream/stream_lavf.c",
+ "stream/stream_memory.c",
+ "stream/stream_mf.c",
+ "stream/stream_null.c",
+ "sub/dec_sub.c",
+ "sub/draw_bmp.c",
+ "sub/filter_sdh.c",
+ "sub/img_convert.c",
+ "sub/lavc_conv.c",
+ "sub/osd.c",
+ "sub/sd_lavc.c",
+ "ta/ta.c",
+ "ta/ta_talloc.c",
+ "ta/ta_utils.c",
+ "video/csputils.c",
+ "video/decode/vd_lavc.c",
+ "video/filter/refqueue.c",
+ "video/filter/vf_format.c",
+ "video/filter/vf_sub.c",
+ "video/fmt-conversion.c",
+ "video/hwdec.c",
+ "video/image_loader.c",
+ "video/image_writer.c",
+ "video/img_format.c",
+ "video/mp_image.c",
+ "video/mp_image_pool.c",
+ "video/out/aspect.c",
+ "video/out/bitmap_packer.c",
+ "video/out/dither.c",
+ "video/out/dr_helper.c",
+ "video/out/filter_kernels.c",
+ "video/out/gpu/context.c",
+ "video/out/gpu/error_diffusion.c",
+ "video/out/gpu/hwdec.c",
+ "video/out/gpu/lcms.c",
+ "video/out/gpu/libmpv_gpu.c",
+ "video/out/gpu/osd.c",
+ "video/out/gpu/ra.c",
+ "video/out/gpu/shader_cache.c",
+ "video/out/gpu/spirv.c",
+ "video/out/gpu/user_shaders.c",
+ "video/out/gpu/utils.c",
+ "video/out/gpu/video.c",
+ "video/out/gpu/video_shaders.c",
+ "video/out/vo.c",
+ "video/out/vo_gpu.c",
+ "video/out/vo_image.c",
+ "video/out/vo_lavc.c",
+ "video/out/vo_libmpv.c",
+ "video/out/vo_null.c",
+ "video/out/vo_tct.c",
+ "video/out/win_state.c",
+ "video/repack.c",
+ "video/sws_utils.c",
+)
+
+if is_running():
+
+ if dep_enabled("cplayer"):
+ pick_first_matching_dep(
+ ("cocoa", "osdep/main-fn-cocoa.c"),
+ ("posix", "osdep/main-fn-unix.c"),
+ ("win32-desktop", "osdep/main-fn-win.c"),
+ )
+
+ pick_first_matching_dep(
+ ("posix", "osdep/terminal-unix.c"),
+ ("win32-desktop", "osdep/terminal-win.c"),
+ ("_", "osdep/terminal-dummy.c"),
+ )
+
+ pick_first_matching_dep(
+ ("win32", "osdep/timer-win2.c"),
+ ("osx", "osdep/timer-darwin.c"),
+ ("_", "osdep/timer-linux.c"),
+ )
+
+ pick_first_matching_dep(
+ ("posix", "input/ipc-unix.c"),
+ ("win32-desktop", "input/ipc-win.c"),
+ ("_", "input/ipc-dummy.c"),
+ )
+
+ pick_first_matching_dep(
+ ("posix", "osdep/subprocess-posix.c"),
+ ("win32-desktop", "osdep/subprocess-win.c"),
+ ("_", "osdep/subprocess-dummy.c"),
+ )
+
+finish()