1
0
Fork 0
mirror of https://github.com/lopsided98/nix-ros-overlay.git synced 2025-07-12 05:16:30 +03:00
nix-ros-overlay/lib/default.nix
Michal Sojka d33788f6fa Allow generating vendored-source.json for packages with multiple ament_vendor calls
This is needed at least for zenoh-cpp-vendor, which is being prepared
in #558, but it also improves the robustness of the whole patching
machanism.

The format of vendored-source.json is changed from a single object to
object of objects, one for each call to ament_vendor. The key in the
top-level object is the target name specified in the ament_vendor call
in CMakeLists.txt.

The principle of operation is slightly different from the previous
version. Instead of introducing "fake" ament_cmake_vendor_package just
for creation of vendored-source.json, we introduce a "wrapped" version
of ament_cmake_vendor_package, which is used both for creation of
vendored-source.json as well as for compiling the package. When
compiling the package, the wrapped version captures ament_vendor calls
and modifies the arguments to use the prefetched data from the Nix
store instead of downloading them directly.

patchAmentVendorGit was updated to understand the new format and to
invoke CMake with the wrapped package and information from
vendored-source.json.

The vendored-source.json files will be changed to the new format in
the next commit.
2025-06-05 13:18:22 +02:00

193 lines
7 KiB
Nix

