Compare commits

...

76 Commits

Author SHA1 Message Date
Max Kellermann
8842650c33 release v0.23.13 2023-05-22 19:46:38 +02:00
Max Kellermann
d5bf128cee storage/curl: throw HttpStatusError 2023-05-22 19:29:46 +02:00
Max Kellermann
5cd86e272f input/curl: disable CURLOPT_FAILONERROR
Let OnHeaders() check the status.

The status checking code was added by commit 4f021cbced in 2011,
but in 2008, commit a8e81326d0 enabled `CURLOPT_FAILONERROR`, which
means the status checking code never had any effect.

This allows `LoadExcludeListOrLog()` to hide boring "404 Not Found"
log messages via `IsFileNotFound()`.
2023-05-22 19:03:12 +02:00
Max Kellermann
740cbe9e02 event/Loop: remove failing assert()
The `assert(!quit)` can fail if the `EventThread` gets stopped before
it enters `EventLoop::Run()`. There is a similar problem with `alive`,
which gets reset by `EventThread::Stop()`.

If that happens, then `EventLoop::Run()` should return immediately
without handling any events.
2023-05-22 18:14:25 +02:00
Max Kellermann
ed890a273a doc/user.rst: document the replaygain_missing_preamp setting
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1785
2023-05-22 16:17:05 +02:00
Max Kellermann
068cd559e1 db/update/Walk: clear Song::in_playlist
Without clearing all `in_playlist` flags, the songs will never be
revealed again if they were hidden once by a CUE sheet, not even after
the CUE sheet gets deleted or modified.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1784
2023-05-22 15:41:58 +02:00
Max Kellermann
dc127f39a7 util/ScopeExit: use std::declval()
Fixes GCC 10 error:

 error: cannot call member function `Foo` without object
2023-05-22 15:01:25 +02:00
Max Kellermann
7a99a7008c util/ScopeExit: use std::exchange() 2023-05-22 14:44:45 +02:00
Max Kellermann
70b451db7b util/ScopeExit: add noexcept 2023-05-22 14:44:17 +02:00
Max Kellermann
2ab03a0914 util/ScopeExit: allow the function to throw
Fixes crash inside AtScopeExit() in the WASAPI output plugin.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1759
2023-05-22 14:43:23 +02:00
Max Kellermann
2fa8c7d2db lib/crypto/meson.build: link with ffmpeg_util_dep
This adds `MakeFfmpegError()` to the executable and fixes a linker
failure when `libavutil` is available, but `libavformat` and
`libavcodec` are not.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1786
2023-05-22 14:05:13 +02:00
Max Kellermann
7c759ba8b0 lib/ffmpeg/meson.build: move libavutil helpers into separate library 2023-05-22 14:03:47 +02:00
Max Kellermann
6d9b452fde lib/ffmpeg/LogError: remove unused library 2023-05-22 13:48:33 +02:00
Michiel Beijen
f7eb1c9a83 Fix meson build warning for get_pkgconfig_variable
Otherwise, building will generate these warnings:

systemd/system/meson.build:5: WARNING: Project targeting '>= 0.56.0' but tried to use feature deprecated since '0.56.0': Dependency.get_pkgconfig_variable. use Dependency.get_variable(pkgconfig : ...) instead
systemd/user/meson.build:5: WARNING: Project targeting '>= 0.56.0' but tried to use feature deprecated since '0.56.0': Dependency.get_pkgconfig_variable. use Dependency.get_variable(pkgconfig : ...) instead
2023-05-21 21:07:14 +02:00
Max Kellermann
2d22e6dee4 subprojects: update sqlite to 3.41.2-2 2023-05-21 21:05:17 +02:00
Max Kellermann
4587bf759d subprojects: update expat to 2.5.0-2 2023-05-21 21:04:56 +02:00
Max Kellermann
e1e37cfe3c TagPrint, command/File: two more libfmt 10 workarounds
libfmt 10 doesn't know how to format a StringView, and doesn't cast to
std::string_view anymore.  The StringView class has been removed from
MPD 0.24 completely, and this is a stable-branch-only workaround.

