Compare commits
185 Commits
Author | SHA1 | Date | |
---|---|---|---|
feac1a3f56 | |||
f3c37e484e | |||
49130c2018 | |||
94af199c49 | |||
2d25f6f57f | |||
cf179ec294 | |||
4d6f220a2f | |||
0ffbe5b5ea | |||
5b83c834ac | |||
da7f32bddb | |||
9a5eac4ea9 | |||
6571b5d118 | |||
12dff8e382 | |||
c4da87a0cb | |||
446f8f29d3 | |||
48cc76f114 | |||
a0892b852e | |||
485c7805eb | |||
23802f4489 | |||
3fedd978a2 | |||
a9f1bed922 | |||
eb23788fec | |||
f6d73555a6 | |||
a56a709406 | |||
5f253e66f6 | |||
4669f7e2b9 | |||
4c90f88704 | |||
a7213b78d6 | |||
719333e16e | |||
100e471b49 | |||
3f2016e552 | |||
dd89ea4505 | |||
101e12cf9a | |||
f382808450 | |||
0cbe3c2a93 | |||
4f0ae28359 | |||
6a4250f485 | |||
3322b29e6a | |||
33ac472601 | |||
561d6fd478 | |||
42a01822bf | |||
38f1237d49 | |||
8df77122e5 | |||
fef6b9df80 | |||
d52eac66db | |||
70879f0abc | |||
bcb393628e | |||
18d3a5c12b | |||
6ee3d0102b | |||
fc9626e2f4 | |||
3bedd94fc8 | |||
8842650c33 | |||
d5bf128cee | |||
5cd86e272f | |||
740cbe9e02 | |||
ed890a273a | |||
068cd559e1 | |||
dc127f39a7 | |||
7a99a7008c | |||
70b451db7b | |||
2ab03a0914 | |||
2fa8c7d2db | |||
7c759ba8b0 | |||
6d9b452fde | |||
f7eb1c9a83 | |||
2d22e6dee4 | |||
4587bf759d | |||
e1e37cfe3c | |||
381934985a | |||
a8042885ac | |||
a71e68db50 | |||
1417578b3d | |||
96befa138c | |||
16a99804de | |||
75a39ed279 | |||
4d357ab77c | |||
d4f3dd49b4 | |||
4ec6d0555a | |||
a6a1182c4c | |||
a59c9c602b | |||
0c4d824d64 | |||
a5281856c9 | |||
0206a46d39 | |||
9475ef2202 | |||
edae00e719 | |||
fb695bc55f | |||
23a5b8fd3c | |||
273a93cfcf | |||
d105985d78 | |||
f8cfeb39e9 | |||
d5d3982d3c | |||
47341107ea | |||
90eaa87a4d | |||
b09a54b2c2 | |||
10aec174d5 | |||
d32ed194e8 | |||
70d0fbd715 | |||
302432e157 | |||
4ab8a677dc | |||
52e4a4c904 | |||
a0f6932ebe | |||
6e700dab69 | |||
35eaed7206 | |||
e7c963f2ce | |||
949d72e368 | |||
8d2a184658 | |||
c877a32d97 | |||
541468f0ca | |||
d2797effa3 | |||
1170fb1e1e | |||
65b9b3195c | |||
258830e913 | |||
d91da96798 | |||
b3897df682 | |||
3cacb56bb7 | |||
15a1973e28 | |||
ad7d47a8ba | |||
0948c607b6 | |||
60d04052c5 | |||
c1780ac657 | |||
e49cf0ec38 | |||
e1d641f684 | |||
4efd0a9f77 | |||
f6f8751332 | |||
abb28593ce | |||
115693b046 | |||
e4b055eb6d | |||
9866adff95 | |||
a8b0c55818 | |||
cac88e8be5 | |||
e9f6a3482c | |||
5d2e80f188 | |||
cfd4d5b13e | |||
06514aec63 | |||
4ded1ae67b | |||
1da974e3fa | |||
94f06f0946 | |||
d9eec8a455 | |||
eaecbcafb2 | |||
73b5d0a9b9 | |||
c2d0f35e7a | |||
ab99a57997 | |||
c8ebaf3521 | |||
52d00f7e30 | |||
309491a6d8 | |||
e7bfd32ccc | |||
6f283b52ab | |||
32bddfabea | |||
1944c826bc | |||
619bb60b26 | |||
c549e16ed1 | |||
01c9c4507f | |||
8c9d7bf07e | |||
44ef34db88 | |||
5781f223f6 | |||
e4c8ebe056 | |||
76b25a1377 | |||
ccc3ee663b | |||
0626661764 | |||
31db04a3ca | |||
0c7163b9db | |||
7d78cad8af | |||
912530ed20 | |||
d3f37199b9 | |||
a4748d84b0 | |||
8f847ec381 | |||
3a70f09dd3 | |||
568f63100b | |||
3e25916b37 | |||
5f9438dae6 | |||
99e65c58ce | |||
df71b07e9d | |||
2694195215 | |||
66450d1f3c | |||
76efea3aa7 | |||
7ab0dfc8ce | |||
15ff7c4cad | |||
9ab9b97f20 | |||
88d92aceab | |||
a2ce4352c8 | |||
84f43ccde8 | |||
38704c9cf3 | |||
910d0ec92b | |||
3b05c89765 | |||
e77b3fa46f |
NEWS
android
doc
meson.buildpython/build
autotools.pycmake.pydownload.pylibs.pymakeproject.pymeson.pyopenssl.pyproject.pyquilt.pytar.pytoolchain.pyverify.pyzlib.py
src
CommandLine.cxxCommandLine.hxxLogInit.cxxMain.cxxPlaylistFile.cxxRemoteTagCache.hxxSongPrint.cxxSongSave.cxxSongSave.hxxStateFileConfig.cxxTagPrint.cxxTimePrint.cxx
android
archive
command
db
decoder
DecoderList.cxx
plugins
encoder
plugins
event
fs
input
io
java
lib
crypto
curl
dbus
ffmpeg
modplug
patches
output
storage
system
tag
thread
time
unix
util
win32
subprojects
systemd
test
win32
64
NEWS
64
NEWS
@ -1,3 +1,67 @@
|
||||
ver 0.23.14 (2023/10/08)
|
||||
* decoder
|
||||
- flac: fix scanning files with non-ASCII names on Windows
|
||||
- mad: fix calculation of LAME peak values
|
||||
* mixer
|
||||
- wasapi: fix problem setting volume
|
||||
* more libfmt 10 fixes
|
||||
* fix auto-detected systemd unit directory
|
||||
* Android
|
||||
- require Android 7 or newer
|
||||
|
||||
ver 0.23.13 (2023/05/22)
|
||||
* input
|
||||
- curl: fix busy loop after connection failed
|
||||
- curl: hide "404" log messages for non-existent ".mpdignore" files
|
||||
* archive
|
||||
- zzip: fix crash bug
|
||||
* database
|
||||
- simple: reveal hidden songs after deleting containing CUE
|
||||
* decoder
|
||||
- ffmpeg: reorder to a lower priority than "gme"
|
||||
- gme: require GME 0.6 or later
|
||||
* output
|
||||
- pipewire: fix corruption bug due to missing lock
|
||||
* Linux
|
||||
- shut down if parent process dies in --no-daemon mode
|
||||
- determine systemd unit directories via pkg-config
|
||||
* support libfmt 10
|
||||
|
||||
ver 0.23.12 (2023/01/17)
|
||||
* input
|
||||
- curl: require CURL 7.55.0 or later
|
||||
* decoder
|
||||
- mad: fix integer underflow with very small files
|
||||
* tags
|
||||
- fix crash bug due to race condition
|
||||
* output
|
||||
- pipewire: adjust to PipeWire 0.3.64 API change
|
||||
* fix build failures with GCC 13
|
||||
|
||||
ver 0.23.11 (2022/11/28)
|
||||
* database
|
||||
- simple: move default database to ~/.cache/mpd/db from ~/.cache/mpd.db
|
||||
- simple: default "cache_directory" to ~/.cache/mpd/mounts
|
||||
* macOS: fix build failure "no archive members specified"
|
||||
* Windows
|
||||
- fix crash bug (stack buffer overflow) after I/O errors
|
||||
- fix path traversal bug because backslash was allowed in playlist names
|
||||
* Android/Windows
|
||||
- update OpenSSL to 3.0.7
|
||||
- re-enable CURL's verbose error strings
|
||||
|
||||
ver 0.23.10 (2022/10/14)
|
||||
* storage
|
||||
- curl: fix file time stamps
|
||||
* decoder
|
||||
- ffmpeg: fix libfmt 9 compiler warning
|
||||
* encoder
|
||||
- flac: fix failure when libFLAC is built without Ogg support
|
||||
* output
|
||||
- alsa: fix crash bug
|
||||
* Windows
|
||||
- log to stdout by default, don't require "log_file" setting
|
||||
|
||||
ver 0.23.9 (2022/08/18)
|
||||
* input
|
||||
- cdio_paranoia: add options "mode" and "skip"
|
||||
|
@ -2,10 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="68"
|
||||
android:versionName="0.23.9">
|
||||
android:versionCode="72"
|
||||
android:versionName="0.23.14">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="30"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
|
127
android/build.py
127
android/build.py
@ -20,130 +20,13 @@ if not os.path.isdir(ndk_path):
|
||||
print("NDK not found in", ndk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'arm-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'llvm_triple': 'armv7-linux-androideabi',
|
||||
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'arm64-v8a': {
|
||||
'arch': 'aarch64-linux-android',
|
||||
'ndk_arch': 'arm64',
|
||||
'llvm_triple': 'aarch64-linux-android',
|
||||
'cflags': '-fpic',
|
||||
},
|
||||
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'llvm_triple': 'i686-linux-android',
|
||||
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
|
||||
'x86_64': {
|
||||
'arch': 'x86_64-linux-android',
|
||||
'ndk_arch': 'x86_64',
|
||||
'llvm_triple': 'x86_64-linux-android',
|
||||
'cflags': '-fPIC -m64',
|
||||
},
|
||||
}
|
||||
|
||||
# select the NDK target
|
||||
abi_info = android_abis[android_abi]
|
||||
arch = abi_info['arch']
|
||||
|
||||
# the path to the MPD sources
|
||||
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
|
||||
sys.path[0] = os.path.join(mpd_path, 'python')
|
||||
|
||||
# output directories
|
||||
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)
|
||||
build_path = os.path.join(arch_path, 'build')
|
||||
|
||||
# build host configuration
|
||||
build_arch = 'linux-x86_64'
|
||||
|
||||
# set up the NDK toolchain
|
||||
|
||||
class AndroidNdkToolchain:
|
||||
def __init__(self, tarball_path, src_path, build_path,
|
||||
use_cxx):
|
||||
self.tarball_path = tarball_path
|
||||
self.src_path = src_path
|
||||
self.build_path = build_path
|
||||
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
android_api_level = '21'
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
self.arch = arch
|
||||
self.actual_arch = arch
|
||||
self.install_prefix = install_prefix
|
||||
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = abi_info['llvm_triple'] + android_api_level
|
||||
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
self.cc = os.path.join(llvm_bin, 'clang')
|
||||
self.cxx = os.path.join(llvm_bin, 'clang++')
|
||||
common_flags += ' -target ' + llvm_triple
|
||||
|
||||
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
|
||||
|
||||
self.ar = os.path.join(llvm_bin, 'llvm-ar')
|
||||
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
|
||||
self.nm = os.path.join(llvm_bin, 'llvm-nm')
|
||||
self.strip = os.path.join(llvm_bin, 'llvm-strip')
|
||||
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = ' -isystem ' + os.path.join(install_prefix, 'include')
|
||||
self.ldflags = ' -L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -Wl,--exclude-libs=ALL' + \
|
||||
' ' + common_flags
|
||||
self.ldflags = common_flags
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = ndk_arch == 'arm'
|
||||
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
|
||||
self.is_aarch64 = ndk_arch == 'arm64'
|
||||
self.is_windows = False
|
||||
|
||||
libstdcxx_flags = ''
|
||||
libstdcxx_cxxflags = ''
|
||||
libstdcxx_ldflags = ''
|
||||
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:
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
self.ldflags += ' ' + libstdcxx_ldflags
|
||||
self.libs += ' ' + libstdcxx_libs
|
||||
|
||||
self.env = dict(os.environ)
|
||||
|
||||
# redirect pkg-config to use our root directory instead of the
|
||||
# default one on the build host
|
||||
import shutil
|
||||
bin_dir = os.path.join(install_prefix, 'bin')
|
||||
os.makedirs(bin_dir, exist_ok=True)
|
||||
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
|
||||
from build.toolchain import AndroidNdkToolchain
|
||||
|
||||
# a list of third-party libraries to be used by MPD on Android
|
||||
from build.libs import *
|
||||
@ -165,13 +48,17 @@ thirdparty_libs = [
|
||||
|
||||
# build the third-party libraries
|
||||
for x in thirdparty_libs:
|
||||
toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
|
||||
toolchain = AndroidNdkToolchain(mpd_path, lib_path,
|
||||
tarball_path, src_path,
|
||||
ndk_path, android_abi,
|
||||
use_cxx=x.use_cxx)
|
||||
if not x.is_installed(toolchain):
|
||||
x.build(toolchain)
|
||||
|
||||
# configure and build MPD
|
||||
toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
|
||||
toolchain = AndroidNdkToolchain(mpd_path, lib_path,
|
||||
tarball_path, src_path,
|
||||
ndk_path, android_abi,
|
||||
use_cxx=True)
|
||||
|
||||
configure_args += [
|
||||
|
@ -38,7 +38,10 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.23.9'
|
||||
with open('../meson.build') as f:
|
||||
import re
|
||||
version = re.match(r"project\([^\)]*\bversion:\s*'([^']+)'",
|
||||
f.read(4096)).group(1)
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
#release = version + '~git'
|
||||
|
||||
@ -47,7 +50,7 @@ version = '0.23.9'
|
||||
#
|
||||
# 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
|
||||
language = "en"
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
|
@ -11,6 +11,12 @@ Music Player Daemon
|
||||
client
|
||||
protocol
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: man pages:
|
||||
|
||||
mpd.1
|
||||
mpd.conf.5
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
@ -181,7 +181,7 @@
|
||||
#
|
||||
#database {
|
||||
# plugin "simple"
|
||||
# path "~/.local/share/mpd/db
|
||||
# path "~/.local/share/mpd/db"
|
||||
# cache_directory "~/.local/share/mpd/cache"
|
||||
#}
|
||||
#
|
||||
@ -314,6 +314,7 @@ input {
|
||||
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
|
||||
# or
|
||||
## device "0" # optional
|
||||
## mixer_type "hardware" # optional
|
||||
## Exclusive mode blocks all other audio source, and get best audio quality without resampling.
|
||||
## exclusive "no" # optional
|
||||
## Enumerate all devices in log.
|
||||
|
@ -219,8 +219,9 @@ Opens remote files or streams over HTTP using libcurl.
|
||||
|
||||
Note that unless overridden by the below settings (e.g. by setting
|
||||
them to a blank value), general curl configuration from environment
|
||||
variables such as ``http_proxy`` or specified in :file:`~/.curlrc`
|
||||
will be in effect.
|
||||
variables such as ``http_proxy`` will be in effect.
|
||||
|
||||
User name and password are read from an optional :file:`~/.netrc`, :file:`~/.curlrc` is not read.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
@ -1114,7 +1115,7 @@ Connect to a `PipeWire <https://pipewire.org/>`_ server. Requires
|
||||
* - **target NAME**
|
||||
- Link to the given target. If not specified, let the PipeWire
|
||||
manager select a target. To get a list of available targets,
|
||||
type ``pw-cli dump short Node``
|
||||
type ``pw-cli ls Node``
|
||||
* - **remote NAME**
|
||||
- The name of the remote to connect to. The default is
|
||||
``pipewire-0``.
|
||||
|
@ -199,7 +199,7 @@ Compiling for Android
|
||||
You need:
|
||||
|
||||
* Android SDK
|
||||
* `Android NDK r23 <https://developer.android.com/ndk/downloads>`_
|
||||
* `Android NDK r25b <https://developer.android.com/ndk/downloads>`_
|
||||
* `Meson 0.56.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* cmake
|
||||
@ -611,6 +611,11 @@ If ReplayGain is enabled, then the setting ``replaygain_preamp`` is
|
||||
set to a value (in dB) between ``-15`` and ``15``. This is the gain
|
||||
applied to songs with ReplayGain tags.
|
||||
|
||||
On songs without ReplayGain tags, the setting
|
||||
``replaygain_missing_preamp`` is used instead. If this setting is not
|
||||
configured, then no ReplayGain is applied to such songs, and they will
|
||||
appear too loud.
|
||||
|
||||
ReplayGain is usually implemented with a software volume filter (which
|
||||
prevents `Bit-perfect playback`_). To use a hardware mixer, set
|
||||
``replay_gain_handler`` to ``mixer`` in the ``audio_output`` section
|
||||
@ -656,9 +661,11 @@ MPD enables MixRamp if:
|
||||
- Cross-fade is enabled
|
||||
- :ref:`mixrampdelay <command_mixrampdelay>` is set to a positive
|
||||
value, e.g.::
|
||||
|
||||
mpc mixrampdelay 1
|
||||
- :ref:`mixrampdb <command_mixrampdb>` is set to a reasonable value,
|
||||
e.g.::
|
||||
|
||||
mpc mixrampdb -17
|
||||
- both songs have MixRamp tags
|
||||
- both songs have the same audio format (or :ref:`audio_output_format`
|
||||
|
12
meson.build
12
meson.build
@ -1,7 +1,7 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.23.9',
|
||||
version: '0.23.14',
|
||||
meson_version: '>= 0.56.0',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
@ -205,7 +205,6 @@ enable_daemon = not is_windows and not is_android and get_option('daemon')
|
||||
conf.set('ENABLE_DAEMON', enable_daemon)
|
||||
|
||||
conf.set('HAVE_GETPWNAM_R', compiler.has_function('getpwnam_r'))
|
||||
conf.set('HAVE_GETPWUID_R', compiler.has_function('getpwuid_r'))
|
||||
conf.set('HAVE_INITGROUPS', compiler.has_function('initgroups'))
|
||||
conf.set('HAVE_FNMATCH', compiler.has_function('fnmatch'))
|
||||
|
||||
@ -251,6 +250,14 @@ endif
|
||||
|
||||
fmt_dep = dependency('fmt', fallback: ['fmt', 'fmt_dep'])
|
||||
|
||||
if compiler.get_id() == 'clang' and compiler.version().version_compare('<15')
|
||||
fmt_dep = declare_dependency(
|
||||
dependencies: fmt_dep,
|
||||
# suppress bogus clang 14 warning (the version in Android NDK r25b)
|
||||
compile_args: ['-Wno-unused-local-typedef'],
|
||||
)
|
||||
endif
|
||||
|
||||
log = static_library(
|
||||
'log',
|
||||
'src/Log.cxx',
|
||||
@ -382,6 +389,7 @@ endif
|
||||
|
||||
if enable_database
|
||||
sources += [
|
||||
'src/storage/StorageState.cxx',
|
||||
'src/queue/PlaylistUpdate.cxx',
|
||||
'src/command/StorageCommands.cxx',
|
||||
'src/command/DatabaseCommands.cxx',
|
||||
|
@ -1,26 +1,32 @@
|
||||
import os.path, subprocess, sys
|
||||
from typing import Collection, Iterable, Optional, Sequence, Union
|
||||
from collections.abc import Mapping
|
||||
|
||||
from build.makeproject import MakeProject
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
class AutotoolsProject(MakeProject):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
autogen=False,
|
||||
autoreconf=False,
|
||||
cppflags='',
|
||||
ldflags='',
|
||||
libs='',
|
||||
subdirs=None,
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
configure_args: Iterable[str]=[],
|
||||
autogen: bool=False,
|
||||
autoreconf: bool=False,
|
||||
per_arch_cflags: Optional[Mapping[str, str]]=None,
|
||||
cppflags: str='',
|
||||
ldflags: str='',
|
||||
libs: str='',
|
||||
subdirs: Optional[Collection[str]]=None,
|
||||
**kwargs):
|
||||
MakeProject.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
self.autogen = autogen
|
||||
self.autoreconf = autoreconf
|
||||
self.per_arch_cflags = per_arch_cflags
|
||||
self.cppflags = cppflags
|
||||
self.ldflags = ldflags
|
||||
self.libs = libs
|
||||
self.subdirs = subdirs
|
||||
|
||||
def configure(self, toolchain):
|
||||
def configure(self, toolchain: AnyToolchain) -> str:
|
||||
src = self.unpack(toolchain)
|
||||
if self.autogen:
|
||||
if sys.platform == 'darwin':
|
||||
@ -35,27 +41,48 @@ class AutotoolsProject(MakeProject):
|
||||
|
||||
build = self.make_build_path(toolchain)
|
||||
|
||||
arch_cflags = ''
|
||||
if self.per_arch_cflags is not None and toolchain.host_triplet is not None:
|
||||
arch_cflags = self.per_arch_cflags.get(toolchain.host_triplet, '')
|
||||
|
||||
configure = [
|
||||
os.path.join(src, 'configure'),
|
||||
'CC=' + toolchain.cc,
|
||||
'CXX=' + toolchain.cxx,
|
||||
'CFLAGS=' + toolchain.cflags,
|
||||
'CXXFLAGS=' + toolchain.cxxflags,
|
||||
'CFLAGS=' + toolchain.cflags + ' ' + arch_cflags,
|
||||
'CXXFLAGS=' + toolchain.cxxflags + ' ' + arch_cflags,
|
||||
'CPPFLAGS=' + toolchain.cppflags + ' ' + self.cppflags,
|
||||
'LDFLAGS=' + toolchain.ldflags + ' ' + self.ldflags,
|
||||
'LIBS=' + toolchain.libs + ' ' + self.libs,
|
||||
'AR=' + toolchain.ar,
|
||||
'ARFLAGS=' + toolchain.arflags,
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
'STRIP=' + toolchain.strip,
|
||||
'--host=' + toolchain.arch,
|
||||
'--prefix=' + toolchain.install_prefix,
|
||||
'--enable-silent-rules',
|
||||
] + self.configure_args
|
||||
'--disable-silent-rules',
|
||||
]
|
||||
|
||||
if toolchain.host_triplet is not None:
|
||||
configure.append('--host=' + toolchain.host_triplet)
|
||||
|
||||
configure.extend(self.configure_args)
|
||||
|
||||
try:
|
||||
print(configure)
|
||||
subprocess.check_call(configure, cwd=build, env=toolchain.env)
|
||||
except subprocess.CalledProcessError:
|
||||
# dump config.log after a failed configure run
|
||||
try:
|
||||
with open(os.path.join(build, 'config.log')) as f:
|
||||
sys.stdout.write(f.read())
|
||||
except:
|
||||
pass
|
||||
# re-raise the exception
|
||||
raise
|
||||
|
||||
subprocess.check_call(configure, cwd=build, env=toolchain.env)
|
||||
return build
|
||||
|
||||
def _build(self, toolchain):
|
||||
def _build(self, toolchain: AnyToolchain) -> None:
|
||||
build = self.configure(toolchain)
|
||||
if self.subdirs is not None:
|
||||
for subdir in self.subdirs:
|
||||
|
@ -1,17 +1,21 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from typing import cast, Optional, Sequence, TextIO, Union
|
||||
from collections.abc import Mapping
|
||||
|
||||
from build.project import Project
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
def __write_cmake_compiler(f, language, compiler):
|
||||
def __write_cmake_compiler(f: TextIO, language: str, compiler: str) -> None:
|
||||
s = compiler.split(' ', 1)
|
||||
if len(s) == 2:
|
||||
print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f)
|
||||
compiler = s[1]
|
||||
print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f)
|
||||
|
||||
def __write_cmake_toolchain_file(f, toolchain):
|
||||
if '-darwin' in toolchain.actual_arch:
|
||||
def __write_cmake_toolchain_file(f: TextIO, toolchain: AnyToolchain) -> None:
|
||||
if toolchain.is_darwin:
|
||||
cmake_system_name = 'Darwin'
|
||||
elif toolchain.is_windows:
|
||||
cmake_system_name = 'Windows'
|
||||
@ -20,67 +24,99 @@ def __write_cmake_toolchain_file(f, toolchain):
|
||||
|
||||
f.write(f"""
|
||||
set(CMAKE_SYSTEM_NAME {cmake_system_name})
|
||||
set(CMAKE_SYSTEM_PROCESSOR {toolchain.actual_arch.split('-', 1)[0]})
|
||||
set(CMAKE_SYSTEM_PROCESSOR {toolchain.host_triplet.split('-', 1)[0]})
|
||||
|
||||
set(CMAKE_C_COMPILER_TARGET {toolchain.actual_arch})
|
||||
set(CMAKE_CXX_COMPILER_TARGET {toolchain.actual_arch})
|
||||
set(CMAKE_C_COMPILER_TARGET {toolchain.host_triplet})
|
||||
set(CMAKE_CXX_COMPILER_TARGET {toolchain.host_triplet})
|
||||
|
||||
set(CMAKE_C_FLAGS "{toolchain.cflags} {toolchain.cppflags}")
|
||||
set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
|
||||
set(CMAKE_C_FLAGS_INIT "{toolchain.cflags} {toolchain.cppflags}")
|
||||
set(CMAKE_CXX_FLAGS_INIT "{toolchain.cxxflags} {toolchain.cppflags}")
|
||||
""")
|
||||
__write_cmake_compiler(f, 'C', toolchain.cc)
|
||||
__write_cmake_compiler(f, 'CXX', toolchain.cxx)
|
||||
|
||||
def configure(toolchain, src, build, args=()):
|
||||
cross_args = []
|
||||
if cmake_system_name == 'Darwin':
|
||||
# On macOS, cmake forcibly adds an "-isysroot" flag even if
|
||||
# one is already present in the flags variable; this breaks
|
||||
# cross-compiling for iOS, and can be worked around by setting
|
||||
# the CMAKE_OSX_SYSROOT variable
|
||||
# (https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html).
|
||||
m = re.search(r'-isysroot +(\S+)', toolchain.cflags)
|
||||
if m:
|
||||
sysroot = m.group(1)
|
||||
|
||||
print(f'set(CMAKE_OSX_SYSROOT {sysroot})', file=f)
|
||||
|
||||
# search libraries and headers only in the sysroot, not on
|
||||
# the build host
|
||||
f.write(f"""
|
||||
set(CMAKE_FIND_ROOT_PATH "{toolchain.install_prefix};{sysroot}")
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
""")
|
||||
|
||||
def configure(toolchain: AnyToolchain, src: str, build: str, args: list[str]=[], env: Optional[Mapping[str, str]]=None) -> None:
|
||||
cross_args: list[str] = []
|
||||
|
||||
if toolchain.is_windows:
|
||||
cross_args.append('-DCMAKE_RC_COMPILER=' + toolchain.windres)
|
||||
|
||||
# Several targets need a sysroot to prevent pkg-config from
|
||||
# looking for libraries on the build host (TODO: fix this
|
||||
# properly); but we must not do that on Android because the NDK
|
||||
# has a sysroot already
|
||||
if '-android' not in toolchain.actual_arch and '-darwin' not in toolchain.actual_arch:
|
||||
cross_args.append('-DCMAKE_SYSROOT=' + toolchain.install_prefix)
|
||||
|
||||
os.makedirs(build, exist_ok=True)
|
||||
cmake_toolchain_file = os.path.join(build, 'cmake_toolchain_file')
|
||||
with open(cmake_toolchain_file, 'w') as f:
|
||||
__write_cmake_toolchain_file(f, toolchain)
|
||||
cross_args.append('-DCMAKE_RC_COMPILER=' + cast(str, toolchain.windres))
|
||||
|
||||
configure = [
|
||||
'cmake',
|
||||
src,
|
||||
|
||||
'-DCMAKE_TOOLCHAIN_FILE=' + cmake_toolchain_file,
|
||||
|
||||
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
|
||||
'-DCMAKE_BUILD_TYPE=release',
|
||||
|
||||
'-GNinja',
|
||||
] + cross_args + args
|
||||
|
||||
subprocess.check_call(configure, env=toolchain.env, cwd=build)
|
||||
if toolchain.host_triplet is not None:
|
||||
# cross-compiling: write a toolchain file
|
||||
os.makedirs(build, exist_ok=True)
|
||||
|
||||
# Several targets need a sysroot to prevent pkg-config from
|
||||
# looking for libraries on the build host (TODO: fix this
|
||||
# properly); but we must not do that on Android because the NDK
|
||||
# has a sysroot already
|
||||
if not toolchain.is_android and not toolchain.is_darwin:
|
||||
cross_args.append('-DCMAKE_SYSROOT=' + toolchain.install_prefix)
|
||||
|
||||
cmake_toolchain_file = os.path.join(build, 'cmake_toolchain_file')
|
||||
with open(cmake_toolchain_file, 'w') as f:
|
||||
__write_cmake_toolchain_file(f, toolchain)
|
||||
|
||||
configure.append('-DCMAKE_TOOLCHAIN_FILE=' + cmake_toolchain_file)
|
||||
|
||||
if env is None:
|
||||
env = toolchain.env
|
||||
else:
|
||||
env = {**toolchain.env, **env}
|
||||
|
||||
print(configure)
|
||||
subprocess.check_call(configure, env=env, cwd=build)
|
||||
|
||||
class CmakeProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
windows_configure_args=[],
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
configure_args: list[str]=[],
|
||||
windows_configure_args: list[str]=[],
|
||||
env: Optional[Mapping[str, str]]=None,
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
self.windows_configure_args = windows_configure_args
|
||||
self.env = env
|
||||
|
||||
def configure(self, toolchain):
|
||||
def configure(self, toolchain: AnyToolchain) -> str:
|
||||
src = self.unpack(toolchain)
|
||||
build = self.make_build_path(toolchain)
|
||||
configure_args = self.configure_args
|
||||
if toolchain.is_windows:
|
||||
configure_args = configure_args + self.windows_configure_args
|
||||
configure(toolchain, src, build, configure_args)
|
||||
configure(toolchain, src, build, configure_args, self.env)
|
||||
return build
|
||||
|
||||
def _build(self, toolchain):
|
||||
def _build(self, toolchain: AnyToolchain) -> None:
|
||||
build = self.configure(toolchain)
|
||||
subprocess.check_call(['ninja', 'install'],
|
||||
subprocess.check_call(['ninja', '-v', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
||||
|
@ -1,12 +1,50 @@
|
||||
from build.verify import verify_file_digest
|
||||
from typing import Sequence, Union
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
def download_and_verify(url, md5, parent_path):
|
||||
from .verify import verify_file_digest
|
||||
|
||||
def __to_string_sequence(x: Union[str, Sequence[str]]) -> Sequence[str]:
|
||||
if isinstance(x, str):
|
||||
return (x,)
|
||||
else:
|
||||
return x
|
||||
|
||||
def __get_any(x: Union[str, Sequence[str]]) -> str:
|
||||
if isinstance(x, str):
|
||||
return x
|
||||
else:
|
||||
return x[0]
|
||||
|
||||
def __download_one(url: str, path: str) -> None:
|
||||
print("download", url)
|
||||
urllib.request.urlretrieve(url, path)
|
||||
|
||||
def __download(urls: Sequence[str], path: str) -> None:
|
||||
for url in urls[:-1]:
|
||||
try:
|
||||
__download_one(url, path)
|
||||
return
|
||||
except:
|
||||
print("download error:", sys.exc_info()[0])
|
||||
__download_one(urls[-1], path)
|
||||
|
||||
def __download_and_verify_to(urls: Sequence[str], md5: str, path: str) -> None:
|
||||
__download(urls, path)
|
||||
if not verify_file_digest(path, md5):
|
||||
raise RuntimeError("Digest mismatch")
|
||||
|
||||
def download_basename(urls: Union[str, Sequence[str]]) -> str:
|
||||
return os.path.basename(__get_any(urls))
|
||||
|
||||
def download_and_verify(urls: Union[str, Sequence[str]], md5: str, parent_path: str) -> str:
|
||||
"""Download a file, verify its MD5 checksum and return the local path."""
|
||||
|
||||
base = download_basename(urls)
|
||||
|
||||
os.makedirs(parent_path, exist_ok=True)
|
||||
path = os.path.join(parent_path, os.path.basename(url))
|
||||
path = os.path.join(parent_path, base)
|
||||
|
||||
try:
|
||||
if verify_file_digest(path, md5): return path
|
||||
@ -16,11 +54,6 @@ def download_and_verify(url, md5, parent_path):
|
||||
|
||||
tmp_path = path + '.tmp'
|
||||
|
||||
print("download", url)
|
||||
urllib.request.urlretrieve(url, tmp_path)
|
||||
if not verify_file_digest(tmp_path, md5):
|
||||
os.unlink(tmp_path)
|
||||
raise RuntimeError("Digest mismatch")
|
||||
|
||||
__download_and_verify_to(__to_string_sequence(urls), md5, tmp_path)
|
||||
os.rename(tmp_path, path)
|
||||
return path
|
||||
|
@ -29,8 +29,8 @@ libogg = CmakeProject(
|
||||
)
|
||||
|
||||
opus = AutotoolsProject(
|
||||
'https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz',
|
||||
'65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d',
|
||||
'https://downloads.xiph.org/releases/opus/opus-1.4.tar.gz',
|
||||
'c9b32b4253be5ae63d1ff16eea06b94b5f0f2951b7a02aceef58e3a3ce49c51f',
|
||||
'lib/libopus.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@ -43,20 +43,23 @@ opus = AutotoolsProject(
|
||||
)
|
||||
|
||||
flac = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.3.4.tar.xz',
|
||||
'8ff0607e75a322dd7cd6ec48f4f225471404ae2730d0ea945127b1355155e737',
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.4.3.tar.xz',
|
||||
'6c58e69cd22348f441b861092b825e591d0b822e106de6eb0ee4d05d27205b70',
|
||||
'lib/libFLAC.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-stack-smash-protection',
|
||||
'--disable-xmms-plugin', '--disable-cpplibs',
|
||||
'--disable-doxygen-docs',
|
||||
'--disable-programs',
|
||||
],
|
||||
subdirs=['include', 'src/libFLAC'],
|
||||
)
|
||||
|
||||
zlib = ZlibProject(
|
||||
'http://zlib.net/zlib-1.2.12.tar.xz',
|
||||
'7db46b8d7726232a621befaab4a1c870f00a90805511c0e0090441dac57def18',
|
||||
('http://zlib.net/zlib-1.3.tar.xz',
|
||||
'https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.xz'),
|
||||
'8a9ba2898e1d0d774eca6ba5b4627a11e5588ba85c8851336eb38de4683050a7',
|
||||
'lib/libz.a',
|
||||
)
|
||||
|
||||
@ -109,33 +112,35 @@ libmodplug = AutotoolsProject(
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
],
|
||||
patches='src/lib/modplug/patches',
|
||||
)
|
||||
|
||||
libopenmpt = AutotoolsProject(
|
||||
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.5.12+release.autotools.tar.gz',
|
||||
'892aea7a599b5d21842bebf463b5aafdad5711be7008dd84401920c6234820af',
|
||||
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.6.6+release.autotools.tar.gz',
|
||||
'6ddb9e26a430620944891796fefb1bbb38bd9148f6cfc558810c0d3f269876c7',
|
||||
'lib/libopenmpt.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-openmpt123',
|
||||
'--disable-examples',
|
||||
'--disable-tests',
|
||||
'--disable-doxygen-doc',
|
||||
'--without-mpg123', '--without-ogg', '--without-vorbis', '--without-vorbisfile',
|
||||
'--without-portaudio', '--without-portaudiocpp', '--without-sndfile',
|
||||
'--without-flac',
|
||||
],
|
||||
base='libopenmpt-0.5.12+release.autotools',
|
||||
base='libopenmpt-0.6.6+release.autotools',
|
||||
)
|
||||
|
||||
wildmidi = CmakeProject(
|
||||
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.4',
|
||||
'6f267c8d331e9859906837e2c197093fddec31829d2ebf7b958cf6b7ae935430',
|
||||
'https://github.com/Mindwerks/wildmidi/releases/download/wildmidi-0.4.5/wildmidi-0.4.5.tar.gz',
|
||||
'd5e7bef00a7aa47534a53d43b1265f8d3d27f6a28e7f563c1cdf02ff4fa35b99',
|
||||
'lib/libWildMidi.a',
|
||||
[
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DWANT_PLAYER=OFF',
|
||||
'-DWANT_STATIC=ON',
|
||||
],
|
||||
base='wildmidi-wildmidi-0.4.4',
|
||||
name='wildmidi',
|
||||
version='0.4.4',
|
||||
)
|
||||
|
||||
gme = CmakeProject(
|
||||
@ -146,13 +151,13 @@ gme = CmakeProject(
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DENABLE_UBSAN=OFF',
|
||||
'-DZLIB_INCLUDE_DIR=OFF',
|
||||
'-DSDL2_DIR=OFF',
|
||||
'-DCMAKE_DISABLE_FIND_PACKAGE_SDL2=ON',
|
||||
],
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-5.1.tar.xz',
|
||||
'55eb6aab5ee235550fa54a33eaf8bf1b4ec66c01453182b12f6a993d75698b03',
|
||||
'http://ffmpeg.org/releases/ffmpeg-6.0.tar.xz',
|
||||
'57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@ -170,13 +175,16 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-pixelutils',
|
||||
'--disable-network',
|
||||
'--disable-encoders',
|
||||
'--disable-hwaccels',
|
||||
'--disable-muxers',
|
||||
'--disable-protocols',
|
||||
'--disable-devices',
|
||||
'--disable-filters',
|
||||
'--disable-v4l2_m2m',
|
||||
|
||||
'--disable-sdl2',
|
||||
'--disable-vulkan',
|
||||
'--disable-xlib',
|
||||
|
||||
'--disable-parser=bmp',
|
||||
'--disable-parser=cavsvideo',
|
||||
@ -190,17 +198,22 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-parser=h263',
|
||||
'--disable-parser=h264',
|
||||
'--disable-parser=hevc',
|
||||
'--disable-parser=jpeg2000',
|
||||
'--disable-parser=mjpeg',
|
||||
'--disable-parser=mlp',
|
||||
'--disable-parser=mpeg4video',
|
||||
'--disable-parser=mpegvideo',
|
||||
'--disable-parser=opus',
|
||||
'--disable-parser=qoi',
|
||||
'--disable-parser=rv30',
|
||||
'--disable-parser=rv40',
|
||||
'--disable-parser=vc1',
|
||||
'--disable-parser=vp3',
|
||||
'--disable-parser=vp8',
|
||||
'--disable-parser=vp9',
|
||||
'--disable-parser=png',
|
||||
'--disable-parser=pnm',
|
||||
'--disable-parser=webp',
|
||||
'--disable-parser=xma',
|
||||
|
||||
'--disable-demuxer=aqtitle',
|
||||
@ -216,6 +229,42 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-demuxer=h264',
|
||||
'--disable-demuxer=ico',
|
||||
'--disable-demuxer=image2',
|
||||
'--disable-demuxer=image2pipe',
|
||||
'--disable-demuxer=image_bmp_pipe',
|
||||
'--disable-demuxer=image_cri_pipe',
|
||||
'--disable-demuxer=image_dds_pipe',
|
||||
'--disable-demuxer=image_dpx_pipe',
|
||||
'--disable-demuxer=image_exr_pipe',
|
||||
'--disable-demuxer=image_gem_pipe',
|
||||
'--disable-demuxer=image_gif_pipe',
|
||||
'--disable-demuxer=image_j2k_pipe',
|
||||
'--disable-demuxer=image_jpeg_pipe',
|
||||
'--disable-demuxer=image_jpegls_pipe',
|
||||
'--disable-demuxer=image_jpegxl_pipe',
|
||||
'--disable-demuxer=image_pam_pipe',
|
||||
'--disable-demuxer=image_pbm_pipe',
|
||||
'--disable-demuxer=image_pcx_pipe',
|
||||
'--disable-demuxer=image_pfm_pipe',
|
||||
'--disable-demuxer=image_pgm_pipe',
|
||||
'--disable-demuxer=image_pgmyuv_pipe',
|
||||
'--disable-demuxer=image_pgx_pipe',
|
||||
'--disable-demuxer=image_phm_pipe',
|
||||
'--disable-demuxer=image_photocd_pipe',
|
||||
'--disable-demuxer=image_pictor_pipe',
|
||||
'--disable-demuxer=image_png_pipe',
|
||||
'--disable-demuxer=image_ppm_pipe',
|
||||
'--disable-demuxer=image_psd_pipe',
|
||||
'--disable-demuxer=image_qdraw_pipe',
|
||||
'--disable-demuxer=image_qoi_pipe',
|
||||
'--disable-demuxer=image_sgi_pipe',
|
||||
'--disable-demuxer=image_sunrast_pipe',
|
||||
'--disable-demuxer=image_svg_pipe',
|
||||
'--disable-demuxer=image_tiff_pipe',
|
||||
'--disable-demuxer=image_vbn_pipe',
|
||||
'--disable-demuxer=image_webp_pipe',
|
||||
'--disable-demuxer=image_xbm_pipe',
|
||||
'--disable-demuxer=image_xpm_pipe',
|
||||
'--disable-demuxer=image_xwd_pipe',
|
||||
'--disable-demuxer=jacosub',
|
||||
'--disable-demuxer=lrc',
|
||||
'--disable-demuxer=microdvd',
|
||||
@ -238,6 +287,7 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-demuxer=tedcaptions',
|
||||
'--disable-demuxer=vobsub',
|
||||
'--disable-demuxer=vplayer',
|
||||
'--disable-demuxer=webm_dash_manifest',
|
||||
'--disable-demuxer=webvtt',
|
||||
'--disable-demuxer=yuv4mpegpipe',
|
||||
|
||||
@ -267,78 +317,179 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-decoder=qdmc',
|
||||
|
||||
# disable lots of image and video codecs
|
||||
'--disable-decoder=acelp_kelvin',
|
||||
'--disable-decoder=agm',
|
||||
'--disable-decoder=aic',
|
||||
'--disable-decoder=alias_pix',
|
||||
'--disable-decoder=ansi',
|
||||
'--disable-decoder=apng',
|
||||
'--disable-decoder=arbc',
|
||||
'--disable-decoder=argo',
|
||||
'--disable-decoder=ass',
|
||||
'--disable-decoder=asv1',
|
||||
'--disable-decoder=asv2',
|
||||
'--disable-decoder=apng',
|
||||
'--disable-decoder=aura',
|
||||
'--disable-decoder=aura2',
|
||||
'--disable-decoder=avrn',
|
||||
'--disable-decoder=avrp',
|
||||
'--disable-decoder=avui',
|
||||
'--disable-decoder=ayuv',
|
||||
'--disable-decoder=bethsoftvid',
|
||||
'--disable-decoder=bfi',
|
||||
'--disable-decoder=bink',
|
||||
'--disable-decoder=bintext',
|
||||
'--disable-decoder=bitpacked',
|
||||
'--disable-decoder=bmp',
|
||||
'--disable-decoder=bmv_video',
|
||||
'--disable-decoder=brender_pix',
|
||||
'--disable-decoder=c93',
|
||||
'--disable-decoder=cavs',
|
||||
'--disable-decoder=ccaption',
|
||||
'--disable-decoder=cdgraphics',
|
||||
'--disable-decoder=cdtoons',
|
||||
'--disable-decoder=cdxl',
|
||||
'--disable-decoder=cfhd',
|
||||
'--disable-decoder=cinepak',
|
||||
'--disable-decoder=clearvideo',
|
||||
'--disable-decoder=cljr',
|
||||
'--disable-decoder=cllc',
|
||||
'--disable-decoder=cpia',
|
||||
'--disable-decoder=cscd',
|
||||
'--disable-decoder=cyuv',
|
||||
'--disable-decoder=dds',
|
||||
'--disable-decoder=dirac',
|
||||
'--disable-decoder=dnxhd',
|
||||
'--disable-decoder=dpx',
|
||||
'--disable-decoder=dsicinvideo',
|
||||
'--disable-decoder=dvbsub',
|
||||
'--disable-decoder=dvdsub',
|
||||
'--disable-decoder=dvvideo',
|
||||
'--disable-decoder=dxa',
|
||||
'--disable-decoder=dxtory',
|
||||
'--disable-decoder=dxv',
|
||||
'--disable-decoder=eacmv',
|
||||
'--disable-decoder=eamad',
|
||||
'--disable-decoder=eatgq',
|
||||
'--disable-decoder=eatgv',
|
||||
'--disable-decoder=eatqi',
|
||||
'--disable-decoder=eightbps',
|
||||
'--disable-decoder=escape124',
|
||||
'--disable-decoder=escape130',
|
||||
'--disable-decoder=exr',
|
||||
'--disable-decoder=ffv1',
|
||||
'--disable-decoder=ffvhuff',
|
||||
'--disable-decoder=ffwavesynth',
|
||||
'--disable-decoder=fic',
|
||||
'--disable-decoder=fits',
|
||||
'--disable-decoder=flashsv',
|
||||
'--disable-decoder=flashsv2',
|
||||
'--disable-decoder=flic',
|
||||
'--disable-decoder=flv',
|
||||
'--disable-decoder=fmvc',
|
||||
'--disable-decoder=fraps',
|
||||
'--disable-decoder=fourxm',
|
||||
'--disable-decoder=frwu',
|
||||
'--disable-decoder=g2m',
|
||||
'--disable-decoder=gdv',
|
||||
'--disable-decoder=gem',
|
||||
'--disable-decoder=gif',
|
||||
'--disable-decoder=h261',
|
||||
'--disable-decoder=h263',
|
||||
'--disable-decoder=h263i',
|
||||
'--disable-decoder=h263p',
|
||||
'--disable-decoder=h264',
|
||||
'--disable-decoder=hap',
|
||||
'--disable-decoder=hevc',
|
||||
'--disable-decoder=hnm4_video',
|
||||
'--disable-decoder=hq_hqa',
|
||||
'--disable-decoder=hqx',
|
||||
'--disable-decoder=huffyuv',
|
||||
'--disable-decoder=hymt',
|
||||
'--disable-decoder=idcin',
|
||||
'--disable-decoder=idf',
|
||||
'--disable-decoder=iff_ilbm',
|
||||
'--disable-decoder=imm4',
|
||||
'--disable-decoder=indeo2',
|
||||
'--disable-decoder=indeo3',
|
||||
'--disable-decoder=indeo4',
|
||||
'--disable-decoder=indeo5',
|
||||
'--disable-decoder=interplay_video',
|
||||
'--disable-decoder=ipu',
|
||||
'--disable-decoder=jacosub',
|
||||
'--disable-decoder=jpeg2000',
|
||||
'--disable-decoder=jpegls',
|
||||
'--disable-decoder=jv',
|
||||
'--disable-decoder=kgv1',
|
||||
'--disable-decoder=kmvc',
|
||||
'--disable-decoder=lagarith',
|
||||
'--disable-decoder=loco',
|
||||
'--disable-decoder=lscr',
|
||||
'--disable-decoder=m101',
|
||||
'--disable-decoder=magicyuv',
|
||||
'--disable-decoder=mdec',
|
||||
'--disable-decoder=microdvd',
|
||||
'--disable-decoder=mimic',
|
||||
'--disable-decoder=mjpeg',
|
||||
'--disable-decoder=mmvideo',
|
||||
'--disable-decoder=mpl2',
|
||||
'--disable-decoder=mobiclip',
|
||||
'--disable-decoder=motionpixels',
|
||||
'--disable-decoder=movtext',
|
||||
'--disable-decoder=mpeg1video',
|
||||
'--disable-decoder=mpeg2video',
|
||||
'--disable-decoder=mpeg4',
|
||||
'--disable-decoder=mpegvideo',
|
||||
'--disable-decoder=msa1',
|
||||
'--disable-decoder=mscc',
|
||||
'--disable-decoder=msmpeg4_crystalhd',
|
||||
'--disable-decoder=msmpeg4v1',
|
||||
'--disable-decoder=msmpeg4v2',
|
||||
'--disable-decoder=msmpeg4v3',
|
||||
'--disable-decoder=msp2',
|
||||
'--disable-decoder=msrle',
|
||||
'--disable-decoder=mss1',
|
||||
'--disable-decoder=msvideo1',
|
||||
'--disable-decoder=mszh',
|
||||
'--disable-decoder=mts2',
|
||||
'--disable-decoder=mv30',
|
||||
'--disable-decoder=mvc1',
|
||||
'--disable-decoder=mvc2',
|
||||
'--disable-decoder=mvdv',
|
||||
'--disable-decoder=mvha',
|
||||
'--disable-decoder=mwsc',
|
||||
'--disable-decoder=notchlc',
|
||||
'--disable-decoder=nuv',
|
||||
'--disable-decoder=on2avc',
|
||||
'--disable-decoder=paf_video',
|
||||
'--disable-decoder=pam',
|
||||
'--disable-decoder=pbm',
|
||||
'--disable-decoder=pcx',
|
||||
'--disable-decoder=pgm',
|
||||
'--disable-decoder=pgmyuv',
|
||||
'--disable-decoder=pgssub',
|
||||
'--disable-decoder=pgx',
|
||||
'--disable-decoder=phm',
|
||||
'--disable-decoder=photocd',
|
||||
'--disable-decoder=png',
|
||||
'--disable-decoder=pictor',
|
||||
'--disable-decoder=pixlet',
|
||||
'--disable-decoder=pjs',
|
||||
'--disable-decoder=ppm',
|
||||
'--disable-decoder=prores',
|
||||
'--disable-decoder=prosumer',
|
||||
'--disable-decoder=psd',
|
||||
'--disable-decoder=ptx',
|
||||
'--disable-decoder=qdraw',
|
||||
'--disable-decoder=qoi',
|
||||
'--disable-decoder=qpeg',
|
||||
'--disable-decoder=qtrle',
|
||||
'--disable-decoder=rawvideo',
|
||||
'--disable-decoder=r10k',
|
||||
'--disable-decoder=r210',
|
||||
'--disable-decoder=rasc',
|
||||
'--disable-decoder=realtext',
|
||||
'--disable-decoder=rl2',
|
||||
'--disable-decoder=rpza',
|
||||
'--disable-decoder=roq',
|
||||
'--disable-decoder=roq_dpcm',
|
||||
'--disable-decoder=rscc',
|
||||
@ -347,53 +498,122 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-decoder=rv30',
|
||||
'--disable-decoder=rv40',
|
||||
'--disable-decoder=sami',
|
||||
'--disable-decoder=sanm',
|
||||
'--disable-decoder=scpr',
|
||||
'--disable-decoder=screenpresso',
|
||||
'--disable-decoder=sga',
|
||||
'--disable-decoder=sgi',
|
||||
'--disable-decoder=sgirle',
|
||||
'--disable-decoder=sheervideo',
|
||||
'--disable-decoder=simbiosis_imx',
|
||||
'--disable-decoder=smc',
|
||||
'--disable-decoder=snow',
|
||||
'--disable-decoder=speedhq',
|
||||
'--disable-decoder=srgc',
|
||||
'--disable-decoder=srt',
|
||||
'--disable-decoder=ssa',
|
||||
'--disable-decoder=stl',
|
||||
'--disable-decoder=subrip',
|
||||
'--disable-decoder=subviewer',
|
||||
'--disable-decoder=subviewer1',
|
||||
'--disable-decoder=sunrast',
|
||||
'--disable-decoder=svq1',
|
||||
'--disable-decoder=svq3',
|
||||
'--disable-decoder=targa',
|
||||
'--disable-decoder=targa_y216',
|
||||
'--disable-decoder=text',
|
||||
'--disable-decoder=tiff',
|
||||
'--disable-decoder=tiertexseqvideo',
|
||||
'--disable-decoder=tmv',
|
||||
'--disable-decoder=truemotion1',
|
||||
'--disable-decoder=truemotion2',
|
||||
'--disable-decoder=truemotion2rt',
|
||||
'--disable-decoder=tscc',
|
||||
'--disable-decoder=tscc2',
|
||||
'--disable-decoder=twinvq',
|
||||
'--disable-decoder=txd',
|
||||
'--disable-decoder=ulti',
|
||||
'--disable-decoder=utvideo',
|
||||
'--disable-decoder=v210',
|
||||
'--disable-decoder=v210x',
|
||||
'--disable-decoder=v308',
|
||||
'--disable-decoder=v408',
|
||||
'--disable-decoder=v410',
|
||||
'--disable-decoder=vb',
|
||||
'--disable-decoder=vble',
|
||||
'--disable-decoder=vbn',
|
||||
'--disable-decoder=vc1',
|
||||
'--disable-decoder=vcr1',
|
||||
'--disable-decoder=vmdvideo',
|
||||
'--disable-decoder=vmnc',
|
||||
'--disable-decoder=vp3',
|
||||
'--disable-decoder=vp5',
|
||||
'--disable-decoder=vp6',
|
||||
'--disable-decoder=vp7',
|
||||
'--disable-decoder=vp8',
|
||||
'--disable-decoder=vp9',
|
||||
'--disable-decoder=vplayer',
|
||||
'--disable-decoder=vqa',
|
||||
'--disable-decoder=webvtt',
|
||||
'--disable-decoder=wcmv',
|
||||
'--disable-decoder=wmv1',
|
||||
'--disable-decoder=wmv2',
|
||||
'--disable-decoder=wmv3',
|
||||
'--disable-decoder=wnv1',
|
||||
'--disable-decoder=wrapped_avframe',
|
||||
'--disable-decoder=xan_wc3',
|
||||
'--disable-decoder=xan_wc4',
|
||||
'--disable-decoder=xbin',
|
||||
'--disable-decoder=xbm',
|
||||
'--disable-decoder=xface',
|
||||
'--disable-decoder=xl',
|
||||
'--disable-decoder=xpm',
|
||||
'--disable-decoder=xsub',
|
||||
'--disable-decoder=xwd',
|
||||
'--disable-decoder=y41p',
|
||||
'--disable-decoder=ylc',
|
||||
'--disable-decoder=yop',
|
||||
'--disable-decoder=yuv4',
|
||||
'--disable-decoder=zero12v',
|
||||
'--disable-decoder=zerocodec',
|
||||
'--disable-decoder=zlib',
|
||||
'--disable-decoder=zmbv',
|
||||
|
||||
'--disable-bsf=av1_frame_merge',
|
||||
'--disable-bsf=av1_frame_split',
|
||||
'--disable-bsf=av1_metadata',
|
||||
'--disable-bsf=dts2pts',
|
||||
'--disable-bsf=h264_metadata',
|
||||
'--disable-bsf=h264_mp4toannexb',
|
||||
'--disable-bsf=h264_redundant_pps',
|
||||
'--disable-bsf=hevc_metadata',
|
||||
'--disable-bsf=hevc_mp4toannexb',
|
||||
'--disable-bsf=mjpeg2jpeg',
|
||||
'--disable-bsf=opus_metadata',
|
||||
'--disable-bsf=pgs_frame_merge',
|
||||
'--disable-bsf=text2movsub',
|
||||
'--disable-bsf=vp9_metadata',
|
||||
'--disable-bsf=vp9_raw_reorder',
|
||||
'--disable-bsf=vp9_superframe',
|
||||
'--disable-bsf=vp9_superframe_split',
|
||||
],
|
||||
)
|
||||
|
||||
openssl = OpenSSLProject(
|
||||
'https://www.openssl.org/source/openssl-3.0.5.tar.gz',
|
||||
'aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a',
|
||||
('https://www.openssl.org/source/openssl-3.1.3.tar.gz',
|
||||
'https://artfiles.org/openssl.org/source/openssl-3.1.3.tar.gz'),
|
||||
'f0316a2ebd89e7f2352976445458689f80302093788c466692fb2a188b2eacf6',
|
||||
'include/openssl/ossl_typ.h',
|
||||
)
|
||||
|
||||
curl = CmakeProject(
|
||||
'https://curl.se/download/curl-7.84.0.tar.xz',
|
||||
'2d118b43f547bfe5bae806d8d47b4e596ea5b25a6c1f080aef49fbcd817c5db8',
|
||||
('https://curl.se/download/curl-8.2.1.tar.xz',
|
||||
'https://github.com/curl/curl/releases/download/curl-8_2_1/curl-8.2.1.tar.xz'),
|
||||
'dd322f6bd0a20e6cebdfd388f69e98c3d183bed792cf4713c8a7ef498cba4894',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'-DBUILD_CURL_EXE=OFF',
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DCURL_DISABLE_VERBOSE_STRINGS=ON',
|
||||
'-DCURL_DISABLE_LDAP=ON',
|
||||
'-DCURL_DISABLE_TELNET=ON',
|
||||
'-DCURL_DISABLE_DICT=ON',
|
||||
@ -422,8 +642,8 @@ curl = CmakeProject(
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.1.tar.gz',
|
||||
'7ef445410b42f36b9bad426608b53ccb9ccca4101e545c383f564c11db672ca8',
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.2.tar.gz',
|
||||
'637e56643b19da9fba98f06847788c4dad308b723156a64748041035dcdf9bd3',
|
||||
'lib/libnfs.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@ -434,7 +654,7 @@ libnfs = AutotoolsProject(
|
||||
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
base='libnfs-libnfs-5.0.1',
|
||||
base='libnfs-libnfs-5.0.2',
|
||||
autoreconf=True,
|
||||
)
|
||||
|
||||
@ -445,7 +665,7 @@ jack = JackProject(
|
||||
)
|
||||
|
||||
boost = BoostProject(
|
||||
'https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2',
|
||||
'475d589d51a7f8b3ba2ba4eda022b170e562ca3b760ee922c146b6c65856ef39',
|
||||
'https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.bz2',
|
||||
'71feeed900fbccca04a3b4f2f84a7c217186f28a940ed8b7ed4725986baf99fa',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
@ -1,28 +1,35 @@
|
||||
import subprocess
|
||||
import subprocess, multiprocessing
|
||||
from typing import Optional, Sequence, Union
|
||||
|
||||
from build.project import Project
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
class MakeProject(Project):
|
||||
def __init__(self, url, md5, installed,
|
||||
install_target='install',
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
install_target: str='install',
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.install_target = install_target
|
||||
|
||||
def get_simultaneous_jobs(self):
|
||||
return 12
|
||||
def get_simultaneous_jobs(self) -> int:
|
||||
try:
|
||||
# use twice as many simultaneous jobs as we have CPU cores
|
||||
return multiprocessing.cpu_count() * 2
|
||||
except NotImplementedError:
|
||||
# default to 12, if multiprocessing.cpu_count() is not implemented
|
||||
return 12
|
||||
|
||||
def get_make_args(self, toolchain):
|
||||
def get_make_args(self, toolchain: AnyToolchain) -> list[str]:
|
||||
return ['--quiet', '-j' + str(self.get_simultaneous_jobs())]
|
||||
|
||||
def get_make_install_args(self, toolchain):
|
||||
def get_make_install_args(self, toolchain: AnyToolchain) -> list[str]:
|
||||
return ['--quiet', self.install_target]
|
||||
|
||||
def make(self, toolchain, wd, args):
|
||||
subprocess.check_call(['/usr/bin/make'] + args,
|
||||
def make(self, toolchain: AnyToolchain, wd: str, args: list[str]) -> None:
|
||||
subprocess.check_call(['make'] + args,
|
||||
cwd=wd, env=toolchain.env)
|
||||
|
||||
def build_make(self, toolchain, wd, install=True):
|
||||
def build_make(self, toolchain: AnyToolchain, wd: str, install: bool=True) -> None:
|
||||
self.make(toolchain, wd, self.get_make_args(toolchain))
|
||||
if install:
|
||||
self.make(toolchain, wd, self.get_make_install_args(toolchain))
|
||||
|
@ -1,9 +1,17 @@
|
||||
import os.path, subprocess, sys
|
||||
import os
|
||||
import subprocess
|
||||
import platform
|
||||
from typing import Optional, Sequence, Union
|
||||
|
||||
from build.project import Project
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
def make_cross_file(toolchain):
|
||||
def __no_ccache(cmd: str) -> str:
|
||||
if cmd.startswith('ccache '):
|
||||
cmd = cmd[7:]
|
||||
return cmd
|
||||
|
||||
def make_cross_file(toolchain: AnyToolchain) -> str:
|
||||
if toolchain.is_windows:
|
||||
system = 'windows'
|
||||
windres = "windres = '%s'" % toolchain.windres
|
||||
@ -22,7 +30,7 @@ def make_cross_file(toolchain):
|
||||
cpu = 'arm64-v8a'
|
||||
else:
|
||||
cpu_family = 'x86'
|
||||
if 'x86_64' in toolchain.arch:
|
||||
if 'x86_64' in toolchain.host_triplet:
|
||||
cpu = 'x86_64'
|
||||
else:
|
||||
cpu = 'i686'
|
||||
@ -37,8 +45,8 @@ def make_cross_file(toolchain):
|
||||
with open(path, 'w') as f:
|
||||
f.write(f"""
|
||||
[binaries]
|
||||
c = '{toolchain.cc}'
|
||||
cpp = '{toolchain.cxx}'
|
||||
c = '{__no_ccache(toolchain.cc)}'
|
||||
cpp = '{__no_ccache(toolchain.cxx)}'
|
||||
ar = '{toolchain.ar}'
|
||||
strip = '{toolchain.strip}'
|
||||
pkgconfig = '{toolchain.pkg_config}'
|
||||
@ -55,7 +63,7 @@ pkgconfig = '{toolchain.pkg_config}'
|
||||
root = '{toolchain.install_prefix}'
|
||||
""")
|
||||
|
||||
if 'android' in toolchain.arch:
|
||||
if toolchain.is_android:
|
||||
f.write("""
|
||||
# Keep Meson from executing Android-x86 test binariees
|
||||
needs_exe_wrapper = true
|
||||
@ -79,21 +87,23 @@ endian = '{endian}'
|
||||
""")
|
||||
return path
|
||||
|
||||
def configure(toolchain, src, build, args=()):
|
||||
cross_file = make_cross_file(toolchain)
|
||||
def configure(toolchain: AnyToolchain, src: str, build: str, args: list[str]=[]) -> None:
|
||||
configure = [
|
||||
'meson',
|
||||
src, build,
|
||||
'meson', 'setup',
|
||||
build, src,
|
||||
|
||||
'--prefix', toolchain.install_prefix,
|
||||
|
||||
'--buildtype', 'plain',
|
||||
|
||||
'--default-library=static',
|
||||
|
||||
'--cross-file', cross_file,
|
||||
] + args
|
||||
|
||||
if toolchain.host_triplet is not None:
|
||||
# cross-compiling: write a cross-file
|
||||
cross_file = make_cross_file(toolchain)
|
||||
configure.append(f'--cross-file={cross_file}')
|
||||
|
||||
env = toolchain.env.copy()
|
||||
|
||||
# Meson 0.54 requires the BOOST_ROOT environment variable
|
||||
@ -102,18 +112,19 @@ def configure(toolchain, src, build, args=()):
|
||||
subprocess.check_call(configure, env=env)
|
||||
|
||||
class MesonProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
configure_args: list[str]=[],
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
|
||||
def configure(self, toolchain):
|
||||
def configure(self, toolchain: AnyToolchain) -> str:
|
||||
src = self.unpack(toolchain)
|
||||
build = self.make_build_path(toolchain)
|
||||
configure(toolchain, src, build, self.configure_args)
|
||||
return build
|
||||
|
||||
def _build(self, toolchain):
|
||||
def _build(self, toolchain: AnyToolchain) -> None:
|
||||
build = self.configure(toolchain)
|
||||
subprocess.check_call(['ninja', 'install'],
|
||||
subprocess.check_call(['ninja', '-v', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
||||
|
@ -1,13 +1,15 @@
|
||||
import subprocess
|
||||
from typing import Optional, Sequence, Union
|
||||
|
||||
from build.makeproject import MakeProject
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
class OpenSSLProject(MakeProject):
|
||||
def __init__(self, url, md5, installed,
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
**kwargs):
|
||||
MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs)
|
||||
|
||||
def get_make_args(self, toolchain):
|
||||
def get_make_args(self, toolchain: AnyToolchain) -> list[str]:
|
||||
return MakeProject.get_make_args(self, toolchain) + [
|
||||
'CC=' + toolchain.cc,
|
||||
'CFLAGS=' + toolchain.cflags,
|
||||
@ -17,45 +19,60 @@ class OpenSSLProject(MakeProject):
|
||||
'build_libs',
|
||||
]
|
||||
|
||||
def get_make_install_args(self, toolchain):
|
||||
def get_make_install_args(self, toolchain: AnyToolchain) -> list[str]:
|
||||
# OpenSSL's Makefile runs "ranlib" during installation
|
||||
return MakeProject.get_make_install_args(self, toolchain) + [
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
]
|
||||
|
||||
def _build(self, toolchain):
|
||||
def _build(self, toolchain: AnyToolchain) -> None:
|
||||
src = self.unpack(toolchain, out_of_tree=False)
|
||||
|
||||
# OpenSSL has a weird target architecture scheme with lots of
|
||||
# hard-coded architectures; this table translates between our
|
||||
# "toolchain_arch" (HOST_TRIPLET) and the OpenSSL target
|
||||
# host triplet and the OpenSSL target
|
||||
openssl_archs = {
|
||||
# not using "android-*" because those OpenSSL targets want
|
||||
# to know where the SDK is, but our own build scripts
|
||||
# prepared everything already to look like a regular Linux
|
||||
# build
|
||||
'arm-linux-androideabi': 'linux-generic32',
|
||||
'armv7a-linux-androideabi': 'linux-generic32',
|
||||
'aarch64-linux-android': 'linux-aarch64',
|
||||
'i686-linux-android': 'linux-x86-clang',
|
||||
'x86_64-linux-android': 'linux-x86_64-clang',
|
||||
|
||||
# Kobo
|
||||
# generic Linux
|
||||
'arm-linux-gnueabihf': 'linux-generic32',
|
||||
|
||||
# Windows
|
||||
'i686-w64-mingw32': 'mingw',
|
||||
'x86_64-w64-mingw32': 'mingw64',
|
||||
|
||||
# Apple
|
||||
'x86_64-apple-darwin': 'darwin64-x86_64-cc',
|
||||
'aarch64-apple-darwin': 'darwin64-arm64-cc',
|
||||
}
|
||||
|
||||
openssl_arch = openssl_archs[toolchain.arch]
|
||||
configure = [
|
||||
'./Configure',
|
||||
'no-shared',
|
||||
'no-module',
|
||||
'no-engine',
|
||||
'no-static-engine',
|
||||
'no-async',
|
||||
'no-tests',
|
||||
'no-makedepend',
|
||||
'--libdir=lib', # no "lib64" on amd64, please
|
||||
'--prefix=' + toolchain.install_prefix,
|
||||
]
|
||||
|
||||
subprocess.check_call(['./Configure',
|
||||
'no-shared',
|
||||
'no-module', 'no-engine', 'no-static-engine',
|
||||
'no-async',
|
||||
'no-tests',
|
||||
'no-asm', # "asm" causes build failures on Windows
|
||||
openssl_arch,
|
||||
'--prefix=' + toolchain.install_prefix],
|
||||
cwd=src, env=toolchain.env)
|
||||
if toolchain.is_windows:
|
||||
# workaround for build failures
|
||||
configure.append('no-asm')
|
||||
|
||||
if toolchain.host_triplet is not None:
|
||||
configure.append(openssl_archs[toolchain.host_triplet])
|
||||
configure.append(f'--cross-compile-prefix={toolchain.host_triplet}-')
|
||||
|
||||
subprocess.check_call(configure, cwd=src, env=toolchain.env)
|
||||
self.build_make(toolchain, src)
|
||||
|
@ -1,26 +1,30 @@
|
||||
import os, shutil
|
||||
import re
|
||||
from typing import cast, BinaryIO, Optional, Sequence, Union
|
||||
|
||||
from build.download import download_and_verify
|
||||
from build.download import download_basename, download_and_verify
|
||||
from build.tar import untar
|
||||
from build.quilt import push_all
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
class Project:
|
||||
def __init__(self, url, md5, installed, name=None, version=None,
|
||||
base=None,
|
||||
patches=None,
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
name: Optional[str]=None, version: Optional[str]=None,
|
||||
base: Optional[str]=None,
|
||||
patches: Optional[str]=None,
|
||||
edits=None,
|
||||
use_cxx=False):
|
||||
use_cxx: bool=False):
|
||||
if base is None:
|
||||
basename = os.path.basename(url)
|
||||
basename = download_basename(url)
|
||||
m = re.match(r'^(.+)\.(tar(\.(gz|bz2|xz|lzma))?|zip)$', basename)
|
||||
if not m: raise
|
||||
if not m: raise RuntimeError('Could not identify tarball name: ' + basename)
|
||||
self.base = m.group(1)
|
||||
else:
|
||||
self.base = base
|
||||
|
||||
if name is None or version is None:
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-(?:alpha|beta)\d+)?)(\+.*)?$', self.base)
|
||||
if not m: raise RuntimeError('Could not identify tarball name: ' + self.base)
|
||||
if name is None: name = m.group(1)
|
||||
if version is None: version = m.group(2)
|
||||
|
||||
@ -38,10 +42,10 @@ class Project:
|
||||
self.edits = edits
|
||||
self.use_cxx = use_cxx
|
||||
|
||||
def download(self, toolchain):
|
||||
def download(self, toolchain: AnyToolchain) -> str:
|
||||
return download_and_verify(self.url, self.md5, toolchain.tarball_path)
|
||||
|
||||
def is_installed(self, toolchain):
|
||||
def is_installed(self, toolchain: AnyToolchain) -> bool:
|
||||
tarball = self.download(toolchain)
|
||||
installed = os.path.join(toolchain.install_prefix, self.installed)
|
||||
tarball_mtime = os.path.getmtime(tarball)
|
||||
@ -50,13 +54,13 @@ class Project:
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
def unpack(self, toolchain, out_of_tree=True):
|
||||
def unpack(self, toolchain: AnyToolchain, out_of_tree: bool=True) -> str:
|
||||
if out_of_tree:
|
||||
parent_path = toolchain.src_path
|
||||
else:
|
||||
parent_path = toolchain.build_path
|
||||
path = untar(self.download(toolchain), parent_path, self.base)
|
||||
|
||||
path = untar(self.download(toolchain), parent_path, self.base,
|
||||
lazy=out_of_tree and self.patches is None)
|
||||
if self.patches is not None:
|
||||
push_all(toolchain, path, self.patches)
|
||||
|
||||
@ -71,8 +75,10 @@ class Project:
|
||||
|
||||
return path
|
||||
|
||||
def make_build_path(self, toolchain):
|
||||
def make_build_path(self, toolchain: AnyToolchain, lazy: bool=False) -> str:
|
||||
path = os.path.join(toolchain.build_path, self.base)
|
||||
if lazy and os.path.isdir(path):
|
||||
return path
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except FileNotFoundError:
|
||||
@ -80,5 +86,5 @@ class Project:
|
||||
os.makedirs(path, exist_ok=True)
|
||||
return path
|
||||
|
||||
def build(self, toolchain):
|
||||
def build(self, toolchain: AnyToolchain) -> None:
|
||||
self._build(toolchain)
|
||||
|
@ -1,9 +1,12 @@
|
||||
import subprocess
|
||||
from typing import Union
|
||||
|
||||
def run_quilt(toolchain, cwd, patches_path, *args):
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
def run_quilt(toolchain: AnyToolchain, cwd: str, patches_path: str, *args: str) -> None:
|
||||
env = dict(toolchain.env)
|
||||
env['QUILT_PATCHES'] = patches_path
|
||||
subprocess.check_call(['quilt'] + list(args), cwd=cwd, env=env)
|
||||
|
||||
def push_all(toolchain, src_path, patches_path):
|
||||
def push_all(toolchain: AnyToolchain, src_path: str, patches_path: str) -> None:
|
||||
run_quilt(toolchain, src_path, patches_path, 'push', '-a')
|
||||
|
@ -1,14 +1,17 @@
|
||||
import os, shutil, subprocess
|
||||
|
||||
def untar(tarball_path, parent_path, base):
|
||||
def untar(tarball_path: str, parent_path: str, base: str,
|
||||
lazy: bool=False) -> str:
|
||||
path = os.path.join(parent_path, base)
|
||||
if lazy and os.path.isdir(path):
|
||||
return path
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.makedirs(parent_path, exist_ok=True)
|
||||
try:
|
||||
subprocess.check_call(['/bin/tar', 'xfC', tarball_path, parent_path])
|
||||
subprocess.check_call(['tar', 'xfC', tarball_path, parent_path])
|
||||
except FileNotFoundError:
|
||||
import tarfile
|
||||
tar = tarfile.open(tarball_path)
|
||||
|
175
python/build/toolchain.py
Normal file
175
python/build/toolchain.py
Normal file
@ -0,0 +1,175 @@
|
||||
import os.path
|
||||
import shutil
|
||||
from typing import Union
|
||||
|
||||
android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'armv7a-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'arm64-v8a': {
|
||||
'arch': 'aarch64-linux-android',
|
||||
'ndk_arch': 'arm64',
|
||||
'cflags': '-fpic',
|
||||
},
|
||||
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
|
||||
'x86_64': {
|
||||
'arch': 'x86_64-linux-android',
|
||||
'ndk_arch': 'x86_64',
|
||||
'cflags': '-fPIC -m64',
|
||||
},
|
||||
}
|
||||
|
||||
class AndroidNdkToolchain:
|
||||
def __init__(self, top_path: str, lib_path: str,
|
||||
tarball_path: str, src_path: str,
|
||||
ndk_path: str, android_abi: str,
|
||||
use_cxx):
|
||||
# build host configuration
|
||||
build_arch = 'linux-x86_64'
|
||||
|
||||
# select the NDK target
|
||||
abi_info = android_abis[android_abi]
|
||||
host_triplet = abi_info['arch']
|
||||
|
||||
arch_path = os.path.join(lib_path, host_triplet)
|
||||
|
||||
self.tarball_path = tarball_path
|
||||
self.src_path = src_path
|
||||
self.build_path = os.path.join(arch_path, 'build')
|
||||
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
android_api_level = '24'
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
self.host_triplet = host_triplet
|
||||
self.install_prefix = install_prefix
|
||||
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = host_triplet + android_api_level
|
||||
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
self.cc = os.path.join(llvm_bin, 'clang')
|
||||
self.cxx = os.path.join(llvm_bin, 'clang++')
|
||||
common_flags += ' -target ' + llvm_triple
|
||||
|
||||
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
|
||||
|
||||
self.ar = os.path.join(llvm_bin, 'llvm-ar')
|
||||
self.arflags = 'rcs'
|
||||
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
|
||||
self.nm = os.path.join(llvm_bin, 'llvm-nm')
|
||||
self.strip = os.path.join(llvm_bin, 'llvm-strip')
|
||||
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = ' -isystem ' + os.path.join(install_prefix, 'include')
|
||||
self.ldflags = ' -L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -Wl,--exclude-libs=ALL' + \
|
||||
' ' + common_flags
|
||||
self.ldflags = common_flags
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = ndk_arch == 'arm'
|
||||
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
|
||||
self.is_aarch64 = ndk_arch == 'arm64'
|
||||
self.is_windows = False
|
||||
self.is_android = True
|
||||
self.is_darwin = False
|
||||
|
||||
libstdcxx_flags = ''
|
||||
libstdcxx_cxxflags = ''
|
||||
libstdcxx_ldflags = ''
|
||||
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:
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
self.ldflags += ' ' + libstdcxx_ldflags
|
||||
self.libs += ' ' + libstdcxx_libs
|
||||
|
||||
self.env = dict(os.environ)
|
||||
|
||||
# redirect pkg-config to use our root directory instead of the
|
||||
# default one on the build host
|
||||
bin_dir = os.path.join(install_prefix, 'bin')
|
||||
os.makedirs(bin_dir, exist_ok=True)
|
||||
self.pkg_config = shutil.copy(os.path.join(top_path, 'build', 'pkg-config.sh'),
|
||||
os.path.join(bin_dir, 'pkg-config'))
|
||||
self.env['PKG_CONFIG'] = self.pkg_config
|
||||
|
||||
class MingwToolchain:
|
||||
def __init__(self, top_path: str,
|
||||
toolchain_path, host_triplet, x64: bool,
|
||||
tarball_path, src_path, build_path, install_prefix):
|
||||
self.host_triplet = host_triplet
|
||||
self.tarball_path = tarball_path
|
||||
self.src_path = src_path
|
||||
self.build_path = build_path
|
||||
self.install_prefix = install_prefix
|
||||
|
||||
toolchain_bin = os.path.join(toolchain_path, 'bin')
|
||||
self.cc = os.path.join(toolchain_bin, host_triplet + '-gcc')
|
||||
self.cxx = os.path.join(toolchain_bin, host_triplet + '-g++')
|
||||
self.ar = os.path.join(toolchain_bin, host_triplet + '-ar')
|
||||
self.arflags = 'rcs'
|
||||
self.ranlib = os.path.join(toolchain_bin, host_triplet + '-ranlib')
|
||||
self.nm = os.path.join(toolchain_bin, host_triplet + '-nm')
|
||||
self.strip = os.path.join(toolchain_bin, host_triplet + '-strip')
|
||||
self.windres = os.path.join(toolchain_bin, host_triplet + '-windres')
|
||||
|
||||
common_flags = '-O2 -g'
|
||||
|
||||
if not x64:
|
||||
# enable SSE support which is required for LAME
|
||||
common_flags += ' -march=pentium3'
|
||||
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include') + \
|
||||
' -DWINVER=0x0600 -D_WIN32_WINNT=0x0600'
|
||||
self.ldflags = '-L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -static-libstdc++ -static-libgcc'
|
||||
self.libs = ''
|
||||
|
||||
# Explicitly disable _FORTIFY_SOURCE because it is broken with
|
||||
# mingw. This prevents some libraries such as libFLAC to
|
||||
# enable it.
|
||||
self.cppflags += ' -D_FORTIFY_SOURCE=0'
|
||||
|
||||
self.is_arm = host_triplet.startswith('arm')
|
||||
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
|
||||
self.is_aarch64 = host_triplet == 'aarch64'
|
||||
self.is_windows = 'mingw32' in host_triplet
|
||||
self.is_android = False
|
||||
self.is_darwin = False
|
||||
|
||||
self.env = dict(os.environ)
|
||||
|
||||
# redirect pkg-config to use our root directory instead of the
|
||||
# default one on the build host
|
||||
import shutil
|
||||
bin_dir = os.path.join(install_prefix, 'bin')
|
||||
os.makedirs(bin_dir, exist_ok=True)
|
||||
self.pkg_config = shutil.copy(os.path.join(top_path, 'build', 'pkg-config.sh'),
|
||||
os.path.join(bin_dir, 'pkg-config'))
|
||||
self.env['PKG_CONFIG'] = self.pkg_config
|
||||
|
||||
AnyToolchain = Union[AndroidNdkToolchain, MingwToolchain]
|
@ -1,6 +1,7 @@
|
||||
import hashlib
|
||||
from typing import cast, Any, BinaryIO
|
||||
|
||||
def feed_file(h, f):
|
||||
def feed_file(h: Any, f: BinaryIO) -> None:
|
||||
"""Feed data read from an open file into the hashlib instance."""
|
||||
|
||||
while True:
|
||||
@ -10,20 +11,20 @@ def feed_file(h, f):
|
||||
break
|
||||
h.update(data)
|
||||
|
||||
def feed_file_path(h, path):
|
||||
def feed_file_path(h: Any, path: str) -> None:
|
||||
"""Feed data read from a file (to be opened by this function) into the hashlib instance."""
|
||||
|
||||
with open(path, 'rb') as f:
|
||||
feed_file(h, f)
|
||||
|
||||
def file_digest(algorithm, path):
|
||||
def file_digest(algorithm: Any, path: str) -> str:
|
||||
"""Calculate the digest of a file and return it in hexadecimal notation."""
|
||||
|
||||
h = algorithm()
|
||||
feed_file_path(h, path)
|
||||
return h.hexdigest()
|
||||
return cast(str, h.hexdigest())
|
||||
|
||||
def guess_digest_algorithm(digest):
|
||||
def guess_digest_algorithm(digest: str) -> Any:
|
||||
l = len(digest)
|
||||
if l == 32:
|
||||
return hashlib.md5
|
||||
@ -36,7 +37,7 @@ def guess_digest_algorithm(digest):
|
||||
else:
|
||||
return None
|
||||
|
||||
def verify_file_digest(path, expected_digest):
|
||||
def verify_file_digest(path: str, expected_digest: str) -> bool:
|
||||
"""Verify the digest of a file, and return True if the digest matches with the given expected digest."""
|
||||
|
||||
algorithm = guess_digest_algorithm(expected_digest)
|
||||
|
@ -1,22 +1,34 @@
|
||||
import os.path, subprocess
|
||||
import subprocess
|
||||
from typing import Optional, Sequence, Union
|
||||
|
||||
from build.project import Project
|
||||
from build.makeproject import MakeProject
|
||||
from .toolchain import AnyToolchain
|
||||
|
||||
class ZlibProject(Project):
|
||||
def __init__(self, url, md5, installed,
|
||||
class ZlibProject(MakeProject):
|
||||
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
MakeProject.__init__(self, url, md5, installed, **kwargs)
|
||||
|
||||
def _build(self, toolchain):
|
||||
def get_make_args(self, toolchain: AnyToolchain) -> list[str]:
|
||||
return MakeProject.get_make_args(self, toolchain) + [
|
||||
'CC=' + toolchain.cc + ' ' + toolchain.cppflags + ' ' + toolchain.cflags,
|
||||
'CPP=' + toolchain.cc + ' -E ' + toolchain.cppflags,
|
||||
'AR=' + toolchain.ar,
|
||||
'ARFLAGS=' + toolchain.arflags,
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
'LDSHARED=' + toolchain.cc + ' -shared',
|
||||
'libz.a'
|
||||
]
|
||||
|
||||
def get_make_install_args(self, toolchain: AnyToolchain) -> list[str]:
|
||||
return [
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
self.install_target
|
||||
]
|
||||
|
||||
def _build(self, toolchain: AnyToolchain) -> None:
|
||||
src = self.unpack(toolchain, out_of_tree=False)
|
||||
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet',
|
||||
'-f', 'win32/Makefile.gcc',
|
||||
'PREFIX=' + toolchain.arch + '-',
|
||||
'-j12',
|
||||
'install',
|
||||
'INCLUDE_PATH='+ os.path.join(toolchain.install_prefix, 'include'),
|
||||
'LIBRARY_PATH=' + os.path.join(toolchain.install_prefix, 'lib'),
|
||||
'BINARY_PATH=' + os.path.join(toolchain.install_prefix, 'bin'),
|
||||
],
|
||||
cwd=src, env=toolchain.env)
|
||||
subprocess.check_call(['./configure', '--prefix=' + toolchain.install_prefix, '--static'],
|
||||
cwd=src, env=toolchain.env)
|
||||
self.build_make(toolchain, src)
|
||||
|
@ -352,12 +352,16 @@ ParseCommandLine(int argc, char **argv, CommandLineOptions &options,
|
||||
break;
|
||||
|
||||
case OPTION_NO_DAEMON:
|
||||
#ifdef ENABLE_DAEMON
|
||||
options.daemon = false;
|
||||
#endif
|
||||
break;
|
||||
|
||||
#ifdef __linux__
|
||||
case OPTION_SYSTEMD:
|
||||
#ifdef ENABLE_DAEMON
|
||||
options.daemon = false;
|
||||
#endif
|
||||
options.systemd = true;
|
||||
break;
|
||||
#endif
|
||||
|
@ -20,11 +20,18 @@
|
||||
#ifndef MPD_COMMAND_LINE_HXX
|
||||
#define MPD_COMMAND_LINE_HXX
|
||||
|
||||
#include "config.h" // for ENABLE_DAEMON
|
||||
|
||||
struct ConfigData;
|
||||
|
||||
struct CommandLineOptions {
|
||||
bool kill = false;
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
bool daemon = true;
|
||||
#else
|
||||
static constexpr bool daemon = false;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
bool systemd = false;
|
||||
|
@ -158,12 +158,15 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
getenv("NOTIFY_SOCKET") != nullptr) {
|
||||
/* if MPD was started as a systemd
|
||||
service, default to journal (which
|
||||
is connected to fd=2) */
|
||||
is connected to stdout&stderr) */
|
||||
out_fd = STDOUT_FILENO;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifndef HAVE_SYSLOG
|
||||
#ifdef _WIN32
|
||||
/* default to stdout on Windows */
|
||||
out_fd = STDOUT_FILENO;
|
||||
#elif !defined(HAVE_SYSLOG)
|
||||
throw std::runtime_error("config parameter 'log_file' not found");
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG
|
||||
|
@ -482,7 +482,10 @@ MainConfigured(const CommandLineOptions &options,
|
||||
#ifndef ANDROID
|
||||
setup_log_output();
|
||||
|
||||
const ScopeSignalHandlersInit signal_handlers_init(instance);
|
||||
const ScopeSignalHandlersInit signal_handlers_init{
|
||||
instance,
|
||||
options.daemon,
|
||||
};
|
||||
#endif
|
||||
|
||||
instance.io_thread.Start();
|
||||
|
@ -81,6 +81,9 @@ spl_valid_name(const char *name_utf8)
|
||||
*/
|
||||
|
||||
return std::strchr(name_utf8, '/') == nullptr &&
|
||||
#ifdef _WIN32
|
||||
std::strchr(name_utf8, '\\') == nullptr &&
|
||||
#endif
|
||||
std::strchr(name_utf8, '\n') == nullptr &&
|
||||
std::strchr(name_utf8, '\r') == nullptr;
|
||||
}
|
||||
|
@ -28,7 +28,11 @@
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/unordered_set.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class RemoteTagCacheHandler;
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "TagPrint.hxx"
|
||||
#include "client/Response.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "lib/fmt/AudioFormatFormatter.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
@ -93,7 +94,7 @@ song_print_info(Response &r, const LightSong &song, bool base) noexcept
|
||||
time_print(r, "Last-Modified", song.mtime);
|
||||
|
||||
if (song.audio_format.IsDefined())
|
||||
r.Fmt(FMT_STRING("Format: {}\n"), ToString(song.audio_format));
|
||||
r.Fmt(FMT_STRING("Format: {}\n"), song.audio_format);
|
||||
|
||||
tag_print_values(r, song.tag);
|
||||
|
||||
@ -116,7 +117,7 @@ song_print_info(Response &r, const DetachedSong &song, bool base) noexcept
|
||||
time_print(r, "Last-Modified", song.GetLastModified());
|
||||
|
||||
if (const auto &f = song.GetAudioFormat(); f.IsDefined())
|
||||
r.Fmt(FMT_STRING("Format: {}\n"), ToString(f));
|
||||
r.Fmt(FMT_STRING("Format: {}\n"), f);
|
||||
|
||||
tag_print_values(r, song.GetTag());
|
||||
|
||||
|
@ -63,6 +63,9 @@ song_save(BufferedOutputStream &os, const Song &song)
|
||||
if (song.audio_format.IsDefined())
|
||||
os.Format("Format: %s\n", ToString(song.audio_format).c_str());
|
||||
|
||||
if (song.in_playlist)
|
||||
os.Write("InPlaylist: yes\n");
|
||||
|
||||
if (!IsNegative(song.mtime))
|
||||
os.Format(SONG_MTIME ": %li\n",
|
||||
(long)std::chrono::system_clock::to_time_t(song.mtime));
|
||||
@ -86,7 +89,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
|
||||
|
||||
DetachedSong
|
||||
song_load(LineReader &file, const char *uri,
|
||||
std::string *target_r)
|
||||
std::string *target_r, bool *in_playlist_r)
|
||||
{
|
||||
DetachedSong song(uri);
|
||||
|
||||
@ -132,6 +135,9 @@ song_load(LineReader &file, const char *uri,
|
||||
|
||||
song.SetStartTime(SongTime::FromMS(start_ms));
|
||||
song.SetEndTime(SongTime::FromMS(end_ms));
|
||||
} else if (StringIsEqual(line, "InPlaylist")) {
|
||||
if (in_playlist_r != nullptr)
|
||||
*in_playlist_r = StringIsEqual(value, "yes");
|
||||
} else {
|
||||
throw FormatRuntimeError("unknown line in db: %s", line);
|
||||
}
|
||||
|
@ -44,6 +44,6 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
|
||||
*/
|
||||
DetachedSong
|
||||
song_load(LineReader &file, const char *uri,
|
||||
std::string *target_r=nullptr);
|
||||
std::string *target_r=nullptr, bool *in_playlist_r=nullptr);
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,7 @@ StateFileConfig::StateFileConfig(const ConfigData &config)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
if (path.IsNull()) {
|
||||
const auto cache_dir = GetUserCacheDir();
|
||||
const auto cache_dir = GetAppCacheDir();
|
||||
if (cache_dir.IsNull())
|
||||
return;
|
||||
|
||||
|
@ -35,8 +35,9 @@ tag_print_types(Response &r) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
tag_print(Response &r, TagType type, StringView value) noexcept
|
||||
tag_print(Response &r, TagType type, StringView _value) noexcept
|
||||
{
|
||||
const std::string_view value{_value};
|
||||
r.Fmt(FMT_STRING("{}: {}\n"), tag_item_names[type], value);
|
||||
}
|
||||
|
||||
|
@ -36,5 +36,5 @@ time_print(Response &r, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
r.Fmt(FMT_STRING("{}: {}\n"), name, s);
|
||||
r.Fmt(FMT_STRING("{}: {}\n"), name, s.c_str());
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ Context::Initialise(JNIEnv *env) noexcept
|
||||
AllocatedPath
|
||||
Context::GetExternalFilesDir(JNIEnv *env, const char *type) noexcept
|
||||
{
|
||||
assert(_type != nullptr);
|
||||
assert(type != nullptr);
|
||||
|
||||
jobject file = env->CallObjectMethod(Get(), getExternalFilesDir_method,
|
||||
Java::String::Optional(env, type).Get());
|
||||
|
@ -166,7 +166,7 @@ class Iso9660InputStream final : public InputStream {
|
||||
assert(fill <= data.size());
|
||||
assert(position <= fill);
|
||||
|
||||
return {&data[position], &data[fill]};
|
||||
return {data.data() + position, data.data() + fill};
|
||||
}
|
||||
|
||||
void Consume(size_t nbytes) noexcept {
|
||||
|
@ -22,6 +22,10 @@ if libzzip_dep.found()
|
||||
found_archive_plugin = true
|
||||
endif
|
||||
|
||||
if not found_archive_plugin
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
archive_plugins = static_library(
|
||||
'archive_plugins',
|
||||
archive_plugins_sources,
|
||||
|
@ -41,6 +41,8 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <limits.h> // for UINT_MAX
|
||||
|
||||
CommandResult
|
||||
handle_listfiles_db(Client &client, Response &r, const char *uri)
|
||||
{
|
||||
|
@ -100,10 +100,6 @@ handle_listfiles_local(Response &r, Path path_fs)
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
gcc_pure
|
||||
static bool
|
||||
IsValidName(const StringView s) noexcept
|
||||
@ -130,7 +126,8 @@ public:
|
||||
explicit PrintCommentHandler(Response &_response) noexcept
|
||||
:NullTagHandler(WANT_PAIR), response(_response) {}
|
||||
|
||||
void OnPair(StringView key, StringView value) noexcept override {
|
||||
void OnPair(StringView _key, StringView _value) noexcept override {
|
||||
const std::string_view key{_key}, value{_value};
|
||||
if (IsValidName(key) && IsValidValue(value))
|
||||
response.Fmt(FMT_STRING("{}: {}\n"), key, value);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
#include "lib/fmt/AudioFormatFormatter.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/Exception.hxx"
|
||||
@ -185,7 +186,7 @@ handle_status(Client &client, [[maybe_unused]] Request args, Response &r)
|
||||
|
||||
if (player_status.audio_format.IsDefined())
|
||||
r.Fmt(FMT_STRING(COMMAND_STATUS_AUDIO ": {}\n"),
|
||||
ToString(player_status.audio_format));
|
||||
player_status.audio_format);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
@ -83,10 +83,6 @@ handle_listfiles_storage(Response &r, StorageDirectoryReader &reader)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
CommandResult
|
||||
handle_listfiles_storage(Response &r, Storage &storage, const char *uri)
|
||||
{
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
@ -51,17 +52,30 @@ CreateConfiguredDatabase(const ConfigData &config,
|
||||
} else {
|
||||
/* if there is no override, use the cache directory */
|
||||
|
||||
const AllocatedPath cache_dir = GetUserCacheDir();
|
||||
const AllocatedPath cache_dir = GetAppCacheDir();
|
||||
if (cache_dir.IsNull())
|
||||
return nullptr;
|
||||
|
||||
const auto db_file = cache_dir / Path::FromFS(PATH_LITERAL("mpd.db"));
|
||||
const auto db_file = cache_dir / Path::FromFS(PATH_LITERAL("db"));
|
||||
auto db_file_utf8 = db_file.ToUTF8();
|
||||
if (db_file_utf8.empty())
|
||||
return nullptr;
|
||||
|
||||
ConfigBlock block;
|
||||
block.AddBlockParam("path", std::move(db_file_utf8), -1);
|
||||
|
||||
{
|
||||
const auto mounts_dir = cache_dir
|
||||
/ Path::FromFS(PATH_LITERAL("mounts"));
|
||||
CreateDirectoryNoThrow(mounts_dir);
|
||||
|
||||
if (auto mounts_dir_utf8 = mounts_dir.ToUTF8();
|
||||
!mounts_dir_utf8.empty())
|
||||
block.AddBlockParam("cache_directory",
|
||||
std::move(mounts_dir_utf8),
|
||||
-1);
|
||||
}
|
||||
|
||||
return DatabaseGlobalInit(main_event_loop, io_event_loop,
|
||||
listener, block);
|
||||
}
|
||||
|
@ -126,6 +126,18 @@ Directory::LookupTargetSong(std::string_view _target) noexcept
|
||||
return lr.directory->FindSong(lr.rest);
|
||||
}
|
||||
|
||||
void
|
||||
Directory::ClearInPlaylist() noexcept
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
|
||||
for (auto &child : children)
|
||||
child.ClearInPlaylist();
|
||||
|
||||
for (auto &song : songs)
|
||||
song.in_playlist = false;
|
||||
}
|
||||
|
||||
void
|
||||
Directory::PruneEmpty() noexcept
|
||||
{
|
||||
|
@ -287,6 +287,14 @@ public:
|
||||
*/
|
||||
SongPtr RemoveSong(Song *song) noexcept;
|
||||
|
||||
/**
|
||||
* Recursively walk through the whole tree and set all
|
||||
* `Song::in_playlist` fields to `false`.
|
||||
*
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
void ClearInPlaylist() noexcept;
|
||||
|
||||
/**
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
|
@ -168,12 +168,14 @@ directory_load(LineReader &file, Directory &directory)
|
||||
throw FormatRuntimeError("Duplicate song '%s'", name);
|
||||
|
||||
std::string target;
|
||||
bool in_playlist = false;
|
||||
auto detached_song = song_load(file, name,
|
||||
&target);
|
||||
&target, &in_playlist);
|
||||
|
||||
auto song = std::make_unique<Song>(std::move(detached_song),
|
||||
directory);
|
||||
song->target = std::move(target);
|
||||
song->in_playlist = in_playlist;
|
||||
|
||||
directory.AddSong(std::move(song));
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) {
|
||||
|
@ -51,6 +51,15 @@ LockFindSong(Directory &directory, std::string_view name) noexcept
|
||||
return directory.FindSong(name);
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
static bool
|
||||
IsAcceptableFilename(std::string_view name) noexcept
|
||||
{
|
||||
return !name.empty() &&
|
||||
/* newlines cannot be represented in MPD's protocol */
|
||||
name.find('\n') == name.npos;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
|
||||
const char *name) noexcept
|
||||
@ -58,6 +67,9 @@ UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
|
||||
const char *tmp = std::strchr(name, '/');
|
||||
if (tmp) {
|
||||
const std::string_view child_name(name, tmp - name);
|
||||
if (!IsAcceptableFilename(child_name))
|
||||
return;
|
||||
|
||||
//add dir is not there already
|
||||
Directory *subdir = LockMakeChild(directory, child_name);
|
||||
subdir->device = DEVICE_INARCHIVE;
|
||||
@ -65,11 +77,8 @@ UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
|
||||
//create directories first
|
||||
UpdateArchiveTree(archive, *subdir, tmp + 1);
|
||||
} else {
|
||||
if (StringIsEmpty(name)) {
|
||||
LogWarning(update_domain,
|
||||
"archive returned directory only");
|
||||
if (!IsAcceptableFilename(name))
|
||||
return;
|
||||
}
|
||||
|
||||
//add file
|
||||
Song *song = LockFindSong(directory, name);
|
||||
|
@ -531,6 +531,7 @@ UpdateWalk::Walk(Directory &root, const char *path, bool discard) noexcept
|
||||
|
||||
{
|
||||
const ScopeDatabaseLock protect;
|
||||
root.ClearInPlaylist();
|
||||
PurgeDanglingFromPlaylists(root);
|
||||
}
|
||||
|
||||
|
@ -114,11 +114,11 @@ constexpr const struct DecoderPlugin *decoder_plugins[] = {
|
||||
#ifdef ENABLE_ADPLUG
|
||||
&adplug_decoder_plugin,
|
||||
#endif
|
||||
#ifdef ENABLE_FFMPEG
|
||||
&ffmpeg_decoder_plugin,
|
||||
#endif
|
||||
#ifdef ENABLE_GME
|
||||
&gme_decoder_plugin,
|
||||
#endif
|
||||
#ifdef ENABLE_FFMPEG
|
||||
&ffmpeg_decoder_plugin,
|
||||
#endif
|
||||
&pcm_decoder_plugin,
|
||||
nullptr
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "lib/ffmpeg/Format.hxx"
|
||||
#include "lib/ffmpeg/Codec.hxx"
|
||||
#include "lib/ffmpeg/SampleFormat.hxx"
|
||||
#include "lib/ffmpeg/LibFmt.hxx"
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "FfmpegMetaData.hxx"
|
||||
#include "FfmpegIo.hxx"
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
bool
|
||||
FlacDecoder::Initialize(unsigned sample_rate, unsigned bits_per_sample,
|
||||
unsigned channels, FLAC__uint64 total_frames)
|
||||
unsigned channels, FLAC__uint64 total_frames) noexcept
|
||||
{
|
||||
assert(!initialized);
|
||||
assert(!unsupported);
|
||||
@ -60,7 +60,7 @@ FlacDecoder::Initialize(unsigned sample_rate, unsigned bits_per_sample,
|
||||
}
|
||||
|
||||
inline void
|
||||
FlacDecoder::OnStreamInfo(const FLAC__StreamMetadata_StreamInfo &stream_info)
|
||||
FlacDecoder::OnStreamInfo(const FLAC__StreamMetadata_StreamInfo &stream_info) noexcept
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
@ -72,7 +72,7 @@ FlacDecoder::OnStreamInfo(const FLAC__StreamMetadata_StreamInfo &stream_info)
|
||||
}
|
||||
|
||||
inline void
|
||||
FlacDecoder::OnVorbisComment(const FLAC__StreamMetadata_VorbisComment &vc)
|
||||
FlacDecoder::OnVorbisComment(const FLAC__StreamMetadata_VorbisComment &vc) noexcept
|
||||
{
|
||||
ReplayGainInfo rgi;
|
||||
if (flac_parse_replay_gain(rgi, vc))
|
||||
@ -86,7 +86,7 @@ FlacDecoder::OnVorbisComment(const FLAC__StreamMetadata_VorbisComment &vc)
|
||||
}
|
||||
|
||||
void
|
||||
FlacDecoder::OnMetadata(const FLAC__StreamMetadata &metadata)
|
||||
FlacDecoder::OnMetadata(const FLAC__StreamMetadata &metadata) noexcept
|
||||
{
|
||||
if (unsupported)
|
||||
return;
|
||||
@ -106,7 +106,7 @@ FlacDecoder::OnMetadata(const FLAC__StreamMetadata &metadata)
|
||||
}
|
||||
|
||||
inline bool
|
||||
FlacDecoder::OnFirstFrame(const FLAC__FrameHeader &header)
|
||||
FlacDecoder::OnFirstFrame(const FLAC__FrameHeader &header) noexcept
|
||||
{
|
||||
if (unsupported)
|
||||
return false;
|
||||
@ -139,7 +139,7 @@ FlacDecoder::GetDeltaPosition(const FLAC__StreamDecoder &sd)
|
||||
FLAC__StreamDecoderWriteStatus
|
||||
FlacDecoder::OnWrite(const FLAC__Frame &frame,
|
||||
const FLAC__int32 *const buf[],
|
||||
FLAC__uint64 nbytes)
|
||||
FLAC__uint64 nbytes) noexcept
|
||||
{
|
||||
if (!initialized && !OnFirstFrame(frame.header))
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
|
@ -65,20 +65,21 @@ struct FlacDecoder : public FlacInput {
|
||||
*/
|
||||
ConstBuffer<void> chunk = nullptr;
|
||||
|
||||
FlacDecoder(DecoderClient &_client, InputStream &_input_stream)
|
||||
FlacDecoder(DecoderClient &_client,
|
||||
InputStream &_input_stream) noexcept
|
||||
:FlacInput(_input_stream, &_client) {}
|
||||
|
||||
/**
|
||||
* Wrapper for DecoderClient::Ready().
|
||||
*/
|
||||
bool Initialize(unsigned sample_rate, unsigned bits_per_sample,
|
||||
unsigned channels, FLAC__uint64 total_frames);
|
||||
unsigned channels, FLAC__uint64 total_frames) noexcept;
|
||||
|
||||
void OnMetadata(const FLAC__StreamMetadata &metadata);
|
||||
void OnMetadata(const FLAC__StreamMetadata &metadata) noexcept;
|
||||
|
||||
FLAC__StreamDecoderWriteStatus OnWrite(const FLAC__Frame &frame,
|
||||
const FLAC__int32 *const buf[],
|
||||
FLAC__uint64 nbytes);
|
||||
FLAC__uint64 nbytes) noexcept;
|
||||
|
||||
/**
|
||||
* Calculate the delta (in bytes) between the last frame and
|
||||
@ -87,8 +88,8 @@ struct FlacDecoder : public FlacInput {
|
||||
FLAC__uint64 GetDeltaPosition(const FLAC__StreamDecoder &sd);
|
||||
|
||||
private:
|
||||
void OnStreamInfo(const FLAC__StreamMetadata_StreamInfo &stream_info);
|
||||
void OnVorbisComment(const FLAC__StreamMetadata_VorbisComment &vc);
|
||||
void OnStreamInfo(const FLAC__StreamMetadata_StreamInfo &stream_info) noexcept;
|
||||
void OnVorbisComment(const FLAC__StreamMetadata_VorbisComment &vc) noexcept;
|
||||
|
||||
/**
|
||||
* This function attempts to call DecoderClient::Ready() in case there
|
||||
@ -97,7 +98,7 @@ private:
|
||||
* providing the STREAMINFO block from the beginning of the file
|
||||
* (e.g. when seeking with SqueezeBox Server).
|
||||
*/
|
||||
bool OnFirstFrame(const FLAC__FrameHeader &header);
|
||||
bool OnFirstFrame(const FLAC__FrameHeader &header) noexcept;
|
||||
};
|
||||
|
||||
#endif /* _FLAC_COMMON_H */
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "lib/xiph/FlacMetadataChain.hxx"
|
||||
#include "OggCodec.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "input/LocalOpen.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "Log.hxx"
|
||||
@ -32,7 +33,8 @@
|
||||
#error libFLAC is too old
|
||||
#endif
|
||||
|
||||
static void flacPrintErroredState(FLAC__StreamDecoderState state)
|
||||
static void
|
||||
flacPrintErroredState(FLAC__StreamDecoderState state) noexcept
|
||||
{
|
||||
switch (state) {
|
||||
case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
|
||||
@ -53,8 +55,9 @@ static void flacPrintErroredState(FLAC__StreamDecoderState state)
|
||||
LogError(flac_domain, FLAC__StreamDecoderStateString[state]);
|
||||
}
|
||||
|
||||
static void flacMetadata([[maybe_unused]] const FLAC__StreamDecoder * dec,
|
||||
const FLAC__StreamMetadata * block, void *vdata)
|
||||
static void
|
||||
flacMetadata([[maybe_unused]] const FLAC__StreamDecoder * dec,
|
||||
const FLAC__StreamMetadata * block, void *vdata) noexcept
|
||||
{
|
||||
auto &fd = *(FlacDecoder *)vdata;
|
||||
fd.OnMetadata(*block);
|
||||
@ -62,29 +65,45 @@ static void flacMetadata([[maybe_unused]] const FLAC__StreamDecoder * dec,
|
||||
|
||||
static FLAC__StreamDecoderWriteStatus
|
||||
flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
|
||||
const FLAC__int32 *const buf[], void *vdata)
|
||||
const FLAC__int32 *const buf[], void *vdata) noexcept
|
||||
{
|
||||
auto &fd = *(FlacDecoder *)vdata;
|
||||
return fd.OnWrite(*frame, buf, fd.GetDeltaPosition(*dec));
|
||||
}
|
||||
|
||||
static bool
|
||||
flac_scan_file(Path path_fs, TagHandler &handler)
|
||||
{
|
||||
flac_scan_file(Path path_fs, TagHandler &handler) noexcept {
|
||||
FlacMetadataChain chain;
|
||||
if (!chain.Read(NarrowPath(path_fs))) {
|
||||
const bool succeed = [&chain, &path_fs]() noexcept {
|
||||
// read by NarrowPath
|
||||
if (chain.Read(NarrowPath(path_fs))) {
|
||||
return true;
|
||||
}
|
||||
if (std::is_same_v<Path::value_type, char> ||
|
||||
chain.GetStatus() != FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE) {
|
||||
return false;
|
||||
}
|
||||
// read by InputStream
|
||||
Mutex mutex;
|
||||
auto is = OpenLocalInputStream(path_fs, mutex);
|
||||
if (is && chain.Read(*is)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (!succeed) {
|
||||
FmtDebug(flac_domain,
|
||||
"Failed to read FLAC tags: {}",
|
||||
chain.GetStatusString());
|
||||
return false;
|
||||
}
|
||||
|
||||
chain.Scan(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
flac_scan_stream(InputStream &is, TagHandler &handler)
|
||||
flac_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
FlacMetadataChain chain;
|
||||
if (!chain.Read(is)) {
|
||||
@ -102,7 +121,7 @@ flac_scan_stream(InputStream &is, TagHandler &handler)
|
||||
* Some glue code around FLAC__stream_decoder_new().
|
||||
*/
|
||||
static FlacStreamDecoder
|
||||
flac_decoder_new()
|
||||
flac_decoder_new() noexcept
|
||||
{
|
||||
FlacStreamDecoder sd;
|
||||
if(!FLAC__stream_decoder_set_metadata_respond(sd.get(), FLAC__METADATA_TYPE_VORBIS_COMMENT))
|
||||
@ -113,7 +132,7 @@ flac_decoder_new()
|
||||
}
|
||||
|
||||
static bool
|
||||
flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
|
||||
flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd) noexcept
|
||||
{
|
||||
if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) {
|
||||
if (FLAC__stream_decoder_get_state(sd) != FLAC__STREAM_DECODER_END_OF_STREAM)
|
||||
@ -231,7 +250,7 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderInitStatus
|
||||
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data)
|
||||
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data) noexcept
|
||||
{
|
||||
return FLAC__stream_decoder_init_ogg_stream(flac_dec,
|
||||
FlacInput::Read,
|
||||
@ -246,7 +265,7 @@ stream_init_oggflac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data)
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderInitStatus
|
||||
stream_init_flac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data)
|
||||
stream_init_flac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data) noexcept
|
||||
{
|
||||
return FLAC__stream_decoder_init_stream(flac_dec,
|
||||
FlacInput::Read,
|
||||
@ -261,7 +280,8 @@ stream_init_flac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data)
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderInitStatus
|
||||
stream_init(FLAC__StreamDecoder *flac_dec, FlacDecoder *data, bool is_ogg)
|
||||
stream_init(FLAC__StreamDecoder *flac_dec, FlacDecoder *data,
|
||||
bool is_ogg) noexcept
|
||||
{
|
||||
return is_ogg
|
||||
? stream_init_oggflac(flac_dec, data)
|
||||
@ -307,7 +327,7 @@ flac_decode(DecoderClient &client, InputStream &input_stream)
|
||||
}
|
||||
|
||||
static bool
|
||||
oggflac_init([[maybe_unused]] const ConfigBlock &block)
|
||||
oggflac_init([[maybe_unused]] const ConfigBlock &block) noexcept
|
||||
{
|
||||
return !!FLAC_API_SUPPORTS_OGG_FLAC;
|
||||
}
|
||||
|
@ -22,12 +22,11 @@
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <exception>
|
||||
|
||||
FLAC__StreamDecoderReadStatus
|
||||
FlacInput::Read(FLAC__byte buffer[], size_t *bytes)
|
||||
inline FLAC__StreamDecoderReadStatus
|
||||
FlacInput::Read(FLAC__byte buffer[], size_t *bytes) noexcept
|
||||
{
|
||||
size_t r = decoder_read(client, input_stream, (void *)buffer, *bytes);
|
||||
*bytes = r;
|
||||
@ -44,8 +43,8 @@ FlacInput::Read(FLAC__byte buffer[], size_t *bytes)
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus
|
||||
FlacInput::Seek(FLAC__uint64 absolute_byte_offset)
|
||||
inline FLAC__StreamDecoderSeekStatus
|
||||
FlacInput::Seek(FLAC__uint64 absolute_byte_offset) noexcept
|
||||
{
|
||||
if (!input_stream.IsSeekable())
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
|
||||
@ -59,8 +58,8 @@ FlacInput::Seek(FLAC__uint64 absolute_byte_offset)
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus
|
||||
FlacInput::Tell(FLAC__uint64 *absolute_byte_offset)
|
||||
inline FLAC__StreamDecoderTellStatus
|
||||
FlacInput::Tell(FLAC__uint64 *absolute_byte_offset) noexcept
|
||||
{
|
||||
if (!input_stream.IsSeekable())
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
|
||||
@ -69,8 +68,8 @@ FlacInput::Tell(FLAC__uint64 *absolute_byte_offset)
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus
|
||||
FlacInput::Length(FLAC__uint64 *stream_length)
|
||||
inline FLAC__StreamDecoderLengthStatus
|
||||
FlacInput::Length(FLAC__uint64 *stream_length) noexcept
|
||||
{
|
||||
if (!input_stream.KnownSize())
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
|
||||
@ -79,8 +78,8 @@ FlacInput::Length(FLAC__uint64 *stream_length)
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
}
|
||||
|
||||
FLAC__bool
|
||||
FlacInput::Eof()
|
||||
inline FLAC__bool
|
||||
FlacInput::Eof() noexcept
|
||||
{
|
||||
return (client != nullptr &&
|
||||
client->GetCommand() != DecoderCommand::NONE &&
|
||||
@ -88,8 +87,8 @@ FlacInput::Eof()
|
||||
input_stream.LockIsEOF();
|
||||
}
|
||||
|
||||
void
|
||||
FlacInput::Error(FLAC__StreamDecoderErrorStatus status)
|
||||
inline void
|
||||
FlacInput::Error(FLAC__StreamDecoderErrorStatus status) noexcept
|
||||
{
|
||||
if (client == nullptr ||
|
||||
client->GetCommand() != DecoderCommand::STOP)
|
||||
@ -100,7 +99,7 @@ FlacInput::Error(FLAC__StreamDecoderErrorStatus status)
|
||||
FLAC__StreamDecoderReadStatus
|
||||
FlacInput::Read([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__byte buffer[], size_t *bytes,
|
||||
void *client_data)
|
||||
void *client_data) noexcept
|
||||
{
|
||||
auto *i = (FlacInput *)client_data;
|
||||
|
||||
@ -109,7 +108,7 @@ FlacInput::Read([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
|
||||
FLAC__StreamDecoderSeekStatus
|
||||
FlacInput::Seek([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__uint64 absolute_byte_offset, void *client_data)
|
||||
FLAC__uint64 absolute_byte_offset, void *client_data) noexcept
|
||||
{
|
||||
auto *i = (FlacInput *)client_data;
|
||||
|
||||
@ -118,7 +117,7 @@ FlacInput::Seek([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
|
||||
FLAC__StreamDecoderTellStatus
|
||||
FlacInput::Tell([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||||
FLAC__uint64 *absolute_byte_offset, void *client_data) noexcept
|
||||
{
|
||||
auto *i = (FlacInput *)client_data;
|
||||
|
||||
@ -127,7 +126,7 @@ FlacInput::Tell([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
|
||||
FLAC__StreamDecoderLengthStatus
|
||||
FlacInput::Length([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__uint64 *stream_length, void *client_data)
|
||||
FLAC__uint64 *stream_length, void *client_data) noexcept
|
||||
{
|
||||
auto *i = (FlacInput *)client_data;
|
||||
|
||||
@ -136,7 +135,7 @@ FlacInput::Length([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
|
||||
FLAC__bool
|
||||
FlacInput::Eof([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
void *client_data)
|
||||
void *client_data) noexcept
|
||||
{
|
||||
auto *i = (FlacInput *)client_data;
|
||||
|
||||
@ -145,7 +144,8 @@ FlacInput::Eof([[maybe_unused]] const FLAC__StreamDecoder *flac_decoder,
|
||||
|
||||
void
|
||||
FlacInput::Error([[maybe_unused]] const FLAC__StreamDecoder *decoder,
|
||||
FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||
FLAC__StreamDecoderErrorStatus status,
|
||||
void *client_data) noexcept
|
||||
{
|
||||
auto *i = (FlacInput *)client_data;
|
||||
|
||||
|
@ -48,36 +48,38 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
FLAC__StreamDecoderReadStatus Read(FLAC__byte buffer[], size_t *bytes);
|
||||
FLAC__StreamDecoderSeekStatus Seek(FLAC__uint64 absolute_byte_offset);
|
||||
FLAC__StreamDecoderTellStatus Tell(FLAC__uint64 *absolute_byte_offset);
|
||||
FLAC__StreamDecoderLengthStatus Length(FLAC__uint64 *stream_length);
|
||||
FLAC__bool Eof();
|
||||
void Error(FLAC__StreamDecoderErrorStatus status);
|
||||
FLAC__StreamDecoderReadStatus Read(FLAC__byte buffer[], size_t *bytes) noexcept;
|
||||
FLAC__StreamDecoderSeekStatus Seek(FLAC__uint64 absolute_byte_offset) noexcept;
|
||||
FLAC__StreamDecoderTellStatus Tell(FLAC__uint64 *absolute_byte_offset) noexcept;
|
||||
FLAC__StreamDecoderLengthStatus Length(FLAC__uint64 *stream_length) noexcept;
|
||||
FLAC__bool Eof() noexcept;
|
||||
void Error(FLAC__StreamDecoderErrorStatus status) noexcept;
|
||||
|
||||
public:
|
||||
static FLAC__StreamDecoderReadStatus
|
||||
Read(const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__byte buffer[], size_t *bytes, void *client_data);
|
||||
FLAC__byte buffer[], size_t *bytes, void *client_data) noexcept;
|
||||
|
||||
static FLAC__StreamDecoderSeekStatus
|
||||
Seek(const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__uint64 absolute_byte_offset, void *client_data);
|
||||
FLAC__uint64 absolute_byte_offset, void *client_data) noexcept;
|
||||
|
||||
static FLAC__StreamDecoderTellStatus
|
||||
Tell(const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__uint64 *absolute_byte_offset, void *client_data);
|
||||
FLAC__uint64 *absolute_byte_offset, void *client_data) noexcept;
|
||||
|
||||
static FLAC__StreamDecoderLengthStatus
|
||||
Length(const FLAC__StreamDecoder *flac_decoder,
|
||||
FLAC__uint64 *stream_length, void *client_data);
|
||||
FLAC__uint64 *stream_length, void *client_data) noexcept;
|
||||
|
||||
static FLAC__bool
|
||||
Eof(const FLAC__StreamDecoder *flac_decoder, void *client_data);
|
||||
Eof(const FLAC__StreamDecoder *flac_decoder,
|
||||
void *client_data) noexcept;
|
||||
|
||||
static void
|
||||
Error(const FLAC__StreamDecoder *decoder,
|
||||
FLAC__StreamDecoderErrorStatus status, void *client_data);
|
||||
FLAC__StreamDecoderErrorStatus status,
|
||||
void *client_data) noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -39,7 +39,8 @@ FlacPcmImport::Open(unsigned sample_rate, unsigned bits_per_sample,
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
FlacImportStereo(T *dest, const FLAC__int32 *const src[], size_t n_frames)
|
||||
FlacImportStereo(T *dest, const FLAC__int32 *const src[],
|
||||
size_t n_frames) noexcept
|
||||
{
|
||||
for (size_t i = 0; i != n_frames; ++i) {
|
||||
*dest++ = (T)src[0][i];
|
||||
@ -50,7 +51,7 @@ FlacImportStereo(T *dest, const FLAC__int32 *const src[], size_t n_frames)
|
||||
template<typename T>
|
||||
static void
|
||||
FlacImportAny(T *dest, const FLAC__int32 *const src[], size_t n_frames,
|
||||
unsigned n_channels)
|
||||
unsigned n_channels) noexcept
|
||||
{
|
||||
for (size_t i = 0; i != n_frames; ++i)
|
||||
for (unsigned c = 0; c != n_channels; ++c)
|
||||
@ -60,7 +61,7 @@ FlacImportAny(T *dest, const FLAC__int32 *const src[], size_t n_frames,
|
||||
template<typename T>
|
||||
static void
|
||||
FlacImport(T *dest, const FLAC__int32 *const src[], size_t n_frames,
|
||||
unsigned n_channels)
|
||||
unsigned n_channels) noexcept
|
||||
{
|
||||
if (n_channels == 2)
|
||||
FlacImportStereo(dest, src, n_frames);
|
||||
@ -71,7 +72,7 @@ FlacImport(T *dest, const FLAC__int32 *const src[], size_t n_frames,
|
||||
template<typename T>
|
||||
static ConstBuffer<void>
|
||||
FlacImport(PcmBuffer &buffer, const FLAC__int32 *const src[], size_t n_frames,
|
||||
unsigned n_channels)
|
||||
unsigned n_channels) noexcept
|
||||
{
|
||||
size_t n_samples = n_frames * n_channels;
|
||||
size_t dest_size = n_samples * sizeof(T);
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
void Open(unsigned sample_rate, unsigned bits_per_sample,
|
||||
unsigned channels);
|
||||
|
||||
const AudioFormat &GetAudioFormat() const {
|
||||
const AudioFormat &GetAudioFormat() const noexcept {
|
||||
return audio_format;
|
||||
}
|
||||
|
||||
|
@ -56,20 +56,17 @@ struct GmeContainerPath {
|
||||
unsigned track;
|
||||
};
|
||||
|
||||
#if GME_VERSION >= 0x000600
|
||||
static int gme_accuracy;
|
||||
#endif
|
||||
static unsigned gme_default_fade;
|
||||
|
||||
static bool
|
||||
gme_plugin_init([[maybe_unused]] const ConfigBlock &block)
|
||||
{
|
||||
#if GME_VERSION >= 0x000600
|
||||
auto accuracy = block.GetBlockParam("accuracy");
|
||||
gme_accuracy = accuracy != nullptr
|
||||
? (int)accuracy->GetBoolValue()
|
||||
: -1;
|
||||
#endif
|
||||
|
||||
auto fade = block.GetBlockParam("default_fade");
|
||||
gme_default_fade = fade != nullptr
|
||||
? fade->GetUnsignedValue() * 1000
|
||||
@ -163,10 +160,8 @@ gme_file_decode(DecoderClient &client, Path path_fs)
|
||||
FmtDebug(gme_domain, "emulator type '{}'",
|
||||
gme_type_system(gme_type(emu)));
|
||||
|
||||
#if GME_VERSION >= 0x000600
|
||||
if (gme_accuracy >= 0)
|
||||
gme_enable_accuracy(emu, gme_accuracy);
|
||||
#endif
|
||||
|
||||
gme_info_t *ti;
|
||||
const char *gme_err = gme_track_info(emu, &ti, container.track);
|
||||
|
@ -562,7 +562,21 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) noexcept
|
||||
|
||||
mad_bit_skip(ptr, 16);
|
||||
|
||||
lame->peak = MAD_F(mad_bit_read(ptr, 32) << 5); /* peak */
|
||||
/* The lame peak value is a float multiplied by 2^23 and stored as an
|
||||
* unsigned integer (it is always positive). MAD's fixed-point format uses
|
||||
* 28 bits for the fractional part, so shift the 23 bit fraction up before
|
||||
* converting to a float.
|
||||
*/
|
||||
unsigned long peak_int = mad_bit_read(ptr, 32);
|
||||
|
||||
#define LAME_PEAK_FRACBITS 23
|
||||
#if MAD_F_FRACBITS > LAME_PEAK_FRACBITS
|
||||
peak_int <<= (MAD_F_FRACBITS - LAME_PEAK_FRACBITS);
|
||||
#elif LAME_PEAK_FRACBITS > MAD_F_FRACBITS
|
||||
peak_int >>= (LAME_PEAK_FRACBITS - MAD_F_FRACBITS);
|
||||
#endif
|
||||
|
||||
lame->peak = mad_f_todouble(peak_int); /* peak */
|
||||
FmtDebug(mad_domain, "LAME peak found: {}", lame->peak);
|
||||
|
||||
lame->track_gain = 0;
|
||||
@ -798,6 +812,8 @@ MadDecoder::UpdateTimerNextFrame() noexcept
|
||||
DecoderCommand
|
||||
MadDecoder::SubmitPCM(size_t i, size_t pcm_length) noexcept
|
||||
{
|
||||
assert(i <= pcm_length);
|
||||
|
||||
size_t num_samples = pcm_length - i;
|
||||
|
||||
mad_fixed_to_24_buffer(output_buffer, synth.pcm,
|
||||
@ -843,7 +859,7 @@ MadDecoder::SynthAndSubmit() noexcept
|
||||
size_t pcm_length = synth.pcm.length;
|
||||
if (drop_end_samples &&
|
||||
current_frame == max_frames - drop_end_frames - 1) {
|
||||
if (drop_end_samples >= pcm_length)
|
||||
if (i + drop_end_samples >= pcm_length)
|
||||
return DecoderCommand::STOP;
|
||||
|
||||
pcm_length -= drop_end_samples;
|
||||
|
@ -81,7 +81,7 @@ if libfaad_dep.found()
|
||||
decoder_plugins_sources += 'FaadDecoderPlugin.cxx'
|
||||
endif
|
||||
|
||||
libgme_dep = c_compiler.find_library('gme', required: get_option('gme'))
|
||||
libgme_dep = dependency('libgme', version: '>= 0.6', required: get_option('gme'))
|
||||
decoder_features.set('ENABLE_GME', libgme_dep.found())
|
||||
if libgme_dep.found()
|
||||
decoder_plugins_sources += 'GmeDecoderPlugin.cxx'
|
||||
|
@ -38,6 +38,7 @@ class FlacEncoder final : public Encoder {
|
||||
|
||||
FLAC__StreamEncoder *const fse;
|
||||
const unsigned compression;
|
||||
const bool oggflac;
|
||||
|
||||
PcmBuffer expand_buffer;
|
||||
|
||||
@ -122,7 +123,7 @@ flac_encoder_init(const ConfigBlock &block)
|
||||
}
|
||||
|
||||
static void
|
||||
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
|
||||
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression, bool oggflac,
|
||||
const AudioFormat &audio_format)
|
||||
{
|
||||
unsigned bits_per_sample;
|
||||
@ -157,7 +158,7 @@ flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
|
||||
throw FormatRuntimeError("error setting flac sample rate to %d",
|
||||
audio_format.sample_rate);
|
||||
|
||||
if (!FLAC__stream_encoder_set_ogg_serial_number(fse,
|
||||
if (oggflac && !FLAC__stream_encoder_set_ogg_serial_number(fse,
|
||||
GenerateSerial()))
|
||||
throw FormatRuntimeError("error setting ogg serial number");
|
||||
}
|
||||
@ -166,11 +167,12 @@ FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse, u
|
||||
:Encoder(_oggchaining),
|
||||
audio_format(_audio_format), fse(_fse),
|
||||
compression(_compression),
|
||||
oggflac(_oggflac),
|
||||
output_buffer(8192)
|
||||
{
|
||||
/* this immediately outputs data through callback */
|
||||
|
||||
auto init_status = _oggflac ?
|
||||
auto init_status = oggflac ?
|
||||
FLAC__stream_encoder_init_ogg_stream(fse,
|
||||
nullptr, WriteCallback,
|
||||
nullptr, nullptr, nullptr,
|
||||
@ -209,7 +211,7 @@ PreparedFlacEncoder::Open(AudioFormat &audio_format)
|
||||
throw std::runtime_error("FLAC__stream_encoder_new() failed");
|
||||
|
||||
try {
|
||||
flac_encoder_setup(fse, compression, audio_format);
|
||||
flac_encoder_setup(fse, compression, oggflac, audio_format);
|
||||
} catch (...) {
|
||||
FLAC__stream_encoder_delete(fse);
|
||||
throw;
|
||||
@ -222,7 +224,7 @@ void
|
||||
FlacEncoder::SendTag(const Tag &tag)
|
||||
{
|
||||
/* re-initialize encoder since flac_encoder_finish resets everything */
|
||||
flac_encoder_setup(fse, compression, audio_format);
|
||||
flac_encoder_setup(fse, compression, oggflac, audio_format);
|
||||
|
||||
FLAC__StreamMetadata *metadata = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
FLAC__StreamMetadata_VorbisComment_Entry entry;
|
||||
|
@ -272,9 +272,8 @@ EventLoop::Run() noexcept
|
||||
#endif
|
||||
|
||||
assert(IsInside());
|
||||
assert(!quit);
|
||||
#ifdef HAVE_THREADED_EVENT_LOOP
|
||||
assert(alive);
|
||||
assert(alive || quit);
|
||||
assert(busy);
|
||||
|
||||
wake_event.Schedule(SocketEvent::READ);
|
||||
@ -299,7 +298,7 @@ EventLoop::Run() noexcept
|
||||
|
||||
steady_clock_cache.flush();
|
||||
|
||||
do {
|
||||
while (!quit) {
|
||||
again = false;
|
||||
|
||||
/* invoke timers */
|
||||
@ -361,7 +360,7 @@ EventLoop::Run() noexcept
|
||||
|
||||
socket_event.Dispatch();
|
||||
}
|
||||
} while (!quit);
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREADED_EVENT_LOOP
|
||||
#ifndef NDEBUG
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "Charset.hxx"
|
||||
#include "Features.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "lib/icu/Converter.hxx"
|
||||
#include "util/AllocatedString.hxx"
|
||||
#include "config.h"
|
||||
@ -45,11 +44,9 @@ SetFSCharset(const char *charset)
|
||||
assert(charset != nullptr);
|
||||
assert(fs_converter == nullptr);
|
||||
|
||||
fs_charset = charset;
|
||||
fs_converter = IcuConverter::Create(charset);
|
||||
assert(fs_converter != nullptr);
|
||||
|
||||
FmtDebug(path_domain,
|
||||
"SetFSCharset: fs charset is {}", fs_charset);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -67,6 +67,16 @@ StatFile(Path file, struct stat &buf, bool follow_symlinks = true)
|
||||
|
||||
#endif
|
||||
|
||||
static inline bool
|
||||
CreateDirectoryNoThrow(Path path) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return CreateDirectory(path.c_str(), nullptr);
|
||||
#else
|
||||
return mkdir(path.c_str(), 0777);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a file that exists already. Throws std::system_error on
|
||||
* error.
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <shlobj.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
@ -53,6 +52,12 @@
|
||||
#include "Main.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef USE_XDG
|
||||
#include "Version.h" // for PACKAGE_NAME
|
||||
#define APP_FILENAME PATH_LITERAL(PACKAGE_NAME)
|
||||
static constexpr Path app_filename = Path::FromFS(APP_FILENAME);
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(ANDROID)
|
||||
class PasswdEntry
|
||||
{
|
||||
@ -74,15 +79,6 @@ public:
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
bool ReadByUid(uid_t uid) {
|
||||
#ifdef HAVE_GETPWUID_R
|
||||
getpwuid_r(uid, &pw, buf.data(), buf.size(), &result);
|
||||
#else
|
||||
result = getpwuid(uid);
|
||||
#endif
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
const passwd *operator->() {
|
||||
assert(result != nullptr);
|
||||
return result;
|
||||
@ -284,6 +280,24 @@ GetUserCacheDir() noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
GetAppCacheDir() noexcept
|
||||
{
|
||||
#ifdef USE_XDG
|
||||
if (const auto user_dir = GetUserCacheDir(); !user_dir.IsNull()) {
|
||||
auto dir = user_dir / app_filename;
|
||||
CreateDirectoryNoThrow(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#elif defined(ANDROID)
|
||||
return context->GetCacheDir(Java::GetEnv());
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
GetUserRuntimeDir() noexcept
|
||||
{
|
||||
@ -297,7 +311,7 @@ GetUserRuntimeDir() noexcept
|
||||
AllocatedPath
|
||||
GetAppRuntimeDir() noexcept
|
||||
{
|
||||
#ifdef __linux__
|
||||
#if defined(__linux__) && !defined(ANDROID)
|
||||
/* systemd specific; see systemd.exec(5) */
|
||||
if (const char *runtime_directory = getenv("RUNTIME_DIRECTORY"))
|
||||
if (auto dir = StringView{runtime_directory}.Split(':').first;
|
||||
@ -307,8 +321,8 @@ GetAppRuntimeDir() noexcept
|
||||
|
||||
#ifdef USE_XDG
|
||||
if (const auto user_dir = GetUserRuntimeDir(); !user_dir.IsNull()) {
|
||||
auto dir = user_dir / Path::FromFS("mpd");
|
||||
mkdir(dir.c_str(), 0700);
|
||||
auto dir = user_dir / app_filename;
|
||||
CreateDirectoryNoThrow(dir);
|
||||
return dir;
|
||||
}
|
||||
#endif
|
||||
@ -351,10 +365,8 @@ GetHomeDir() noexcept
|
||||
if (const auto home = getenv("HOME");
|
||||
IsValidPathString(home) && IsValidDir(home))
|
||||
return AllocatedPath::FromFS(home);
|
||||
|
||||
if (PasswdEntry pw; pw.ReadByUid(getuid()))
|
||||
return SafePathFromFS(pw->pw_dir);
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,13 @@ GetUserMusicDir() noexcept;
|
||||
AllocatedPath
|
||||
GetUserCacheDir() noexcept;
|
||||
|
||||
/**
|
||||
* Obtains cache directory for this application.
|
||||
*/
|
||||
[[gnu::const]]
|
||||
AllocatedPath
|
||||
GetAppCacheDir() noexcept;
|
||||
|
||||
/**
|
||||
* Obtains the runtime directory for the current user.
|
||||
*/
|
||||
|
@ -101,9 +101,17 @@ AsyncInputStream::Seek(std::unique_lock<Mutex> &lock,
|
||||
assert(IsReady());
|
||||
assert(seek_state == SeekState::NONE);
|
||||
|
||||
if (new_offset == offset)
|
||||
/* no-op */
|
||||
if (new_offset == offset) {
|
||||
/* no-op, but if the stream is not open anymore (maybe
|
||||
because it has failed), nothing can be read, so we
|
||||
should check for errors here instead of pretending
|
||||
everything's fine */
|
||||
|
||||
if (!open)
|
||||
Check();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsSeekable())
|
||||
throw std::runtime_error("Not seekable");
|
||||
|
@ -417,7 +417,6 @@ CurlInputStream::InitEasy()
|
||||
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
|
||||
request->SetOption(CURLOPT_FOLLOWLOCATION, 1L);
|
||||
request->SetOption(CURLOPT_MAXREDIRS, 5L);
|
||||
request->SetOption(CURLOPT_FAILONERROR, 1L);
|
||||
|
||||
/* this option eliminates the probe request when
|
||||
username/password are specified */
|
||||
@ -439,6 +438,14 @@ CurlInputStream::InitEasy()
|
||||
request->SetVerifyPeer(verify_peer);
|
||||
request->SetVerifyHost(verify_host);
|
||||
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
|
||||
|
||||
try {
|
||||
request->SetProxyVerifyPeer(verify_peer);
|
||||
request->SetProxyVerifyHost(verify_host);
|
||||
} catch (...) {
|
||||
/* these methods fail if libCURL was compiled with
|
||||
CURL_DISABLE_PROXY; ignore silently */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include "io/UniqueFileDescriptor.hxx"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Path;
|
||||
class FileInfo;
|
||||
|
||||
|
@ -49,9 +49,6 @@ Java::File::Initialise(JNIEnv *env) noexcept
|
||||
AllocatedPath
|
||||
Java::File::ToAbsolutePath(JNIEnv *env, jobject _file) noexcept
|
||||
{
|
||||
assert(env != nullptr);
|
||||
assert(_file != nullptr);
|
||||
|
||||
LocalObject file(env, _file);
|
||||
|
||||
const jstring path = GetAbsolutePath(env, file);
|
||||
|
@ -18,13 +18,13 @@ endif
|
||||
|
||||
conf.set('HAVE_MD5', crypto_md5_dep.found())
|
||||
|
||||
if libavutil_dep.found()
|
||||
if ffmpeg_util_dep.found()
|
||||
crypto_base64 = static_library(
|
||||
'crypto_base64',
|
||||
'Base64.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
libavutil_dep,
|
||||
ffmpeg_util_dep,
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -186,10 +186,6 @@ public:
|
||||
SetOption(CURLOPT_POSTFIELDSIZE, (long)size);
|
||||
}
|
||||
|
||||
void SetHttpPost(const struct curl_httppost *post) {
|
||||
SetOption(CURLOPT_HTTPPOST, post);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetInfo(CURLINFO info, T value_r) const noexcept {
|
||||
return ::curl_easy_getinfo(handle, info, value_r) == CURLE_OK;
|
||||
@ -199,10 +195,10 @@ public:
|
||||
* Returns the response body's size, or -1 if that is unknown.
|
||||
*/
|
||||
[[gnu::pure]]
|
||||
int64_t GetContentLength() const noexcept {
|
||||
double value;
|
||||
return GetInfo(CURLINFO_CONTENT_LENGTH_DOWNLOAD, &value)
|
||||
? (int64_t)value
|
||||
curl_off_t GetContentLength() const noexcept {
|
||||
curl_off_t value;
|
||||
return GetInfo(CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &value)
|
||||
? value
|
||||
: -1;
|
||||
}
|
||||
|
||||
|
@ -123,6 +123,14 @@ public:
|
||||
easy.SetVerifyPeer(value);
|
||||
}
|
||||
|
||||
void SetProxyVerifyHost(bool value) {
|
||||
easy.SetOption(CURLOPT_PROXY_SSL_VERIFYHOST, value ? 2L : 0L);
|
||||
}
|
||||
|
||||
void SetProxyVerifyPeer(bool value) {
|
||||
easy.SetOption(CURLOPT_PROXY_SSL_VERIFYPEER, value);
|
||||
}
|
||||
|
||||
void SetNoBody(bool value=true) {
|
||||
easy.SetNoBody(value);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
curl_dep = dependency('libcurl', version: '>= 7.33', required: get_option('curl'))
|
||||
curl_dep = dependency('libcurl', version: '>= 7.55', required: get_option('curl'))
|
||||
conf.set('ENABLE_CURL', curl_dep.found())
|
||||
if not curl_dep.found()
|
||||
subdir_done()
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "Iter.hxx"
|
||||
#include "Values.hxx"
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ODBus {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* Copyright 2003-2022 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -17,26 +17,23 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "LogError.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
}
|
||||
|
||||
void
|
||||
LogFfmpegError(int errnum)
|
||||
{
|
||||
char msg[256];
|
||||
av_strerror(errnum, msg, sizeof(msg));
|
||||
LogError(ffmpeg_domain, msg);
|
||||
}
|
||||
#include <fmt/format.h>
|
||||
|
||||
void
|
||||
LogFfmpegError(int errnum, const char *prefix)
|
||||
template<>
|
||||
struct fmt::formatter<AVSampleFormat> : formatter<string_view>
|
||||
{
|
||||
char msg[256];
|
||||
av_strerror(errnum, msg, sizeof(msg));
|
||||
FmtError(ffmpeg_domain, "{}: {}", prefix, msg);
|
||||
}
|
||||
template<typename FormatContext>
|
||||
auto format(const AVSampleFormat format, FormatContext &ctx) {
|
||||
const char *name = av_get_sample_fmt_name(format);
|
||||
if (name == nullptr)
|
||||
name = "?";
|
||||
|
||||
return formatter<string_view>::format(name, ctx);
|
||||
}
|
||||
};
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2021 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_FFMPEG_LOG_ERROR_HXX
|
||||
#define MPD_FFMPEG_LOG_ERROR_HXX
|
||||
|
||||
void
|
||||
LogFfmpegError(int errnum);
|
||||
|
||||
void
|
||||
LogFfmpegError(int errnum, const char *prefix);
|
||||
|
||||
#endif
|
@ -13,6 +13,29 @@ else
|
||||
endif
|
||||
conf.set('HAVE_LIBAVFILTER', libavfilter_dep.found())
|
||||
|
||||
if not libavutil_dep.found()
|
||||
ffmpeg_util_dep = dependency('', required: false)
|
||||
ffmpeg_dep = dependency('', required: false)
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
ffmpeg_util = static_library(
|
||||
'ffmpeg_util',
|
||||
'Interleave.cxx',
|
||||
'Error.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
libavutil_dep,
|
||||
],
|
||||
)
|
||||
|
||||
ffmpeg_util_dep = declare_dependency(
|
||||
link_with: ffmpeg_util,
|
||||
dependencies: [
|
||||
libavutil_dep,
|
||||
],
|
||||
)
|
||||
|
||||
if not enable_ffmpeg
|
||||
ffmpeg_dep = dependency('', required: false)
|
||||
subdir_done()
|
||||
@ -30,17 +53,16 @@ ffmpeg = static_library(
|
||||
'ffmpeg',
|
||||
'Init.cxx',
|
||||
'Interleave.cxx',
|
||||
'LogError.cxx',
|
||||
'LogCallback.cxx',
|
||||
'Error.cxx',
|
||||
'Domain.cxx',
|
||||
ffmpeg_sources,
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
ffmpeg_util_dep,
|
||||
libavformat_dep,
|
||||
libavcodec_dep,
|
||||
libavfilter_dep,
|
||||
libavutil_dep,
|
||||
log_dep,
|
||||
],
|
||||
)
|
||||
@ -48,9 +70,9 @@ ffmpeg = static_library(
|
||||
ffmpeg_dep = declare_dependency(
|
||||
link_with: ffmpeg,
|
||||
dependencies: [
|
||||
ffmpeg_util_dep,
|
||||
libavformat_dep,
|
||||
libavcodec_dep,
|
||||
libavfilter_dep,
|
||||
libavutil_dep,
|
||||
],
|
||||
)
|
||||
|
22
src/lib/modplug/patches/no_register
Normal file
22
src/lib/modplug/patches/no_register
Normal file
@ -0,0 +1,22 @@
|
||||
Index: libmodplug-0.8.9.0/src/fastmix.cpp
|
||||
===================================================================
|
||||
--- libmodplug-0.8.9.0.orig/src/fastmix.cpp
|
||||
+++ libmodplug-0.8.9.0/src/fastmix.cpp
|
||||
@@ -288,7 +288,7 @@ CzWINDOWEDFIR sfir;
|
||||
// MIXING MACROS
|
||||
// ----------------------------------------------------------------------------
|
||||
#define SNDMIX_BEGINSAMPLELOOP8\
|
||||
- register MODCHANNEL * const pChn = pChannel;\
|
||||
+ MODCHANNEL * const pChn = pChannel;\
|
||||
nPos = pChn->nPosLo;\
|
||||
const signed char *p = (signed char *)(pChn->pCurrentSample+pChn->nPos);\
|
||||
if (pChn->dwFlags & CHN_STEREO) p += pChn->nPos;\
|
||||
@@ -296,7 +296,7 @@ CzWINDOWEDFIR sfir;
|
||||
do {
|
||||
|
||||
#define SNDMIX_BEGINSAMPLELOOP16\
|
||||
- register MODCHANNEL * const pChn = pChannel;\
|
||||
+ MODCHANNEL * const pChn = pChannel;\
|
||||
nPos = pChn->nPosLo;\
|
||||
const signed short *p = (signed short *)(pChn->pCurrentSample+(pChn->nPos*2));\
|
||||
if (pChn->dwFlags & CHN_STEREO) p += pChn->nPos;\
|
1
src/lib/modplug/patches/series
Normal file
1
src/lib/modplug/patches/series
Normal file
@ -0,0 +1 @@
|
||||
no_register
|
@ -812,8 +812,12 @@ AlsaOutput::Open(AudioFormat &audio_format)
|
||||
fmt::format("Failed to open ALSA device \"{}\"",
|
||||
GetDevice()).c_str());
|
||||
|
||||
const char *pcm_name = snd_pcm_name(pcm);
|
||||
if (pcm_name == nullptr)
|
||||
pcm_name = "?";
|
||||
|
||||
FmtDebug(alsa_output_domain, "opened {} type={}",
|
||||
snd_pcm_name(pcm),
|
||||
pcm_name,
|
||||
snd_pcm_type_name(snd_pcm_type(pcm)));
|
||||
|
||||
#ifdef ENABLE_DSD
|
||||
|
@ -627,7 +627,7 @@ osx_render(void *vdata,
|
||||
{
|
||||
OSXOutput *od = (OSXOutput *) vdata;
|
||||
|
||||
int count = in_number_frames * od->asbd.mBytesPerFrame;
|
||||
std::size_t count = in_number_frames * od->asbd.mBytesPerFrame;
|
||||
buffer_list->mBuffers[0].mDataByteSize =
|
||||
od->ring_buffer->pop((uint8_t *)buffer_list->mBuffers[0].mData,
|
||||
count);
|
||||
|
@ -523,7 +523,13 @@ PipeWireOutput::Open(AudioFormat &audio_format)
|
||||
pw_properties_setf(props, PW_KEY_REMOTE_NAME, "%s", remote);
|
||||
|
||||
if (target != nullptr && target_id == PW_ID_ANY)
|
||||
pw_properties_setf(props, PW_KEY_NODE_TARGET, "%s", target);
|
||||
pw_properties_setf(props,
|
||||
#if PW_CHECK_VERSION(0, 3, 64)
|
||||
PW_KEY_TARGET_OBJECT,
|
||||
#else
|
||||
PW_KEY_NODE_TARGET,
|
||||
#endif
|
||||
"%s", target);
|
||||
|
||||
#ifdef PW_KEY_NODE_RATE
|
||||
/* ask PipeWire to change the graph sample rate to ours
|
||||
@ -967,6 +973,8 @@ PipeWireOutput::SendTag(const Tag &tag)
|
||||
|
||||
struct spa_dict dict = SPA_DICT_INIT(items, n_items);
|
||||
|
||||
const PipeWire::ThreadLoopLock lock(thread_loop);
|
||||
|
||||
auto rc = pw_stream_update_properties(stream, &dict);
|
||||
if (rc < 0)
|
||||
LogWarning(pipewire_output_domain, "Error updating properties");
|
||||
|
@ -1026,7 +1026,7 @@ WasapiOutput::EnumerateDevices(IMMDeviceEnumerator &enumerator)
|
||||
continue;
|
||||
|
||||
FmtNotice(wasapi_output_domain,
|
||||
"Device \"{}\" \"{}\"", i, name);
|
||||
"Device \"{}\" \"{}\"", i, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ storage_glue = static_library(
|
||||
'CompositeStorage.cxx',
|
||||
'MemoryDirectoryReader.cxx',
|
||||
'Configured.cxx',
|
||||
'StorageState.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
boost_dep,
|
||||
@ -31,4 +30,3 @@ storage_glue_dep = declare_dependency(
|
||||
storage_plugins_dep,
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "storage/FileInfo.hxx"
|
||||
#include "storage/MemoryDirectoryReader.hxx"
|
||||
#include "lib/curl/Error.hxx"
|
||||
#include "lib/curl/Init.hxx"
|
||||
#include "lib/curl/Global.hxx"
|
||||
#include "lib/curl/Slist.hxx"
|
||||
@ -34,7 +35,6 @@
|
||||
#include "event/InjectEvent.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "time/Parser.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
@ -171,8 +171,9 @@ struct DavResponse {
|
||||
}
|
||||
};
|
||||
|
||||
[[gnu::pure]]
|
||||
static unsigned
|
||||
ParseStatus(const char *s)
|
||||
ParseStatus(const char *s) noexcept
|
||||
{
|
||||
/* skip the "HTTP/1.1" prefix */
|
||||
const char *space = std::strchr(s, ' ');
|
||||
@ -182,37 +183,37 @@ ParseStatus(const char *s)
|
||||
return strtoul(space + 1, nullptr, 10);
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
static unsigned
|
||||
ParseStatus(const char *s, size_t length)
|
||||
ParseStatus(const char *s, size_t length) noexcept
|
||||
{
|
||||
return ParseStatus(std::string(s, length).c_str());
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
static std::chrono::system_clock::time_point
|
||||
ParseTimeStamp(const char *s)
|
||||
ParseTimeStamp(const char *s) noexcept
|
||||
{
|
||||
try {
|
||||
// TODO: make this more robust
|
||||
return ParseTimePoint(s, "%a, %d %b %Y %T");
|
||||
} catch (...) {
|
||||
return std::chrono::system_clock::time_point::min();
|
||||
}
|
||||
return std::chrono::system_clock::from_time_t(curl_getdate(s, nullptr));
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
static std::chrono::system_clock::time_point
|
||||
ParseTimeStamp(const char *s, size_t length)
|
||||
ParseTimeStamp(const char *s, size_t length) noexcept
|
||||
{
|
||||
return ParseTimeStamp(std::string(s, length).c_str());
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
static uint64_t
|
||||
ParseU64(const char *s)
|
||||
ParseU64(const char *s) noexcept
|
||||
{
|
||||
return strtoull(s, nullptr, 10);
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
static uint64_t
|
||||
ParseU64(const char *s, size_t length)
|
||||
ParseU64(const char *s, size_t length) noexcept
|
||||
{
|
||||
return ParseU64(std::string(s, length).c_str());
|
||||
}
|
||||
@ -278,6 +279,7 @@ public:
|
||||
"<a:resourcetype/>"
|
||||
"<a:getcontenttype/>"
|
||||
"<a:getcontentlength/>"
|
||||
"<a:getlastmodified/>"
|
||||
"</a:prop>"
|
||||
"</a:propfind>");
|
||||
}
|
||||
@ -299,8 +301,9 @@ private:
|
||||
/* virtual methods from CurlResponseHandler */
|
||||
void OnHeaders(unsigned status, Curl::Headers &&headers) final {
|
||||
if (status != 207)
|
||||
throw FormatRuntimeError("Status %d from WebDAV server; expected \"207 Multi-Status\"",
|
||||
status);
|
||||
throw HttpStatusError(status,
|
||||
StringFormat<80>("Status %u from WebDAV server; expected \"207 Multi-Status\"",
|
||||
status).c_str());
|
||||
|
||||
if (!IsXmlContentType(headers))
|
||||
throw std::runtime_error("Unexpected Content-Type from WebDAV server");
|
||||
|
@ -70,8 +70,11 @@ FormatLastError(DWORD code, const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
char buffer[512];
|
||||
const auto end = buffer + sizeof(buffer);
|
||||
size_t length = snprintf(buffer, sizeof(buffer) - 128,
|
||||
constexpr std::size_t max_prefix = sizeof(buffer) - 128;
|
||||
size_t length = snprintf(buffer, max_prefix,
|
||||
fmt, std::forward<Args>(args)...);
|
||||
if (length >= max_prefix)
|
||||
length = max_prefix - 1;
|
||||
char *p = buffer + length;
|
||||
*p++ = ':';
|
||||
*p++ = ' ';
|
||||
|
@ -263,8 +263,14 @@ TagBuilder::RemoveAll() noexcept
|
||||
void
|
||||
TagBuilder::RemoveType(TagType type) noexcept
|
||||
{
|
||||
if (items.empty())
|
||||
/* don't acquire the tag_pool_lock if we're not going
|
||||
to call tag_pool_put_item() anyway */
|
||||
return;
|
||||
|
||||
const auto begin = items.begin(), end = items.end();
|
||||
|
||||
const std::scoped_lock<Mutex> protect(tag_pool_lock);
|
||||
items.erase(std::remove_if(begin, end,
|
||||
[type](TagItem *item) {
|
||||
if (item->type != type)
|
||||
|
@ -26,6 +26,11 @@
|
||||
|
||||
class TagMask {
|
||||
typedef uint_least32_t mask_t;
|
||||
|
||||
/* the mask must have enough bits to represent all tags
|
||||
supported by MPD */
|
||||
static_assert(TAG_NUM_OF_ITEM_TYPES <= sizeof(mask_t) * 8);
|
||||
|
||||
mask_t value;
|
||||
|
||||
explicit constexpr TagMask(uint_least32_t _value) noexcept
|
||||
|
@ -130,7 +130,7 @@ public:
|
||||
|
||||
void set_value(const T &value) {
|
||||
std::unique_lock<CriticalSection> lock(mutex);
|
||||
if (!std::holds_alternative<std::monostate>(&result)) {
|
||||
if (!std::holds_alternative<std::monostate>(result)) {
|
||||
throw WinFutureError(WinFutureErrc::promise_already_satisfied);
|
||||
}
|
||||
result.template emplace<T>(value);
|
||||
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "Parser.hxx"
|
||||
#include "Convert.hxx"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
ParseTimePoint(const char *s, const char *format)
|
||||
{
|
||||
assert(s != nullptr);
|
||||
assert(format != nullptr);
|
||||
|
||||
#ifdef _WIN32
|
||||
/* TODO: emulate strptime()? */
|
||||
(void)s;
|
||||
(void)format;
|
||||
throw std::runtime_error("Time parsing not implemented on Windows");
|
||||
#else
|
||||
struct tm tm{};
|
||||
const char *end = strptime(s, format, &tm);
|
||||
if (end == nullptr || *end != 0)
|
||||
throw std::runtime_error("Failed to parse time stamp");
|
||||
|
||||
return TimeGm(tm);
|
||||
#endif /* !_WIN32 */
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TIME_PARSER_HXX
|
||||
#define TIME_PARSER_HXX
|
||||
|
||||
#include <chrono>
|
||||
|
||||
/**
|
||||
* Parse a time stamp.
|
||||
*
|
||||
* Throws std::runtime_error on error.
|
||||
*/
|
||||
std::chrono::system_clock::time_point
|
||||
ParseTimePoint(const char *s, const char *format);
|
||||
|
||||
#endif
|
@ -1,6 +1,5 @@
|
||||
time = static_library(
|
||||
'time',
|
||||
'Parser.cxx',
|
||||
'Convert.cxx',
|
||||
'ISO8601.cxx',
|
||||
'Math.cxx',
|
||||
|
@ -30,6 +30,10 @@
|
||||
|
||||
#include <csignal>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
static constexpr Domain signal_handlers_domain("signal_handlers");
|
||||
|
||||
static void
|
||||
@ -60,7 +64,7 @@ handle_reload_event(void *ctx) noexcept
|
||||
#endif
|
||||
|
||||
void
|
||||
SignalHandlersInit(Instance &instance)
|
||||
SignalHandlersInit(Instance &instance, bool daemon)
|
||||
{
|
||||
auto &loop = instance.event_loop;
|
||||
|
||||
@ -79,6 +83,14 @@ SignalHandlersInit(Instance &instance)
|
||||
|
||||
SignalMonitorRegister(SIGHUP, {&instance, handle_reload_event});
|
||||
#endif
|
||||
|
||||
if (!daemon) {
|
||||
#ifdef __linux__
|
||||
/* if MPD was not daemonized, shut it down when the
|
||||
parent process dies */
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -23,15 +23,15 @@
|
||||
struct Instance;
|
||||
|
||||
void
|
||||
SignalHandlersInit(Instance &instance);
|
||||
SignalHandlersInit(Instance &instance, bool daemon);
|
||||
|
||||
void
|
||||
SignalHandlersFinish() noexcept;
|
||||
|
||||
class ScopeSignalHandlersInit {
|
||||
public:
|
||||
ScopeSignalHandlersInit(Instance &instance) {
|
||||
SignalHandlersInit(instance);
|
||||
ScopeSignalHandlersInit(Instance &instance, bool daemon) {
|
||||
SignalHandlersInit(instance, daemon);
|
||||
}
|
||||
|
||||
~ScopeSignalHandlersInit() noexcept {
|
||||
|
@ -40,14 +40,17 @@ class ScopeExitGuard : F {
|
||||
bool enabled = true;
|
||||
|
||||
public:
|
||||
explicit ScopeExitGuard(F &&f):F(std::forward<F>(f)) {}
|
||||
explicit ScopeExitGuard(F &&f) noexcept:F(std::forward<F>(f)) {}
|
||||
|
||||
ScopeExitGuard(ScopeExitGuard &&src)
|
||||
:F(std::move(src)), enabled(src.enabled) {
|
||||
src.enabled = false;
|
||||
}
|
||||
ScopeExitGuard(ScopeExitGuard &&src) noexcept
|
||||
:F(std::move(src)),
|
||||
enabled(std::exchange(src.enabled, false)) {}
|
||||
|
||||
~ScopeExitGuard() {
|
||||
/* destructors are "noexcept" by default; this explicit
|
||||
"noexcept" declaration allows the destructor to throw if
|
||||
the function can throw; without this, a throwing function
|
||||
would std::terminate() */
|
||||
~ScopeExitGuard() noexcept(noexcept(std::declval<F>()())) {
|
||||
if (enabled)
|
||||
F::operator()();
|
||||
}
|
||||
@ -64,7 +67,7 @@ struct ScopeExitTag {
|
||||
parantheses at the end of the expression AtScopeExit()
|
||||
call */
|
||||
template<typename F>
|
||||
ScopeExitGuard<F> operator+(F &&f) {
|
||||
ScopeExitGuard<F> operator+(F &&f) noexcept {
|
||||
return ScopeExitGuard<F>(std::forward<F>(f));
|
||||
}
|
||||
};
|
||||
|
@ -52,17 +52,17 @@ public:
|
||||
using R = std::invoke_result_t<std::decay_t<Function>>;
|
||||
auto promise = std::make_shared<Promise<R>>();
|
||||
auto future = promise->get_future();
|
||||
Push([function = std::forward<Function>(function),
|
||||
promise = std::move(promise)]() mutable {
|
||||
Push([func = std::forward<Function>(function),
|
||||
prom = std::move(promise)]() mutable {
|
||||
try {
|
||||
if constexpr (std::is_void_v<R>) {
|
||||
std::invoke(std::forward<Function>(function));
|
||||
promise->set_value();
|
||||
std::invoke(std::forward<Function>(func));
|
||||
prom->set_value();
|
||||
} else {
|
||||
promise->set_value(std::invoke(std::forward<Function>(function)));
|
||||
prom->set_value(std::invoke(std::forward<Function>(func)));
|
||||
}
|
||||
} catch (...) {
|
||||
promise->set_exception(std::current_exception());
|
||||
prom->set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
return future;
|
||||
|
@ -1,12 +1,13 @@
|
||||
[wrap-file]
|
||||
directory = expat-2.4.8
|
||||
source_url = https://github.com/libexpat/libexpat/releases/download/R_2_4_8/expat-2.4.8.tar.xz
|
||||
source_filename = expat-2.4.8.tar.bz2
|
||||
source_hash = f79b8f904b749e3e0d20afeadecf8249c55b2e32d4ebb089ae378df479dcaf25
|
||||
patch_filename = expat_2.4.8-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.4.8-1/get_patch
|
||||
patch_hash = 9aec253a2c6d1c0feb852c5c6920298d14701eeec7acc6832bb402438b52112a
|
||||
directory = expat-2.5.0
|
||||
source_url = https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.xz
|
||||
source_filename = expat-2.5.0.tar.bz2
|
||||
source_hash = ef2420f0232c087801abf705e89ae65f6257df6b7931d37846a193ef2e8cdcbe
|
||||
patch_filename = expat_2.5.0-2_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.5.0-2/get_patch
|
||||
patch_hash = f6cc5ff0d909a2f51a907cc6ca655fb18517a0f58bbe67e4a9c621f1549560c9
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/expat_2.5.0-2/expat-2.5.0.tar.bz2
|
||||
wrapdb_version = 2.5.0-2
|
||||
|
||||
[provide]
|
||||
expat = expat_dep
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
[wrap-file]
|
||||
directory = fmt-8.1.1
|
||||
source_url = https://github.com/fmtlib/fmt/archive/8.1.1.tar.gz
|
||||
source_filename = fmt-8.1.1.tar.gz
|
||||
source_hash = 3d794d3cf67633b34b2771eb9f073bde87e846e0d395d254df7b211ef1ec7346
|
||||
patch_filename = fmt_8.1.1-2_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_8.1.1-2/get_patch
|
||||
patch_hash = cd001046281330a8862591780a9ea71a1fa594edd0d015deb24e44680c9ea33b
|
||||
wrapdb_version = 8.1.1-2
|
||||
directory = fmt-9.1.0
|
||||
source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz
|
||||
source_filename = fmt-9.1.0.tar.gz
|
||||
source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2
|
||||
patch_filename = fmt_9.1.0-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-1/get_patch
|
||||
patch_hash = 4557b9ba87b3eb63694ed9b21d1a2117d4a97ca56b91085b10288e9a5294adf8
|
||||
wrapdb_version = 9.1.0-1
|
||||
|
||||
[provide]
|
||||
fmt = fmt_dep
|
||||
|
@ -1,12 +1,13 @@
|
||||
[wrap-file]
|
||||
directory = sqlite-amalgamation-3380000
|
||||
source_url = https://sqlite.org/2022/sqlite-amalgamation-3380000.zip
|
||||
source_filename = sqlite-amalgamation-3380000.zip
|
||||
source_hash = e055f6054e97747a135c89e36520c0a423249e8a91c5fc445163f4a6adb20df6
|
||||
patch_filename = sqlite3_3.38.0-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.38.0-1/get_patch
|
||||
patch_hash = 49e30bf010ff63ab772d5417885e6905379025ceac80382e292c6dbd3a9da744
|
||||
directory = sqlite-amalgamation-3410200
|
||||
source_url = https://www.sqlite.org/2023/sqlite-amalgamation-3410200.zip
|
||||
source_filename = sqlite-amalgamation-3410200.zip
|
||||
source_hash = 01df06a84803c1ab4d62c64e995b151b2dbcf5dbc93bbc5eee213cb18225d987
|
||||
patch_filename = sqlite3_3.41.2-2_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.41.2-2/get_patch
|
||||
patch_hash = 246681dfb731a14bfa61bcde651d5581a7e1c7d14851bfb57a941fac540a6810
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.41.2-2/sqlite-amalgamation-3410200.zip
|
||||
wrapdb_version = 3.41.2-2
|
||||
|
||||
[provide]
|
||||
sqlite3 = sqlite3_dep
|
||||
|
||||
|
@ -3,13 +3,12 @@ directory = libvorbis-1.3.7
|
||||
source_url = https://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz
|
||||
source_filename = libvorbis-1.3.7.tar.xz
|
||||
source_hash = b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b
|
||||
patch_filename = vorbis_1.3.7-3_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/vorbis_1.3.7-3/get_patch
|
||||
patch_hash = 6cb90a61ede8c64d3e8e379b96dcc800c9dd69e925122b3d73d8f59a563c3afa
|
||||
wrapdb_version = 1.3.7-3
|
||||
patch_filename = vorbis_1.3.7-4_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/vorbis_1.3.7-4/get_patch
|
||||
patch_hash = 979e22b24b16c927040700dfd8319cd6ba29bf52a14dbc66b1cb4ea60504f14a
|
||||
wrapdb_version = 1.3.7-4
|
||||
|
||||
[provide]
|
||||
vorbis = vorbis_dep
|
||||
vorbisfile = vorbisfile_dep
|
||||
vorbisenc = vorbisenc_dep
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user