{ lib ? rosSelf.lib or self.lib
, self ? null
, rosSelf ? null }:
{
mkOverlay = overlays: let
s = lib.composeManyExtensions overlays s {};
in s;
# Create a tarball of a package source. If the source is already an archive,
# it will be unpacked and repacked as a tarball.
tarSource = {
compress ? false,
hook ? "",
hash ? null
}: src: self.runCommand ("${src.name}.tar" + lib.optionalString compress ".gz") ({
inherit src;
} // lib.optionalAttrs (hash != null) {
outputHashMode = "flat";
outputHash = hash;
}) ''
unpackPhase
pushd "$sourceRoot"
${hook}
popd
tar --sort=name \
--format=gnu \
--owner=0 --group=0 --numeric-owner \
--mtime="@$SOURCE_DATE_EPOCH" \
${lib.optionalString compress "-z"} \
-cf "$out" "$sourceRoot"
'';
patchVendorUrl = pkg: {
url, hash ? "", sha256 ? "",
originalUrl ? url,
file ? "CMakeLists.txt"
}: pkg.overrideAttrs ({
postPatch ? "", ...
}: {
postPatch = ''
substituteInPlace ${lib.escapeShellArg file} \
--replace-fail ${lib.escapeShellArg originalUrl} ${lib.escapeShellArg (self.fetchurl { inherit url hash sha256; })}
'' + postPatch;
});
patchExternalProjectGit = pkg: {
url,
rev,
originalRev ? rev,
originalUrl ? url,
revVariable ? "",
file ? "CMakeLists.txt",
fetchgitArgs ? {}
}: pkg.overrideAttrs ({
postPatch ? "", ...
}: {
postPatch = let
script = ''
$0 ~ "GIT_REPOSITORY[[:blank:]]+" originalUrl \
{ print "URL \"" path "\""; foundUrl=1; next } \
{ print }
$0 ~ "GIT_TAG[[:blank:]]+" originalRev { print; foundRev=1 }
$0 ~ "set\\(" revVariable "[[:blank:]]+\"?" originalRev "\"?\\)" { print; foundRev=1 }
END {
if (!foundUrl) print "patchExternalProjectGit: did not find URL: " originalUrl > "/dev/stderr"
if (!foundRev) print "patchExternalProjectGit: did not find revision: " originalRev > "/dev/stderr"
exit !(foundUrl && foundRev)
}
'';
in ''
awk -i inplace \
-v originalUrl=${lib.escapeShellArg originalUrl} \
-v originalRev=${lib.escapeShellArg originalRev} \
-v revVariable=${lib.escapeShellArg revVariable} \
-v path=${lib.escapeShellArg (self.fetchgit ({ inherit url rev; } // fetchgitArgs))} \
${lib.escapeShellArg script} \
${lib.escapeShellArg file}
'' + postPatch;
});
# Patch a vendored download that uses ament_vendor() with a Git repo as the
# source.
patchAmentVendorGit = pkg: {
file ? "CMakeLists.txt",
fetchgitArgs ? {},
tarSourceArgs ? {}
}: pkg.overrideAttrs ({
cmakeFlags ? [],
nativeBuildInputs ? [],
passthru ? {},
postPatch ? "", ...
}: let
# Make sure that non-existence of vendored-source.json file
# doesn't cause eval errors. This would break automatic updates.
vendoredSourceJson = "${dirOf pkg.meta.position}/vendored-source.json";
inherit (builtins) stringLength substring pathExists mapAttrs attrValues;
nameStart = 5 + stringLength pkg.rosDistro; # e.g. ros-jazzy- => 10
attr = substring nameStart (-1) pkg.pname;
errMsg = ''
error: File ${vendoredSourceJson} missing.
Run "$(nix-build -A rosPackages.${pkg.rosDistro}.${attr}.updateAmentVendor)" to create it.
'';
sourceInfos = builtins.fromJSON (builtins.readFile vendoredSourceJson);
# ament_vendor doesn't allow patches for path inputs, so we have to pack it
# into a tar first. Additionally, vcstool only accepts tarballs with the
# version number as the root directory name.
vendor = sourceInfo: lib.tarSource tarSourceArgs (
self.fetchgit (sourceInfo // fetchgitArgs // {
name = sourceInfo.rev;
}));
in {
nativeBuildInputs = [
# Prepend wrapped ament_vendor to be found by CMake before the
# unwrapped one
rosSelf.ament-cmake-vendor-package-wrapped
] ++ nativeBuildInputs ++ [
# CMake ExternalProject patches are applied with git apply
self.git
];
cmakeFlags = cmakeFlags ++ lib.optionals (pathExists vendoredSourceJson)
(
# Tell ament_vendor_wrapper.cmake where to find tarballs with vendored sources
attrValues (mapAttrs (n: v: "-DAMENT_VENDOR_NIX_TAR_${n}=${vendor v}") sourceInfos)
);
postPatch =
if pathExists vendoredSourceJson then
postPatch
else ''
echo >&2 ${lib.escapeShellArg errMsg}
exit 1
'';
passthru = passthru // {
# Script to automatically update vendored-source.json by running
# CMake with injected modified version of ament_cmake macro.
updateAmentVendor = let
source = self.srcOnly pkg;
sourceDrvPath = builtins.unsafeDiscardOutputDependency source.drvPath;
updateScript = self.writeShellScript "ament-vendor-update.sh" ''
set -eo pipefail
cd "$(${self.coreutils}/bin/mktemp -d)"
trap "${self.coreutils}/bin/rm -rf '$PWD'" SIGINT SIGTERM ERR EXIT
source "$stdenv/setup"
export NIX_SSL_CERT_FILE="${self.cacert}/etc/ssl/certs/ca-bundle.crt"
export PATH="${lib.makeBinPath (with self; [ nix-prefetch-git jq nix ])}:$PATH"
# Ask CMake to generate vendored-source.json
export CMAKE_PREFIX_PATH=${rosSelf.ament-cmake-vendor-package-wrapped}
cmakeFlags+='-DAMENT_VENDOR_NIX_PREFETCH=ON'
phases="''${prePhases[*]:-} unpackPhase patchPhase ''${preConfigurePhases[*]:-} configurePhase ''${preBuildPhases[*]:-}" \
genericBuild
# Copy the resulting data to package source directory
cp -v vendored-source.json ${dirOf pkg.meta.position}
'';
in self.writeShellScript "update-${pkg.pname}" ''
set -eo pipefail
echo ============== Updating ${pkg.pname} ==============
NIX_BUILD_SHELL=${self.runtimeShell} nix-shell --pure ${sourceDrvPath} --run ${updateScript}
'';
};
});
# patchAmentVendorGit specialized for gz-*-vendor packages. In
# addition to patching ament_vendor() calls, it patches other things
# in CMakeLists.txt.
patchGzAmentVendorGit = pkg: {
tarSourceArgs ? {}
}: let
patchedPkg = lib.patchAmentVendorGit pkg {
inherit tarSourceArgs;
};
in patchedPkg.overrideAttrs ({
pname, postPatch ? "", ...
}: {
postPatch = postPatch + ''
# Use standard installation paths rather than /opt
substituteInPlace CMakeLists.txt \
--replace-fail 'opt/''${PROJECT_NAME}/extra_cmake' 'share/extra_cmake'
substituteInPlace *-extras.cmake.in \
--replace-fail 'opt/@PROJECT_NAME@/extra_cmake' 'share/extra_cmake'
'';
});
# Many ROS packages claim to have a dependency on Boost signals when they
# really don't or they actually depend on signals2. Boost 1.69 removed
# signals causing these packages to fail to build.
patchBoostSignals = pkg: pkg.overrideAttrs ({
postPatch ? "", ...
}: {
postPatch = ''
sed -i '/find_package(Boost [^)]*/s/signals//g' CMakeLists.txt
'' + postPatch;
});
}