Closes https://github.com/MusicPlayerDaemon/MPD/pull/1814
2023-05-21 21:03:20 +02:00
latex
381934985a reorder ffmpeg to be lower priority than gme
This should prevent ffmpeg from taking priority over the gme plugin.
The ffmpeg plugin is more buggy than gme.
One of the prominent bugs of preferring ffmpeg over gme is that ffmpeg
cannot seek SAP files while gme can. This should prevent that from
happening.
2023-05-21 20:58:53 +02:00
Max Kellermann
a8042885ac TimePrint: minor fixup for libfmt 10
libfmt version 10 has difficulties formatting a `StringBuffer`, and we
need to help it by explicitly invoking the `c_str()` method.
2023-05-21 20:58:19 +02:00
Max Kellermann
a71e68db50 command/player, SongPrint: use AudioFormatFormatter()
libfmt version 10 apparently doesn't know how to format a
`StringBuffer`, failing the MPD build.  Since we have a formatter
specialization for `AudioFormat`, let's use that - it's better and
easier to use.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1807
2023-05-21 20:57:59 +02:00
Max Kellermann
1417578b3d db/update/Archive: validate directory names
Fixes assertion failure if the ZIP file contains a path that begins
with a slash.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1793
2023-05-21 20:57:38 +02:00
Max Kellermann
96befa138c db/update/Archive: ignore filenames with newline character 2023-05-21 20:56:38 +02:00
Max Kellermann
16a99804de db/update/Archive: move check to IsAcceptableFilename() 2023-05-21 20:56:32 +02:00
Max Kellermann
75a39ed279 db/update/Archive: remove useless log message 2023-05-21 20:55:59 +02:00
Benjamin Cremer
4d357ab77c Fix syntax error in mpdconf.example 2023-05-21 20:51:57 +02:00
datasone
d4f3dd49b4 db/SimpleDatabasePlugin: store in_playlist value of songs into database
Fixes hide_playlist_targets not working after server restart

Currently, `hide_playlists_targets` works by skipping songs with
`in_playlist` value set to true in
[`Directory::Walk`](a57bcd0238/src/db/plugins/simple/Directory.cxx (L237)). But
`in_playlist` is not stored and only updated in
[`UpdateWalk::PurgeDanglingFromPlaylists`](a57bcd0238/src/db/update/Playlist.cxx (L139)),
which will only be executed while updating DB.

This causes the problem that playlist target songs are correctly
hidden after database update, but will remain visible after mpd server
restarted. This pr solves the problem by storing `in_playlist` value
of songs into the `SimpleDatabase` file.
2023-05-21 20:51:47 +02:00
kaliko
4ec6d0555a check systemd unit dir from systemd.pc in meson 2023-05-21 20:51:33 +02:00
Max Kellermann
a6a1182c4c python/build/libs.py: update OpenSSL to 3.1.0 2023-05-21 20:50:03 +02:00
Max Kellermann
a59c9c602b python/build/libs.py: update CURL to 8.0.1 2023-05-21 20:50:03 +02:00
Max Kellermann
0c4d824d64 subprojects/sqlite3.wrap: update to 3.41.0-1 2023-05-21 20:50:03 +02:00
Max Kellermann
a5281856c9 python/build/libs.py: update WildMidi to 0.4.5 2023-05-21 20:50:03 +02:00
Max Kellermann
0206a46d39 decoder/gme: require GME 0.6 or later
This allows dropping a few compile-time version checks and we can use
pkg-config to detect the library.
2023-05-21 20:50:03 +02:00
Max Kellermann
9475ef2202 fs/Charset: assign fs_charset
This got lost 8 years ago in commit 87c88fcb27

