From 7ef11ed3666a4a47895eca52e98df4055fec9f85 Mon Sep 17 00:00:00 2001 From: 0mp <0mp@FreeBSD.org> Date: Fri, 25 Sep 2020 12:54:38 +0000 Subject: [PATCH] Add Nix, a purely functional package manager Nix is a purely functional package manager. This means that it treats packages like values in purely functional programming languages such as Haskell -- they are built by functions that don't have side-effects, and they never change after they have been built. FreeBSD support in Nix is not fully complete yet. This commit only brings the Nix package manager to the ports. Hopefully, this port will streamline the work of bootstrapping of Nix packages on FreeBSD. Thanks to all the kind folks who contributed to the porting efforts. It was a fun journey. WWW: https://nixos.org/nix/ Differential Revision: https://reviews.freebsd.org/D17766 --- GIDs | 2 +- sysutils/Makefile | 1 + sysutils/nix/Makefile | 114 ++++++++++++++++ sysutils/nix/distinfo | 3 + sysutils/nix/files/add-nixbld-users | 23 ++++ .../patch-src_libexpr_primops_fetchGit.cc | 19 +++ .../nix/files/patch-src_libstore_build.cc | 29 ++++ ...atch-src_libstore_s3-binary-cache-store.cc | 104 ++++++++++++++ sysutils/nix/files/pkg-message.in | 29 ++++ sysutils/nix/pkg-descr | 14 ++ sysutils/nix/pkg-plist | 129 ++++++++++++++++++ 11 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 sysutils/nix/Makefile create mode 100644 sysutils/nix/distinfo create mode 100644 sysutils/nix/files/add-nixbld-users create mode 100644 sysutils/nix/files/patch-src_libexpr_primops_fetchGit.cc create mode 100644 sysutils/nix/files/patch-src_libstore_build.cc create mode 100644 sysutils/nix/files/patch-src_libstore_s3-binary-cache-store.cc create mode 100644 sysutils/nix/files/pkg-message.in create mode 100644 sysutils/nix/pkg-descr create mode 100644 sysutils/nix/pkg-plist diff --git a/GIDs b/GIDs index da575690205c..75a7de87a44f 100644 --- a/GIDs +++ b/GIDs @@ -92,7 +92,7 @@ kdm:*:147: ipv6mon:*:148: avenger:*:149: cbsd:*:150: -# free: 151 +nixbld:*:151: frrvty:*:152:frr rbldns:*:153: trircd:*:154: diff --git a/sysutils/Makefile b/sysutils/Makefile index 6545b56b23dd..331b1be76986 100644 --- a/sysutils/Makefile +++ b/sysutils/Makefile @@ -735,6 +735,7 @@ SUBDIR += netevent SUBDIR += nfcutils SUBDIR += nitrogen + SUBDIR += nix SUBDIR += no-login SUBDIR += node_exporter SUBDIR += nomad diff --git a/sysutils/nix/Makefile b/sysutils/nix/Makefile new file mode 100644 index 000000000000..988f1217d945 --- /dev/null +++ b/sysutils/nix/Makefile @@ -0,0 +1,114 @@ +# Created by: Mateusz Piotrowski <0mp@FreeBSD.org> +# $FreeBSD$ + +PORTNAME= nix +DISTVERSION= 2.3.7 +CATEGORIES= sysutils + +MAINTAINER= 0mp@FreeBSD.org +COMMENT= Purely functional package manager + +LICENSE= LGPL21 +LICENSE_FILE= ${WRKSRC}/COPYING + +BUILD_DEPENDS= ${LOCALBASE}/share/aclocal/ax_cxx_compile_stdcxx.m4:devel/autoconf-archive \ + bash:shells/bash \ + docbook-xsl-ns>=0:textproc/docbook-xsl-ns \ + gnustat:sysutils/coreutils \ + grealpath:sysutils/coreutils \ + xmllint:textproc/libxml2 \ + xsltproc:textproc/libxslt +LIB_DEPENDS= libaws-cpp-sdk-core.so:devel/aws-sdk-cpp \ + libboost_context.so:devel/boost-libs \ + libbrotlienc.so:archivers/brotli \ + libcurl.so:ftp/curl \ + libeditline.so:devel/editline \ + libgc.so:devel/boehm-gc \ + libsodium.so:security/libsodium +TEST_DEPENDS= dot:graphics/graphviz \ + git:devel/git \ + gxargs:misc/findutils \ + hg:devel/mercurial + +USES= autoreconf bison compiler:c++17-lang gmake localbase pkgconfig \ + sqlite:3 ssl +USE_GITHUB= yes +GH_ACCOUNT= NixOS +USE_LDCONFIG= yes + +HAS_CONFIGURE= yes +# Workaround for bashisms in the configure script. +CONFIGURE_SHELL= ${_BASH} +CONFIGURE_ARGS= --disable-seccomp-sandboxing \ + --enable-gc +CONFIGURE_ENV= OPENSSL_CFLAGS="-I ${OPENSSLINC}" \ + OPENSSL_LIBS="-L ${OPENSSLLIB}" +# Workaround for: +# /usr/bin/ld: error: undefined symbol: SHA512_Update +MAKE_ARGS= libutil_ALLOW_UNDEFINED=yes +# XXX: Tests require the port to be installed on the system. It is not enough +# to have the port staged. +TEST_ARGS= nix_tests="${_PASSING_TESTS}" +TEST_TARGET= installcheck + +# grealpath and gnustat are needed for tests. +BINARY_ALIAS= realpath=grealpath stat=gnustat + +SUB_FILES= pkg-message + +GROUPS= nixbld + +OPTIONS_DEFINE= DOCS + +_BASH= ${LOCALBASE}/bin/bash +_STRIP_TARGETS= bin/nix bin/nix-build bin/nix-channel bin/nix-collect-garbage \ + bin/nix-copy-closure bin/nix-daemon bin/nix-env \ + bin/nix-instantiate bin/nix-prefetch-url bin/nix-store \ + lib/libnixexpr.so lib/libnixmain.so lib/libnixstore.so \ + lib/libnixutil.so +# Regenerate the list of all tests with: +# make patch && make -f $(make -V WRKSRC)/tests/local.mk -V nix_tests +_ALL_TESTS= init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh gc.sh \ + gc-concurrent.sh gc-auto.sh referrers.sh user-envs.sh \ + logging.sh nix-build.sh misc.sh fixed.sh gc-runtime.sh \ + check-refs.sh filter-source.sh remote-store.sh export.sh \ + export-graph.sh timeout.sh secure-drv-outputs.sh nix-channel.sh \ + multiple-outputs.sh import-derivation.sh fetchurl.sh \ + optimise-store.sh binary-cache.sh nix-profile.sh repair.sh \ + dump-db.sh case-hack.sh check-reqs.sh pass-as-file.sh \ + tarball.sh restricted.sh placeholders.sh nix-shell.sh \ + linux-sandbox.sh build-dry.sh build-remote.sh nar-access.sh \ + structured-attrs.sh fetchGit.sh fetchMercurial.sh signing.sh \ + run.sh brotli.sh pure-eval.sh check.sh plugins.sh search.sh \ + nix-copy-ssh.sh post-hook.sh function-trace.sh +# Remove problematic tests from the list: +# - restricted.sh is hanging and never finishes. +_PASSING_TESTS= ${_ALL_TESTS:Nrestricted.sh} + +post-install: + @${MKDIR} ${STAGEDIR}${DATADIR} + ${INSTALL_SCRIPT} ${FILESDIR}/add-nixbld-users ${STAGEDIR}${DATADIR} + + @${RM} ${STAGEDIR}${PREFIX}/libexec/nix/build-remote + @${RLN} ${STAGEDIR}${PREFIX}/bin/nix ${STAGEDIR}${PREFIX}/libexec/nix/build-remote + + @cd ${STAGEDIR}${PREFIX} && ${STRIP_CMD} ${_STRIP_TARGETS} + +pre-test: + ${MKDIR} /tmp/nix-test + + ${REINPLACE_CMD} -e 's| xargs | gxargs |g' ${WRKSRC}/tests/push-to-store.sh + ${REINPLACE_CMD} -e 's| touch | /usr/bin/touch |g' ${WRKSRC}/tests/timeout.nix + ${REINPLACE_CMD} -e 's| touch | /usr/bin/touch |g' ${WRKSRC}/tests/check-reqs.nix + ${REINPLACE_CMD} -e 's| touch | /usr/bin/touch |g' ${WRKSRC}/tests/nar-access.nix + ${REINPLACE_CMD} -e 's| touch | /usr/bin/touch |g' ${WRKSRC}/tests/pass-as-file.sh + ${REINPLACE_CMD} -e 's| date | ${LOCALBASE}/bin/gdate |g' ${WRKSRC}/tests/check.nix + + ${REINPLACE_CMD} -e 's| wc -l)| /usr/bin/grep -c .)|g' ${WRKSRC}/tests/gc-auto.sh + ${REINPLACE_CMD} -e 's| tar c tarball)| tar -cf - tarball)|' ${WRKSRC}/tests/tarball.sh + ${REINPLACE_CMD} -e 's|^grep |/usr/bin/grep |' ${WRKSRC}/tests/check.sh + +post-test: + ${RM} -r /tmp/nix-test + +.include diff --git a/sysutils/nix/distinfo b/sysutils/nix/distinfo new file mode 100644 index 000000000000..44d514ccbbdc --- /dev/null +++ b/sysutils/nix/distinfo @@ -0,0 +1,3 @@ +TIMESTAMP = 1600718771 +SHA256 (NixOS-nix-2.3.7_GH0.tar.gz) = d5ea34196922c5f0d962649929be2a489b8cd29effcf1481c63aca154c65e739 +SIZE (NixOS-nix-2.3.7_GH0.tar.gz) = 900906 diff --git a/sysutils/nix/files/add-nixbld-users b/sysutils/nix/files/add-nixbld-users new file mode 100644 index 000000000000..2b4ac4e88d98 --- /dev/null +++ b/sysutils/nix/files/add-nixbld-users @@ -0,0 +1,23 @@ +#! /bin/sh - +# $FreeBSD$ + +set -eu + +if [ -z "$1" ] || ! expr -- "$1" : "[0-9][0-9]*" >/dev/null; then + echo "Usage: $0 NUMBER" 2>/dev/null + exit 1 +fi + +if [ "$1" -lt 1 ]; then + echo "ERROR: NUMBER should be at least 1" 2>/dev/null + exit 1 +fi + +for n in $(seq 1 "$1"); do + user="nixbld${n}" + if pw usershow "$user" >/dev/null 2>&1; then + continue + fi + pw useradd "$user" -c "Nix build user $n" -d /var/empty -G nixbld \ + -g nixbld -s /usr/sbin/nologin +done diff --git a/sysutils/nix/files/patch-src_libexpr_primops_fetchGit.cc b/sysutils/nix/files/patch-src_libexpr_primops_fetchGit.cc new file mode 100644 index 000000000000..ca3448a64c99 --- /dev/null +++ b/sysutils/nix/files/patch-src_libexpr_primops_fetchGit.cc @@ -0,0 +1,19 @@ +--- src/libexpr/primops/fetchGit.cc.orig 2019-10-10 13:03:46 UTC ++++ src/libexpr/primops/fetchGit.cc +@@ -6,6 +6,7 @@ + #include "hash.hh" + + #include ++#include + + #include + +@@ -173,7 +174,7 @@ GitInfo exportGit(ref store, const std::string + Path tmpDir = createTempDir(); + AutoDelete delTmpDir(tmpDir, true); + +- runProgram("tar", true, { "x", "-C", tmpDir }, tar); ++ runProgram("tar", true, { "x", "-C", tmpDir, "-f", "-"}, tar); + + gitInfo.storePath = store->addToStore(name, tmpDir); + diff --git a/sysutils/nix/files/patch-src_libstore_build.cc b/sysutils/nix/files/patch-src_libstore_build.cc new file mode 100644 index 000000000000..ac12a7bd0573 --- /dev/null +++ b/sysutils/nix/files/patch-src_libstore_build.cc @@ -0,0 +1,29 @@ +--- src/libstore/build.cc.orig 2019-11-28 21:39:47 UTC ++++ src/libstore/build.cc +@@ -44,12 +44,16 @@ + #include + + /* Includes required for chroot support. */ +-#if __linux__ ++#if __linux__ || __FreeBSD__ + #include + #include + #include ++#endif ++#if __linux__ + #include + #include ++#endif ++#if __linux__ || __FreeBSD__ + #include + #include + #include +@@ -1943,7 +1947,7 @@ void DerivationGoal::startBuilder() + /* If `build-users-group' is not empty, then we have to build as + one of the members of that group. */ + if (settings.buildUsersGroup != "" && getuid() == 0) { +-#if defined(__linux__) || defined(__APPLE__) ++#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) + buildUser = std::make_unique(); + + /* Make sure that no other processes are executing under this diff --git a/sysutils/nix/files/patch-src_libstore_s3-binary-cache-store.cc b/sysutils/nix/files/patch-src_libstore_s3-binary-cache-store.cc new file mode 100644 index 000000000000..8c9de342292f --- /dev/null +++ b/sysutils/nix/files/patch-src_libstore_s3-binary-cache-store.cc @@ -0,0 +1,104 @@ +--- src/libstore/s3-binary-cache-store.cc.orig 2019-10-10 13:03:46 UTC ++++ src/libstore/s3-binary-cache-store.cc +@@ -44,7 +44,7 @@ R && checkAws(const FormatOrString & fs, Aws::Utils::O + if (!outcome.IsSuccess()) + throw S3Error( + outcome.GetError().GetErrorType(), +- fs.s + ": " + outcome.GetError().GetMessage()); ++ fs.s + ": " + outcome.GetError().GetMessage().c_str()); + return outcome.GetResultWithOwnership(); + } + +@@ -54,8 +54,10 @@ class AwsLogger : public Aws::Utils::Logging::Formatte + + void ProcessFormattedStatement(Aws::String && statement) override + { +- debug("AWS: %s", chomp(statement)); ++ debug("AWS: %s", chomp((const std::string &)statement)); + } ++ ++ void Flush() override {} + }; + + static void initAWS() +@@ -139,8 +141,8 @@ S3Helper::DownloadResult S3Helper::getObject( + + auto request = + Aws::S3::Model::GetObjectRequest() +- .WithBucket(bucketName) +- .WithKey(key); ++ .WithBucket(bucketName.c_str()) ++ .WithKey(key.c_str()); + + request.SetResponseStreamFactory([&]() { + return Aws::New("STRINGSTREAM"); +@@ -155,7 +157,7 @@ S3Helper::DownloadResult S3Helper::getObject( + auto result = checkAws(fmt("AWS error fetching '%s'", key), + client->GetObject(request)); + +- res.data = decompress(result.GetContentEncoding(), ++ res.data = decompress(result.GetContentEncoding().c_str(), + dynamic_cast(result.GetBody()).str()); + + } catch (S3Error & e) { +@@ -238,8 +240,8 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheSt + + auto res = s3Helper.client->HeadObject( + Aws::S3::Model::HeadObjectRequest() +- .WithBucket(bucketName) +- .WithKey(path)); ++ .WithBucket(bucketName.c_str()) ++ .WithKey(path.c_str())); + + if (!res.IsSuccess()) { + auto & error = res.GetError(); +@@ -302,7 +304,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheSt + + std::shared_ptr transferHandle = + transferManager->UploadFile( +- stream, bucketName, path, mimeType, ++ stream, bucketName.c_str(), path.c_str(), mimeType.c_str(), + Aws::Map(), + nullptr /*, contentEncoding */); + +@@ -320,13 +322,13 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheSt + + auto request = + Aws::S3::Model::PutObjectRequest() +- .WithBucket(bucketName) +- .WithKey(path); ++ .WithBucket(bucketName.c_str()) ++ .WithKey(path.c_str()); + +- request.SetContentType(mimeType); ++ request.SetContentType(mimeType.c_str()); + + if (contentEncoding != "") +- request.SetContentEncoding(contentEncoding); ++ request.SetContentEncoding(contentEncoding.c_str()); + + auto stream = std::make_shared(data); + +@@ -393,9 +395,9 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheSt + auto res = checkAws(format("AWS error listing bucket '%s'") % bucketName, + s3Helper.client->ListObjects( + Aws::S3::Model::ListObjectsRequest() +- .WithBucket(bucketName) ++ .WithBucket(bucketName.c_str()) + .WithDelimiter("/") +- .WithMarker(marker))); ++ .WithMarker(marker.c_str()))); + + auto & contents = res.GetContents(); + +@@ -404,8 +406,8 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheSt + + for (auto object : contents) { + auto & key = object.GetKey(); +- if (key.size() != 40 || !hasSuffix(key, ".narinfo")) continue; +- paths.insert(storeDir + "/" + key.substr(0, key.size() - 8)); ++ if (key.size() != 40 || !hasSuffix(key.c_str(), ".narinfo")) continue; ++ paths.insert(storeDir + "/" + key.substr(0, key.size() - 8).c_str()); + } + + marker = res.GetNextMarker(); diff --git a/sysutils/nix/files/pkg-message.in b/sysutils/nix/files/pkg-message.in new file mode 100644 index 000000000000..ca99d9b51e8b --- /dev/null +++ b/sysutils/nix/files/pkg-message.in @@ -0,0 +1,29 @@ +[ +{ + type: install + message: <