Commit 2ee55544 authored by Aleksei Nikiforov's avatar Aleksei Nikiforov

Merge tag 'v0.21.24' into sisyphus

release v0.21.24
parents 8f18daab 24741c5d

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

*.Plo
*.Po
*.a
*.d
*.la
*.lo
*.o
*.exe
*~ *~
.#* .#*
.stgit* .stgit*
.deps
.dirstamp
tags
/Makefile
/Makefile.in
/aclocal.m4
/autom4te.cache
/config.h
/config.h.in
/config.log
/config.mk
/config.status
/config_detected.h
/config_detected.mk
/configure
/configure.lineno
/depmode
/libtool
/ltmain.sh
/mkinstalldirs
/build
/src/mpd
/systemd/system/mpd.service
/systemd/user/mpd.service
/stamp-h1
/src/dsd2pcm/dsd2pcm
/src/win32/mpd_win32_rc.rc
/doc/doxygen.conf /output/
/doc/protocol.html
/doc/protocol
/doc/user
/doc/developer
/doc/sticker
/doc/api
/test/software_volume
/test/run_convert
/test/run_decoder
/test/read_tags
/test/run_filter
/test/run_encoder
/test/run_output
/test/read_conf
/test/run_input
/test/read_mixer
/test/dump_playlist
/test/run_normalize
/test/tmp
/test/run_inotify
/test/test_queue_priority
/test/test_protocol
/test/run_ntp_server
/test/run_resolver
/test/run_tcp_connect
/test/test_pcm
/test/dump_rva2
/test/dump_text_file
/test/test_util
/test/test_byte_reverse
/test/test_mixramp
/test/test_vorbis_encoder
/test/DumpDatabase
/lib/
/*.tar.gz
/*.tar.bz2
/*.tar.xz
/mpd-*/
__pycache__/ __pycache__/
/.clangd/
/compile_commands.json
dist: trusty
language: cpp language: cpp
addons: jobs:
apt: include:
packages: # Ubuntu Bionic (18.04) with GCC 7
- libcppunit-dev - os: linux
- libboost-dev dist: bionic
addons:
apt:
sources:
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- libgtest-dev
- libboost-dev
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
os: # Ubuntu Bionic (18.04) with GCC 7 on big-endian
- linux - os: linux
- osx arch: s390x
dist: bionic
addons:
apt:
sources:
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- libgtest-dev
- libboost-dev
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
env: # Ubuntu Bionic (18.04) with GCC 7 on ARM64
global: - os: linux
- MAKEFLAGS="-j2" arch: arm64
dist: bionic
addons:
apt:
sources:
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- libgtest-dev
- libboost-dev
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
# Ubuntu Trusty (16.04) with GCC 6
- os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- g++-6
- libgtest-dev
- boost1.67
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
# use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068
- MATRIX_EVAL="export CC='ccache gcc-6' CXX='ccache g++-6' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
# Ubuntu Trusty (16.04) with GCC 8
- os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- g++-8
- libgtest-dev
- boost1.67
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
# use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068
- MATRIX_EVAL="export CC='ccache gcc-8' CXX='ccache g++-8' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
- os: osx
osx_image: xcode9.4
addons:
homebrew:
packages:
- ccache
- meson
- icu4c
- ffmpeg
- libnfs
- yajl
- libupnp
- libid3tag
- chromaprint
- libsamplerate
- libsoxr
# libzzip appears to be broken on Homebrew: "ld: library not found for -lzzip"
#- libzzip
- flac
- opus
- libvorbis
- faad2
- wavpack
- libmpdclient
update: true
env:
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
cache:
apt: true
ccache: true
directories:
- $HOME/Library/Caches/Homebrew
before_cache:
- test "$TRAVIS_OS_NAME" != "osx" || brew cleanup
before_install: before_install:
# C++14 - eval "${MATRIX_EVAL}"
- test "$TRAVIS_OS_NAME" != "linux" || sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- test "$TRAVIS_OS_NAME" != "linux" || sudo apt-get update -qq
- test "$TRAVIS_OS_NAME" != "osx" || brew update
install: install:
# C++14 # C++14
- test "$TRAVIS_OS_NAME" != "linux" || sudo apt-get install -qq g++-5
- test "$TRAVIS_OS_NAME" != "osx" || brew install cppunit # Work around "Target /usr/local/lib/libgtest.a is a symlink
# belonging to nss. You can unlink it" during gtest install
- test "$TRAVIS_OS_NAME" != "osx" || brew unlink nss
- test "$TRAVIS_OS_NAME" != "osx" || brew install https://gist.githubusercontent.com/Kronuz/96ac10fbd8472eb1e7566d740c4034f8/raw/gtest.rb
before_script:
- ccache -s
script: script:
- OPTIONS="--enable-test" - eval "${MATRIX_EVAL}"
- test "$TRAVIS_OS_NAME" != "linux" || export CC=gcc-5 CXX=g++-5 - OPTIONS="-Dtest=true"
- test "$TRAVIS_OS_NAME" != "osx" || OPTIONS="$OPTIONS --enable-osx" - meson . output --werror $OPTIONS
- ./autogen.sh - ninja -C output -v test
- ./configure --disable-silent-rules --disable-dependency-tracking $OPTIONS - ccache -s
- make
- make check
Music Player Daemon - http://www.musicpd.org Music Player Daemon - http://www.musicpd.org
Copyright 2003-2017 The Music Player Daemon Project Copyright 2003-2018 The Music Player Daemon Project
The following people have contributed code to MPD: The following people have contributed code to MPD:
...@@ -36,3 +36,14 @@ The following people have contributed code to MPD: ...@@ -36,3 +36,14 @@ The following people have contributed code to MPD:
François Revol <revol@free.fr> François Revol <revol@free.fr>
Jacob Vosmaer <contact@jacobvosmaer.nl> Jacob Vosmaer <contact@jacobvosmaer.nl>
Thomas Guillem <thomas@gllm.fr> Thomas Guillem <thomas@gllm.fr>
Andrew Basterfield <abasterfield@gmail.com>
Bart Nagel <bart@tremby.net>
Christian Kröner <ckroener@gmx.net>
Christopher Zimmermann <madroach@gmerlin.de>
John Regan <john@jrjrtech.com>
Joshua Wise <joshua@joshuawise.com>
Ryan Walklin
Stefan Roellin <stefan@roellin-baumann.ch>
Stefano Miccoli <stefano.miccoli@polimi.it>
Steven O'Brien <steven_obrien1@yahoo.co.uk>
Thomas Zander <thomas.e.zander@googlemail.com>
...@@ -2,18 +2,25 @@ ...@@ -2,18 +2,25 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd" package="org.musicpd"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="22" android:versionCode="47"
android:versionName="0.20.23"> android:versionName="0.21.24">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-feature android:name="android.software.leanback"
android:required="false" />
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application android:allowBackup="true" <application android:allowBackup="true"
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:banner="@drawable/icon"
android:label="@string/app_name"> android:label="@string/app_name">
<activity android:name=".Settings" <activity android:name=".Settings"
android:label="@string/app_name"> android:label="@string/app_name">
...@@ -22,6 +29,14 @@ ...@@ -22,6 +29,14 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".Settings"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".Receiver"> <receiver android:name=".Receiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
......
#!/bin/sh -e
S=`dirname "$0"`
ANDROID_ABI=$1
STRIP=$2
ZIP=$3
UNSIGNED_APK=$4
LIBMPD_SO=$5
CLASSES_DEX=$6
RESOURCES_APK=$7
D=`dirname "$UNSIGNED_APK"`
rm -rf "$D/apk"
mkdir -p "$D/apk/lib/$ANDROID_ABI"
"$STRIP" "$LIBMPD_SO" -o "$D/apk/lib/$ANDROID_ABI/`basename $LIBMPD_SO`"
cp "$CLASSES_DEX" "$D/apk/"
cp "$RESOURCES_APK" "$UNSIGNED_APK"
cd "$D/apk"
exec zip -q -r -X "../`basename $UNSIGNED_APK`" .
unsigned_apk = custom_target(
'mpd-unsigned.apk',
output: 'mpd-unsigned.apk',
input: [mpd, classes_dex, resources_apk[0]],
command: [
join_paths(meson.current_source_dir(), 'make-unsigned-apk.sh'),
android_abi,
get_option('android_strip'),
zip,
'@OUTPUT0@',
'@INPUT@',
],
)
if get_option('android_debug_keystore') != ''
debug_apk = custom_target(
'mpd-debug.apk',
output: 'mpd-debug.apk',
input: unsigned_apk,
command: [
jarsigner,
'-keystore', get_option('android_debug_keystore'),
'-storepass', 'android',
'-signedjar', '@OUTPUT@',
'@INPUT@',
'androiddebugkey',
],
build_by_default: true
)
endif
if get_option('android_keystore') != '' and get_option('android_keyalias') != '' and get_option('android_keypass') != ''
unaligned_apk = custom_target(
'mpd-unaligned.apk',
output: 'mpd-unaligned.apk',
input: unsigned_apk,
command: [
jarsigner,
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
'-keystore', get_option('android_keystore'),
'-storepass', get_option('android_keypass'),
'-signedjar', '@OUTPUT@',
'@INPUT@',
get_option('android_keyalias'),
],
)
apk = custom_target(
'mpd.apk',
output: 'mpd.apk',
input: unaligned_apk,
command: [
android_zipalign,
'-f', '4',
'@INPUT@', '@OUTPUT@',
],
build_by_default: true
)
endif
...@@ -25,25 +25,32 @@ android_abis = { ...@@ -25,25 +25,32 @@ android_abis = {
'arch': 'arm-linux-androideabi', 'arch': 'arm-linux-androideabi',
'ndk_arch': 'arm', 'ndk_arch': 'arm',
'toolchain_arch': 'arm-linux-androideabi', 'toolchain_arch': 'arm-linux-androideabi',
'llvm_triple': 'armv7-none-linux-androideabi', 'llvm_triple': 'armv7-linux-androideabi',
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp', 'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp',
}, },
'arm64-v8a': { 'arm64-v8a': {
'android_api_level': '21',
'arch': 'aarch64-linux-android', 'arch': 'aarch64-linux-android',
'ndk_arch': 'arm64', 'ndk_arch': 'arm64',
'toolchain_arch': 'aarch64-linux-android', 'toolchain_arch': 'aarch64-linux-android',
'llvm_triple': 'aarch64-none-linux-android', 'llvm_triple': 'aarch64-linux-android',
'cflags': '', 'cflags': '-fpic',
}, },
'x86': { 'x86': {
'arch': 'i686-linux-android', 'arch': 'i686-linux-android',
'ndk_arch': 'x86', 'ndk_arch': 'x86',
'toolchain_arch': 'x86', 'toolchain_arch': 'x86',
'llvm_triple': 'i686-none-linux-android', 'llvm_triple': 'i686-linux-android',
'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32', 'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
},
'x86_64': {
'arch': 'x86_64-linux-android',
'ndk_arch': 'x86_64',
'toolchain_arch': 'x86_64',
'llvm_triple': 'x86_64-linux-android',
'cflags': '-fPIC -m64',
}, },
} }
...@@ -57,6 +64,7 @@ sys.path[0] = os.path.join(mpd_path, 'python') ...@@ -57,6 +64,7 @@ sys.path[0] = os.path.join(mpd_path, 'python')
# output directories # output directories
from build.dirs import lib_path, tarball_path, src_path from build.dirs import lib_path, tarball_path, src_path
from build.meson import configure as run_meson
arch_path = os.path.join(lib_path, arch) arch_path = os.path.join(lib_path, arch)
build_path = os.path.join(arch_path, 'build') build_path = os.path.join(arch_path, 'build')
...@@ -75,27 +83,20 @@ class AndroidNdkToolchain: ...@@ -75,27 +83,20 @@ class AndroidNdkToolchain:
ndk_arch = abi_info['ndk_arch'] ndk_arch = abi_info['ndk_arch']
android_api_level = '21' android_api_level = '21'
ndk_platform = 'android-' + android_api_level
# select the NDK compiler # select the NDK compiler
gcc_version = '4.9' gcc_version = '4.9'
ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
sysroot = os.path.join(ndk_path, 'sysroot')
target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
install_prefix = os.path.join(arch_path, 'root') install_prefix = os.path.join(arch_path, 'root')
self.arch = arch self.arch = arch
self.install_prefix = install_prefix self.install_prefix = install_prefix
self.sysroot = sysroot
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch) toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch) llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
llvm_triple = abi_info['llvm_triple'] llvm_triple = abi_info['llvm_triple'] + android_api_level
common_flags = '-Os -g' common_flags = '-Os -g'
common_flags += ' -fPIC'
common_flags += ' ' + abi_info['cflags'] common_flags += ' ' + abi_info['cflags']
toolchain_bin = os.path.join(toolchain_path, 'bin') toolchain_bin = os.path.join(toolchain_path, 'bin')
...@@ -106,6 +107,9 @@ class AndroidNdkToolchain: ...@@ -106,6 +107,9 @@ class AndroidNdkToolchain:
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections' common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
# required flags from https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md#additional-required-arguments
common_flags += ' -fno-addrsig'
self.ar = os.path.join(toolchain_bin, arch + '-ar') self.ar = os.path.join(toolchain_bin, arch + '-ar')
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib') self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
self.nm = os.path.join(toolchain_bin, arch + '-nm') self.nm = os.path.join(toolchain_bin, arch + '-nm')
...@@ -113,15 +117,11 @@ class AndroidNdkToolchain: ...@@ -113,15 +117,11 @@ class AndroidNdkToolchain:
self.cflags = common_flags self.cflags = common_flags
self.cxxflags = common_flags self.cxxflags = common_flags
self.cppflags = '--sysroot=' + sysroot + \ self.cppflags = ' -isystem ' + os.path.join(install_prefix, 'include')
' -isystem ' + os.path.join(install_prefix, 'include') + \ self.ldflags = ' -L' + os.path.join(install_prefix, 'lib') + \
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \ ' -Wl,--exclude-libs=ALL' + \
' -D__ANDROID_API__=' + android_api_level
self.ldflags = '--sysroot=' + sysroot + \
' -L' + os.path.join(install_prefix, 'lib') + \
' -L' + os.path.join(target_root, 'usr', 'lib') + \
' -B' + os.path.join(target_root, 'usr', 'lib') + \
' ' + common_flags ' ' + common_flags
self.ldflags = common_flags
self.libs = '' self.libs = ''
self.is_arm = ndk_arch == 'arm' self.is_arm = ndk_arch == 'arm'
...@@ -129,13 +129,16 @@ class AndroidNdkToolchain: ...@@ -129,13 +129,16 @@ class AndroidNdkToolchain:
self.is_aarch64 = ndk_arch == 'arm64' self.is_aarch64 = ndk_arch == 'arm64'
self.is_windows = False self.is_windows = False
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
libstdcxx_flags = '' libstdcxx_flags = ''
libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include') libstdcxx_cxxflags = ''
libstdcxx_ldflags = libstdcxx_flags + ' -L' + libcxx_libs_path libstdcxx_ldflags = ''
libstdcxx_libs = '-lc++_static -lc++abi' libstdcxx_libs = '-static-libstdc++'
if self.is_armv7:
# On 32 bit ARM, clang generates no ".eh_frame" section;
# instead, the LLVM unwinder library is used for unwinding
# the stack after a C++ exception was thrown
libstdcxx_libs += ' -lunwind'
if use_cxx: if use_cxx:
self.cxxflags += ' ' + libstdcxx_cxxflags self.cxxflags += ' ' + libstdcxx_cxxflags
...@@ -146,7 +149,15 @@ class AndroidNdkToolchain: ...@@ -146,7 +149,15 @@ class AndroidNdkToolchain:
# redirect pkg-config to use our root directory instead of the # redirect pkg-config to use our root directory instead of the
# default one on the build host # default one on the build host
self.env['PKG_CONFIG_LIBDIR'] = os.path.join(install_prefix, 'lib/pkgconfig') import shutil
bin_dir = os.path.join(install_prefix, 'bin')
try:
os.makedirs(bin_dir)
except:
pass
self.pkg_config = shutil.copy(os.path.join(mpd_path, 'build', 'pkg-config.sh'),
os.path.join(bin_dir, 'pkg-config'))
self.env['PKG_CONFIG'] = self.pkg_config
# a list of third-party libraries to be used by MPD on Android # a list of third-party libraries to be used by MPD on Android
from build.libs import * from build.libs import *
...@@ -157,6 +168,9 @@ thirdparty_libs = [ ...@@ -157,6 +168,9 @@ thirdparty_libs = [
opus, opus,
flac, flac,
libid3tag, libid3tag,
libmodplug,
wildmidi,
gme,
ffmpeg, ffmpeg,
curl, curl,
libexpat, libexpat,
...@@ -175,32 +189,13 @@ for x in thirdparty_libs: ...@@ -175,32 +189,13 @@ for x in thirdparty_libs:
toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path, toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
use_cxx=True) use_cxx=True)
configure = [ configure_args += [
os.path.join(mpd_path, 'configure'), '-Dandroid_sdk=' + sdk_path,
'CC=' + toolchain.cc, '-Dandroid_ndk=' + ndk_path,
'CXX=' + toolchain.cxx, '-Dandroid_abi=' + android_abi,
'CFLAGS=' + toolchain.cflags, '-Dandroid_strip=' + toolchain.strip,
'CXXFLAGS=' + toolchain.cxxflags, ]
'CPPFLAGS=' + toolchain.cppflags,
'LDFLAGS=' + toolchain.ldflags, from build.meson import configure as run_meson
'LIBS=' + toolchain.libs, run_meson(toolchain, mpd_path, '.', configure_args)
'AR=' + toolchain.ar, subprocess.check_call(['/usr/bin/ninja'], env=toolchain.env)
'RANLIB=' + toolchain.ranlib,
'STRIP=' + toolchain.strip,
'--host=' + toolchain.arch,
'--prefix=' + toolchain.install_prefix,
'--with-sysroot=' + toolchain.sysroot,
'--with-android-sdk=' + sdk_path,
'--enable-silent-rules',
'--disable-icu',
] + configure_args
from build.cmdline import concatenate_cmdline_variables
configure = concatenate_cmdline_variables(configure,
set(('CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS', 'LIBS')))
subprocess.check_call(configure, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], env=toolchain.env)
#!/bin/sh -e
S=`dirname "$0"`
AAPT=$1
BASE_JAR=$2
JAVA_PKG=$3
JAVA_PKG_PATH=$4
APK_FILE="$5"
D=`dirname "$APK_FILE"`
rm -rf "$D/res"
mkdir -p "$D/res/drawable" "$D/src"
cp "$D/icon.png" "$D/notification_icon.png" "$D/res/drawable/"
"$AAPT" package -f -m --auto-add-overlay \
--custom-package "$JAVA_PKG" \
-M "$S/AndroidManifest.xml" \
-S "$D/res" \
-S "$S/res" \
-J "$D/src" \
-I "$BASE_JAR" \
-F "$D/resources.apk"
cp "$D/src/$JAVA_PKG_PATH/R.java" "$D/"
android_package = 'org.musicpd'
android_package_path = join_paths(android_package.split('.'))
android_ndk = get_option('android_ndk')
android_sdk = get_option('android_sdk')
android_abi = get_option('android_abi')
android_sdk_build_tools_version = '27.0.0'
android_sdk_platform = 'android-23'
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
android_aidl = join_paths(android_build_tools_dir, 'aidl')
android_aapt = join_paths(android_build_tools_dir, 'aapt')
android_dx = join_paths(android_build_tools_dir, 'dx')
android_zipalign = join_paths(android_build_tools_dir, 'zipalign')
javac = find_program('javac')
jarsigner = find_program('jarsigner')
rsvg_convert = find_program('rsvg-convert')
convert = find_program('convert')
zip = find_program('zip')
common_cppflags += '-I' + join_paths(meson.current_build_dir(), 'include')
#
# AIDL
#
IMainCallback_java = custom_target(
'IMainCallback.java',
output: 'IMainCallback.java',
input: join_paths(meson.current_source_dir(), 'src', 'IMainCallback.aidl'),
command: [
join_paths(meson.current_source_dir(), 'run-aidl.sh'),
android_aidl,
'@INPUT@',
'@OUTPUT@',
join_paths(meson.current_build_dir(), 'src'),
android_package_path,
],
)
IMain_java = custom_target(
'IMain.java',
output: 'IMain.java',
input: join_paths(meson.current_source_dir(), 'src', 'IMain.aidl'),
depends: IMainCallback_java,
command: [
join_paths(meson.current_source_dir(), 'run-aidl.sh'),
android_aidl,
'@INPUT@',
'@OUTPUT@',
join_paths(meson.current_build_dir(), 'src'),
android_package_path,
],
)
#
# Resources
#
android_icon = custom_target(
'Android icon',
output: 'icon.png',
input: '../mpd.svg',
command: [
rsvg_convert, '--width=48', '--height=48', '@INPUT@', '-o', '@OUTPUT@',
],
)
android_notification_icon = custom_target(
'Android notification icon',
output: 'notification_icon.png',
input: android_icon,
command: [
convert, '@INPUT@', '-colorspace', 'Gray', '-gamma', '2.2', '@OUTPUT@',
],
)
resources_apk = custom_target(
'resources.apk',
output: ['resources.apk', 'R.java'],
input: [
'res/layout/custom_notification_gb.xml',
'res/layout/log_item.xml',
'res/layout/settings.xml',
'res/values/strings.xml',
android_icon,
android_notification_icon,
],
command: [
join_paths(meson.current_source_dir(), 'make-resources-apk.sh'),
android_aapt,
join_paths(android_sdk_platform_dir, 'android.jar'),
android_package,
android_package_path,
'@OUTPUT0@',
],
)
#
# Compile Java
#
classes_jar = custom_target(
'classes.jar',
output: 'classes.jar',
input: [
'src/Bridge.java',
'src/Loader.java',
'src/Main.java',
'src/Receiver.java',
'src/Settings.java',
IMain_java,
IMainCallback_java,
resources_apk[1],
],
command: [
join_paths(meson.current_source_dir(), 'run-javac.sh'),
javac,
join_paths(android_sdk_platform_dir, 'android.jar'),
android_package_path,
zip,
'@OUTPUT@',
'@INPUT@',
],
)
classes_dex = custom_target(
'classes.dex',
output: 'classes.dex',
input: classes_jar,
command: [
android_dx,
'--dex', '--output', '@OUTPUT@',
'@INPUT@',
],
)
#!/bin/sh -e
AIDL=$1
SRC=$2
DST=$3
GENSRC=$4
JAVA_PKG_PATH=$5
mkdir -p "$GENSRC/$JAVA_PKG_PATH"
cp "$SRC" "$GENSRC/$JAVA_PKG_PATH/"
"$AIDL" -I"$GENSRC" -o"$GENSRC" "$GENSRC/$JAVA_PKG_PATH/`basename $SRC`"
exec cp "$GENSRC/$JAVA_PKG_PATH/`basename $DST`" "$DST"
#!/bin/sh -e
JAVAC=$1
CLASSPATH=$2
JAVA_PKG_PATH=$3
ZIP=$4
JARFILE=`realpath "$5"`
shift 5
D=`dirname "$JARFILE"`
GENSRC="$D/src"
GENCLASS="$D/classes"
GENINCLUDE="$D/include"
mkdir -p "$GENSRC/$JAVA_PKG_PATH"
"$JAVAC" -source 1.6 -target 1.6 -Xlint:-options \
-cp "$CLASSPATH" \
-h "$GENINCLUDE" \
-d "$GENCLASS" \
"$@"
cd "$GENCLASS"
zip -q -r "$JARFILE" .
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -21,6 +21,7 @@ package org.musicpd; ...@@ -21,6 +21,7 @@ package org.musicpd;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
...@@ -35,6 +36,9 @@ import android.os.RemoteException; ...@@ -35,6 +36,9 @@ import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Main extends Service implements Runnable { public class Main extends Service implements Runnable {
private static final String TAG = "Main"; private static final String TAG = "Main";
private static final String REMOTE_ERROR = "MPD process was killed"; private static final String REMOTE_ERROR = "MPD process was killed";
...@@ -156,11 +160,36 @@ public class Main extends Service implements Runnable { ...@@ -156,11 +160,36 @@ public class Main extends Service implements Runnable {
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError); sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
} }
private Notification.Builder createNotificationBuilderWithChannel() {
final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null)
return null;
final String id = "org.musicpd";
final String name = "MPD service";
final int importance = 3; /* NotificationManager.IMPORTANCE_DEFAULT */
try {
Class<?> ncClass = Class.forName("android.app.NotificationChannel");
Constructor<?> ncCtor = ncClass.getConstructor(String.class, CharSequence.class, int.class);
Object nc = ncCtor.newInstance(id, name, importance);
Method nmCreateNotificationChannelMethod =
NotificationManager.class.getMethod("createNotificationChannel", ncClass);
nmCreateNotificationChannelMethod.invoke(notificationManager, nc);
Constructor nbCtor = Notification.Builder.class.getConstructor(Context.class, String.class);
return (Notification.Builder) nbCtor.newInstance(this, id);
} catch (Exception e)
{
Log.e(TAG, "error creating the NotificationChannel", e);
return null;
}
}
private void start() { private void start() {
if (mThread != null) if (mThread != null)
return; return;
mThread = new Thread(this);
mThread.start();
final Intent mainIntent = new Intent(this, Settings.class); final Intent mainIntent = new Intent(this, Settings.class);
mainIntent.setAction("android.intent.action.MAIN"); mainIntent.setAction("android.intent.action.MAIN");
...@@ -168,13 +197,25 @@ public class Main extends Service implements Runnable { ...@@ -168,13 +197,25 @@ public class Main extends Service implements Runnable {
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT); mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification notification = new Notification.Builder(this) Notification.Builder nBuilder;
.setContentTitle(getText(R.string.notification_title_mpd_running)) if (Build.VERSION.SDK_INT >= 26 /* Build.VERSION_CODES.O */)
{
nBuilder = createNotificationBuilderWithChannel();
if (nBuilder == null)
return;
}
else
nBuilder = new Notification.Builder(this);
Notification notification = nBuilder.setContentTitle(getText(R.string.notification_title_mpd_running))
.setContentText(getText(R.string.notification_text_mpd_running)) .setContentText(getText(R.string.notification_text_mpd_running))
.setSmallIcon(R.drawable.notification_icon) .setSmallIcon(R.drawable.notification_icon)
.setContentIntent(contentIntent) .setContentIntent(contentIntent)
.build(); .build();
mThread = new Thread(this);
mThread.start();
startForeground(R.string.notification_title_mpd_running, notification); startForeground(R.string.notification_title_mpd_running, notification);
startService(new Intent(this, Main.class)); startService(new Intent(this, Main.class));
} }
......
...@@ -21,10 +21,12 @@ package org.musicpd; ...@@ -21,10 +21,12 @@ package org.musicpd;
import java.util.LinkedList; import java.util.LinkedList;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
...@@ -103,12 +105,13 @@ public class Settings extends Activity { ...@@ -103,12 +105,13 @@ public class Settings extends Activity {
else else
mRunButton.setChecked(false); mRunButton.setChecked(false);
mFirstRun = true; mFirstRun = true;
mTextStatus.setText("");
break; break;
case MSG_STARTED: case MSG_STARTED:
Log.d(TAG, "onStarted"); Log.d(TAG, "onStarted");
mRunButton.setChecked(true); mRunButton.setChecked(true);
mFirstRun = true; mFirstRun = true;
mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX mTextStatus.setText("MPD service started");
break; break;
case MSG_LOG: case MSG_LOG:
if (mLogListArray.size() > MAX_LOGS) if (mLogListArray.size() > MAX_LOGS)
...@@ -178,6 +181,14 @@ public class Settings extends Activity { ...@@ -178,6 +181,14 @@ public class Settings extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
/* TODO: this sure is the wrong place to request
permissions - it will cause MPD to quit
immediately; we should request permissions when we
need them, but implementing that is complicated, so
for now, we do it here to give users a quick
solution for the problem */
requestAllPermissions();
setContentView(R.layout.settings); setContentView(R.layout.settings);
mRunButton = (ToggleButton) findViewById(R.id.run); mRunButton = (ToggleButton) findViewById(R.id.run);
mRunButton.setOnCheckedChangeListener(mOnRunChangeListener); mRunButton.setOnCheckedChangeListener(mOnRunChangeListener);
...@@ -203,6 +214,31 @@ public class Settings extends Activity { ...@@ -203,6 +214,31 @@ public class Settings extends Activity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }
private void checkRequestPermission(String permission) {
if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED)
return;
try {
this.requestPermissions(new String[]{permission}, 0);
} catch (Exception e) {
Log.e(TAG, "requestPermissions(" + permission + ") failed",
e);
}
}
private void requestAllPermissions() {
if (android.os.Build.VERSION.SDK_INT < 23)
/* we don't need to request permissions on
this old Android version */
return;
/* starting with Android 6.0, we need to explicitly
request all permissions before using them;
mentioning them in the manifest is not enough */
checkRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
}
private void connectClient() { private void connectClient() {
mClient = new Main.Client(this, new Main.Client.Callback() { mClient = new Main.Client(this, new Main.Client.Callback() {
......
#!/bin/sh
set -e
rm -rf config.cache build
mkdir build
aclocal -I m4 $ACLOCAL_FLAGS
autoheader
automake --add-missing $AUTOMAKE_FLAGS
autoconf
#!/bin/sh -e
# This is a wrapper for pkg-config which helps with cross-compiling;
# it sets up environment variables to pkg-config searches for
# libraries in the sysroot where a copy of this script is located.
BIN=`dirname $0`
ROOT=`dirname "$BIN"`
export PKG_CONFIG_DIR=
export PKG_CONFIG_LIBDIR="${ROOT}/lib/pkgconfig:${ROOT}/share/pkgconfig"
exec /usr/bin/pkg-config "$@"
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Music Player Daemon'
copyright = '2003-2018 The Music Player Daemon Project'
author = 'Max Kellermann'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.21.24'
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#
# today = ''
#
# Else, today_fmt is used as the format for a strftime call.
#
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'classic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
#
# html_title = ''
# A shorter title for the navigation bar. Default is the same as html_title.
#
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#
# html_logo = None
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#
# html_extra_path = []
# If not None, a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
# The empty string is equivalent to '%b %d, %Y'.
#
# html_last_updated_fmt = None
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#
# html_additional_pages = {}
# If false, no module index is generated.
#
# html_domain_indices = True
# If false, no index is generated.
#
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
#
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# 'ja' uses this config value.
# 'zh' user can custom change `jieba` dictionary path.
#
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#
# html_search_scorer = 'scorer.js'
Developer's Manual
##################
Introduction
************
This is a guide for those who wish to hack on the MPD source code. MPD is an open project, and we are always happy about contributions. So far, more than 150 people have contributed patches. This document is work in progress. Most of it may be incomplete yet. Please help!
Code Style
**********
* indent with tabs (width 8)
* don't write CPP when you can write C++: use inline functions and constexpr instead of macros
* comment your code, document your APIs
* the code should be C++14 compliant, and must compile with :program:`GCC` 6.0 and :program:`clang` 3.4
* report error conditions with C++ exceptions, preferable derived from :envvar:`std::runtime_error`
* all code must be exception-safe
* classes and functions names use CamelCase; variables are lower-case with words separated by underscore
Some example code:
.. code-block:: c
Foo(const char *abc, int xyz)
{
if (abc == nullptr) {
LogWarning("Foo happened!");
return -1;
}
return xyz;
}
Hacking The Source
******************
MPD sources are managed in a git repository on
`Github <https://github.com/MusicPlayerDaemon/>`_.
Always write your code against the latest git:
.. code-block:: none
git clone git://github.com/MusicPlayerDaemon/MPD
If you already have a clone, update it:
.. code-block:: none
git pull --rebase git://github.com/MusicPlayerDaemon/MPD master
You can do without :code:`--rebase`, but we recommend that you rebase
your repository on the "master" repository all the time.
Configure with the option :code:`--werror`. Enable as many plugins as
possible, to be sure that you don't break any disabled code.
Don't mix several changes in one single patch. Create a separate patch for every change. Tools like :program:`stgit` help you with that. This way, we can review your patches more easily, and we can pick the patches we like most first.
Basic stgit usage
=================
stgit allows you to create a set of patches and refine all of them: you can go back to any patch at any time, and re-edit it (both the code and the commit message). You can reorder patches and insert new patches at any position. It encourages creating separate patches for tiny changes.
stgit needs to be initialized on a git repository:
.. code-block:: sh
stg init
Before you edit the code, create a patch:
.. code-block:: sh
stg new my-patch-name
stgit now asks you for the commit message.
Now edit the code. Once you're finished, you have to "refresh" the patch, i.e. your edits are incorporated into the patch you have created:
.. code-block:: sh
stg refresh
You may now continue editing the same patch, and refresh it as often as you like. Create more patches, edit and refresh them.
To view the list of patches, type stg series. To go back to a specific patch, type stg goto my-patch-name; now you can re-edit it (don't forget stg refresh when you're finished with that patch).
When the whole patch series is finished, convert stgit patches to git commits:
.. code-block:: sh
stg commit
Submitting Patches
******************
Submit pull requests on GitHub:
https://github.com/MusicPlayerDaemon/MPD/pulls
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>The Music Player Daemon - Developer's Manual</title>
<chapter id="introduction">
<title>Introduction</title>
<para>
This is a guide for those who wish to hack on the MPD source
code. MPD is an open project, and we are always happy about
contributions. So far, more than 150 people have contributed
patches.
</para>
<para>
This document is work in progress. Most of it may be incomplete
yet. Please help!
</para>
</chapter>
<chapter id="code_style">
<title>Code Style</title>
<itemizedlist>
<listitem>
<para>
indent with tabs (width 8)
</para>
</listitem>
<listitem>
<para>
don't write CPP when you can write C++: use inline
functions and constexpr instead of macros
</para>
</listitem>
<listitem>
<para>
comment your code, document your APIs
</para>
</listitem>
<listitem>
<para>
the code should be C++14 compliant, and must compile with
<application>GCC</application> 4.9 and
<application>clang</application> 3.4
</para>
</listitem>
<listitem>
<para>
report error conditions with C++ exceptions, preferable
derived from <varname>std::runtime_error</varname>
</para>
</listitem>
<listitem>
<para>
all code must be exception-safe
</para>
</listitem>
<listitem>
<para>
classes and functions names use CamelCase; variables are
lower-case with words separated by underscore
</para>
</listitem>
<listitem>
<para>
Some example code:
</para>
<programlisting lang="C">static inline int
Foo(const char *abc, int xyz)
{
if (abc == nullptr) {
LogWarning("Foo happened!");
return -1;
}
return xyz;
}
</programlisting>
</listitem>
</itemizedlist>
</chapter>
<chapter id="hacking">
<title>Hacking The Source</title>
<para>
MPD sources are managed in a git repository on <ulink
url="https://github.com/MusicPlayerDaemon/">GitHub</ulink>.
</para>
<para>
Always write your code against the latest git:
</para>
<programlisting>git clone git://github.com/MusicPlayerDaemon/MPD</programlisting>
<para>
If you already have a clone, update it:
</para>
<programlisting>git pull --rebase git://github.com/MusicPlayerDaemon/MPD master</programlisting>
<para>
You can do without "--rebase", but we recommend that you rebase
your repository on the "master" repository all the time.
</para>
<para>
Configure with the options <option>--enable-debug
--enable-werror</option>. Enable as many plugins as possible,
to be sure that you don't break any disabled code.
</para>
<para>
Don't mix several changes in one single patch. Create a
separate patch for every change. Tools like
<application>stgit</application> help you with that. This way,
we can review your patches more easily, and we can pick the
patches we like most first.
</para>
<section>
<title> Basic stgit usage</title>
<para>
stgit allows you to create a set of patches and refine all of
them: you can go back to any patch at any time, and re-edit it
(both the code and the commit message). You can reorder
patches and insert new patches at any position. It encourages
creating separate patches for tiny changes.
</para>
<para>
stgit needs to be initialized on a git repository: stg init
</para>
<para>
Before you edit the code, create a patch: stg new
my-patch-name (stgit now asks you for the commit message).
</para>
<para>
Now edit the code. Once you're finished, you have to "refresh"
the patch, i.e. your edits are incorporated into the patch you
have created: stg refresh
</para>
<para>
You may now continue editing the same patch, and refresh it as
often as you like. Create more patches, edit and refresh them.
</para>
<para>
To view the list of patches, type stg series. To go back to a
specific patch, type stg goto my-patch-name; now you can
re-edit it (don't forget stg refresh when you're finished with
that patch).
</para>
<para>
When the whole patch series is finished, convert stgit patches
to git commits: stg commit
</para>
</section>
</chapter>
<chapter id="submitting_patches">
<title>Submitting Patches</title>
<para>
Send your patches to the mailing list:
<email>mpd-devel@musicpd.org</email> (<ulink
url="http://mailman.blarg.de/listinfo/mpd-devel">subscribe
here</ulink>)
</para>
<para>
<command>git pull</command> requests are preferred.
</para>
</chapter>
<chapter id="tools">
<title>Development Tools</title>
<section>
<title>Clang Static Analyzer</title>
<para>
The <ulink url="http://clang-analyzer.llvm.org/">clang static
analyzer</ulink> is a tool that helps find bugs. To run it on
the MPD code base, install LLVM and clang. Configure MPD to
use clang:
</para>
<programlisting>./configure --enable-debug CXX=clang++ CC=clang ...</programlisting>
<para>
It is recommended to use <option>--enable-debug</option>,
because the analyzer takes advantage of
<function>assert()</function> calls, which are only enabled in
the debug build.
</para>
<para>
Now run the analyzer:
</para>
<programlisting>scan-build --use-c++=clang++ --use-cc=clang make</programlisting>
<para>
The options <option>--use-c++</option> and
<option>--use-cc</option> are necessary because it invokes
<command>cc</command> for actually compiling the sources by
default. That breaks, because MPD requires a C99 compiler.
</para>
</section>
</chapter>
</book>
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE itemizedlist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<itemizedlist>
<listitem>
<para>
<varname>artist</varname>: the artist name. Its meaning is not
well-defined; see <varname>composer</varname> and
<varname>performer</varname> for more specific tags.
</para>
</listitem>
<listitem>
<para>
<varname>artistsort</varname>: same as
<varname>artist</varname>, but for sorting. This usually omits
prefixes such as "The".
</para>
</listitem>
<listitem>
<para>
<varname>album</varname>: the album name.
</para>
</listitem>
<listitem>
<para>
<varname>albumsort</varname>: same as <varname>album</varname>,
but for sorting.
</para>
</listitem>
<listitem>
<para>
<varname>albumartist</varname>: on multi-artist albums, this is
the artist name which shall be used for the whole album. The
exact meaning of this tag is not well-defined.
</para>
</listitem>
<listitem>
<para>
<varname>albumartistsort</varname>: same as
<varname>albumartist</varname>, but for sorting.
</para>
</listitem>
<listitem>
<para>
<varname>title</varname>: the song title.
</para>
</listitem>
<listitem>
<para>
<varname>track</varname>: the decimal track number within the
album.
</para>
</listitem>
<listitem>
<para>
<varname>name</varname>: a name for this song. This is not the
song title. The exact meaning of this tag is not well-defined.
It is often used by badly configured internet radio stations
with broken tags to squeeze both the artist name and the song
title in one tag.
</para>
</listitem>
<listitem>
<para>
<varname>genre</varname>: the music genre.
</para>
</listitem>
<listitem>
<para>
<varname>date</varname>: the song's release date. This is
usually a 4-digit year.
</para>
</listitem>
<listitem>
<para>
<varname>composer</varname>: the artist who composed the song.
</para>
</listitem>
<listitem>
<para>
<varname>performer</varname>: the artist who performed the song.
</para>
</listitem>
<listitem>
<para>
<varname>comment</varname>: a human-readable comment about this
song. The exact meaning of this tag is not well-defined.
</para>
</listitem>
<listitem>
<para>
<varname>disc</varname>: the decimal disc number in a multi-disc
album.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_artistid</varname>: the artist id in the
<ulink
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_albumid</varname>: the album id in the
<ulink
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_albumartistid</varname>: the album artist
id in the <ulink
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_trackid</varname>: the track id in the
<ulink
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_releasetrackid</varname>: the release track
id in the <ulink
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
</itemizedlist>
Music Player Daemon
===================
.. toctree::
:maxdepth: 2
:caption: Contents:
user
plugins
developer
protocol
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
install_man(['mpd.1', 'mpd.conf.5'])
sphinx = find_program('sphinx-build')
sphinx_output = custom_target(
'HTML documentation',
output: 'html',
input: [
'index.rst', 'user.rst', 'developer.rst',
'plugins.rst',
'protocol.rst',
'conf.py',
],
command: [sphinx, '-q', '-b', 'html', '-d', '@OUTDIR@/doctrees', meson.current_source_dir(), '@OUTPUT@'],
build_by_default: true,
install: true,
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
)
custom_target(
'upload',
input: sphinx_output,
output: 'upload',
build_always_stale: true,
command: [
'rsync', '-vpruz', '--delete', meson.current_build_dir() + '/',
'www.musicpd.org:/var/www/mpd/doc/',
'--chmod=Dug+rwx,Do+rx,Fug+rw,Fo+r',
'--include=html', '--include=html/**',
'--exclude=*',
],
)
...@@ -72,19 +72,6 @@ never run as root, and you may use this option to make MPD change its ...@@ -72,19 +72,6 @@ never run as root, and you may use this option to make MPD change its
user id after initialization. Do not use this option if you start MPD user id after initialization. Do not use this option if you start MPD
as an unprivileged user. as an unprivileged user.
.TP .TP
.B bind_to_address <ip address or hostname or any>
This specifies which address mpd binds to and listens on. Multiple
bind_to_address parameters may be specified. The default is "any", which binds
to all available addresses.
You can set a port that is different from the global port setting,
e.g. "localhost:6602". IPv6 addresses must be enclosed in square
brackets if you want to configure a port, e.g. "[::1]:6602".
To bind to a Unix domain socket, specify an absolute path or a path starting
with a tilde (~). For a system-wide MPD, we suggest the path
"\fB/var/run/mpd/socket\fP".
.TP
.B port <port> .B port <port>
This specifies the port that mpd listens on. The default is 6600. This specifies the port that mpd listens on. The default is 6600.
.TP .TP
...@@ -112,23 +99,8 @@ information will be published with Zeroconf. The default is yes. ...@@ -112,23 +99,8 @@ information will be published with Zeroconf. The default is yes.
.B zeroconf_name <name> .B zeroconf_name <name>
If Zeroconf is enabled, this is the service name to publish. This name should If Zeroconf is enabled, this is the service name to publish. This name should
be unique to your local network, but name collisions will be properly dealt be unique to your local network, but name collisions will be properly dealt
with. The default is "Music Player". with. The default is "Music Player @ %h", where %h will be replaced with the
.TP hostname of the machine running MPD.
.B password <password@permissions>
This specifies a password for access to mpd. The format is
"password@permissions" where permissions is a comma delimited list composed
of "read", "add", "control", and/or "admin". "read" allows for reading of the
database, displaying the current playlist, and current status of mpd. "add"
allows for adding songs and loading playlists. "control" allows for all other
player and playlist manipulations. "admin" allows the db to be updated and for
the client to kill mpd. An example value is "somePassword@read,add". Multiple
password parameters may be specified.
.TP
.B default_permissions <permissions>
This specifies the permissions of a client that has not been authenticated
using a password. The format of permissions is specified in the description of
the "password" config parameter. If no passwords are specified, the default is
"read,add,control,admin", otherwise it is "" (no permissions).
.TP .TP
.B audio_output .B audio_output
See \fBDESCRIPTION\fP and the various \fBAUDIO OUTPUT PARAMETERS\fP sections See \fBDESCRIPTION\fP and the various \fBAUDIO OUTPUT PARAMETERS\fP sections
...@@ -156,14 +128,6 @@ This specifies the character set used for the filesystem. A list of supported ...@@ -156,14 +128,6 @@ This specifies the character set used for the filesystem. A list of supported
character sets can be obtained by running "iconv \-l". The default is character sets can be obtained by running "iconv \-l". The default is
determined from the locale when the db was originally created. determined from the locale when the db was originally created.
.TP .TP
.B gapless_mp3_playback <yes or no>
This specifies whether to support gapless playback of MP3s which have the
necessary headers. Useful if your MP3s have headers with incorrect
information. If you have such MP3s, it is highly recommended that you fix them
using vbrfix (available from <http://www.willwap.co.uk/Programs/vbrfix.php>)
instead of disabling gapless MP3 playback. The default is to support gapless
MP3 playback.
.TP
.B save_absolute_paths_in_playlists <yes or no> .B save_absolute_paths_in_playlists <yes or no>
This specifies whether relative or absolute paths for song filenames are used This specifies whether relative or absolute paths for song filenames are used
when saving playlists. The default is "no". when saving playlists. The default is "no".
...@@ -176,7 +140,6 @@ of database. ...@@ -176,7 +140,6 @@ of database.
.B auto_update_depth <N> .B auto_update_depth <N>
Limit the depth of the directories being watched, 0 means only watch Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default. the music directory itself. There is no limit by default.
.TP
.SH REQUIRED AUDIO OUTPUT PARAMETERS .SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP .TP
.B type <type> .B type <type>
...@@ -200,57 +163,12 @@ Specifies how replay gain is applied. The default is "software", ...@@ -200,57 +163,12 @@ Specifies how replay gain is applied. The default is "software",
which uses an internal software volume control. "mixer" uses the which uses an internal software volume control. "mixer" uses the
configured (hardware) mixer control. "none" disables replay gain on configured (hardware) mixer control. "none" disables replay gain on
this audio output. this audio output.
.SH OPTIONAL ALSA OUTPUT PARAMETERS
.TP
.B device <dev>
This specifies the device to use for audio output. The default is "default".
.TP .TP
.B mixer_type <hardware, software or none> .B mixer_type <hardware, software or none>
Specifies which mixer should be used for this audio output: the Specifies which mixer should be used for this audio output: the
hardware mixer (available for ALSA, OSS and PulseAudio), the software hardware mixer (available for ALSA, OSS and PulseAudio), the software
mixer or no mixer ("none"). By default, the hardware mixer is used mixer or no mixer ("none"). By default, the hardware mixer is used
for devices which support it, and none for the others. for devices which support it, and none for the others.
.TP
.B mixer_device <mixer dev>
This specifies which mixer to use. The default is "default". To use
the second sound card in a system, use "hw:1".
.TP
.B mixer_control <mixer ctrl>
This specifies which mixer control to use (sometimes referred to as
the "device"). The default is "PCM". Use "amixer scontrols" to see
the list of possible controls.
.TP
.B mixer_index <mixer index>
A number identifying the index of the named mixer control. This is
probably only useful if your alsa device has more than one
identically\-named mixer control. The default is "0". Use "amixer
scontrols" to see the list of controls with their indexes.
.TP
.B auto_resample <yes or no>
Setting this to "no" disables ALSA's software resampling, if the
hardware does not support a specific sample rate. This lets MPD do
the resampling. "yes" is the default and allows ALSA to resample.
.TP
.B auto_channels <yes or no>
Setting this to "no" disables ALSA's channel conversion, if the
hardware does not support a specific number of channels. Default: "yes".
.TP
.B auto_format <yes or no>
Setting this to "no" disables ALSA's sample format conversion, if the
hardware does not support a specific sample format. Default: "yes".
.TP
.B buffer_time <time in microseconds>
This sets the length of the hardware sample buffer in microseconds. Increasing
it may help to reduce or eliminate skipping on certain setups. Most users do
not need to change this. The default is 500000 microseconds (0.5 seconds).
.TP
.B period_time <time in microseconds>
This sets the time between hardware sample transfers in microseconds.
Increasing this can reduce CPU usage while lowering it can reduce underrun
errors on bandwidth-limited devices. Some users have reported good results
with this set to 50000, but not all devices support values this high. Most
users do not need to change this. The default is 256000000 / sample_rate(kHz),
or 5804 microseconds for CD-quality audio.
.SH FILES .SH FILES
.TP .TP
.BI ~/.mpdconf .BI ~/.mpdconf
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
# settings. # settings.
# #
# The special value "syslog" makes MPD use the local syslog daemon. This # The special value "syslog" makes MPD use the local syslog daemon. This
# setting defaults to logging to syslog, otherwise logging is disabled. # setting defaults to logging to syslog.
# #
#log_file "~/.mpd/log" #log_file "~/.mpd/log"
# #
...@@ -96,14 +96,6 @@ ...@@ -96,14 +96,6 @@
# #
#log_level "default" #log_level "default"
# #
# If you have a problem with your MP3s ending abruptly it is recommended that
# you set this argument to "no" to attempt to fix the problem. If this solves
# the problem, it is highly recommended to fix the MP3 files with vbrfix
# (available from <http://www.willwap.co.uk/Programs/vbrfix.php>), at which
# point gapless MP3 playback can be enabled.
#
#gapless_mp3_playback "yes"
#
# Setting "restore_paused" to "yes" puts MPD into pause mode instead # Setting "restore_paused" to "yes" puts MPD into pause mode instead
# of starting playback after startup. # of starting playback after startup.
# #
...@@ -119,6 +111,10 @@ ...@@ -119,6 +111,10 @@
# found in the user manual. # found in the user manual.
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc" #metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
# #
# This example just enables the "comment" tag without disabling all
# the other supported tags:
#metadata_to_use "+comment"
#
# This setting enables automatic update of MPD's database when files in # This setting enables automatic update of MPD's database when files in
# music_directory are changed. # music_directory are changed.
# #
...@@ -155,9 +151,9 @@ ...@@ -155,9 +151,9 @@
#zeroconf_enabled "yes" #zeroconf_enabled "yes"
# #
# The argument to this setting will be the Zeroconf / Avahi unique name for # The argument to this setting will be the Zeroconf / Avahi unique name for
# this MPD server on the network. # this MPD server on the network. %h will be replaced with the hostname.
# #
#zeroconf_name "Music Player" #zeroconf_name "Music Player @ %h"
# #
############################################################################### ###############################################################################
...@@ -310,7 +306,7 @@ input { ...@@ -310,7 +306,7 @@ input {
#audio_output { #audio_output {
# type "sndio" # type "sndio"
# name "sndio output" # name "sndio output"
# mixer_type "software" # mixer_type "hardware"
#} #}
# #
# An example of an OS X output: # An example of an OS X output:
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This source diff could not be displayed because it is too large. You can view the blob instead.
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# For every FLAG1, FLAG2 it is checked whether the compiler works with the
# flag. If it does, the flag is added FLAGS-VARIABLE
#
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
# CFLAGS) is used. During the check the flag is always added to the
# current language's flags.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: This macro depends on the AX_APPEND_FLAG and
# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
# AX_APPEND_LINK_FLAGS.
#
# LICENSE
#
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 5
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
for flag in $1; do
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4])
done
])dnl AX_APPEND_COMPILE_FLAGS
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
#
# DESCRIPTION
#
# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
# added in between.
#
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
# FLAG.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AC_DEFUN([AX_APPEND_FLAG],
[dnl
AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
AS_VAR_SET_IF(FLAGS,[
AS_CASE([" AS_VAR_GET(FLAGS) "],
[*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
[
AS_VAR_APPEND(FLAGS,[" $1"])
AC_RUN_LOG([: FLAGS="$FLAGS"])
])
],
[
AS_VAR_SET(FLAGS,[$1])
AC_RUN_LOG([: FLAGS="$FLAGS"])
])
AS_VAR_POPDEF([FLAGS])dnl
])dnl AX_APPEND_FLAG
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# For every FLAG1, FLAG2 it is checked whether the linker works with the
# flag. If it does, the flag is added FLAGS-VARIABLE
#
# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
# used. During the check the flag is always added to the linker's flags.
#
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
# when the check is done. The check is thus made with the flags: "LDFLAGS
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
# issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
#
# LICENSE
#
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 5
AC_DEFUN([AX_APPEND_LINK_FLAGS],
[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
for flag in $1; do
AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4])
done
])dnl AX_APPEND_LINK_FLAGS
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the current language's compiler
# or gives an error. (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 4
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the linker or gives an error.
# (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
# when the check is done. The check is thus made with the flags: "LDFLAGS
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
# issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_LINK_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 4
AC_DEFUN([AX_CHECK_LINK_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
ax_check_save_flags=$LDFLAGS
LDFLAGS="$LDFLAGS $4 $1"
AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
LDFLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_LINK_FLAGS
# ============================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_14.html
# ============================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX_14([ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the C++14
# standard; if necessary, add switches to CXX and CXXCPP to enable
# support.
#
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
# macro with the version set to C++14. The two optional arguments are
# forwarded literally as the second and third argument respectively.
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
# more information. If you want to use this macro, you also need to
# download the ax_cxx_compile_stdcxx.m4 file.
#
# LICENSE
#
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 4
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
AC_DEFUN([AX_CXX_COMPILE_STDCXX_14], [AX_CXX_COMPILE_STDCXX([14], [$1], [$2])])
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_require_defined.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_REQUIRE_DEFINED(MACRO)
#
# DESCRIPTION
#
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
# been defined and thus are available for use. This avoids random issues
# where a macro isn't expanded. Instead the configure script emits a
# non-fatal:
#
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
#
# It's like AC_REQUIRE except it doesn't expand the required macro.
#
# Here's an example:
#
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
#
# LICENSE
#
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 1
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
])dnl AX_REQUIRE_DEFINED
dnl
dnl Usage:
dnl AC_CHECK_LIBWRAP([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl
AC_DEFUN([AC_CHECK_LIBWRAP],[
AC_CHECK_HEADERS([tcpd.h],
AC_CHECK_LIB([wrap], [request_init],
[LIBWRAP_CFLAGS=""
LIBWRAP_LDFLAGS="-lwrap"
$1],
$2),
$2)
])
dnl Parameters: varname1, description
AC_DEFUN([MPD_AUTO_ENABLED], [
if test x$[]enable_$1 = xauto; then
AC_MSG_NOTICE([auto-detected $2])
enable_$1=yes
fi
])
dnl Parameters: varname1, description, errmsg
AC_DEFUN([MPD_AUTO_DISABLED], [
if test x$[]enable_$1 = xauto; then
AC_MSG_WARN([$3 -- disabling $2])
enable_$1=no
elif test x$[]enable_$1 = xyes; then
AC_MSG_ERROR([$2: $3])
fi
])
dnl Check whether a prerequisite for a feature was found. This is
dnl very similar to MPD_AUTO_RESULT, but does not finalize the
dnl detection; it assumes that more checks will follow.
AC_DEFUN([MPD_AUTO_PRE], [
if test x$[]enable_$1 != xno && test x$[]found_$1 = xno; then
MPD_AUTO_DISABLED([$1], [$2], [$3])
fi
])
dnl Evaluate a check's result. Abort if the feature was requested
dnl explicitly but is unavailable.
dnl
dnl Parameters: varname1, description, errmsg
AC_DEFUN([MPD_AUTO_RESULT], [
if test x$[]enable_$1 = xno; then
found_$1=no
fi
if test x$[]found_$1 = xyes; then
MPD_AUTO_ENABLED([$1], [$2])
else
MPD_AUTO_DISABLED([$1], [$2], [$3])
fi
])
dnl Invoke a check if its configuration is "yes" or "auto" and call
dnl MPD_AUTO_RESULT.
dnl
dnl Parameters: varname1, description, errmsg, check
AC_DEFUN([MPD_AUTO], [
if test x$[]enable_$1 != xno; then
$4
fi
MPD_AUTO_RESULT([$1], [$2], [$3])
])
dnl Wrapper for MPD_AUTO and PKG_CHECK_MODULES.
dnl
dnl Parameters: varname1, varname2, pkgname, description, errmsg
AC_DEFUN([MPD_AUTO_PKG], [
MPD_AUTO([$1], [$4], [$5],
[PKG_CHECK_MODULES([$2], [$3],
[found_$1=yes],
[found_$1=no])])
])
dnl Check with pkg-config first, fall back to AC_CHECK_LIB.
dnl
dnl Parameters: varname1, varname2, pkgname, libname, symname, libs, cflags, description, errmsg
AC_DEFUN([MPD_AUTO_PKG_LIB], [
MPD_AUTO([$1], [$8], [$9],
[PKG_CHECK_MODULES([$2], [$3],
[found_$1=yes],
AC_CHECK_LIB($4, $5,
[found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'],
[found_$1=no],
[$6]))])
])
dnl Wrapper for AC_CHECK_LIB.
dnl
dnl Parameters: varname1, varname2, libname, symname, libs, cflags, description, errmsg
AC_DEFUN([MPD_AUTO_LIB], [
AC_SUBST([$2_LIBS], [])
AC_SUBST([$2_CFLAGS], [])
MPD_AUTO([$1], [$7], [$8],
[AC_CHECK_LIB($3, $4,
[found_$1=yes $2_LIBS='$5' $2_CFLAGS='$6'],
[found_$1=no],
[$5])])
])
dnl Wrapper for AC_CHECK_HEADER.
dnl
dnl Parameters: varname1, varname2, header, libs, cflags, description, errmsg
AC_DEFUN([MPD_AUTO_HEADER], [
AC_SUBST([$2_LIBS], [])
AC_SUBST([$2_CFLAGS], [])
MPD_AUTO([$1], [$6], [$7],
[AC_CHECK_HEADER([$3],
[found_$1=yes $2_LIBS='$4' $2_CFLAGS='$5'],
[found_$1=no])])
])
dnl Convert the given string into a string for the "default value" in
dnl the help text. If the string is a literal, then it is returned
dnl as-is; if it contains a variable reference, just "auto" is
dnl emitted.
dnl
dnl Parameters: varname1
AC_DEFUN([MPD_FORMAT_DEFAULT],
[ifelse([$1], [], [auto],
index([$1], [$]), [-1], [$1],
[auto])])
dnl Wrapper for AC_ARG_ENABLE, AC_DEFINE and AM_CONDITIONAL
dnl
dnl Parameters: varname1, varname2, description, default, check
AC_DEFUN([MPD_ARG_ENABLE], [
AC_ARG_ENABLE(translit([$1], [_], [-]),
AS_HELP_STRING([--enable-]translit([$1], [_], [-]),
[enable $3 (default: ]MPD_FORMAT_DEFAULT([$4])[)]),,
[enable_$1=]ifelse([$4], [], [auto], [$4]))
$5
MPD_DEFINE_CONDITIONAL(enable_$1, ENABLE_$2, [$3])
])
dnl Wrapper for MPD_ARG_ENABLE and MPD_AUTO
dnl
dnl Parameters: varname1, varname2, description, errmsg, default, check
AC_DEFUN([MPD_ENABLE_AUTO], [
MPD_ARG_ENABLE([$1], [$2], [$3], [$5], [
MPD_AUTO([$1], [$3], [$4], [$6])
])
])
dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_PKG
dnl
dnl Parameters: varname1, varname2, pkg, description, errmsg, default, pre
AC_DEFUN([MPD_ENABLE_AUTO_PKG], [
MPD_ARG_ENABLE([$1], [$2], [$4], [$6], [
$7
MPD_AUTO_PKG($1, $2, $3, $4, $5)
])
])
dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_PKG_LIB
dnl
dnl Parameters: varname1, varname2, pkg, libname, symname, libs, cflags, description, errmsg, default, pre
AC_DEFUN([MPD_ENABLE_AUTO_PKG_LIB], [
MPD_ARG_ENABLE([$1], [$2], [$8], [$10], [
$11
MPD_AUTO_PKG_LIB($1, $2, $3, $4, $5, $6, $7, $8, $9)
])
])
dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_LIB
dnl
dnl Parameters: varname1, varname2, libname, symname, libs, cflags, description, errmsg, default, pre
AC_DEFUN([MPD_ENABLE_AUTO_LIB], [
MPD_ARG_ENABLE([$1], [$2], [$7], [$9], [
$10
MPD_AUTO_LIB($1, $2, $3, $4, $5, $6, $7, $8)
])
])
dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_HEADER
dnl
dnl Parameters: varname1, varname2, header, libs, cflags, description, errmsg, default, pre
AC_DEFUN([MPD_ENABLE_AUTO_HEADER], [
MPD_ARG_ENABLE([$1], [$2], [$6], [$8], [
$9
MPD_AUTO_HEADER($1, $2, $3, $4, $5, $6, $7)
])
])
dnl Wrapper for MPD_ENABLE_AUTO_PKG and MPD_DEPENDS
dnl
dnl Parameters: varname1, varname2, pkg, description, errmsg, default, dep_variable, dep_errmsg
AC_DEFUN([MPD_ENABLE_AUTO_PKG_DEPENDS], [
MPD_ENABLE_AUTO_PKG([$1], [$2], [$3], [$4], [$5], [$6],
[MPD_DEPENDS([enable_$1], [$7], [$8])])
])
dnl Wrapper for AC_DEFINE and AM_CONDITIONAL
dnl
dnl Parameters: varname1, varname2, description
AC_DEFUN([MPD_DEFINE_CONDITIONAL], [dnl
AM_CONDITIONAL($2, test x$[]$1 = xyes)
if test x$[]$1 = xyes; then
AC_DEFINE($2, 1, [Define to enable $3])
fi])
AC_DEFUN([MPD_DEPENDS], [
if test x$$2 = xno; then
if test x$$1 = xauto; then
$1=no
elif test x$$1 = xyes; then
AC_MSG_ERROR([$3])
fi
fi
])
dnl MPD_OPTIONAL_FUNC(name, func, macro)
dnl
dnl Allow the user to enable or disable the use of a function. If the
dnl option is not specified, the function is auto-detected.
AC_DEFUN([MPD_OPTIONAL_FUNC], [
AC_ARG_ENABLE([$1],
AS_HELP_STRING([--enable-$1],
[use the function "$1" (default: auto)]),
[test x$[]enable_$1 = xyes && AC_DEFINE([$3], 1, [Define to use $1])],
[AC_CHECK_FUNC([$2],
[AC_DEFINE([$3], 1, [Define to use $1])],)])
])
dnl MPD_OPTIONAL_FUNC_NODEF(name, func)
dnl
dnl Allow the user to enable or disable the use of a function.
dnl Works similar to MPD_OPTIONAL_FUNC, however MPD_OPTIONAL_FUNC_NODEF
dnl does not invoke AC_DEFINE when function is enabled. Shell variable
dnl enable_$name is set to "yes" instead.
AC_DEFUN([MPD_OPTIONAL_FUNC_NODEF], [
AC_ARG_ENABLE([$1],
AS_HELP_STRING([--enable-$1],
[use the function "$1" (default: auto)]),,
[AC_CHECK_FUNC([$2], [enable_$1=yes],)])
])
dnl Run code with the specified CFLAGS/CXXFLAGS and LIBS appended.
dnl Restores the old values afterwards.
dnl
dnl Parameters: cflags, libs, code
AC_DEFUN([MPD_WITH_FLAGS], [
ac_save_CFLAGS="$[]CFLAGS"
ac_save_CXXFLAGS="$[]CXXFLAGS"
ac_save_LIBS="$[]LIBS"
CFLAGS="$[]CFLAGS $1"
CXXFLAGS="$[]CXXFLAGS $1"
LIBS="$[]LIBS $2"
$3
CFLAGS="$[]ac_save_CFLAGS"
CXXFLAGS="$[]ac_save_CXXFLAGS"
LIBS="$[]ac_save_LIBS"
])
dnl Run code with the specified library's CFLAGS/CXXFLAGS and LIBS
dnl appended. Restores the old values afterwards.
dnl
dnl Parameters: libname, code
AC_DEFUN([MPD_WITH_LIBRARY],
[MPD_WITH_FLAGS([$[]$1_CFLAGS], [$[]$1_LIBS], [$2])])
AC_DEFUN([results], [
printf '('
if test x$[]enable_$1 = xyes; then
printf '+'
else
printf '-'
fi
printf '%s) ' "$2"
])
# Check if "struct ucred" is available.
#
# Author: Max Kellermann <max.kellermann@gmail.com>
AC_DEFUN([STRUCT_UCRED],[
AC_MSG_CHECKING([for struct ucred])
AC_CACHE_VAL(mpd_cv_have_struct_ucred, [
AC_TRY_COMPILE([#include <sys/socket.h>],
[struct ucred cred;],
mpd_cv_have_struct_ucred=yes,
mpd_cv_have_struct_ucred=no)
])
AC_MSG_RESULT($mpd_cv_have_struct_ucred)
if test x$mpd_cv_have_struct_ucred = xyes; then
AC_DEFINE(HAVE_STRUCT_UCRED, 1, [Define if struct ucred is present from sys/socket.h])
fi
])
This diff is collapsed. Click to expand it.
option('documentation', type: 'boolean', value: false, description: 'Build documentation')
option('test', type: 'boolean', value: false, description: 'Build the unit tests and debug programs')
option('syslog', type: 'feature', description: 'syslog support')
option('inotify', type: 'boolean', value: true, description: 'inotify support (for automatic database update)')
option('daemon', type: 'boolean', value: true, description: 'enable daemonization')
option('systemd', type: 'feature', description: 'systemd support')
option('systemd_system_unit_dir', type: 'string', description: 'systemd system service directory')
option('systemd_user_unit_dir', type: 'string', description: 'systemd user service directory')
#
# Android
#
option('android_sdk', type: 'string', description: 'The path where Android SDK is installed')
option('android_ndk', type: 'string', description: 'The path where Android NDK is installed')
option('android_abi', type: 'string', value: 'armeabi-v7a', description: 'The Android ABI to be built')
option('android_strip', type: 'string', value: 'strip', description: 'The "strip" tool from the NDK')
option('android_debug_keystore', type: 'string', description: 'The keystore file used to sign debug APK files')
option('android_keystore', type: 'string', description: 'The keystore file used to sign APK files')
option('android_keyalias', type: 'string', description: 'The key alias used to sign APK files')
option('android_keypass', type: 'string', description: 'The password of the keystore used to sign APK files')
#
# System call support
#
option('epoll', type: 'boolean', value: true, description: 'Use epoll on Linux')
option('eventfd', type: 'boolean', value: true, description: 'Use eventfd() on Linux')
option('signalfd', type: 'boolean', value: true, description: 'Use signalfd() on Linux')
#
# Network support
#
option('tcp', type: 'boolean', value: true, description: 'Support for clients connecting via TCP')
option('ipv6', type: 'feature', description: 'Support for IPv6')
option('local_socket', type: 'boolean', value: true, description: 'Support for clients connecting via local sockets')
#
# Audio formats
#
option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio format')
#
# Database plugins
#
option('database', type: 'boolean', value: true, description: 'enable support for the music database')
option('upnp', type: 'feature', description: 'UPnP client support')
option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
#
# Neighbor plugins
#
option('neighbor', type: 'boolean', value: true, description: 'enable support for neighbor discovery')
#
# Storage plugins
#
option('udisks', type: 'feature', description: 'Support for removable media using udisks2')
option('webdav', type: 'feature', description: 'WebDAV support using CURL and Expat')
#
# Playlist plugins
#
option('cue', type: 'boolean', value: true, description: 'CUE sheet support')
#
# Input plugins
#
option('cdio_paranoia', type: 'feature', description: 'libcdio_paranoia input plugin')
option('curl', type: 'feature', description: 'HTTP client using CURL')
option('mms', type: 'feature', description: 'MMS protocol support using libmms')
option('nfs', type: 'feature', description: 'NFS protocol support using libnfs')
option('smbclient', type: 'feature', description: 'SMB support using libsmbclient')
#
# Commercial services
#
option('qobuz', type: 'feature', description: 'Qobuz client')
option('soundcloud', type: 'feature', description: 'SoundCloud client')
option('tidal', type: 'feature', description: 'Tidal client')
#
# Archive plugins
#
option('bzip2', type: 'feature', description: 'bzip2 support using libbz2')
option('iso9660', type: 'feature', description: 'ISO9660 support using libiso9660')
option('zzip', type: 'feature', description: 'ZIP support using zziplib')
#
# Tag plugins
#
option('id3tag', type: 'feature', description: 'ID3 support using libid3tag')
option('chromaprint', type: 'feature', description: 'ChromaPrint / AcoustID support')
#
# Decoder plugins
#
option('adplug', type: 'feature', description: 'AdPlug decoder plugin')
option('audiofile', type: 'feature', description: 'libaudiofile decoder plugin')
option('faad', type: 'feature', description: 'AAC decoder using libfaad')
option('ffmpeg', type: 'feature', description: 'FFmpeg codec support')
option('flac', type: 'feature', description: 'FLAC decoder plugin')
option('fluidsynth', type: 'feature', description: 'fluidsynth MIDI decoder plugin')
option('gme', type: 'feature', description: 'Game Music Emulator decoder plugin')
option('mad', type: 'feature', description: 'MP3 decoder using libmad')
option('mikmod', type: 'feature', description: 'MikMod decoder plugin')
option('modplug', type: 'feature', description: 'Modplug decoder plugin')
option('mpcdec', type: 'feature', description: 'Musepack decoder plugin')
option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
option('opus', type: 'feature', description: 'Opus decoder plugin')
option('sidplay', type: 'feature', description: 'C64 SID support via libsidplayfp or libsidplay2')
option('sndfile', type: 'feature', description: 'libsndfile decoder plugin')
option('tremor', type: 'feature', description: 'Fixed-point vorbis decoder plugin')
option('vorbis', type: 'feature', description: 'Vorbis decoder plugin')
option('wavpack', type: 'feature', description: 'WavPack decoder plugin')
option('wildmidi', type: 'feature', description: 'WildMidi decoder plugin')
#
# Encoder plugins
#
option('vorbisenc', type: 'feature', description: 'Vorbis encoder plugin')
option('lame', type: 'feature', description: 'LAME MP3 encoder plugin')
option('twolame', type: 'feature', description: 'TwoLAME MP2 encoder plugin')
option('shine', type: 'feature', description: 'shine MP3 encoder plugin')
option('wave_encoder', type: 'boolean', value: true, description: 'PCM wave encoder encoder plugin')
#
# Filter plugins
#
option('libsamplerate', type: 'feature', description: 'libsamplerate resampler')
option('soxr', type: 'feature', description: 'libsoxr resampler')
#
# Output plugins
#
option('alsa', type: 'feature', description: 'ALSA support')
option('ao', type: 'feature', description: 'libao output plugin')
option('fifo', type: 'boolean', value: true, description: 'FIFO output plugin')
option('httpd', type: 'boolean', value: true, description: 'HTTP streaming output plugin')
option('jack', type: 'feature', description: 'JACK output plugin')
option('openal', type: 'feature', description: 'OpenAL output plugin')
option('oss', type: 'feature', description: 'Open Sound System support')
option('pipe', type: 'boolean', value: true, description: 'Pipe output plugin')
option('pulse', type: 'feature', description: 'PulseAudio support')
option('recorder', type: 'boolean', value: true, description: 'Recorder output plugin')
option('shout', type: 'feature', description: 'Shoutcast streaming support using libshout')
option('sndio', type: 'feature', description: 'sndio output plugin')
option('solaris_output', type: 'feature', description: 'Solaris /dev/audio support')
#
# Misc libraries
#
option('dbus', type: 'feature', description: 'D-Bus support')
option('expat', type: 'feature', description: 'Expat XML support')
option('icu', type: 'feature', description: 'Use libicu for Unicode')
option('iconv', type: 'feature', description: 'Use iconv() for character set conversion')
option('pcre', type: 'feature', description: 'Enable regular expression support (using libpcre)')
option('sqlite', type: 'feature', description: 'SQLite database support (for stickers)')
option('yajl', type: 'feature', description: 'libyajl for YAML support')
option('zlib', type: 'feature', description: 'zlib support (for database compression)')
option('zeroconf', type: 'combo',
choices: ['auto', 'avahi', 'bonjour', 'disabled'],
value: 'auto',
description: 'Zeroconf support')
import subprocess
from build.project import Project
def configure(toolchain, src, build, args=()):
cross_args = []
if toolchain.is_windows:
cross_args.append('-DCMAKE_SYSTEM_NAME=Windows')
cross_args.append('-DCMAKE_RC_COMPILER=' + toolchain.windres)
configure = [
'cmake',
src,
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
'-DCMAKE_BUILD_TYPE=release',
'-DCMAKE_C_COMPILER=' + toolchain.cc,
'-DCMAKE_CXX_COMPILER=' + toolchain.cxx,
'-DCMAKE_C_FLAGS=' + toolchain.cflags + ' ' + toolchain.cppflags,
'-DCMAKE_CXX_FLAGS=' + toolchain.cxxflags + ' ' + toolchain.cppflags,
'-GNinja',
] + cross_args + args
subprocess.check_call(configure, env=toolchain.env, cwd=build)
class CmakeProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
def configure(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure(toolchain, src, build, self.configure_args)
return build
def build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)
def concatenate_cmdline_variables(src, names):
"""Find duplicate variable declarations on the given source list, and
concatenate the values of those in the 'names' list."""
# the result list being constructed
dest = []
# a map of variable name to destination list index
positions = {}
for item in src:
i = item.find('=')
if i > 0:
# it's a variable
name = item[:i]
if name in names:
# it's a known variable
if name in positions:
# already specified: concatenate instead of
# appending it
dest[positions[name]] += ' ' + item[i + 1:]
continue
else:
# not yet seen: append it and remember the list
# index
positions[name] = len(dest)
dest.append(item)
return dest
...@@ -4,19 +4,20 @@ from os.path import abspath ...@@ -4,19 +4,20 @@ from os.path import abspath
from build.project import Project from build.project import Project
from build.zlib import ZlibProject from build.zlib import ZlibProject
from build.meson import MesonProject from build.meson import MesonProject
from build.cmake import CmakeProject
from build.autotools import AutotoolsProject from build.autotools import AutotoolsProject
from build.ffmpeg import FfmpegProject from build.ffmpeg import FfmpegProject
from build.boost import BoostProject from build.boost import BoostProject
libmpdclient = MesonProject( libmpdclient = MesonProject(
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.16.tar.xz', 'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
'fa6bdab67c0e0490302b38f00c27b4959735c3ec8aef7a88327adb1407654464', '4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
'lib/libmpdclient.a', 'lib/libmpdclient.a',
) )
libogg = AutotoolsProject( libogg = AutotoolsProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz', 'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
'4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08', 'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
'lib/libogg.a', 'lib/libogg.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -38,8 +39,8 @@ libvorbis = AutotoolsProject( ...@@ -38,8 +39,8 @@ libvorbis = AutotoolsProject(
) )
opus = AutotoolsProject( opus = AutotoolsProject(
'https://archive.mozilla.org/pub/opus/opus-1.3.tar.gz', 'https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz',
'4f3d69aefdf2dbaf9825408e452a8a414ffc60494c70633560700398820dc550', '65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d',
'lib/libopus.a', 'lib/libopus.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -52,8 +53,8 @@ opus = AutotoolsProject( ...@@ -52,8 +53,8 @@ opus = AutotoolsProject(
) )
flac = AutotoolsProject( flac = AutotoolsProject(
'http://downloads.xiph.org/releases/flac/flac-1.3.2.tar.xz', 'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz',
'91cfc3ed61dc40f47f050a109b08610667d73477af6ef36dcad31c31a4a8d53f', '213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748',
'lib/libFLAC.a', 'lib/libFLAC.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -111,9 +112,44 @@ liblame = AutotoolsProject( ...@@ -111,9 +112,44 @@ liblame = AutotoolsProject(
], ],
) )
libmodplug = AutotoolsProject(
'https://downloads.sourceforge.net/modplug-xmms/libmodplug/0.8.9.0/libmodplug-0.8.9.0.tar.gz',
'457ca5a6c179656d66c01505c0d95fafaead4329b9dbaa0f997d00a3508ad9de',
'lib/libmodplug.a',
[
'--disable-shared', '--enable-static',
],
)
wildmidi = CmakeProject(
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',
'lib/libWildMidi.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DWANT_PLAYER=OFF',
'-DWANT_STATIC=ON',
],
base='wildmidi-wildmidi-0.4.3',
name='wildmidi',
version='0.4.3',
)
gme = CmakeProject(
'https://bitbucket.org/mpyne/game-music-emu/downloads/game-music-emu-0.6.3.tar.xz',
'aba34e53ef0ec6a34b58b84e28bf8cfbccee6585cebca25333604c35db3e051d',
'lib/libgme.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DENABLE_UBSAN=OFF',
'-DZLIB_INCLUDE_DIR=OFF',
'-DSDL2_DIR=OFF',
],
)
ffmpeg = FfmpegProject( ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.0.2.tar.xz', 'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz',
'a95c0cc9eb990e94031d2183f2e6e444cc61c99f6f182d1575c433d62afb2f97', '9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31',
'lib/libavcodec.a', 'lib/libavcodec.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -341,8 +377,8 @@ ffmpeg = FfmpegProject( ...@@ -341,8 +377,8 @@ ffmpeg = FfmpegProject(
) )
curl = AutotoolsProject( curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.61.1.tar.xz', 'http://curl.haxx.se/download/curl-7.70.0.tar.xz',
'3d5913d6a39bd22e68e34dff697fd6e4c3c81563f580c76fca2009315cd81891', '032f43f2674008c761af19bf536374128c16241fb234699a55f9fb603fcfbae7',
'lib/libcurl.a', 'lib/libcurl.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -358,6 +394,11 @@ curl = AutotoolsProject( ...@@ -358,6 +394,11 @@ curl = AutotoolsProject(
'--disable-manual', '--disable-manual',
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi', '--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies', '--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
'--disable-doh',
'--disable-mime',
'--disable-netrc',
'--disable-progress-meter',
'--disable-alt-svc',
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2', '--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
], ],
...@@ -365,8 +406,8 @@ curl = AutotoolsProject( ...@@ -365,8 +406,8 @@ curl = AutotoolsProject(
) )
libexpat = AutotoolsProject( libexpat = AutotoolsProject(
'https://github.com/libexpat/libexpat/releases/download/R_2_2_6/expat-2.2.6.tar.bz2', 'https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.bz2',
'17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2', 'f1063084dc4302a427dabcca499c8312b3a32a29b7d2506653ecc8f950a9a237',
'lib/libexpat.a', 'lib/libexpat.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -375,8 +416,8 @@ libexpat = AutotoolsProject( ...@@ -375,8 +416,8 @@ libexpat = AutotoolsProject(
) )
libnfs = AutotoolsProject( libnfs = AutotoolsProject(
'https://github.com/sahlberg/libnfs/archive/libnfs-3.0.0.tar.gz', 'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
'445d92c5fc55e4a5b115e358e60486cf8f87ee50e0103d46a02e7fb4618566a5', '6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
'lib/libnfs.a', 'lib/libnfs.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
...@@ -387,12 +428,12 @@ libnfs = AutotoolsProject( ...@@ -387,12 +428,12 @@ libnfs = AutotoolsProject(
'--disable-utils', '--disable-examples', '--disable-utils', '--disable-examples',
], ],
base='libnfs-libnfs-3.0.0', base='libnfs-libnfs-4.0.0',
autoreconf=True, autoreconf=True,
) )
boost = BoostProject( boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.68.0/boost_1_68_0.tar.bz2', 'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2',
'7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7', '4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402',
'include/boost/version.hpp', 'include/boost/version.hpp',
) )
...@@ -2,48 +2,46 @@ import os.path, subprocess, sys ...@@ -2,48 +2,46 @@ import os.path, subprocess, sys
from build.project import Project from build.project import Project
class MesonProject(Project): def make_cross_file(toolchain):
def __init__(self, url, md5, installed, configure_args=[], if toolchain.is_windows:
**kwargs): system = 'windows'
Project.__init__(self, url, md5, installed, **kwargs) windres = "windres = '%s'" % toolchain.windres
self.configure_args = configure_args else:
system = 'linux'
def _make_cross_file(self, toolchain): windres = ''
if toolchain.is_windows:
system = 'windows' if toolchain.is_arm:
cpu_family = 'arm'
if toolchain.is_armv7:
cpu = 'armv7'
else: else:
system = 'linux' cpu = 'armv6'
elif toolchain.is_aarch64:
if toolchain.is_arm: cpu_family = 'aarch64'
cpu_family = 'arm' cpu = 'arm64-v8a'
if toolchain.is_armv7: else:
cpu = 'armv7' cpu_family = 'x86'
else: if 'x86_64' in toolchain.arch:
cpu = 'armv6' cpu = 'x86_64'
elif toolchain.is_aarch64:
cpu_family = 'aarch64'
cpu = 'arm64-v8a'
else: else:
cpu_family = 'x86' cpu = 'i686'
if 'x86_64' in toolchain.arch:
cpu = 'x86_64'
else:
cpu = 'i686'
# TODO: support more CPUs # TODO: support more CPUs
endian = 'little' endian = 'little'
# TODO: write pkg-config wrapper # TODO: write pkg-config wrapper
path = os.path.join(toolchain.build_path, 'meson.cross') path = os.path.join(toolchain.build_path, 'meson.cross')
os.makedirs(toolchain.build_path, exist_ok=True) os.makedirs(toolchain.build_path, exist_ok=True)
with open(path, 'w') as f: with open(path, 'w') as f:
f.write(""" f.write("""
[binaries] [binaries]
c = '%s' c = '%s'
cpp = '%s' cpp = '%s'
ar = '%s' ar = '%s'
strip = '%s' strip = '%s'
pkgconfig = '%s'
%s
[properties] [properties]
root = '%s' root = '%s'
...@@ -63,37 +61,48 @@ cpu_family = '%s' ...@@ -63,37 +61,48 @@ cpu_family = '%s'
cpu = '%s' cpu = '%s'
endian = '%s' endian = '%s'
""" % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip, """ % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip,
toolchain.pkg_config,
windres,
toolchain.install_prefix, toolchain.install_prefix,
repr((toolchain.cppflags + ' ' + toolchain.cflags).split()), repr((toolchain.cppflags + ' ' + toolchain.cflags).split()),
repr(toolchain.ldflags.split()), repr(toolchain.ldflags.split() + toolchain.libs.split()),
repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()), repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()),
repr(toolchain.ldflags.split()), repr(toolchain.ldflags.split() + toolchain.libs.split()),
system, cpu_family, cpu, endian)) system, cpu_family, cpu, endian))
return path return path
def configure(self, toolchain): def configure(toolchain, src, build, args=()):
src = self.unpack(toolchain) cross_file = make_cross_file(toolchain)
cross_file = self._make_cross_file(toolchain) configure = [
build = self.make_build_path(toolchain) 'meson',
configure = [ src, build,
'meson',
src, build, '--prefix', toolchain.install_prefix,
'--prefix', toolchain.install_prefix, # this is necessary because Meson uses Debian's build machine
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
# builds, which is obviously wrong
'--libdir', 'lib',
# this is necessary because Meson uses Debian's build machine '--buildtype', 'plain',
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
# builds, which is obviously wrong
'--libdir', 'lib',
'--buildtype', 'plain', '--default-library=static',
'--default-library=static', '--cross-file', cross_file,
] + args
'--cross-file', cross_file, subprocess.check_call(configure, env=toolchain.env)
] + self.configure_args
subprocess.check_call(configure, env=toolchain.env) class MesonProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
def configure(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure(toolchain, src, build, self.configure_args)
return build return build
def build(self, toolchain): def build(self, toolchain):
......
...@@ -18,5 +18,5 @@ class ZlibProject(Project): ...@@ -18,5 +18,5 @@ class ZlibProject(Project):
'INCLUDE_PATH='+ os.path.join(toolchain.install_prefix, 'include'), 'INCLUDE_PATH='+ os.path.join(toolchain.install_prefix, 'include'),
'LIBRARY_PATH=' + os.path.join(toolchain.install_prefix, 'lib'), 'LIBRARY_PATH=' + os.path.join(toolchain.install_prefix, 'lib'),
'BINARY_PATH=' + os.path.join(toolchain.install_prefix, 'bin'), 'BINARY_PATH=' + os.path.join(toolchain.install_prefix, 'bin'),
'SHARED_MODE=1'], ],
cwd=src, env=toolchain.env) cwd=src, env=toolchain.env)
#!/usr/bin/env ruby
#
# This script verifies that every source includes config.h first.
# This is very important for consistent Large File Support.
#
def check_file(file)
first = true
file.each_line do |line|
if line =~ /^\#include\s+(\S+)/ then
if $1 == '"config.h"'
unless first
puts "#{file.path}: config.h included too late"
end
else
if first
puts "#{file.path}: config.h missing"
end
end
first = false
end
end
end
def check_path(path)
File.open(path) do |file|
check_file(file)
end
end
if ARGV.empty?
Dir["src/*.c"].each do |path|
check_path(path)
end
Dir["src/*/*.c"].each do |path|
check_path(path)
end
Dir["test/*.c"].each do |path|
check_path(path)
end
else
ARGV.each do |path|
check_path(path)
end
end
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -44,16 +44,32 @@ AudioFormat::ApplyMask(AudioFormat mask) noexcept ...@@ -44,16 +44,32 @@ AudioFormat::ApplyMask(AudioFormat mask) noexcept
StringBuffer<24> StringBuffer<24>
ToString(const AudioFormat af) noexcept ToString(const AudioFormat af) noexcept
{ {
StringBuffer<24> buffer;
char *p = buffer.data();
if (af.format == SampleFormat::DSD && af.sample_rate > 0 && if (af.format == SampleFormat::DSD && af.sample_rate > 0 &&
af.sample_rate % 44100 == 0) { af.sample_rate % 44100 == 0) {
/* use shortcuts such as "dsd64" which implies the /* use shortcuts such as "dsd64" which implies the
sample rate */ sample rate */
return StringFormat<24>("dsd%u:%u", p += sprintf(p, "dsd%u:", af.sample_rate * 8 / 44100);
af.sample_rate * 8 / 44100, } else {
af.channels); const char *sample_format = af.format != SampleFormat::UNDEFINED
? sample_format_to_string(af.format)
: "*";
if (af.sample_rate > 0)
p += sprintf(p, "%u:%s:", af.sample_rate,
sample_format);
else
p += sprintf(p, "*:%s:", sample_format);
}
if (af.channels > 0)
p += sprintf(p, "%u", af.channels);
else {
*p++ = '*';
*p = 0;
} }
return StringFormat<24>("%u:%s:%u", return buffer;
af.sample_rate, sample_format_to_string(af.format),
af.channels);
} }
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -20,8 +20,10 @@ ...@@ -20,8 +20,10 @@
#ifndef MPD_AUDIO_FORMAT_HXX #ifndef MPD_AUDIO_FORMAT_HXX
#define MPD_AUDIO_FORMAT_HXX #define MPD_AUDIO_FORMAT_HXX
#include "pcm/SampleFormat.hxx" #include "pcm/SampleFormat.hxx" // IWYU pragma: export
#include "Compiler.h" #include "util/Compiler.h"
#include <chrono>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
...@@ -132,6 +134,11 @@ struct AudioFormat { ...@@ -132,6 +134,11 @@ struct AudioFormat {
return result; return result;
} }
gcc_pure
bool MatchMask(AudioFormat mask) const noexcept {
return WithMask(mask) == *this;
}
/** /**
* Returns the size of each (mono) sample in bytes. * Returns the size of each (mono) sample in bytes.
*/ */
...@@ -142,11 +149,28 @@ struct AudioFormat { ...@@ -142,11 +149,28 @@ struct AudioFormat {
*/ */
unsigned GetFrameSize() const; unsigned GetFrameSize() const;
/** template<typename D>
* Returns the floating point factor which converts a time constexpr auto TimeToFrames(D t) const noexcept {
* span to a storage size in bytes. using Period = typename D::period;
*/ return ((t.count() * sample_rate) / Period::den) * Period::num;
double GetTimeToSize() const; }
template<typename D>
constexpr size_t TimeToSize(D t) const noexcept {
return size_t(size_t(TimeToFrames(t)) * GetFrameSize());
}
template<typename D>
constexpr D FramesToTime(std::uintmax_t size) const noexcept {
using Rep = typename D::rep;
using Period = typename D::period;
return D(((Rep(1) * size / Period::num) * Period::den) / sample_rate);
}
template<typename D>
constexpr D SizeToTime(std::uintmax_t size) const noexcept {
return FramesToTime<D>(size / GetFrameSize());
}
}; };
/** /**
...@@ -207,12 +231,6 @@ AudioFormat::GetFrameSize() const ...@@ -207,12 +231,6 @@ AudioFormat::GetFrameSize() const
return GetSampleSize() * channels; return GetSampleSize() * channels;
} }
inline double
AudioFormat::GetTimeToSize() const
{
return sample_rate * GetFrameSize();
}
/** /**
* Renders the #AudioFormat object into a string, e.g. for printing * Renders the #AudioFormat object into a string, e.g. for printing
* it in a log file. * it in a log file.
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
* *
*/ */
#include "config.h"
#include "AudioParser.hxx" #include "AudioParser.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
...@@ -44,10 +43,9 @@ ParseSampleRate(const char *src, bool mask, const char **endptr_r) ...@@ -44,10 +43,9 @@ ParseSampleRate(const char *src, bool mask, const char **endptr_r)
value = strtoul(src, &endptr, 10); value = strtoul(src, &endptr, 10);
if (endptr == src) { if (endptr == src) {
throw std::runtime_error("Failed to parse the sample rate"); throw std::invalid_argument("Failed to parse the sample rate");
} else if (!audio_valid_sample_rate(value)) } else if (!audio_valid_sample_rate(value))
throw FormatRuntimeError("Invalid sample rate: %lu", throw FormatInvalidArgument("Invalid sample rate: %lu", value);
value);
*endptr_r = endptr; *endptr_r = endptr;
return value; return value;
...@@ -77,7 +75,7 @@ ParseSampleFormat(const char *src, bool mask, const char **endptr_r) ...@@ -77,7 +75,7 @@ ParseSampleFormat(const char *src, bool mask, const char **endptr_r)
value = strtoul(src, &endptr, 10); value = strtoul(src, &endptr, 10);
if (endptr == src) if (endptr == src)
throw std::runtime_error("Failed to parse the sample format"); throw std::invalid_argument("Failed to parse the sample format");
switch (value) { switch (value) {
case 8: case 8:
...@@ -101,7 +99,8 @@ ParseSampleFormat(const char *src, bool mask, const char **endptr_r) ...@@ -101,7 +99,8 @@ ParseSampleFormat(const char *src, bool mask, const char **endptr_r)
break; break;
default: default:
throw FormatRuntimeError("Invalid sample format: %lu", value); throw FormatInvalidArgument("Invalid sample format: %lu",
value);
} }
assert(audio_valid_sample_format(sample_format)); assert(audio_valid_sample_format(sample_format));
...@@ -123,9 +122,10 @@ ParseChannelCount(const char *src, bool mask, const char **endptr_r) ...@@ -123,9 +122,10 @@ ParseChannelCount(const char *src, bool mask, const char **endptr_r)
value = strtoul(src, &endptr, 10); value = strtoul(src, &endptr, 10);
if (endptr == src) if (endptr == src)
throw std::runtime_error("Failed to parse the channel count"); throw std::invalid_argument("Failed to parse the channel count");
else if (!audio_valid_channel_count(value)) else if (!audio_valid_channel_count(value))
throw FormatRuntimeError("Invalid channel count: %u", value); throw FormatInvalidArgument("Invalid channel count: %u",
value);
*endptr_r = endptr; *endptr_r = endptr;
return value; return value;
...@@ -151,7 +151,8 @@ ParseAudioFormat(const char *src, bool mask) ...@@ -151,7 +151,8 @@ ParseAudioFormat(const char *src, bool mask)
src = endptr + 1; src = endptr + 1;
dest.channels = ParseChannelCount(src, mask, &src); dest.channels = ParseChannelCount(src, mask, &src);
if (*src != 0) if (*src != 0)
throw FormatRuntimeError("Extra data after channel count: %s", src); throw FormatInvalidArgument("Extra data after channel count: %s",
src);
return dest; return dest;
} }
...@@ -162,21 +163,22 @@ ParseAudioFormat(const char *src, bool mask) ...@@ -162,21 +163,22 @@ ParseAudioFormat(const char *src, bool mask)
dest.sample_rate = ParseSampleRate(src, mask, &src); dest.sample_rate = ParseSampleRate(src, mask, &src);
if (*src++ != ':') if (*src++ != ':')
throw std::runtime_error("Sample format missing"); throw std::invalid_argument("Sample format missing");
/* parse sample format */ /* parse sample format */
dest.format = ParseSampleFormat(src, mask, &src); dest.format = ParseSampleFormat(src, mask, &src);
if (*src++ != ':') if (*src++ != ':')
throw std::runtime_error("Channel count missing"); throw std::invalid_argument("Channel count missing");
/* parse channel count */ /* parse channel count */
dest.channels = ParseChannelCount(src, mask, &src); dest.channels = ParseChannelCount(src, mask, &src);
if (*src != 0) if (*src != 0)
throw FormatRuntimeError("Extra data after channel count: %s", src); throw FormatInvalidArgument("Extra data after channel count: %s",
src);
assert(mask assert(mask
? dest.IsMaskValid() ? dest.IsMaskValid()
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "config.h"
#include "CheckAudioFormat.hxx" #include "CheckAudioFormat.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -24,13 +24,14 @@ ...@@ -24,13 +24,14 @@
#include <utility> #include <utility>
#include <cstdint> #include <cstdint>
using FloatDuration = std::chrono::duration<double>;
/** /**
* A time stamp within a song. Granularity is 1 millisecond and the * A time stamp within a song. Granularity is 1 millisecond and the
* maximum value is about 49 days. * maximum value is about 49 days.
*/ */
class SongTime : public std::chrono::duration<std::uint32_t, std::milli> { class SongTime : public std::chrono::duration<std::uint32_t, std::milli> {
typedef std::chrono::duration<std::uint32_t, std::milli> Base; typedef std::chrono::duration<std::uint32_t, std::milli> Base;
typedef Base::rep rep;
public: public:
SongTime() = default; SongTime() = default;
...@@ -42,6 +43,11 @@ public: ...@@ -42,6 +43,11 @@ public:
return SongTime(Base::zero()); return SongTime(Base::zero());
} }
template<typename D>
static constexpr SongTime Cast(D src) {
return SongTime(std::chrono::duration_cast<Base>(src));
}
static constexpr SongTime FromS(unsigned s) { static constexpr SongTime FromS(unsigned s) {
return SongTime(rep(s) * 1000); return SongTime(rep(s) * 1000);
} }
...@@ -114,7 +120,6 @@ public: ...@@ -114,7 +120,6 @@ public:
*/ */
class SignedSongTime : public std::chrono::duration<std::int32_t, std::milli> { class SignedSongTime : public std::chrono::duration<std::int32_t, std::milli> {
typedef std::chrono::duration<std::int32_t, std::milli> Base; typedef std::chrono::duration<std::int32_t, std::milli> Base;
typedef Base::rep rep;
public: public:
SignedSongTime() = default; SignedSongTime() = default;
...@@ -138,6 +143,11 @@ public: ...@@ -138,6 +143,11 @@ public:
return SignedSongTime(-1); return SignedSongTime(-1);
} }
template<typename D>
static constexpr SongTime Cast(D src) {
return SongTime(std::chrono::duration_cast<Base>(src));
}
static constexpr SignedSongTime FromS(int s) { static constexpr SignedSongTime FromS(int s) {
return SignedSongTime(rep(s) * 1000); return SignedSongTime(rep(s) * 1000);
} }
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -19,10 +19,11 @@ ...@@ -19,10 +19,11 @@
#include "config.h" #include "config.h"
#include "CommandLine.hxx" #include "CommandLine.hxx"
#include "GitVersion.hxx"
#include "ls.hxx" #include "ls.hxx"
#include "LogInit.hxx" #include "LogInit.hxx"
#include "Log.hxx" #include "Log.hxx"
#include "config/ConfigGlobal.hxx" #include "config/File.hxx"
#include "decoder/DecoderList.hxx" #include "decoder/DecoderList.hxx"
#include "decoder/DecoderPlugin.hxx" #include "decoder/DecoderPlugin.hxx"
#include "output/Registry.hxx" #include "output/Registry.hxx"
...@@ -32,11 +33,11 @@ ...@@ -32,11 +33,11 @@
#include "playlist/PlaylistRegistry.hxx" #include "playlist/PlaylistRegistry.hxx"
#include "playlist/PlaylistPlugin.hxx" #include "playlist/PlaylistPlugin.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
#include "fs/FileSystem.hxx" #include "fs/FileSystem.hxx"
#include "fs/StandardDirectory.hxx" #include "fs/StandardDirectory.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
#include "util/Macros.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/OptionDef.hxx" #include "util/OptionDef.hxx"
...@@ -67,52 +68,56 @@ ...@@ -67,52 +68,56 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
namespace {
#ifdef _WIN32 #ifdef _WIN32
#define CONFIG_FILE_LOCATION PATH_LITERAL("mpd\\mpd.conf") constexpr auto CONFIG_FILE_LOCATION = Path::FromFS(PATH_LITERAL("mpd\\mpd.conf"));
#define APP_CONFIG_FILE_LOCATION PATH_LITERAL("conf\\mpd.conf") constexpr auto APP_CONFIG_FILE_LOCATION = Path::FromFS(PATH_LITERAL("conf\\mpd.conf"));
#else #else
#define USER_CONFIG_FILE_LOCATION1 PATH_LITERAL(".mpdconf") constexpr auto USER_CONFIG_FILE_LOCATION1 = Path::FromFS(PATH_LITERAL(".mpdconf"));
#define USER_CONFIG_FILE_LOCATION2 PATH_LITERAL(".mpd/mpd.conf") constexpr auto USER_CONFIG_FILE_LOCATION2 = Path::FromFS(PATH_LITERAL(".mpd/mpd.conf"));
#define USER_CONFIG_FILE_LOCATION_XDG PATH_LITERAL("mpd/mpd.conf") constexpr auto USER_CONFIG_FILE_LOCATION_XDG = Path::FromFS(PATH_LITERAL("mpd/mpd.conf"));
#endif #endif
}
static constexpr OptionDef opt_kill( enum Option {
"kill", "kill the currently running mpd session"); OPTION_KILL,
static constexpr OptionDef opt_no_config( OPTION_NO_CONFIG,
"no-config", "don't read from config"); OPTION_NO_DAEMON,
static constexpr OptionDef opt_no_daemon( OPTION_STDOUT,
"no-daemon", "don't detach from console"); OPTION_STDERR,
static constexpr OptionDef opt_stdout( OPTION_VERBOSE,
"stdout", nullptr); // hidden, compatibility with old versions OPTION_VERSION,
static constexpr OptionDef opt_stderr( OPTION_HELP,
"stderr", "print messages to stderr"); OPTION_HELP2,
static constexpr OptionDef opt_verbose( };
"verbose", 'v', "verbose logging");
static constexpr OptionDef opt_version( static constexpr OptionDef option_defs[] = {
"version", 'V', "print version number"); {"kill", "kill the currently running mpd session"},
static constexpr OptionDef opt_help( {"no-config", "don't read from config"},
"help", 'h', "show help options"); {"no-daemon", "don't detach from console"},
static constexpr OptionDef opt_help_alt( {"stdout", nullptr}, // hidden, compatibility with old versions
nullptr, '?', nullptr); // hidden, standard alias for --help {"stderr", "print messages to stderr"},
{"verbose", 'v', "verbose logging"},
{"version", 'V', "print version number"},
{"help", 'h', "show help options"},
{nullptr, '?', nullptr}, // hidden, standard alias for --help
};
static constexpr Domain cmdline_domain("cmdline"); static constexpr Domain cmdline_domain("cmdline");
gcc_noreturn gcc_noreturn
static void version(void) static void version(void)
{ {
printf("Music Player Daemon " VERSION printf("Music Player Daemon " VERSION " (%s)"
#ifdef GIT_COMMIT
" (" GIT_COMMIT ")"
#endif
"\n" "\n"
"\n" "Copyright 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n" "Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>\n"
"Copyright 2008-2017 Max Kellermann <max.kellermann@gmail.com>\n"
"This is free software; see the source for copying conditions. There is NO\n" "This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" "warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
GIT_VERSION);
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
"\n" printf("\n"
"Database plugins:\n"); "Database plugins:\n");
for (auto i = database_plugins; *i != nullptr; ++i) for (auto i = database_plugins; *i != nullptr; ++i)
...@@ -124,18 +129,18 @@ static void version(void) ...@@ -124,18 +129,18 @@ static void version(void)
for (auto i = storage_plugins; *i != nullptr; ++i) for (auto i = storage_plugins; *i != nullptr; ++i)
printf(" %s", (*i)->name); printf(" %s", (*i)->name);
printf("\n" printf("\n");
#endif #endif
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
"\n" printf("\n"
"Neighbor plugins:\n"); "Neighbor plugins:\n");
for (auto i = neighbor_plugins; *i != nullptr; ++i) for (auto i = neighbor_plugins; *i != nullptr; ++i)
printf(" %s", (*i)->name); printf(" %s", (*i)->name);
printf("\n"
#endif #endif
printf("\n"
"\n" "\n"
"Decoders plugins:\n"); "Decoders plugins:\n");
...@@ -195,7 +200,12 @@ static void version(void) ...@@ -195,7 +200,12 @@ static void version(void)
#endif #endif
"\n" "\n"
"Input plugins:\n"); "Input plugins:\n"
" file"
#ifdef ENABLE_ARCHIVE
" archive"
#endif
);
input_plugins_for_each(plugin) input_plugins_for_each(plugin)
printf(" %s", plugin->name); printf(" %s", plugin->name);
...@@ -213,6 +223,12 @@ static void version(void) ...@@ -213,6 +223,12 @@ static void version(void)
#ifdef HAVE_AVAHI #ifdef HAVE_AVAHI
" avahi" " avahi"
#endif #endif
#ifdef ENABLE_DBUS
" dbus"
#endif
#ifdef ENABLE_UDISKS
" udisks"
#endif
#ifdef USE_EPOLL #ifdef USE_EPOLL
" epoll" " epoll"
#endif #endif
...@@ -265,89 +281,85 @@ static void help(void) ...@@ -265,89 +281,85 @@ static void help(void)
"\n" "\n"
"Options:\n"); "Options:\n");
PrintOption(opt_help); for (const auto &i : option_defs)
PrintOption(opt_kill); if(i.HasDescription() == true) // hide hidden options from help print
PrintOption(opt_no_config); PrintOption(i);
PrintOption(opt_no_daemon);
PrintOption(opt_stderr);
PrintOption(opt_verbose);
PrintOption(opt_version);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
class ConfigLoader class ConfigLoader
{ {
ConfigData &config;
public: public:
explicit ConfigLoader(ConfigData &_config) noexcept
:config(_config) {}
bool TryFile(const Path path); bool TryFile(const Path path);
bool TryFile(const AllocatedPath &base_path, bool TryFile(const AllocatedPath &base_path, Path path);
PathTraitsFS::const_pointer_type path);
}; };
bool ConfigLoader::TryFile(Path path) bool ConfigLoader::TryFile(Path path)
{ {
if (FileExists(path)) { if (FileExists(path)) {
ReadConfigFile(path); ReadConfigFile(config, path);
return true; return true;
} }
return false; return false;
} }
bool ConfigLoader::TryFile(const AllocatedPath &base_path, bool ConfigLoader::TryFile(const AllocatedPath &base_path, Path path)
PathTraitsFS::const_pointer_type path)
{ {
if (base_path.IsNull()) if (base_path.IsNull())
return false; return false;
auto full_path = AllocatedPath::Build(base_path, path); auto full_path = base_path / path;
return TryFile(full_path); return TryFile(full_path);
} }
void void
ParseCommandLine(int argc, char **argv, struct options *options) ParseCommandLine(int argc, char **argv, struct options &options,
ConfigData &config)
{ {
bool use_config_file = true; bool use_config_file = true;
options->kill = false;
options->daemon = true;
options->log_stderr = false;
options->verbose = false;
// First pass: handle command line options // First pass: handle command line options
OptionParser parser(argc, argv); OptionParser parser(option_defs, argc, argv);
while (parser.HasEntries()) { while (auto o = parser.Next()) {
if (!parser.ParseNext()) switch (Option(o.index)) {
continue; case OPTION_KILL:
if (parser.CheckOption(opt_kill)) { options.kill = true;
options->kill = true; break;
continue;
} case OPTION_NO_CONFIG:
if (parser.CheckOption(opt_no_config)) {
use_config_file = false; use_config_file = false;
continue; break;
}
if (parser.CheckOption(opt_no_daemon)) { case OPTION_NO_DAEMON:
options->daemon = false; options.daemon = false;
continue; break;
}
if (parser.CheckOption(opt_stderr, opt_stdout)) { case OPTION_STDOUT:
options->log_stderr = true; case OPTION_STDERR:
continue; options.log_stderr = true;
} break;
if (parser.CheckOption(opt_verbose)) {
options->verbose = true; case OPTION_VERBOSE:
continue; options.verbose = true;
} break;
if (parser.CheckOption(opt_version))
case OPTION_VERSION:
version(); version();
if (parser.CheckOption(opt_help, opt_help_alt))
help();
throw FormatRuntimeError("invalid option: %s", case OPTION_HELP:
parser.GetOption()); case OPTION_HELP2:
help();
}
} }
/* initialize the logging library, so the configuration file /* initialize the logging library, so the configuration file
parser can use it already */ parser can use it already */
log_early_init(options->verbose); log_early_init(options.verbose);
if (!use_config_file) { if (!use_config_file) {
LogDebug(cmdline_domain, LogDebug(cmdline_domain,
...@@ -357,11 +369,9 @@ ParseCommandLine(int argc, char **argv, struct options *options) ...@@ -357,11 +369,9 @@ ParseCommandLine(int argc, char **argv, struct options *options)
// Second pass: find non-option parameters (i.e. config file) // Second pass: find non-option parameters (i.e. config file)
const char *config_file = nullptr; const char *config_file = nullptr;
for (int i = 1; i < argc; ++i) { for (const char *i : parser.GetRemaining()) {
if (OptionParser::IsOption(argv[i]))
continue;
if (config_file == nullptr) { if (config_file == nullptr) {
config_file = argv[i]; config_file = i;
continue; continue;
} }
...@@ -370,23 +380,13 @@ ParseCommandLine(int argc, char **argv, struct options *options) ...@@ -370,23 +380,13 @@ ParseCommandLine(int argc, char **argv, struct options *options)
if (config_file != nullptr) { if (config_file != nullptr) {
/* use specified configuration file */ /* use specified configuration file */
#ifdef _UNICODE ReadConfigFile(config, FromNarrowPath(config_file));
wchar_t buffer[MAX_PATH];
auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1,
buffer, ARRAY_SIZE(buffer));
if (result <= 0)
throw MakeLastError("MultiByteToWideChar() failed");
ReadConfigFile(Path::FromFS(buffer));
#else
ReadConfigFile(Path::FromFS(config_file));
#endif
return; return;
} }
/* use default configuration file path */ /* use default configuration file path */
ConfigLoader loader; ConfigLoader loader(config);
bool found = bool found =
#ifdef _WIN32 #ifdef _WIN32
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -20,14 +20,17 @@ ...@@ -20,14 +20,17 @@
#ifndef MPD_COMMAND_LINE_HXX #ifndef MPD_COMMAND_LINE_HXX
#define MPD_COMMAND_LINE_HXX #define MPD_COMMAND_LINE_HXX
struct ConfigData;
struct options { struct options {
bool kill; bool kill = false;
bool daemon; bool daemon = true;
bool log_stderr; bool log_stderr = false;
bool verbose; bool verbose = false;
}; };
void void
ParseCommandLine(int argc, char **argv, struct options *options); ParseCommandLine(int argc, char **argv, struct options &options,
ConfigData &config);
#endif #endif
/*
* Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "GitVersion.hxx"
char GIT_VERSION[] = "@VCS_TAG@";
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -17,12 +17,9 @@ ...@@ -17,12 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef MPD_DECODER_THREAD_HXX #ifndef MPD_GIT_VERSION_HXX
#define MPD_DECODER_THREAD_HXX #define MPD_GIT_VERSION_HXX
struct DecoderControl; extern char GIT_VERSION[];
void
decoder_thread_start(DecoderControl &dc);
#endif #endif
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "IOThread.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "thread/Thread.hxx"
#include "thread/Name.hxx"
#include "event/Loop.hxx"
#include <assert.h>
static struct IOThread {
Mutex mutex;
Cond cond;
EventLoop *loop;
Thread thread;
IOThread():thread(BIND_THIS_METHOD(Run)) {}
private:
void Run() noexcept;
} io;
void
io_thread_run(void)
{
assert(io_thread_inside());
assert(io.loop != nullptr);
io.loop->Run();
}
inline void
IOThread::Run() noexcept
{
SetThreadName("io");
/* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */
mutex.lock();
mutex.unlock();
io_thread_run();
}
void
io_thread_init(void)
{
assert(io.loop == nullptr);
assert(!io.thread.IsDefined());
io.loop = new EventLoop();
}
void
io_thread_start()
{
assert(io.loop != nullptr);
assert(!io.thread.IsDefined());
const std::lock_guard<Mutex> protect(io.mutex);
io.thread.Start();
}
void
io_thread_quit(void)
{
assert(io.loop != nullptr);
io.loop->Break();
}
void
io_thread_deinit(void)
{
if (io.thread.IsDefined()) {
io_thread_quit();
io.thread.Join();
}
delete io.loop;
}
EventLoop &
io_thread_get() noexcept
{
assert(io.loop != nullptr);
return *io.loop;
}
#ifndef NDEBUG
bool
io_thread_inside() noexcept
{
return io.thread.IsInside();
}
#endif
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_IO_THREAD_HXX
#define MPD_IO_THREAD_HXX
#include "check.h"
#include "Compiler.h"
class EventLoop;
void
io_thread_init();
void
io_thread_start();
/**
* Run the I/O event loop synchronously in the current thread. This
* can be called instead of io_thread_start(). For testing purposes
* only.
*/
void
io_thread_run();
/**
* Ask the I/O thread to quit, but does not wait for it. Usually, you
* don't need to call this function, because io_thread_deinit()
* includes this.
*/
void
io_thread_quit();
void
io_thread_deinit();
gcc_const
EventLoop &
io_thread_get() noexcept;
#ifndef NDEBUG
/**
* Is the current thread the I/O thread?
*/
gcc_pure
bool
io_thread_inside() noexcept;
#endif
#endif
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -17,10 +17,9 @@ ...@@ -17,10 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "config.h"
#include "IcyMetaDataParser.hxx" #include "IcyMetaDataParser.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx" #include "tag/Builder.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/StringView.hxx" #include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
...@@ -31,7 +30,7 @@ ...@@ -31,7 +30,7 @@
static constexpr Domain icy_metadata_domain("icy_metadata"); static constexpr Domain icy_metadata_domain("icy_metadata");
void void
IcyMetaDataParser::Reset() IcyMetaDataParser::Reset() noexcept
{ {
if (!IsDefined()) if (!IsDefined())
return; return;
...@@ -39,14 +38,14 @@ IcyMetaDataParser::Reset() ...@@ -39,14 +38,14 @@ IcyMetaDataParser::Reset()
if (data_rest == 0 && meta_size > 0) if (data_rest == 0 && meta_size > 0)
delete[] meta_data; delete[] meta_data;
delete tag; tag.reset();
data_rest = data_size; data_rest = data_size;
meta_size = 0; meta_size = 0;
} }
size_t size_t
IcyMetaDataParser::Data(size_t length) IcyMetaDataParser::Data(size_t length) noexcept
{ {
assert(length > 0); assert(length > 0);
...@@ -66,7 +65,7 @@ IcyMetaDataParser::Data(size_t length) ...@@ -66,7 +65,7 @@ IcyMetaDataParser::Data(size_t length)
} }
static void static void
icy_add_item(TagBuilder &tag, TagType type, const char *value) icy_add_item(TagBuilder &tag, TagType type, const char *value) noexcept
{ {
size_t length = strlen(value); size_t length = strlen(value);
...@@ -81,7 +80,8 @@ icy_add_item(TagBuilder &tag, TagType type, const char *value) ...@@ -81,7 +80,8 @@ icy_add_item(TagBuilder &tag, TagType type, const char *value)
} }
static void static void
icy_parse_tag_item(TagBuilder &tag, const char *name, const char *value) icy_parse_tag_item(TagBuilder &tag,
const char *name, const char *value) noexcept
{ {
if (strcmp(name, "StreamTitle") == 0) if (strcmp(name, "StreamTitle") == 0)
icy_add_item(tag, TAG_TITLE, value); icy_add_item(tag, TAG_TITLE, value);
...@@ -96,7 +96,7 @@ icy_parse_tag_item(TagBuilder &tag, const char *name, const char *value) ...@@ -96,7 +96,7 @@ icy_parse_tag_item(TagBuilder &tag, const char *name, const char *value)
* that also fails, return #end. * that also fails, return #end.
*/ */
static char * static char *
find_end_quote(char *p, char *const end) find_end_quote(char *p, char *const end) noexcept
{ {
char *fallback = std::find(p, end, '\''); char *fallback = std::find(p, end, '\'');
if (fallback >= end - 1 || fallback[1] == ';') if (fallback >= end - 1 || fallback[1] == ';')
...@@ -115,8 +115,8 @@ find_end_quote(char *p, char *const end) ...@@ -115,8 +115,8 @@ find_end_quote(char *p, char *const end)
} }
} }
static Tag * static std::unique_ptr<Tag>
icy_parse_tag(char *p, char *const end) icy_parse_tag(char *p, char *const end) noexcept
{ {
assert(p != nullptr); assert(p != nullptr);
assert(end != nullptr); assert(end != nullptr);
...@@ -165,7 +165,7 @@ icy_parse_tag(char *p, char *const end) ...@@ -165,7 +165,7 @@ icy_parse_tag(char *p, char *const end)
} }
size_t size_t
IcyMetaDataParser::Meta(const void *data, size_t length) IcyMetaDataParser::Meta(const void *data, size_t length) noexcept
{ {
const unsigned char *p = (const unsigned char *)data; const unsigned char *p = (const unsigned char *)data;
...@@ -208,8 +208,6 @@ IcyMetaDataParser::Meta(const void *data, size_t length) ...@@ -208,8 +208,6 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
if (meta_position == meta_size) { if (meta_position == meta_size) {
/* parse */ /* parse */
delete tag;
tag = icy_parse_tag(meta_data, meta_data + meta_size); tag = icy_parse_tag(meta_data, meta_data + meta_size);
delete[] meta_data; delete[] meta_data;
...@@ -223,7 +221,7 @@ IcyMetaDataParser::Meta(const void *data, size_t length) ...@@ -223,7 +221,7 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
} }
size_t size_t
IcyMetaDataParser::ParseInPlace(void *data, size_t length) IcyMetaDataParser::ParseInPlace(void *data, size_t length) noexcept
{ {
uint8_t *const dest0 = (uint8_t *)data; uint8_t *const dest0 = (uint8_t *)data;
uint8_t *dest = dest0; uint8_t *dest = dest0;
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -20,21 +20,22 @@ ...@@ -20,21 +20,22 @@
#ifndef MPD_ICY_META_DATA_PARSER_HXX #ifndef MPD_ICY_META_DATA_PARSER_HXX
#define MPD_ICY_META_DATA_PARSER_HXX #define MPD_ICY_META_DATA_PARSER_HXX
#include <stddef.h> #include "tag/Tag.hxx"
#include <memory>
struct Tag; #include <stddef.h>
class IcyMetaDataParser { class IcyMetaDataParser {
size_t data_size, data_rest; size_t data_size = 0, data_rest;
size_t meta_size, meta_position; size_t meta_size, meta_position;
char *meta_data; char *meta_data;
Tag *tag; std::unique_ptr<Tag> tag;
public: public:
IcyMetaDataParser():data_size(0) {} ~IcyMetaDataParser() noexcept {
~IcyMetaDataParser() {
Reset(); Reset();
} }
...@@ -42,7 +43,7 @@ public: ...@@ -42,7 +43,7 @@ public:
* Initialize an enabled icy_metadata object with the specified * Initialize an enabled icy_metadata object with the specified
* data_size (from the icy-metaint HTTP response header). * data_size (from the icy-metaint HTTP response header).
*/ */
void Start(size_t _data_size) { void Start(size_t _data_size) noexcept {
data_size = data_rest = _data_size; data_size = data_rest = _data_size;
meta_size = 0; meta_size = 0;
tag = nullptr; tag = nullptr;
...@@ -51,12 +52,12 @@ public: ...@@ -51,12 +52,12 @@ public:
/** /**
* Resets the icy_metadata. Call this after rewinding the stream. * Resets the icy_metadata. Call this after rewinding the stream.
*/ */
void Reset(); void Reset() noexcept;
/** /**
* Checks whether the icy_metadata object is enabled. * Checks whether the icy_metadata object is enabled.
*/ */
bool IsDefined() const { bool IsDefined() const noexcept {
return data_size > 0; return data_size > 0;
} }
...@@ -66,26 +67,24 @@ public: ...@@ -66,26 +67,24 @@ public:
* return value is smaller than "length", the caller should invoke * return value is smaller than "length", the caller should invoke
* icy_meta(). * icy_meta().
*/ */
size_t Data(size_t length); size_t Data(size_t length) noexcept;
/** /**
* Reads metadata from the stream. Returns the number of bytes * Reads metadata from the stream. Returns the number of bytes
* consumed. If the return value is smaller than "length", the caller * consumed. If the return value is smaller than "length", the caller
* should invoke icy_data(). * should invoke icy_data().
*/ */
size_t Meta(const void *data, size_t length); size_t Meta(const void *data, size_t length) noexcept;
/** /**
* Parse data and eliminate metadata. * Parse data and eliminate metadata.
* *
* @return the number of data bytes remaining in the buffer * @return the number of data bytes remaining in the buffer
*/ */
size_t ParseInPlace(void *data, size_t length); size_t ParseInPlace(void *data, size_t length) noexcept;
Tag *ReadTag() { std::unique_ptr<Tag> ReadTag() noexcept {
Tag *result = tag; return std::exchange(tag, nullptr);
tag = nullptr;
return result;
} }
}; };
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
* *
*/ */
#include "config.h"
#include "Idle.hxx" #include "Idle.hxx"
#include "Main.hxx" #include "Main.hxx"
#include "Instance.hxx" #include "Instance.hxx"
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
* *
*/ */
#include "config.h"
#include "IdleFlags.hxx" #include "IdleFlags.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
...@@ -42,6 +41,7 @@ static const char *const idle_names[] = { ...@@ -42,6 +41,7 @@ static const char *const idle_names[] = {
"message", "message",
"neighbor", "neighbor",
"mount", "mount",
"partition",
nullptr nullptr
}; };
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#ifndef MPD_IDLE_FLAGS_HXX #ifndef MPD_IDLE_FLAGS_HXX
#define MPD_IDLE_FLAGS_HXX #define MPD_IDLE_FLAGS_HXX
#include "Compiler.h" #include "util/Compiler.h"
/** song database has been updated*/ /** song database has been updated*/
static constexpr unsigned IDLE_DATABASE = 0x1; static constexpr unsigned IDLE_DATABASE = 0x1;
...@@ -67,6 +67,9 @@ static constexpr unsigned IDLE_NEIGHBOR = 0x800; ...@@ -67,6 +67,9 @@ static constexpr unsigned IDLE_NEIGHBOR = 0x800;
/** the mount list has changed */ /** the mount list has changed */
static constexpr unsigned IDLE_MOUNT = 0x1000; static constexpr unsigned IDLE_MOUNT = 0x1000;
/** the partition list has changed */
static constexpr unsigned IDLE_PARTITION = 0x2000;
/** /**
* Get idle names * Get idle names
*/ */
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -23,8 +23,16 @@ ...@@ -23,8 +23,16 @@
#include "Idle.hxx" #include "Idle.hxx"
#include "Stats.hxx" #include "Stats.hxx"
#ifdef ENABLE_CURL
#include "RemoteTagCache.hxx"
#include "util/UriUtil.hxx"
#endif
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
#include "db/DatabaseError.hxx" #include "db/DatabaseError.hxx"
#include "db/Interface.hxx"
#include "db/update/Service.hxx"
#include "storage/StorageInterface.hxx"
#ifdef ENABLE_SQLITE #ifdef ENABLE_SQLITE
#include "sticker/StickerDatabase.hxx" #include "sticker/StickerDatabase.hxx"
...@@ -32,7 +40,40 @@ ...@@ -32,7 +40,40 @@
#endif #endif
#endif #endif
#include <stdexcept> #include <exception>
Instance::Instance()
:rtio_thread(true),
#ifdef ENABLE_SYSTEMD_DAEMON
systemd_watchdog(event_loop),
#endif
idle_monitor(event_loop, BIND_THIS_METHOD(OnIdle))
{
}
Instance::~Instance() noexcept
{
#ifdef ENABLE_DATABASE
delete update;
if (database != nullptr) {
database->Close();
database.reset();
}
delete storage;
#endif
}
Partition *
Instance::FindPartition(const char *name) noexcept
{
for (auto &partition : partitions)
if (partition.name == name)
return &partition;
return nullptr;
}
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
...@@ -54,7 +95,9 @@ Instance::OnDatabaseModified() ...@@ -54,7 +95,9 @@ Instance::OnDatabaseModified()
/* propagate the change to all subsystems */ /* propagate the change to all subsystems */
stats_invalidate(); stats_invalidate();
partition->DatabaseModified(*database);
for (auto &partition : partitions)
partition.DatabaseModified(*database);
} }
void void
...@@ -67,12 +110,13 @@ Instance::OnDatabaseSongRemoved(const char *uri) ...@@ -67,12 +110,13 @@ Instance::OnDatabaseSongRemoved(const char *uri)
if (sticker_enabled()) { if (sticker_enabled()) {
try { try {
sticker_song_delete(uri); sticker_song_delete(uri);
} catch (const std::runtime_error &) { } catch (...) {
} }
} }
#endif #endif
partition->StaleSong(uri); for (auto &partition : partitions)
partition.StaleSong(uri);
} }
#endif #endif
...@@ -80,15 +124,45 @@ Instance::OnDatabaseSongRemoved(const char *uri) ...@@ -80,15 +124,45 @@ Instance::OnDatabaseSongRemoved(const char *uri)
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
void void
Instance::FoundNeighbor(gcc_unused const NeighborInfo &info) Instance::FoundNeighbor(gcc_unused const NeighborInfo &info) noexcept
{
for (auto &partition : partitions)
partition.EmitIdle(IDLE_NEIGHBOR);
}
void
Instance::LostNeighbor(gcc_unused const NeighborInfo &info) noexcept
{
for (auto &partition : partitions)
partition.EmitIdle(IDLE_NEIGHBOR);
}
#endif
#ifdef ENABLE_CURL
void
Instance::LookupRemoteTag(const char *uri) noexcept
{ {
partition->EmitIdle(IDLE_NEIGHBOR); if (!uri_has_scheme(uri))
return;
if (!remote_tag_cache)
remote_tag_cache = std::make_unique<RemoteTagCache>(event_loop,
*this);
remote_tag_cache->Lookup(uri);
} }
void void
Instance::LostNeighbor(gcc_unused const NeighborInfo &info) Instance::OnRemoteTag(const char *uri, const Tag &tag) noexcept
{ {
partition->EmitIdle(IDLE_NEIGHBOR); if (!tag.IsDefined())
/* boring */
return;
for (auto &partition : partitions)
partition.TagModified(uri, tag);
} }
#endif #endif
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -20,10 +20,19 @@ ...@@ -20,10 +20,19 @@
#ifndef MPD_INSTANCE_HXX #ifndef MPD_INSTANCE_HXX
#define MPD_INSTANCE_HXX #define MPD_INSTANCE_HXX
#include "check.h" #include "config.h"
#include "event/Loop.hxx" #include "event/Loop.hxx"
#include "event/Thread.hxx"
#include "event/MaskMonitor.hxx" #include "event/MaskMonitor.hxx"
#include "Compiler.h" #include "util/Compiler.h"
#ifdef ENABLE_SYSTEMD_DAEMON
#include "lib/systemd/Watchdog.hxx"
#endif
#ifdef ENABLE_CURL
#include "RemoteTagCacheHandler.hxx"
#endif
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
#include "neighbor/Listener.hxx" #include "neighbor/Listener.hxx"
...@@ -32,14 +41,18 @@ class NeighborGlue; ...@@ -32,14 +41,18 @@ class NeighborGlue;
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
#include "db/DatabaseListener.hxx" #include "db/DatabaseListener.hxx"
class Database; #include "db/Ptr.hxx"
class Storage; class Storage;
class UpdateService; class UpdateService;
#endif #endif
#include <memory>
#include <list>
class ClientList; class ClientList;
struct Partition; struct Partition;
class StateFile; class StateFile;
class RemoteTagCache;
/** /**
* A utility class which, when used as the first base class, ensures * A utility class which, when used as the first base class, ensures
...@@ -63,7 +76,27 @@ struct Instance final ...@@ -63,7 +76,27 @@ struct Instance final
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
public NeighborListener public NeighborListener
#endif #endif
#ifdef ENABLE_CURL
, public RemoteTagCacheHandler
#endif
{ {
/**
* A thread running an #EventLoop for non-blocking (bulk) I/O.
*/
EventThread io_thread;
/**
* Another thread running an #EventLoop for non-blocking
* (real-time) I/O. This is used instead of #io_thread for
* events which require low latency, e.g. for filling hardware
* ring buffers.
*/
EventThread rtio_thread;
#ifdef ENABLE_SYSTEMD_DAEMON
Systemd::Watchdog systemd_watchdog;
#endif
MaskMonitor idle_monitor; MaskMonitor idle_monitor;
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
...@@ -71,7 +104,7 @@ struct Instance final ...@@ -71,7 +104,7 @@ struct Instance final
#endif #endif
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
Database *database; DatabasePtr database;
/** /**
* This is really a #CompositeStorage. To avoid heavy include * This is really a #CompositeStorage. To avoid heavy include
...@@ -82,19 +115,23 @@ struct Instance final ...@@ -82,19 +115,23 @@ struct Instance final
UpdateService *update = nullptr; UpdateService *update = nullptr;
#endif #endif
#ifdef ENABLE_CURL
std::unique_ptr<RemoteTagCache> remote_tag_cache;
#endif
ClientList *client_list; ClientList *client_list;
Partition *partition; std::list<Partition> partitions;
StateFile *state_file; StateFile *state_file = nullptr;
Instance() Instance();
:idle_monitor(event_loop, BIND_THIS_METHOD(OnIdle)), state_file(nullptr) {} ~Instance() noexcept;
/** /**
* Initiate shutdown. Wrapper for EventLoop::Break(). * Wrapper for EventLoop::Break(). Call to initiate shutdown.
*/ */
void Shutdown() { void Break() {
event_loop.Break(); event_loop.Break();
} }
...@@ -102,6 +139,15 @@ struct Instance final ...@@ -102,6 +139,15 @@ struct Instance final
idle_monitor.OrMask(mask); idle_monitor.OrMask(mask);
} }
/**
* Find a #Partition with the given name. Returns nullptr if
* no such partition was found.
*/
gcc_pure
Partition *FindPartition(const char *name) noexcept;
void BeginShutdownPartitions() noexcept;
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
/** /**
* Returns the global #Database instance. May return nullptr * Returns the global #Database instance. May return nullptr
...@@ -109,7 +155,7 @@ struct Instance final ...@@ -109,7 +155,7 @@ struct Instance final
* music_directory was configured). * music_directory was configured).
*/ */
Database *GetDatabase() { Database *GetDatabase() {
return database; return database.get();
} }
/** /**
...@@ -120,6 +166,16 @@ struct Instance final ...@@ -120,6 +166,16 @@ struct Instance final
const Database &GetDatabaseOrThrow() const; const Database &GetDatabaseOrThrow() const;
#endif #endif
void BeginShutdownUpdate() noexcept;
#ifdef ENABLE_CURL
void LookupRemoteTag(const char *uri) noexcept;
#else
void LookupRemoteTag(const char *) noexcept {
/* no-op */
}
#endif
private: private:
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
void OnDatabaseModified() override; void OnDatabaseModified() override;
...@@ -128,8 +184,13 @@ private: ...@@ -128,8 +184,13 @@ private:
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
/* virtual methods from class NeighborListener */ /* virtual methods from class NeighborListener */
void FoundNeighbor(const NeighborInfo &info) override; void FoundNeighbor(const NeighborInfo &info) noexcept override;
void LostNeighbor(const NeighborInfo &info) override; void LostNeighbor(const NeighborInfo &info) noexcept override;
#endif
#ifdef ENABLE_CURL
/* virtual methods from class RemoteTagCacheHandler */
void OnRemoteTag(const char *uri, const Tag &tag) noexcept override;
#endif #endif
/* callback for #idle_monitor */ /* callback for #idle_monitor */
......
/* /*
* Copyright 2003-2017 The Music Player Daemon Project * Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -19,18 +19,21 @@ ...@@ -19,18 +19,21 @@
#include "config.h" #include "config.h"
#include "Listen.hxx" #include "Listen.hxx"
#include "client/Client.hxx" #include "Log.hxx"
#include "client/Listener.hxx"
#include "config/Param.hxx" #include "config/Param.hxx"
#include "config/ConfigGlobal.hxx" #include "config/Data.hxx"
#include "config/ConfigOption.hxx" #include "config/Option.hxx"
#include "net/SocketAddress.hxx" #include "config/Net.hxx"
#include "event/ServerSocket.hxx" #include "net/AllocatedSocketAddress.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/SocketUtil.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "Log.hxx" #include "fs/XDG.hxx"
#include <sys/stat.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
...@@ -38,49 +41,14 @@ ...@@ -38,49 +41,14 @@
#include <systemd/sd-daemon.h> #include <systemd/sd-daemon.h>
#endif #endif
static constexpr Domain listen_domain("listen");
#define DEFAULT_PORT 6600 #define DEFAULT_PORT 6600
class ClientListener final : public ServerSocket {
Partition &partition;
public:
ClientListener(EventLoop &_loop, Partition &_partition)
:ServerSocket(_loop), partition(_partition) {}
private:
void OnAccept(int fd, SocketAddress address, int uid) override {
client_new(GetEventLoop(), partition,
fd, address, uid);
}
};
static ClientListener *listen_socket;
int listen_port; int listen_port;
/**
* Throws #std::runtime_error on error.
*/
static void
listen_add_config_param(unsigned int port,
const ConfigParam *param)
{
assert(param != nullptr);
if (0 == strcmp(param->value.c_str(), "any")) {
listen_socket->AddPort(port);
} else if (param->value[0] == '/' || param->value[0] == '~') {
listen_socket->AddPath(param->GetPath());
} else {
listen_socket->AddHost(param->value.c_str(), port);
}
}
#ifdef ENABLE_SYSTEMD_DAEMON #ifdef ENABLE_SYSTEMD_DAEMON
static bool static bool
listen_systemd_activation() listen_systemd_activation(ClientListener &listener)
{ {
int n = sd_listen_fds(true); int n = sd_listen_fds(true);
if (n <= 0) { if (n <= 0) {
...@@ -91,67 +59,103 @@ listen_systemd_activation() ...@@ -91,67 +59,103 @@ listen_systemd_activation()
for (int i = SD_LISTEN_FDS_START, end = SD_LISTEN_FDS_START + n; for (int i = SD_LISTEN_FDS_START, end = SD_LISTEN_FDS_START + n;
i != end; ++i) i != end; ++i)
listen_socket->AddFD(i); listener.AddFD(UniqueSocketDescriptor(i));
return true; return true;
} }
#endif #endif
void /**
listen_global_init(EventLoop &loop, Partition &partition) * Listen on "$XDG_RUNTIME_DIR/mpd/socket" (if applicable).
*
* @return true if a listener socket was added
*/
static bool
ListenXdgRuntimeDir(ClientListener &listener) noexcept
{ {
int port = config_get_positive(ConfigOption::PORT, DEFAULT_PORT); #if defined(USE_XDG) && defined(HAVE_UN)
const auto *param = config_get_param(ConfigOption::BIND_TO_ADDRESS); if (geteuid() == 0)
/* this MPD instance is a system-wide daemon; don't
use $XDG_RUNTIME_DIR */
return false;
Path xdg_runtime_dir = Path::FromFS(getenv("XDG_RUNTIME_DIR"));
if (xdg_runtime_dir.IsNull())
return false;
const auto mpd_runtime_dir = xdg_runtime_dir / Path::FromFS("mpd");
mkdir(mpd_runtime_dir.c_str(), 0700);
listen_socket = new ClientListener(loop, partition); const auto socket_path = mpd_runtime_dir / Path::FromFS("socket");
unlink(socket_path.c_str());
AllocatedSocketAddress address;
address.SetLocal(socket_path.c_str());
try {
auto fd = socket_bind_listen(AF_LOCAL, SOCK_STREAM, 0,
address, 5);
chmod(socket_path.c_str(), 0600);
listener.AddFD(std::move(fd), std::move(address));
return true;
} catch (...) {
FormatError(std::current_exception(),
"Failed to listen on '%s' (not fatal)",
socket_path.c_str());
return false;
}
#else
(void)listener;
return false;
#endif
}
void
listen_global_init(const ConfigData &config, ClientListener &listener)
{
int port = config.GetPositive(ConfigOption::PORT, DEFAULT_PORT);
#ifdef ENABLE_SYSTEMD_DAEMON #ifdef ENABLE_SYSTEMD_DAEMON
if (listen_systemd_activation()) if (listen_systemd_activation(listener))
return; return;
#endif #endif
if (param != nullptr) { for (const auto &param : config.GetParamList(ConfigOption::BIND_TO_ADDRESS)) {
/* "bind_to_address" is configured, create listeners try {
for all values */ ServerSocketAddGeneric(listener, param.value.c_str(),
port);
do { } catch (...) {
try { std::throw_with_nested(FormatRuntimeError("Failed to listen on %s (line %i)",
listen_add_config_param(port, param); param.value.c_str(),
} catch (const std::runtime_error &e) { param.line));
delete listen_socket; }
std::throw_with_nested(FormatRuntimeError("Failed to listen on %s (line %i)", }
param->value.c_str(),
param->line)); bool have_xdg_runtime_listener = false;
}
} while ((param = param->next) != nullptr); if (listener.IsEmpty()) {
} else {
/* no "bind_to_address" configured, bind the /* no "bind_to_address" configured, bind the
configured port on all interfaces */ configured port on all interfaces */
have_xdg_runtime_listener = ListenXdgRuntimeDir(listener);
try { try {
listen_socket->AddPort(port); listener.AddPort(port);
} catch (const std::runtime_error &e) { } catch (...) {
delete listen_socket;
std::throw_with_nested(FormatRuntimeError("Failed to listen on *:%d: ", port)); std::throw_with_nested(FormatRuntimeError("Failed to listen on *:%d: ", port));
} }
} }
try { try {
listen_socket->Open(); listener.Open();
} catch (const std::runtime_error &e) { } catch (...) {
delete listen_socket; if (have_xdg_runtime_listener)
throw; LogError(std::current_exception(),
"Default TCP listener setup failed, but this is okay because we have a $XDG_RUNTIME_DIR listener");
else
throw;
} }
listen_port = port; listen_port = port;
} }
void listen_global_finish(void)
{
LogDebug(listen_domain, "listen_global_finish called");
assert(listen_socket != nullptr);
delete listen_socket;
}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment