use lib.makeScope and files to organize packages

Some of the improvements here include:

* rocksdb can actually use jemalloc now instead of just pulling in a
  second rocksdb for no reason
* "complement-runtime" factored back out into shell file
* complement image no longer uses `mkDerivation` for `copyToRoot`
  because that's what `buildEnv` is for
* complement image no longer sets `SERVER_NAME`, complement already does
  that
* all packages were factored out into `callPackage`-able files for use
  with a custom `lib.makeScope pkgs.newScope`
* new version of `mkPackage` has options that are easier to use and
  override such as `features`
This commit is contained in:
Charles Hall 2024-04-21 15:28:35 -07:00 committed by June
parent 36774322e1
commit 63fe828120
17 changed files with 466 additions and 2681 deletions

361
flake.nix
View file

@ -16,14 +16,6 @@
let
pkgsHost = inputs.nixpkgs.legacyPackages.${system};
rocksdb' = pkgs: (pkgs.rocksdb.overrideAttrs (old: {
version = "9.1.0";
src = inputs.rocksdb;
}));
# Nix-accessible `Cargo.toml`
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
# The Rust toolchain to use
toolchain = inputs.fenix.packages.${system}.fromToolchainFile {
file = ./rust-toolchain.toml;
@ -32,286 +24,45 @@
sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8=";
};
builder = pkgs:
((inputs.crane.mkLib pkgs).overrideToolchain toolchain).buildPackage;
scope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
book = self.callPackage ./nix/pkgs/book {};
complement = self.callPackage ./nix/pkgs/complement {};
craneLib = ((inputs.crane.mkLib pkgs).overrideToolchain toolchain);
inherit inputs;
main = self.callPackage ./nix/pkgs/main {};
oci-image = self.callPackage ./nix/pkgs/oci-image {};
rocksdb = pkgs.rocksdb.overrideAttrs (old: {
src = inputs.rocksdb;
version = pkgs.lib.removePrefix
"v"
(builtins.fromJSON (builtins.readFile ./flake.lock))
.nodes.rocksdb.original.ref;
});
});
nativeBuildInputs = pkgs: let
darwin = if pkgs.stdenv.isDarwin then [ pkgs.libiconv ] else [];
in [
# bindgen needs the build platform's libclang. Apparently due to
# "splicing weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't
# quite do the right thing here.
pkgs.pkgsBuildHost.rustPlatform.bindgenHook
] ++ darwin;
env = pkgs: {
CONDUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev;
ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include";
ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib";
}
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isStatic {
ROCKSDB_STATIC = "";
}
// {
CARGO_BUILD_RUSTFLAGS = let inherit (pkgs) lib stdenv; in
lib.concatStringsSep " " ([ ]
++ lib.optionals
# This disables PIE for static builds, which isn't great in terms
# of security. Unfortunately, my hand is forced because nixpkgs'
# `libstdc++.a` is built without `-fPIE`, which precludes us from
# leaving PIE enabled.
stdenv.hostPlatform.isStatic
[ "-C" "relocation-model=static" ]
++ lib.optionals
(stdenv.buildPlatform.config != stdenv.hostPlatform.config)
[ "-l" "c" ]
++ lib.optionals
# This check has to match the one [here][0]. We only need to set
# these flags when using a different linker. Don't ask me why,
# though, because I don't know. All I know is it breaks otherwise.
#
# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L37-L40
(
# Nixpkgs doesn't check for x86_64 here but we do, because I
# observed a failure building statically for x86_64 without
# including it here. Linkers are weird.
(stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64)
&& stdenv.hostPlatform.isStatic
&& !stdenv.isDarwin
&& !stdenv.cc.bintools.isLLVM
)
[
"-l"
"stdc++"
"-L"
"${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib"
]
);
}
# What follows is stolen from [here][0]. Its purpose is to properly
# configure compilers and linkers for various stages of the build, and
# even covers the case of build scripts that need native code compiled and
# run on the build platform (I think).
#
# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L57-L80
// (
let
inherit (pkgs.rust.lib) envVars;
in
pkgs.lib.optionalAttrs
(pkgs.stdenv.targetPlatform.rust.rustcTarget
!= pkgs.stdenv.hostPlatform.rust.rustcTarget)
(
let
inherit (pkgs.stdenv.targetPlatform.rust) cargoEnvVarTarget;
in
{
"CC_${cargoEnvVarTarget}" = envVars.ccForTarget;
"CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget;
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" =
envVars.linkerForTarget;
}
)
// (
let
inherit (pkgs.stdenv.hostPlatform.rust) cargoEnvVarTarget rustcTarget;
in
{
"CC_${cargoEnvVarTarget}" = envVars.ccForHost;
"CXX_${cargoEnvVarTarget}" = envVars.cxxForHost;
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost;
CARGO_BUILD_TARGET = rustcTarget;
}
)
// (
let
inherit (pkgs.stdenv.buildPlatform.rust) cargoEnvVarTarget;
in
{
"CC_${cargoEnvVarTarget}" = envVars.ccForBuild;
"CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild;
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild;
HOST_CC = "${pkgs.pkgsBuildHost.stdenv.cc}/bin/cc";
HOST_CXX = "${pkgs.pkgsBuildHost.stdenv.cc}/bin/c++";
}
)
);
mkPackage = pkgs: allocator: cargoArgs: profile: builder pkgs {
src = inputs.nix-filter {
root = ./.;
include = [
"src"
"Cargo.toml"
"Cargo.lock"
];
};
rocksdb' = (if allocator == "jemalloc" then (pkgs.rocksdb.override { enableJemalloc = true; }) else (rocksdb' pkgs));
# This is redundant with CI
doCheck = false;
env = env pkgs;
nativeBuildInputs = nativeBuildInputs pkgs;
cargoExtraArgs = cargoArgs
+ (if allocator == "jemalloc" then " --features jemalloc" else "")
+ (if allocator == "hmalloc" then " --features hardened_malloc" else "")
;
meta.mainProgram = cargoToml.package.name;
CARGO_PROFILE = profile;
};
mkOciImage = pkgs: package:
pkgs.dockerTools.buildLayeredImage {
name = package.pname;
tag = "main";
# Debian makes builds reproducible through using the HEAD commit's date
created = "@${toString inputs.self.lastModified}";
contents = [
pkgs.dockerTools.caCertificates
];
config = {
# Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT)
# are handled as expected
Entrypoint = if !pkgs.stdenv.isDarwin then [
"${pkgs.lib.getExe' pkgs.tini "tini"}"
"--"
] else [];
Cmd = [
"${pkgs.lib.getExe package}"
];
};
};
createComplementRuntime = pkgs: image: let
script = pkgs.writeShellScriptBin "run.sh"
''
export PATH=${pkgs.lib.makeBinPath [ pkgs.olm pkgs.gcc ]}
${pkgs.lib.getExe pkgs.docker} load < ${image}
set +o pipefail
/usr/bin/env -C "${inputs.complement}" COMPLEMENT_BASE_IMAGE="complement-conduit:dev" ${pkgs.lib.getExe pkgs.go} test -json ${inputs.complement}/tests | ${pkgs.toybox}/bin/tee $1
set -o pipefail
# Post-process the results into an easy-to-compare format
${pkgs.coreutils}/bin/cat "$1" | ${pkgs.lib.getExe pkgs.jq} -c '
select(
(.Action == "pass" or .Action == "fail" or .Action == "skip")
and .Test != null
) | {Action: .Action, Test: .Test}
' | ${pkgs.coreutils}/bin/sort > "$2"
'';
in script;
createComplementImage = pkgs: let
conduwuit = mkPackage pkgs "jemalloc" "--features=axum_dual_protocol" "dev";
in pkgs.dockerTools.buildImage {
name = "complement-conduit";
tag = "dev";
copyToRoot = pkgs.stdenv.mkDerivation {
name = "complement_data";
src = inputs.nix-filter {
root = ./.;
include = [
"tests/complement/conduwuit-complement.toml"
"tests/complement/v3.ext"
];
};
phases = [ "unpackPhase" "installPhase" ];
installPhase = ''
mkdir -p $out/conduwuit/data
cp $src/tests/complement/conduwuit-complement.toml $out/conduwuit/conduit.toml
cp $src/tests/complement/v3.ext $out/v3.ext
'';
};
config = {
Cmd = [
"${pkgs.bash}/bin/sh"
"-c"
''
echo "Starting server as $SERVER_NAME" &&
export CONDUIT_SERVER_NAME=$SERVER_NAME CONDUIT_WELL_KNOWN_SERVER="$SERVER_NAME:8448" CONDUIT_WELL_KNOWN_SERVER="$SERVER_NAME:8008" &&
${pkgs.lib.getExe pkgs.openssl} genrsa -out /conduwuit/private_key.key 2048 &&
${pkgs.lib.getExe pkgs.openssl} req -new -sha256 -key /conduwuit/private_key.key -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=$SERVER_NAME" -out /conduwuit/signing_request.csr &&
echo "DNS.1 = $SERVER_NAME" >> /v3.ext &&
echo "IP.1 = $(${pkgs.lib.getExe pkgs.gawk} 'END{print $1}' /etc/hosts)" >> /v3.ext &&
${pkgs.lib.getExe pkgs.openssl} x509 -req -extfile /v3.ext -in /conduwuit/signing_request.csr -CA /complement/ca/ca.crt -CAkey /complement/ca/ca.key -CAcreateserial -out /conduwuit/certificate.crt -days 1 -sha256 &&
${pkgs.lib.getExe conduwuit}
''
];
Entrypoint = if !pkgs.stdenv.isDarwin then [
"${pkgs.lib.getExe' pkgs.tini "tini"}"
"--"
] else [];
Env = [
"SSL_CERT_FILE=/complement/ca/ca.crt"
"SERVER_NAME=localhost"
"CONDUIT_CONFIG=/conduwuit/conduit.toml"
];
ExposedPorts = {
"8008/tcp" = {};
"8448/tcp" = {};
};
};
};
scopeHost = (scope pkgsHost);
in
{
packages = {
default = mkPackage pkgsHost null "" "release";
jemalloc = mkPackage pkgsHost "jemalloc" "" "release";
hmalloc = mkPackage pkgsHost "hmalloc" "" "release";
oci-image = mkOciImage pkgsHost inputs.self.packages.${system}.default;
oci-image-jemalloc =
mkOciImage pkgsHost inputs.self.packages.${system}.jemalloc;
oci-image-hmalloc =
mkOciImage pkgsHost inputs.self.packages.${system}.hmalloc;
default = scopeHost.main;
jemalloc = scopeHost.main.override { features = ["jemalloc"]; };
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
book =
let
package = inputs.self.packages.${system}.default;
in
pkgsHost.stdenv.mkDerivation {
pname = "${package.pname}-book";
version = package.version;
src = inputs.nix-filter {
root = ./.;
include = [
"book.toml"
"conduwuit-example.toml"
"README.md"
"debian/README.md"
"docs"
];
};
nativeBuildInputs = (with pkgsHost; [
mdbook
]);
buildPhase = ''
mdbook build
mv public $out
'';
oci-image = scopeHost.oci-image;
oci-image-jemalloc = scopeHost.oci-image.override {
main = scopeHost.main.override {
features = ["jemalloc"];
};
complement-image = createComplementImage pkgsHost;
complement-runtime = createComplementRuntime pkgsHost inputs.self.outputs.packages.${system}.complement-image;
};
oci-image-hmalloc = scopeHost.oci-image.override {
main = scopeHost.main.override {
features = ["hardened_malloc"];
};
};
book = scopeHost.book;
complement = scopeHost.complement;
}
//
builtins.listToAttrs
@ -327,48 +78,55 @@
config = crossSystem;
};
}).pkgsStatic;
scopeCrossStatic = scope pkgsCrossStatic;
in
[
# An output for a statically-linked binary
{
name = binaryName;
value = mkPackage pkgsCrossStatic null "" "release";
value = scopeCrossStatic.main;
}
# An output for a statically-linked binary with jemalloc
{
name = "${binaryName}-jemalloc";
value = mkPackage pkgsCrossStatic "jemalloc" "" "release";
value = scopeCrossStatic.main.override {
features = ["jemalloc"];
};
}
# An output for a statically-linked binary with hardened_malloc
{
name = "${binaryName}-hmalloc";
value = mkPackage pkgsCrossStatic "hmalloc" "" "release";
value = scopeCrossStatic.main.override {
features = ["hardened_malloc"];
};
}
# An output for an OCI image based on that binary
{
name = "oci-image-${crossSystem}";
value = mkOciImage
pkgsCrossStatic
inputs.self.packages.${system}.${binaryName};
value = scopeCrossStatic.oci-image;
}
# An output for an OCI image based on that binary with jemalloc
{
name = "oci-image-${crossSystem}-jemalloc";
value = mkOciImage
pkgsCrossStatic
inputs.self.packages.${system}."${binaryName}-jemalloc";
value = scopeCrossStatic.oci-image.override {
main = scopeCrossStatic.main.override {
features = ["jemalloc"];
};
};
}
# An output for an OCI image based on that binary with hardened_malloc
{
name = "oci-image-${crossSystem}-hmalloc";
value = mkOciImage
pkgsCrossStatic
inputs.self.packages.${system}."${binaryName}-hmalloc";
value = scopeCrossStatic.oci-image.override {
main = scopeCrossStatic.main.override {
features = ["hardened_malloc"];
};
};
}
]
)
@ -380,15 +138,19 @@
);
devShells.default = pkgsHost.mkShell {
env = env pkgsHost // {
env = scopeHost.main.env // {
# Rust Analyzer needs to be able to find the path to default crate
# sources, and it can read this environment variable to do so. The
# `rust-src` component is required in order for this to work.
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
# Convenient way to access a pinned version of Complement's source
# code.
COMPLEMENT_SRC = inputs.complement.outPath;
};
# Development tools
nativeBuildInputs = nativeBuildInputs pkgsHost ++ [
packages = [
# Always use nightly rustfmt because most of its options are unstable
#
# This needs to come before `toolchain` in this list, otherwise
@ -396,7 +158,8 @@
inputs.fenix.packages.${system}.latest.rustfmt
toolchain
] ++ (with pkgsHost; [
]
++ (with pkgsHost; [
engage
# Needed for producing Debian packages
@ -414,7 +177,9 @@
# Useful for editing the book locally
mdbook
]);
])
++
scopeHost.main.nativeBuildInputs;
};
});
}