D'oh!
2023-05-21 20:50:03 +02:00
Max Kellermann
edae00e719 fs/Charset: remove useless log message 2023-05-21 20:50:03 +02:00
Max Kellermann
fb695bc55f command/{file,storage}: remove stray "#pragma GCC diagnostic pop" 2023-05-21 20:50:03 +02:00
Max Kellermann
23a5b8fd3c python/build/meson.py: remove unused import 2023-05-21 20:43:09 +02:00
Max Kellermann
273a93cfcf build/python/cmake: set CMAKE_C_FLAGS_INIT, not CMAKE_C_FLAGS
According to
https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html
the _INIT variables should be set in the toolchain file.
2023-05-21 20:43:05 +02:00
Max Kellermann
d105985d78 build/python/cmake: set CMAKE_OSX_SYSROOT on macOS 2023-05-21 20:43:00 +02:00
Max Kellermann
f8cfeb39e9 build/python/cmake: add "env" parameter 2023-05-21 20:42:51 +02:00
Max Kellermann
d5d3982d3c build/python/build/project.py: add "lazy" parameter to make_build_path() 2023-05-21 20:42:27 +02:00
Max Kellermann
47341107ea build/python/build/project.py: raise exception on regex mismatch 2023-05-21 20:42:18 +02:00
Max Kellermann
90eaa87a4d python/build/zlib.py: use autotools to be more portable
Right now, zlib is only built for Windows, but we may eventually
changed that, so don't hard-code `win32/Makefile.gcc`.
2023-05-21 20:42:12 +02:00
Max Kellermann
b09a54b2c2 python/build/autotools.py: use toolchain.arflags 2023-05-21 20:42:04 +02:00
Max Kellermann
10aec174d5 python/{cmake,autotools}: build in verbose mode
Make sure all the gory details are visible in CI logs.
2023-05-21 20:41:49 +02:00
Max Kellermann
d32ed194e8 python/build/autotools.py: dump config.log on configure error
For better error logs on CI.
2023-05-21 20:41:44 +02:00
Felix Hädicke
70d0fbd715 python/makeproject: do not use hard-code absolute path to make 2023-05-21 20:41:38 +02:00
Felix Hädicke
302432e157 python/makeproject: set appropriate build jobs count depending on the number of CPUs 2023-05-21 20:41:34 +02:00
Felix Hädicke
4ab8a677dc build/python: do not use absolute path for tar 2023-05-21 20:41:29 +02:00
Max Kellermann
52e4a4c904 build/python/build/project.py: lazy tarball extraction 2023-05-21 20:41:25 +02:00
Max Kellermann
a0f6932ebe unix/SignalHandlers: shut down if parent process dies in --no-daemon mode
By default, if the parent of a process dies, the process gets SIGHUP
and is supposed to shut down.  This however doesn't work for MPD,
because MPD redefines SIGHUP with a different meaning (like most
daemons do).

To work around this, we configure the kernel to send SIGTERM instead
of SIGHUP.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1706
2023-05-21 20:40:47 +02:00
Max Kellermann
6e700dab69 CommandLine: hard-code daemon=false if ENABLE_DAEMON is not set 2023-05-21 20:40:39 +02:00
Max Kellermann
35eaed7206 python/build/libs.py: update FFmpeg to 6.0 2023-05-21 20:40:39 +02:00
Max Kellermann
e7c963f2ce python/build/libs.py: disable more unused FFmpeg features 2023-05-21 20:40:39 +02:00
Naïm Favier
949d72e368 output/PipeWire: lock thread loop in SendTag 2023-05-21 20:40:29 +02:00
Max Kellermann
8d2a184658 python/build/libs.py: update CURL to 7.88.1 2023-05-21 20:36:28 +02:00
Max Kellermann
c877a32d97 python/build/libs.py: update OpenSSL to 3.0.8 2023-05-21 20:36:23 +02:00
Max Kellermann
541468f0ca input/async: check for errors in Seek()
Fixes a busy loop in BufferingInputStream::RunThreadLocked() because
the method never learns that seeking is ignored, even though the HTTP
stream is already broken and can never be read; nobody cared to check
for errors.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1727
2023-05-21 20:34:19 +02:00
Max Kellermann
d2797effa3 command/database: add missing include for UINT_MAX 2023-05-21 20:33:34 +02:00
Max Kellermann
1170fb1e1e output/osx: change type to std::size_t to fix -Wc++11-narrowing 2023-05-21 20:33:03 +02:00
Max Kellermann
65b9b3195c lib/dbus/AppendIter: add missing include 2023-05-21 20:33:03 +02:00
Max Kellermann
258830e913 increment version number to 0.23.13 2023-05-21 20:29:23 +02:00
Max Kellermann
d91da96798 release v0.23.12 2023-01-17 18:54:47 +01:00
Max Kellermann
b3897df682 decoder/mad: add assert() 2023-01-17 18:53:18 +01:00
Max Kellermann
3cacb56bb7 fs/StandardDirectory: don't fall back to getpwuid() without $HOME
If the environment variable $HOME does not exist, don't attempt to
obtain it from /etc/passwd; without $HOME, the calling process
indicates that it does not wish MPD to access the home directory.

