Similarly to Should I use buildPythonApplication or mkDerivation here?, I’m looking to compile a program, and the cmake
instructions end up attempting to install a python shared object into the system python’s site-packages
directory:
CMake Error at core/src/plugins/filed/python/cmake_install.cmake:92 (file):
file INSTALL cannot copy file
"/build/source/build/core/src/plugins/filed/python/python3modules/bareosfd.cpython-312-x86_64-linux-gnu.so"
to
"/nix/store/99hl269v1igvjbp1znfk5jcarhzgy822-python3-3.12.8/lib/python3.12/site-packages/bareosfd.cpython-312-x86_64-linux-gnu.so":
Permission denied.
Call Stack (most recent call first):
core/src/plugins/filed/cmake_install.cmake:47 (include)
core/src/plugins/cmake_install.cmake:47 (include)
core/src/cmake_install.cmake:67 (include)
core/cmake_install.cmake:76 (include)
cmake_install.cmake:47 (include)
How we got here
Long story short, I’m looking to have a local “filedaemon
” on my nixos server, which will allow Bareos (a backup program) to take snapshots of the disk incrementally.
I’m compiling it from here: GitHub - bareos/bareos at bareos-24
It has CMAKE
files, and I’ve been able to work around most of the issues, however, when it gets to the installation of the shared object (shown above), it seems to be unresponsive to any compile flags (I’ve tried plugindir
, Python3_SITEARCH
) and while I’ve had success minimizing the build with several (client-only
, ENABLE_WEBUI=OFF
, etc.) I’ve only been ever able to build it fully with ENABLE_PYTHON=OFF
.
So I’m trying to figure out if there’s a best-practices pattern for NixOS for when I need to build a package that needs to build python modules and have it overlaid into the execution runtime and/or path of the application. But especially, I’d be very much appreciative of any nudge in the right direction so that I can get this building on my systems. Thanks!
You could try building the python program under /bareos/python-bareos
in a separate derivation via buildPythonPackage
, and then include one into the other via the install phase, or such.
I’m not sure if the python project depends on the C project or vice versa, or if they are separate packages anyways, in which case it wouldn’t matter.
I took a chance that they were in fact separate, and especially leaned heavily on the fact that I only needed the mariabackup.py
file (and imports) present on that particular FD, so here’s what I ended up doing - which was to download the prebuilt python package from PYPI, and use that in the build:
{ config, pkgs, lib, ... }:
let
# Define the tl-expected library
tlExpected = pkgs.stdenv.mkDerivation {
pname = "tl-expected";
version = "1.1.0";
src = pkgs.fetchFromGitHub {
owner = "TartanLlama";
repo = "expected";
rev = "v1.1.0";
hash = "sha256-AuRU8VI5l7Th9fJ5jIc/6mPm0Vqbbt6rY8QCCNDOU50=";
};
dontConfigure = true;
dontBuild = true;
installPhase = ''
mkdir -p $out/include
cp -r include/tl $out/include/
mkdir -p $out/lib/cmake/tl-expected
cat > $out/lib/cmake/tl-expected/tl-expected-config.cmake <<'EOF'
add_library(tl::expected INTERFACE IMPORTED)
get_filename_component(_self_dir "''${CMAKE_CURRENT_LIST_FILE}" PATH)
target_include_directories(
tl::expected INTERFACE "''${_self_dir}/../../include"
)
EOF
cat > "$out/lib/cmake/tl-expected/tl-expected-config-version.cmake" <<'VER'
set(PACKAGE_VERSION "1.1.0")
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if(PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
VER
'';
};
# Create a Python environment with the python-bareos package from PyPI
pythonWithBareos = pkgs.python3.withPackages (ps: [
(ps.buildPythonPackage rec {
pname = "python_bareos";
version = "24.0.2";
src = ps.fetchPypi {
inherit pname version;
# You'll need to fill in the correct SHA256 hash
# To get this, you can temporarily set this to lib.fakeSha256,
# run the build once, and copy the correct hash from the error message
sha256 = "sha256-Ru9hEdr69IhGV09bL3AtfnqNvmY01fMi53h1Mh4qZ8g=";
};
doCheck = false;
})
]);
# Build bareos-fd without Python support in the build
bareosFd = pkgs.stdenv.mkDerivation rec {
pname = "bareos-fd";
version = "24.0.2";
src = pkgs.fetchFromGitHub {
owner = "bareos";
repo = "bareos";
rev = "Release/${version}";
hash = "sha256-QVcUwZHguQUmtfZUpvx/1fJVRSErrawJKsCGlJXDnug=";
};
nativeBuildInputs = with pkgs; [
cmake
gitMinimal
pkg-config
makeWrapper
];
buildInputs = with pkgs; [
boost openssl readline zlib lzo acl libcap cli11
fmt microsoft-gsl utf8cpp xxHash tlExpected mariadb systemd
python3
];
cmakeFlags = [
"-DENABLE_FD_ONLY=ON"
"-DENABLE_PYTHON=OFF"
"-DCMAKE_INSTALL_PREFIX=${placeholder "out"}"
"-DCPM_USE_LOCAL_PACKAGES=ON"
"-DCPM_LOCAL_PACKAGES_ONLY=ON"
#"-DUSE_SYSTEM_TL_EXPECTED=ON"
#"-DCPM_SOURCE_CACHE=${tlExpected}"
"-Dclient-only=ON"
"-DENABLE_WEBUI=OFF"
"-DENABLE_GFAPI_FD=OFF"
"-DENABLE_JANSSON=OFF"
"-DENABLE_BCONSOLE=OFF"
"-DSYSTEMD_UNITDIR=$out/lib/systemd/system"
"-Dsystemd=ON"
];
# Create plugins directory & install the mariabackup.py plugin in there
postInstall = ''
mkdir -p $out/lib/bareos/plugins
# copy the python plugin that CMake left in the build tree
cp ../core/src/plugins/filed/python/mariabackup/bareos-fd-mariabackup.py \
../core/src/plugins/filed/python/pyfiles/BareosFdPluginLocalFilesBaseclass.py \
../core/src/plugins/filed/python/pyfiles/BareosFdWrapper.py \
../core/src/plugins/filed/python/pyfiles/BareosFdPluginBaseclass.py \
$out/lib/bareos/plugins/
'';
# Wrap executables
postFixup = ''
for prog in $out/bin/* $out/sbin/*; do
if [ -f "$prog" ] && [ -x "$prog" ]; then
wrapProgram "$prog" \
--prefix PYTHONPATH : "${pythonWithBareos}/${pkgs.python3.sitePackages}" \
--prefix PATH : "${pythonWithBareos}/bin:$PATH"
fi
done
'';
};
in
That has at least allowed the daemon to get up and running, and have connectivity to from the director to the remote fd daemon. Hopefully that helps someone (or even myself!) later.
Here’s the other half of the config under in
, since the post was too long:
{
systemd.tmpfiles.rules = [
# Ensure bareos dirs exist
"d /var/log/bareos 0750 bareos bareos -"
"d /var/lib/bareos 0750 bareos bareos -"
];
# Create a non-root user for Bareos
users.users.bareos = {
uid = 9016;
isSystemUser = true;
home = "/var/lib/bareos";
group = "bareos";
shell = pkgs.bashInteractive;
extraGroups = [ "mysql" ];
};
users.groups.bareos = {
gid = 9016;
};
# Bareos File-Daemon as a systemd service
systemd.services.bareos-fd = {
description = "Bareos File Daemon (local build)";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
#path = [ pkgs.bareosFd python-bareos ];
serviceConfig = {
Type = "simple";
ExecStart = "${bareosFd}/sbin/bareos-fd -f -c /etc/bareos/bareos-fd.conf";
Restart = "on-failure";
User = "bareos"; # or create a confined 'bareos' user
Group = "bareos";
# Security Settings
CapabilityBoundingSet = "CAP_DAC_READ_SEARCH";
NoNewPrivileges = true;
PrivateTmp = true;
ProtectHome = false;
ProtectSystem = "full";
ReadWritePaths = "/var/lib/bareos /var/log/bareos";
};
};
# Minimal matching fd-config (keep the password synced with the Director)
environment.etc."bareos/bareos-fd.conf".text = ''
Director {
Name = "bareos-dir"
Password = "*************"
}
FileDaemon {
Name = "hub-andrewcz-org-firefly-mariadb-ct0-fd"
FDPort = 9102
Plugin Directory = ${bareosFd}/lib/bareos/plugins
}
Messages {
Name = Standard
director = bareos-dir = all, !skipped, !restored
}
'';
}
I really do think there’s probably a nicer, or cleaner, or better way of doing this, but this is what works thus far.