This also prevents MPD from attempting to load
`/root/.config/mpd/mpd.conf` if MPD got started as global systemd
service.  Reading from there makes no sense, only /etc/mpd.conf shall
be used then.

This piece of code was initially added by commit 5d85792178.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1687
2023-01-17 18:51:49 +01:00
Max Kellermann
15a1973e28 decoder/mad: fix integer underflow with very small files
When drop_start_samples and drop_end_samples overlap and are greater
than the actual number of samples, the `num_samples` calculation in
SubmitPCM() could underflow.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1712
2023-01-17 17:41:37 +01:00
Max Kellermann
ad7d47a8ba output/PipeWire: use PW_KEY_TARGET_OBJECT with PipeWire 0.3.64
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1721
2023-01-17 11:50:09 +01:00
Max Kellermann
0948c607b6 lib/curl/meson.build: require CURL 7.55.0 or later
For CURLINFO_CONTENT_LENGTH_DOWNLOAD_T (commit 4efd0a9f77).
2023-01-16 19:42:48 +01:00
Max Kellermann
60d04052c5 NEWS: mention the GCC13 fixes 2023-01-16 19:41:37 +01:00
Max Kellermann
c1780ac657 python/build/libs.py: update CURL to 7.87.0 2023-01-16 19:06:08 +01:00
Max Kellermann
e49cf0ec38 python/build/libs.py: update Boost to 1.81.0 2023-01-16 19:03:50 +01:00
Max Kellermann
e1d641f684 lib/curl/Easy: drop deprecated CURLOPT_HTTPPOST wrapper 2023-01-02 14:29:17 +01:00
Max Kellermann
4efd0a9f77 lib/curl/Easy: use CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
CURLINFO_CONTENT_LENGTH_DOWNLOAD is deprecated and is ugly because it
uses floating point.
2023-01-02 14:28:42 +01:00
Max Kellermann
f6f8751332 io/FileReader: add missing include for uint64_t 2023-01-02 14:27:47 +01:00
gd
abb28593ce TagBuilder::RemoveType: added missing tag pool lock before call to tag_pool_put_item 2022-12-29 08:43:10 +01:00
Max Kellermann
115693b046 increment version number to 0.23.12 2022-12-29 08:42:02 +01:00
Đoàn Trần Công Danh
e4b055eb6d v0.23.x: RemoteTagCache: add missing include
Fix build with Boost 1.81.0. `<array>` was included by one of those boost headers,
however, it's no longer included as of Boost 1.81.0.

`master` doesn't use `std::array` in this file.

While we're at it, add all necessary inclusion files.
2022-12-01 08:29:23 +07:00
61 changed files with 567 additions and 221 deletions

29
NEWS

@@ -1,3 +1,32 @@
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

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="70"
android:versionName="0.23.11">
android:versionCode="71"
android:versionName="0.23.12">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>

@@ -100,6 +100,7 @@ class AndroidNdkToolchain:
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')

@@ -181,7 +181,7 @@
#
#database {
# plugin "simple"
# path "~/.local/share/mpd/db
# path "~/.local/share/mpd/db"
# cache_directory "~/.local/share/mpd/cache"
#}
#

@@ -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

@@ -1,7 +1,7 @@
project(
'mpd',
['c', 'cpp'],
version: '0.23.11',
version: '0.23.13',
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'))

@@ -45,14 +45,27 @@ class AutotoolsProject(MakeProject):
'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',
'--disable-silent-rules',
] + self.configure_args
subprocess.check_call(configure, cwd=build, env=toolchain.env)
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
return build
def _build(self, toolchain):

@@ -1,4 +1,5 @@
import os
import re
import subprocess
from build.project import Project
@@ -25,13 +26,33 @@ set(CMAKE_SYSTEM_PROCESSOR {toolchain.actual_arch.split('-', 1)[0]})
set(CMAKE_C_COMPILER_TARGET {toolchain.actual_arch})
set(CMAKE_CXX_COMPILER_TARGET {toolchain.actual_arch})
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=()):
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, src, build, args=(), env=None):
cross_args = []
if toolchain.is_windows:
@@ -61,15 +82,23 @@ def configure(toolchain, src, build, args=()):
'-GNinja',
] + cross_args + args
subprocess.check_call(configure, env=toolchain.env, cwd=build)
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=[],
env=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):
src = self.unpack(toolchain)
@@ -77,10 +106,10 @@ class CmakeProject(Project):
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):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
subprocess.check_call(['ninja', '-v', 'install'],
cwd=build, env=toolchain.env)

@@ -131,17 +131,14 @@ libopenmpt = AutotoolsProject(
)
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(
@@ -157,8 +154,8 @@ gme = CmakeProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-5.1.2.tar.xz',
'619e706d662c8420859832ddc259cd4d4096a48a2ce1eefd052db9e440eef3dc',
'http://ffmpeg.org/releases/ffmpeg-6.0.tar.xz',
'57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -176,13 +173,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',
@@ -196,17 +196,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',
@@ -222,6 +227,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',
@@ -244,6 +285,7 @@ ffmpeg = FfmpegProject(
'--disable-demuxer=tedcaptions',
'--disable-demuxer=vobsub',
'--disable-demuxer=vplayer',
'--disable-demuxer=webm_dash_manifest',
'--disable-demuxer=webvtt',
'--disable-demuxer=yuv4mpegpipe',
@@ -273,78 +315,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',
@@ -353,48 +496,116 @@ 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.7.tar.gz',
'83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e',
'https://www.openssl.org/source/openssl-3.1.0.tar.gz',
'aaa925ad9828745c4cad9d9efeb273deca820f2cdcf2c3ac7d7c1212b7c497b4',
'include/openssl/ossl_typ.h',
)
curl = CmakeProject(
'https://curl.se/download/curl-7.86.0.tar.xz',
'2d61116e5f485581f6d59865377df4463f2e788677ac43222b496d4e49fb627b',
'https://curl.se/download/curl-8.0.1.tar.xz',
'0a381cd82f4d00a9a334438b8ca239afea5bfefcfa9a1025f2bf118e79e0b5f0',
'lib/libcurl.a',
[
'-DBUILD_CURL_EXE=OFF',
@@ -450,7 +661,7 @@ jack = JackProject(
)
boost = BoostProject(
'https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.bz2',
'1e19565d82e43bc59209a168f5ac899d3ba471d55c7610c677d4ccf2c9c500c0',
'https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.bz2',
'71feeed900fbccca04a3b4f2f84a7c217186f28a940ed8b7ed4725986baf99fa',
'include/boost/version.hpp',
)

@@ -1,4 +1,4 @@
import subprocess
import subprocess, multiprocessing
from build.project import Project
@@ -10,7 +10,12 @@ class MakeProject(Project):
self.install_target = install_target
def get_simultaneous_jobs(self):
return 12
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):
return ['--quiet', '-j' + str(self.get_simultaneous_jobs())]
@@ -19,7 +24,7 @@ class MakeProject(Project):
return ['--quiet', self.install_target]
def make(self, toolchain, wd, args):
subprocess.check_call(['/usr/bin/make'] + args,
subprocess.check_call(['make'] + args,
cwd=wd, env=toolchain.env)
def build_make(self, toolchain, wd, install=True):

@@ -1,4 +1,5 @@
import os.path, subprocess, sys
import os
import subprocess
import platform
from build.project import Project
@@ -115,5 +116,5 @@ class MesonProject(Project):
def _build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
subprocess.check_call(['ninja', '-v', 'install'],
cwd=build, env=toolchain.env)

@@ -14,13 +14,14 @@ class Project:
if base is None:
basename = os.path.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)
@@ -55,8 +56,8 @@ class Project:
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 +72,10 @@ class Project:
return path
def make_build_path(self, toolchain):
def make_build_path(self, toolchain, lazy=False):
path = os.path.join(toolchain.build_path, self.base)
if lazy and os.path.isdir(path):
return path
try:
shutil.rmtree(path)
except FileNotFoundError:

@@ -1,14 +1,16 @@
import os, shutil, subprocess
def untar(tarball_path, parent_path, base):
def untar(tarball_path, parent_path, base, lazy=False):
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)

@@ -1,22 +1,32 @@
import os.path, subprocess
import subprocess
from build.project import Project
from build.makeproject import MakeProject
class ZlibProject(Project):
class ZlibProject(MakeProject):
def __init__(self, url, md5, installed,
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
MakeProject.__init__(self, url, md5, installed, **kwargs)
def get_make_args(self, toolchain):
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):
return [
'RANLIB=' + toolchain.ranlib,
self.install_target
]
def _build(self, toolchain):
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;

@@ -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();

@@ -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

@@ -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());
}

@@ -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)
{

@@ -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

@@ -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);

@@ -798,6 +798,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 +845,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'

@@ -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

@@ -34,7 +34,6 @@
#include <shlobj.h>
#else
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#endif
@@ -80,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;
@@ -375,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;
}

@@ -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 */

@@ -42,6 +42,8 @@
#include "io/UniqueFileDescriptor.hxx"
#endif
#include <cstdint>
class Path;
class FileInfo;

@@ -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;
}

@@ -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,42 +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.
*/
#include "LogError.hxx"
#include "Domain.hxx"
#include "Log.hxx"
extern "C" {
#include <libavutil/error.h>
}
void
LogFfmpegError(int errnum)
{
char msg[256];
av_strerror(errnum, msg, sizeof(msg));
LogError(ffmpeg_domain, msg);
}
void
LogFfmpegError(int errnum, const char *prefix)
{
char msg[256];
av_strerror(errnum, msg, sizeof(msg));
FmtError(ffmpeg_domain, "{}: {}", prefix, msg);
}

@@ -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,
],
)

@@ -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");

@@ -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"
@@ -300,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");

@@ -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)

@@ -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));
}
};

@@ -3,10 +3,11 @@ 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-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.5.0-1/get_patch
patch_hash = 0d0d6e07ed21cf4892126a8270f5fd182012ab34b3ebe24932a2bef5ca608a61
wrapdb_version = 2.5.0-1
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,13 @@
[wrap-file]
directory = sqlite-amalgamation-3390300
source_url = https://sqlite.org/2022/sqlite-amalgamation-3390300.zip
source_filename = sqlite-amalgamation-3390300.zip
source_hash = a89db3030d229d860ae56a8bac50ac9761434047ae886e47e7c8f9f428fa98ad
patch_filename = sqlite3_3.39.3-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.39.3-1/get_patch
patch_hash = f5c41ff7b3da1108ed221b9a820b41188550cafb8a6c3d247bb40bd598775050
wrapdb_version = 3.39.3-1
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

@@ -1,4 +1,10 @@
systemd_system_unit_dir = get_option('systemd_system_unit_dir')
if systemd_system_unit_dir == ''
systemd = dependency('systemd', required: false)
if systemd.found()
systemd_system_unit_dir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir')
endif
endif
if systemd_system_unit_dir == ''
systemd_system_unit_dir = join_paths(get_option('prefix'), 'lib', 'systemd', 'system')
endif

@@ -1,4 +1,10 @@
systemd_user_unit_dir = get_option('systemd_user_unit_dir')
if systemd_user_unit_dir == ''
systemd = dependency('systemd', required: false)
if systemd.found()
systemd_user_unit_dir = systemd.get_variable(pkgconfig: 'systemduserunitdir')
endif
endif
if systemd_user_unit_dir == ''
systemd_user_unit_dir = join_paths(get_option('prefix'), 'lib', 'systemd', 'user')
endif

@@ -48,6 +48,7 @@ class CrossGccToolchain:
self.cc = os.path.join(toolchain_bin, arch + '-gcc')
self.cxx = os.path.join(toolchain_bin, arch + '-g++')
self.ar = os.path.join(toolchain_bin, arch + '-ar')
self.arflags = 'rcs'
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip')