push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
{
lib,
stdenv,
fetchFromGitHub,
jdk8,
ant,
python3,
watchman,
bash,
makeWrapper,
}:
stdenv.mkDerivation rec {
pname = "buck";
version = "2022.05.05.01";
src = fetchFromGitHub {
owner = "facebook";
repo = pname;
rev = "v${version}";
sha256 = "15v4sk1l43pgd5jxr5lxnh0ks6vb3xk5253n66s7vvsnph48j14q";
};
patches = [ ./pex-mtime.patch ];
postPatch = ''
grep -l -r '/bin/bash' --null | xargs -0 sed -i -e "s!/bin/bash!${bash}/bin/bash!g"
'';
nativeBuildInputs = [
makeWrapper
python3
jdk8
ant
watchman
];
buildPhase = ''
# Set correct version, see https://github.com/facebook/buck/issues/2607
echo v${version} > .buckrelease
ant
PYTHONDONTWRITEBYTECODE=true ./bin/buck build -c buck.release_version=${version} buck
'';
installPhase = ''
install -D -m755 buck-out/gen/*/programs/buck.pex $out/bin/buck
wrapProgram $out/bin/buck \
--prefix PATH : "${
lib.makeBinPath [
jdk8
watchman
python3
]
}"
'';
meta = with lib; {
homepage = "https://buck.build/";
description = "High-performance build tool";
mainProgram = "buck";
maintainers = [ maintainers.jgertm ];
license = licenses.asl20;
platforms = platforms.all;
# https://github.com/facebook/buck/issues/2666
broken = stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64;
};
}

View File

@@ -0,0 +1,13 @@
diff --git a/third-party/py/pex/pex/common.py b/third-party/py/pex/pex/common.py
index 76459ce23..eff411b20 100644
--- a/third-party/py/pex/pex/common.py
+++ b/third-party/py/pex/pex/common.py
@@ -328,4 +328,7 @@ class Chroot(object):
def zip(self, filename, mode='wb'):
with contextlib.closing(zipfile.ZipFile(filename, mode)) as zf:
for f in sorted(self.files()):
- zf.write(os.path.join(self.chroot, f), arcname=f, compress_type=zipfile.ZIP_DEFLATED)
+ path = os.path.join(self.chroot, f)
+ instant = 615532801
+ os.utime(path, (instant, instant))
+ zf.write(path, arcname=f, compress_type=zipfile.ZIP_DEFLATED)

View File

@@ -0,0 +1,57 @@
{
lib,
stdenv,
build2,
fetchurl,
libbpkg,
libbutl,
libodb,
libodb-sqlite,
enableShared ? !stdenv.hostPlatform.isStatic,
enableStatic ? !enableShared,
}:
stdenv.mkDerivation rec {
pname = "bdep";
version = "0.17.0";
outputs = [
"out"
"doc"
"man"
];
src = fetchurl {
url = "https://pkg.cppget.org/1/alpha/build2/bdep-${version}.tar.gz";
hash = "sha256-+2Hl5kanxWJmOpfePAvvSBSmG3kZLQv/kYIkT4J+kaQ=";
};
strictDeps = true;
nativeBuildInputs = [
build2
];
buildInputs = [
libbpkg
libbutl
libodb
libodb-sqlite
];
build2ConfigureFlags = [
"config.bin.lib=${build2.configSharedStatic enableShared enableStatic}"
];
meta = with lib; {
description = "Build2 project dependency manager";
mainProgram = "bdep";
# https://build2.org/bdep/doc/bdep.xhtml
longDescription = ''
The build2 project dependency manager is used to manage the dependencies
of a project during development.
'';
homepage = "https://build2.org/";
changelog = "https://git.build2.org/cgit/bdep/tree/NEWS";
license = licenses.mit;
maintainers = with maintainers; [ r-burns ];
platforms = platforms.all;
};
}

View File

@@ -0,0 +1,56 @@
{
lib,
stdenv,
fetchurl,
pkgs,
buildPackages,
fixDarwinDylibNames,
}:
stdenv.mkDerivation rec {
pname = "build2-bootstrap";
version = "0.17.0";
src = fetchurl {
url = "https://download.build2.org/${version}/build2-toolchain-${version}.tar.xz";
hash = "sha256-NyKonqht90JTnQ+Ru0Qp/Ua79mhVOjUHgKY0EbZIv10=";
};
patches = [
# Pick up sysdirs from NIX_LDFLAGS
./nix-ldflags-sysdirs.patch
];
sourceRoot = "build2-toolchain-${version}/build2";
makefile = "bootstrap.gmake";
enableParallelBuilding = true;
setupHook = ./setup-hook.sh;
strictDeps = true;
propagatedBuildInputs = lib.optionals stdenv.targetPlatform.isDarwin [
fixDarwinDylibNames
# Build2 needs to use lld on Darwin because it creates thin archives when it detects `llvm-ar`,
# which ld64 does not support.
(lib.getBin buildPackages.llvmPackages.lld)
];
doCheck = true;
checkPhase = ''
runHook preCheck
build2/b-boot --version
runHook postCheck
'';
installPhase = ''
runHook preInstall
install -D build2/b-boot $out/bin/b
runHook postInstall
'';
postFixup = ''
substituteInPlace $out/nix-support/setup-hook \
--subst-var-by isTargetDarwin '${toString stdenv.targetPlatform.isDarwin}'
'';
inherit (pkgs.build2) passthru;
}

View File

@@ -0,0 +1,76 @@
{
lib,
stdenv,
build2,
fetchurl,
git,
libbpkg,
libbutl,
libodb,
libodb-sqlite,
openssl,
enableShared ? !stdenv.hostPlatform.isStatic,
enableStatic ? !enableShared,
}:
stdenv.mkDerivation rec {
pname = "bpkg";
version = "0.17.0";
outputs = [
"out"
"doc"
"man"
];
src = fetchurl {
url = "https://pkg.cppget.org/1/alpha/build2/bpkg-${version}.tar.gz";
hash = "sha256-Yw6wvTqO+VfCo91B2BUT0A8OIN0MVhGK1USYM7hgGMs=";
};
strictDeps = true;
nativeBuildInputs = [
build2
];
buildInputs = [
build2
libbpkg
libbutl
libodb
libodb-sqlite
];
nativeCheckInputs = [
git
openssl
];
doCheck = true;
# Failing test
postPatch = ''
rm tests/rep-create.testscript
'';
build2ConfigureFlags = [
"config.bin.lib=${build2.configSharedStatic enableShared enableStatic}"
];
postInstall = lib.optionalString stdenv.hostPlatform.isDarwin ''
install_name_tool -add_rpath '${lib.getLib build2}/lib' "''${!outputBin}/bin/bpkg"
'';
meta = with lib; {
description = "Build2 package dependency manager";
mainProgram = "bpkg";
# https://build2.org/bpkg/doc/bpkg.xhtml
longDescription = ''
The build2 package dependency manager is used to manipulate build
configurations, packages, and repositories.
'';
homepage = "https://build2.org/";
changelog = "https://git.build2.org/cgit/bpkg/tree/NEWS";
license = licenses.mit;
maintainers = with maintainers; [ r-burns ];
platforms = platforms.all;
};
}

View File

@@ -0,0 +1,125 @@
{
stdenv,
lib,
build2,
fetchurl,
fixDarwinDylibNames,
libbutl,
libpkgconf,
buildPackages,
enableShared ? !stdenv.hostPlatform.isStatic,
enableStatic ? !enableShared,
}:
let
configSharedStatic =
enableShared: enableStatic:
if enableShared && enableStatic then
"both"
else if enableShared then
"shared"
else if enableStatic then
"static"
else
throw "neither shared nor static libraries requested";
in
stdenv.mkDerivation rec {
pname = "build2";
version = "0.17.0";
outputs = [
"out"
"dev"
"doc"
"man"
];
setupHook = ./setup-hook.sh;
src = fetchurl {
url = "https://pkg.cppget.org/1/alpha/build2/build2-${version}.tar.gz";
hash = "sha256-Kx5X/GV3GjFSbjo1mzteiHnnm4mr6+NAKIR/mEE+IdA=";
};
patches = [
# Remove any build/host config entries which refer to nix store paths
./remove-config-store-paths.patch
# Pick up sysdirs from NIX_LDFLAGS
./nix-ldflags-sysdirs.patch
];
strictDeps = true;
nativeBuildInputs = [
build2
];
disallowedReferences = [
build2
libbutl.dev
libpkgconf.dev
];
buildInputs = [
libbutl
libpkgconf
];
# Build2 uses @rpath on darwin
# https://github.com/build2/build2/issues/166
# N.B. this only adjusts the install_name after all libraries are installed;
# packages containing multiple interdependent libraries may have
# LC_LOAD_DYLIB entries containing @rpath, requiring manual fixup
propagatedBuildInputs = lib.optionals stdenv.targetPlatform.isDarwin [
fixDarwinDylibNames
# Build2 needs to use lld on Darwin because it creates thin archives when it detects `llvm-ar`,
# which ld64 does not support.
(lib.getBin buildPackages.llvmPackages.lld)
];
postPatch = ''
patchShebangs --build tests/bash/testscript
'';
build2ConfigureFlags = [
"config.bin.lib=${configSharedStatic enableShared enableStatic}"
"config.cc.poptions+=-I${lib.getDev libpkgconf}/include/pkgconf"
"config.build2.libpkgconf=true"
];
postInstall = lib.optionalString stdenv.hostPlatform.isDarwin ''
install_name_tool -add_rpath "''${!outputLib}/lib" "''${!outputBin}/bin/b"
'';
postFixup = ''
substituteInPlace $dev/nix-support/setup-hook \
--subst-var-by isTargetDarwin '${toString stdenv.targetPlatform.isDarwin}'
'';
passthru = {
bootstrap = build2;
inherit configSharedStatic;
};
meta = with lib; {
homepage = "https://www.build2.org/";
description = "Build2 build system";
license = licenses.mit;
longDescription = ''
build2 is an open source (MIT), cross-platform build toolchain
that aims to approximate Rust Cargo's convenience for developing
and packaging C/C++ projects while providing more depth and
flexibility, especially in the build system.
build2 is a hierarchy of tools consisting of a general-purpose
build system, package manager (for package consumption), and
project manager (for project development). It is primarily aimed
at C/C++ projects as well as mixed-language projects involving
one of these languages (see bash and rust modules, for example).
'';
changelog = "https://git.build2.org/cgit/build2/tree/NEWS";
platforms = platforms.all;
maintainers = with maintainers; [
hiro98
r-burns
];
mainProgram = "b";
};
}

View File

@@ -0,0 +1,42 @@
diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx
index f848003c..0f14f9a5 100644
--- a/libbuild2/cc/common.cxx
+++ b/libbuild2/cc/common.cxx
@@ -966,6 +966,17 @@ namespace build2
void
msvc_extract_library_search_dirs (const strings&, dir_paths&); // msvc.cxx
+ static strings split (const string& s, const char delim) {
+ stringstream ss (s);
+ string item;
+ strings result;
+
+ while (getline (ss, item, delim)) {
+ result.push_back (item);
+ }
+ return result;
+ }
+
dir_paths common::
extract_library_search_dirs (const scope& bs) const
{
@@ -987,8 +998,19 @@ namespace build2
msvc_extract_library_search_dirs (v, r);
else
gcc_extract_library_search_dirs (v, r);
+
};
+ // NIX_LDFLAGS are implicitly used when linking,
+ // so its -L flags effectively specify system dirs.
+ // However, they are only enabled when actually linking and are thus
+ // not detected by build2, so we need to manually pick them up here.
+ if (auto s = getenv ("NIX_LDFLAGS")) {
+ // TODO: do we need more robust args splitting here? e.g. shlex.split
+ auto args = split (s.value (), ' ');
+ gcc_extract_library_search_dirs (args, r);
+ }
+
// Note that the compiler mode options are in sys_lib_dirs.
//
if (auto l = bs[c_loptions]) extract (*l, c_loptions);

View File

@@ -0,0 +1,14 @@
--- a/libbuild2/buildfile
+++ b/libbuild2/buildfile
@@ -86,8 +86,11 @@ build2_config_lines = [strings]
host_config_lines = [strings]
for l: $regex.replace_lines( \
+ $regex.replace_lines( \
$config.save(), \
'^( *(#|(config\.(test[. ]|dist\.|install\.chroot|config\.hermetic))).*|)$', \
+ [null], return_lines), \
+ '^.*'$getenv(NIX_STORE)'/[a-z0-9]{32}-.*$', \
[null])
{
build2_config_lines += $l

View File

@@ -0,0 +1,91 @@
# shellcheck shell=bash
build2ConfigurePhase() {
runHook preConfigure
local flagsArray=(
"config.c=$CC"
"config.cxx=$CXX"
"config.cc.coptions+=-O2"
"config.cc.poptions+=-DNDEBUG"
"config.install.root=$prefix"
"config.install.bin=${!outputBin}/bin"
"config.install.doc=${!outputDoc}/share/doc/${shareDocName}"
"config.install.exec_root=${!outputBin}"
"config.install.include=${!outputInclude}/include"
"config.install.lib=${!outputLib}/lib"
"config.install.libexec=${!outputLib}/libexec"
"config.install.man=${!outputDoc}/share/man"
"config.install.sbin=${!outputBin}/sbin"
"config.install.bin.mode=755"
)
concatTo flagsArray build2ConfigureFlags build2ConfigureFlagsArray
# shellcheck disable=SC2157
if [ -n "@isTargetDarwin@" ]; then
flagsArray+=("config.bin.ld=ld64-lld")
flagsArray+=("config.cc.loptions+=-fuse-ld=lld")
flagsArray+=("config.cc.loptions+=-headerpad_max_install_names")
fi
echo 'configure flags' "${flagsArray[@]}"
b configure "${flagsArray[@]}"
runHook postConfigure
}
build2BuildPhase() {
runHook preBuild
local flagsArray=()
concatTo flagsArray build2BuildFlags build2BuildFlagsArray
echo 'build flags' "${flagsArray[@]}"
b "${flagsArray[@]}"
runHook postBuild
}
build2CheckPhase() {
runHook preCheck
local flagsArray=()
concatTo flagsArray build2CheckFlags build2CheckFlags
echo 'check flags' "${flagsArray[@]}"
b test "${build2Dir:-.}" "${flagsArray[@]}"
runHook postCheck
}
build2InstallPhase() {
runHook preInstall
local flagsArray=()
concatTo flagsArray build2InstallFlags build2InstallFlagsArray installTargets
echo 'install flags' "${flagsArray[@]}"
b install "${flagsArray[@]}"
runHook postInstall
}
if [ -z "${dontUseBuild2Configure-}" ] && [ -z "${configurePhase-}" ]; then
# shellcheck disable=SC2034
setOutputFlags=
configurePhase=build2ConfigurePhase
fi
if [ -z "${dontUseBuild2Build-}" ] && [ -z "${buildPhase-}" ]; then
buildPhase=build2BuildPhase
fi
if [ -z "${dontUseBuild2Check-}" ] && [ -z "${checkPhase-}" ]; then
checkPhase=build2CheckPhase
fi
if [ -z "${dontUseBuild2Install-}" ] && [ -z "${installPhase-}" ]; then
installPhase=build2InstallPhase
fi

View File

@@ -0,0 +1,2 @@
source 'https://rubygems.org'
gem 'drake'

View File

@@ -0,0 +1,15 @@
GEM
remote: https://rubygems.org/
specs:
comp_tree (1.1.3)
drake (0.9.2.0.3.1)
comp_tree (>= 1.1.3)
PLATFORMS
ruby
DEPENDENCIES
drake
BUNDLED WITH
2.1.4

View File

@@ -0,0 +1,25 @@
{
lib,
bundlerApp,
bundlerUpdateScript,
}:
bundlerApp {
pname = "drake";
gemdir = ./.;
exes = [ "drake" ];
passthru.updateScript = bundlerUpdateScript "drake";
meta = with lib; {
description = "Branch of Rake supporting automatic parallelizing of tasks";
homepage = "http://quix.github.io/rake/";
maintainers = with maintainers; [
romildo
manveru
nicknovitski
];
license = licenses.mit;
platforms = platforms.unix;
};
}

View File

@@ -0,0 +1,23 @@
{
comp_tree = {
groups = [ "default" ];
platforms = [ ];
source = {
remotes = [ "https://rubygems.org" ];
sha256 = "0dj9lkfxcczn67l1j12dcxswrfxxd1zgxa344zk6vqs2gwwhy9m9";
type = "gem";
};
version = "1.1.3";
};
drake = {
dependencies = [ "comp_tree" ];
groups = [ "default" ];
platforms = [ ];
source = {
remotes = [ "https://rubygems.org" ];
sha256 = "09gkmdshwdmdnkdxi03dv4rk1dip0wdv6dx14wscrmi0jyk86yag";
type = "gem";
};
version = "0.9.2.0.3.1";
};
}

View File

@@ -0,0 +1,79 @@
{
lib,
stdenv,
fetchurl,
autoreconfHook,
guileSupport ? false,
guile,
# avoid guile depend on bootstrap to prevent dependency cycles
inBootstrap ? false,
pkg-config,
gnumake,
}:
let
guileEnabled = guileSupport && !inBootstrap;
in
stdenv.mkDerivation rec {
pname = "gnumake";
version = "4.4.1";
src = fetchurl {
url = "mirror://gnu/make/make-${version}.tar.gz";
sha256 = "sha256-3Rb7HWe/q3mnL16DkHNcSePo5wtJRaFasfgd23hlj7M=";
};
# To update patches:
# $ version=4.4.1
# $ git clone https://git.savannah.gnu.org/git/make.git
# $ cd make && git checkout -b nixpkgs $version
# $ git am --directory=../patches
# $ # make changes, resolve conflicts, etc.
# $ git format-patch --output-directory ../patches --diff-algorithm=histogram $version
#
# TODO: stdenvs setup.sh should be aware of patch directories. Its very
# convenient to keep them in a separate directory but we can defer listing the
# directory until derivation realization to avoid unnecessary Nix evaluations.
patches = lib.filesystem.listFilesRecursive ./patches;
nativeBuildInputs = [
autoreconfHook
pkg-config
];
buildInputs = lib.optionals guileEnabled [ guile ];
configureFlags = lib.optional guileEnabled "--with-guile";
outputs = [
"out"
"man"
"info"
];
separateDebugInfo = true;
passthru.tests = {
# make sure that the override doesn't break bootstrapping
gnumakeWithGuile = gnumake.override { guileSupport = true; };
};
meta = with lib; {
description = "Tool to control the generation of non-source files from sources";
longDescription = ''
Make is a tool which controls the generation of executables and
other non-source files of a program from the program's source files.
Make gets its knowledge of how to build your program from a file
called the makefile, which lists each of the non-source files and
how to compute it from other files. When you write a program, you
should write a makefile for it, so that it is possible to use Make
to build and install the program.
'';
homepage = "https://www.gnu.org/software/make/";
license = licenses.gpl3Plus;
maintainers = [ ];
mainProgram = "make";
platforms = platforms.all;
};
}

View File

@@ -0,0 +1,35 @@
From b69e3740e68afaec97b9957d40b9c135db87eaab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io>
Date: Sat, 24 Apr 2021 10:11:40 +0200
Subject: [PATCH 1/3] No impure bin sh
default_shell is used to populuate default shell used to execute jobs.
Unless SHELL is set to a different value this would be /bin/sh.
Our stdenv provides sh in form of bash anyway. Having this value not
hard-coded has some advantages:
- It would ensure that on all systems it uses sh from its PATH rather
than /bin/sh, which helps as different systems might have different
shells there (bash vs. dash)
- In the past I had issues with LD_PRELOAD with BEAR, where /bin/sh
used a different glibc than BEAR which came from my development shell.
---
src/job.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/job.c b/src/job.c
index ea885614..8a9bd8e0 100644
--- a/src/job.c
+++ b/src/job.c
@@ -76,7 +76,7 @@ char * vms_strsignal (int status);
#else
-const char *default_shell = "/bin/sh";
+const char *default_shell = "sh";
int batch_mode_shell = 0;
#endif
--
2.44.1

View File

@@ -0,0 +1,44 @@
From 2db52008be2e2d504889f4f19318c2ba5a2a4797 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io>
Date: Sat, 24 Apr 2021 10:20:16 +0200
Subject: [PATCH 2/3] Remove impure dirs
Purity: don't look for library dependencies (of the form `-lfoo') in
/lib and /usr/lib. It's a stupid feature anyway. Likewise, when
searching for included Makefiles, don't look in /usr/include and
friends.
---
src/read.c | 3 ---
src/remake.c | 2 --
2 files changed, 5 deletions(-)
diff --git a/src/read.c b/src/read.c
index b0fc1e1f..d6b41c17 100644
--- a/src/read.c
+++ b/src/read.c
@@ -111,9 +111,6 @@ static const char *default_include_directories[] =
INCLUDEDIR,
#endif
#ifndef _AMIGA
- "/usr/gnu/include",
- "/usr/local/include",
- "/usr/include",
#endif
0
};
diff --git a/src/remake.c b/src/remake.c
index fe67ab28..1b76560c 100644
--- a/src/remake.c
+++ b/src/remake.c
@@ -1690,8 +1690,6 @@ library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr)
static const char *dirs[] =
{
#ifndef _AMIGA
- "/lib",
- "/usr/lib",
#endif
#if defined(WINDOWS32) && !defined(LIBDIR)
/*
--
2.44.1

View File

@@ -0,0 +1,83 @@
From 3317b3a78666e6073c63f62a322176e3dc680461 Mon Sep 17 00:00:00 2001
From: Ivan Trubach <mr.trubach@icloud.com>
Date: Sat, 17 Aug 2024 22:35:03 +0300
Subject: [PATCH 3/3] Do not search for a C++ compiler and set MAKE_CXX
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Removes unnecessary reference to C++ compiler if CXX is set to an
absolute path. If CXX is not an absolute path, we avoid defaulting CXX
to a compiler name that was used to build the package.
Context: GNU Make defines default values for CC, CXX and other
environment variables. For CXX, it usually defaults to g++, however,
FreeBSD and OpenBSD no longer ship GCC as a system compiler (and use
Clang instead). For C compiler, POSIX standardizes the name to be "cc",
but there is no such standard for C++ compiler name. As a fix, GNU Make
uses CXX set for build as a default (via MAKE_CXX preprocessor macro in
the source code).
We revert the change that added this behavior and set the default to c++
or g++ that does not depend on the build platform.
In stdenv, CXX environment variable is always defined and overrides the
default value.
References:
• https://savannah.gnu.org/bugs/?63668
• https://git.savannah.gnu.org/cgit/make.git/commit/?id=ffa28f3914ff402b3915f75e4fed86ac6fb1449d
---
configure.ac | 2 --
src/default.c | 19 ++++++-------------
2 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/configure.ac b/configure.ac
index cd785754..41a65307 100644
--- a/configure.ac
+++ b/configure.ac
@@ -37,8 +37,6 @@ AM_INIT_AUTOMAKE([1.16.1 foreign -Werror -Wall])
# Checks for programs.
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
-AC_PROG_CXX
-AC_DEFINE_UNQUOTED(MAKE_CXX, ["$CXX"], [Default C++ compiler.])
# Configure gnulib
gl_EARLY
diff --git a/src/default.c b/src/default.c
index e396269b..78ba402f 100644
--- a/src/default.c
+++ b/src/default.c
@@ -528,22 +528,15 @@ static const char *default_variables[] =
#ifdef GCC_IS_NATIVE
"CC", "gcc",
"OBJC", "gcc",
+# ifdef __MSDOS__
+ "CXX", "gpp", /* g++ is an invalid name on MSDOS */
+# else
+ "CXX", "g++",
+# endif /* __MSDOS__ */
#else
"CC", "cc",
"OBJC", "cc",
-#endif
-#ifdef MAKE_CXX
- "CXX", MAKE_CXX,
-#else
-# ifdef GCC_IS_NATIVE
-# ifdef __MSDOS__
- "CXX", "gpp", /* g++ is an invalid name on MSDOS */
-# else
- "CXX", "gcc",
-# endif /* __MSDOS__ */
-# else
- "CXX", "g++",
-# endif
+ "CXX", "c++",
#endif
/* This expands to $(CO) $(COFLAGS) $< $@ if $@ does not exist,
and to the empty string if $@ does exist. */
--
2.44.1

View File

@@ -0,0 +1,245 @@
# Gradle Setup Hook
## Introduction
Gradle build scripts are written in a DSL, computing the list of Gradle
dependencies is a Turing-complete task, not just in theory but also in
practice. Fetching all of the dependencies often requires building some
native code, running some commands to check the host platform, or just
fetching some files using either JVM code or commands like `curl` or
`wget`.
This practice is widespread and isn't considered a bad practice in the
Java world, so all we can do is run Gradle to check what dependencies
end up being fetched, and allow derivation authors to apply workarounds
so they can run the code necessary for fetching the dependencies our
script doesn't fetch.
"Run Gradle to check what dependencies end up being fetched" isn't a
straightforward task. For example, Gradle usually uses Maven
repositories, which have features such as "snapshots", a way to always
use the latest version of a dependency as opposed to a fixed version.
Obviously, this is horrible for reproducibility. Additionally, Gradle
doesn't offer a way to export the list of dependency URLs and hashes (it
does in a way, but it's far from being complete, and as such is useless
for Nixpkgs). Even if it did, it would be annoying to use considering
fetching non-Gradle dependencies in Gradle scripts is commonplace.
That's why the setup hook uses mitm-cache, a program designed for
intercepting all HTTP requests, recording all the files that were
accessed, creating a Nix derivation with all of them, and then allowing
the Gradle derivation to access these files.
## Maven Repositories
(Reference: [Repository
Layout](https://cwiki.apache.org/confluence/display/MAVENOLD/Repository+Layout+-+Final))
Most of Gradle dependencies are fetched from Maven repositories. For
each dependency, Gradle finds the first repo where it can successfully
fetch that dependency, and uses that repo for it. Different repos might
actually return different files for the same artifact because of e.g.
pom normalization. Different repos may be used for the same artifact
even across a single package (for example, if two build scripts define
repositories in a different order).
The artifact metadata is specified in a .pom file, and the artifacts
themselves are typically .jar files. The URL format is as follows:
`<repo>/<group-id>/<artifact-id>/<base-version>/<artifact-id>-<version>[-<classifier>].<ext>`
For example:
- `https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.pom`
- `https://oss.sonatype.org/content/groups/public/com/tobiasdiez/easybind/2.2.1-SNAPSHOT/easybind-2.2.1-20230117.075740-16.pom`
Where:
- `<repo>` is the repo base (`https://repo.maven.apache.org/maven2`)
- `<group-id>` is the group ID with dots replaced with slashes
(`org.slf4j` -> `org/slf4j`)
- `<artifact-id>` is the artifact ID (`slf4j-api`)
- `<base-version>` is the artifact version (`2.0.9` for normal
artifacts, `2.2.1-SNAPSHOT` for snapshots)
- `<version>` is the artifact version - can be either `<base-version>`
or `<version-base>-<timestamp>-<build-num>` (`2.0.9` for normal
artifacts, and either `2.2.1-SNAPSHOT` or `2.2.1-20230117.075740-16`
for snapshots)
- `<version-base>` - `<base-version>` without the `-SNAPSHOT` suffix
- `<timestamp>` - artifact build timestamp in the `YYYYMMDD.HHMMSS`
format (UTC)
- `<build-num>` - a counter that's incremented by 1 for each new
snapshot build
- `<classifier>` is an optional classifier for allowing a single .pom to
refer to multiple .jar files. .pom files don't have classifiers, as
they describe metadata.
- `<ext>` is the extension. .pom
Note that the artifact ID can contain `-`, so you can't extract the
artifact ID and version from just the file name.
Additionally, the files in the repository may have associated signature
files, formed by appending `.asc` to the filename, and hashsum files,
formed by appending `.md5` or `.sha1` to the filename. The signatures
are harmless, but the `.md5`/`.sha1` files are rejected.
The reasoning is as follows - consider two files `a.jar` and `b.jar`,
that have the same hash. Gradle will fetch `a.jar.sha1`, find out that
it hasn't yet downloaded a file with this hash, and then fetch `a.jar`,
and finally download `b.jar.sha1`, locate it in its cache, and then
*not* download `b.jar`. This means `b.jar` won't be stored in the MITM
cache. Then, consider that on a later invocation, the fetching order
changed, whether it was because of running on a different system,
changed behavior after a Gradle update, or any other source of
nondeterminism - `b.jar` is fetched before `a.jar`. Gradle will first
fetch `b.jar.sha1`, not find it in its cache, attempt to fetch `b.jar`,
and fail, as the cache doesn't have that file.
For the same reason, the proxy strips all checksum/etag headers. An
alternative would be to make the proxy remember previous checksums and
etags, but that would complicate the implementation - however, such a
feature can be implemented if necessary. Note that checksum/etag header
stripping is hardcoded, but `.md5/.sha1` file rejection is configured
via CLI arguments.
**Caveat**: Gradle .module files also contain file hashes, in md5, sha1,
sha256, sha512 formats. It has posed no problem as of yet, but it might in
the future. If it does pose problems, the deps derivation code can be
extended to find all checksums in .module files and copy existing files
there if their hash matches.
## Snapshots
Snapshots are a way to publish the very latest, unstable version of a
dependency that constantly changes. Any project that depends on a
snapshot will depend on this rolling version, rather than a fixed
version. It's easy to understand why this is a bad idea for reproducible
builds. Still, they can be dealt with by the logic in `gradle.fetchDeps`
and `gradle.updateDeps`.
First, as you can see above, while normal artifacts have the same
`base-version` and `version`, for snapshots it usually (but not
necessarily) differs.
Second, for figuring out where to download the snapshot, Gradle consults
`maven-metadata.xml`. With that in mind...
## Maven Metadata
(Reference: [Maven
Metadata](https://maven.apache.org/repositories/metadata.html),
[Metadata](https://maven.apache.org/ref/3.9.8/maven-repository-metadata/repository-metadata.html)
Maven metadata files are called `maven-metadata.xml`.
There are three levels of metadata: "G level", "A level", "V level",
representing group, artifact, or version metadata.
G level metadata is currently unsupported. It's only used for Maven
plugins, which Gradle presumably doesn't use.
A level metadata is used for getting the version list for an artifact.
It's an xml with the following items:
- `<groupId>` - group ID
- `<artifactId>` - artifact ID
- `<versioning>`
- `<latest>` - the very latest base version (e.g. `2.2.1-SNAPSHOT`)
- `<release>` - the latest non-snapshot version
- `<versions>` - the version list, each in a `<version>` tag
- `<lastUpdated>` - the metadata update timestamp (UTC,
`YYYYMMDDHHMMSS`)
V level metadata is used for listing the snapshot versions. It has the
following items:
- `<groupId>` - group ID
- `<artifactId>` - artifact ID
- `<versioning>`
- `<lastUpdated>` - the metadata update timestamp (UTC,
`YYYYMMDDHHMMSS`)
- `<snapshot>` - info about the latest snapshot version
- `<timestamp>` - build timestamp (UTC, `YYYYMMDD.HHMMSS`)
- `<buildNumber>` - build number
- `<snapshotVersions>` - the list of all available snapshot file info,
each info is enclosed in a `<snapshotVersion>`
- `<classifier>` - classifier (optional)
- `<extension>` - file extension
- `<value>` - snapshot version (as opposed to base version)
- `<updated>` - snapshot build timestamp (UTC, `YYYYMMDDHHMMSS`)
## Lockfile Format
The mitm-cache lockfile format is described in the [mitm-cache
README](https://github.com/chayleaf/mitm-cache#readme).
The Nixpkgs Gradle lockfile format is more complicated:
```json
{
"!comment": "This is a Nixpkgs Gradle dependency lockfile. For more details, refer to the Gradle section in the Nixpkgs manual.",
"!version": 1,
"https://oss.sonatype.org/content/repositories/snapshots/com/badlogicgames/gdx-controllers": {
"gdx-controllers#gdx-controllers-core/2.2.4-20231021.200112-6/SNAPSHOT": {
"jar": "sha256-Gdz2J1IvDJFktUD2XeGNS0SIrOyym19X/+dCbbbe3/U=",
"pom": "sha256-90QW/Mtz1jbDUhKjdJ88ekhulZR2a7eCaEJoswmeny4="
},
"gdx-controllers-core/2.2.4-SNAPSHOT/maven-metadata": {
"xml": {
"groupId": "com.badlogicgames.gdx-controllers"
}
}
},
"https://repo.maven.apache.org/maven2": {
"com/badlogicgames/gdx#gdx-backend-lwjgl3/1.12.1": {
"jar": "sha256-B3OwjHfBoHcJPFlyy4u2WJuRe4ZF/+tKh7gKsDg41o0=",
"module": "sha256-9O7d2ip5+E6OiwN47WWxC8XqSX/mT+b0iDioCRTTyqc=",
"pom": "sha256-IRSihaCUPC2d0QzB0MVDoOWM1DXjcisTYtnaaxR9SRo="
}
}
}
```
`!comment` is a human-readable description explaining what the file is,
`!version` is the lockfile version (note that while it shares the name
with mitm-cache's `!version`, they don't actually have to be in sync and
can be bumped separately).
The other keys are parts of a URL. Each URL is split into three parts.
They are joined like this: `<part1>/<part2>.<part3>`.
Some URLs may have a `#` in them. In that case, the part after `#` is
parsed as `#<artifact-id>/<version>[/SNAPSHOT][/<classifier>].<ext>` and
expanded into
`<artifact-id>/<base-version>/<artifact-id>-<version>[-<classifier>].<ext>`.
Each URL has a value associated with it. The value may be:
- an SRI hash (string)
- for `maven-metadata.xml` - an attrset containing the parts of the
metadata that can't be generated in Nix code (e.g. `groupId`, which is
challenging to parse from a URL because it's not always possible to
discern where the repo base ends and the group ID begins).
`compress-deps-json.py` converts the JSON from mitm-cache format into
Nixpkgs Gradle lockfile format. `fetch.nix` does the opposite.
## Security Considerations
Lockfiles won't be human-reviewed. They must be tampering-resistant.
That's why it's imperative that nobody can inject their own contents
into the lockfiles.
This is achieved in a very simple way - the `deps.json` only contains
the following:
- `maven-metadata.xml` URLs and small pieces of the contained metadata
(most of it will be generated in Nix, i.e. the area of injection is
minimal, and the parts that aren't generated in Nix are validated).
- artifact/other file URLs and associated hashes (Nix will complain if
the hash doesn't match, and Gradle won't even access the URL if it
doesn't match)
Please be mindful of the above when working on Gradle support for
Nixpkgs.

View File

@@ -0,0 +1,163 @@
import json
import sys
from typing import Dict, Set
# this compresses MITM URL lists with Gradle-specific optimizations
# specifically, it splits each url into up to 3 parts - they will be
# concatenated like part1/part2.part3 or part1.part2
# part3 is simply always the file extension, but part1 and part2 is
# optimized using special heuristics
# additionally, if part2 ends with /a/b/{a}-{b}, the all occurences of
# /{a}/{b}/ are replaced with #
# finally, anything that ends with = is considered SHA256, anything that
# starts with http is considered a redirect URL, anything else is
# considered text
with open(sys.argv[1], "rt") as f:
data: dict = json.load(f)
new_data: Dict[str, Dict[str, Dict[str, dict]]] = {}
for url, info in data.items():
if url == "!version":
continue
ext, base = map(lambda x: x[::-1], url[::-1].split(".", 1))
if base.endswith(".tar"):
base = base[:-4]
ext = "tar." + ext
# special logic for Maven repos
if ext in ["jar", "pom", "module"]:
comps = base.split("/")
if "-" in comps[-1]:
# convert base/name/ver/name-ver into base#name/ver
filename = comps[-1]
name = comps[-3]
basever = comps[-2]
ver = basever
is_snapshot = ver.endswith("-SNAPSHOT")
if is_snapshot:
ver = ver.removesuffix("-SNAPSHOT")
if filename.startswith(f"{name}-{ver}"):
if is_snapshot:
if filename.startswith(f"{name}-{ver}-SNAPSHOT"):
ver += "-SNAPSHOT"
else:
ver += "-".join(
filename.removeprefix(f"{name}-{ver}").split("-")[:3]
)
comp_end = comps[-1].removeprefix(f"{name}-{ver}")
else:
ver, name, comp_end = None, None, None
if name and ver and (not comp_end or comp_end.startswith("-")):
base = "/".join(comps[:-1]) + "/"
base = base.replace(f"/{name}/{basever}/", "#")
base += f"{name}/{ver}"
if is_snapshot:
base += "/SNAPSHOT"
if comp_end:
base += "/" + comp_end[1:]
scheme, rest = base.split("/", 1)
if scheme not in new_data.keys():
new_data[scheme] = {}
if rest not in new_data[scheme].keys():
new_data[scheme][rest] = {}
if "hash" in info.keys():
new_data[scheme][rest][ext] = info["hash"]
elif "text" in info.keys() and ext == "xml":
# nix code in fetch-deps.nix will autogenerate metadata xml files groupId
# is part of the URL, but it can be tricky to parse as we don't know the
# exact repo base, so take it from the xml and pass it to nix
xml = "".join(info["text"].split())
new_data[scheme][rest][ext] = {
"groupId": xml.split("<groupId>")[1].split("</groupId>")[0],
}
if "<release>" in xml:
new_data[scheme][rest][ext]["release"] = xml.split("<release>")[1].split(
"</release>"
)[0]
if "<latest>" in xml:
latest = xml.split("<latest>")[1].split("</latest>")[0]
if latest != new_data[scheme][rest][ext].get("release"):
new_data[scheme][rest][ext]["latest"] = latest
if "<lastUpdated>" in xml:
new_data[scheme][rest][ext]["lastUpdated"] = xml.split("<lastUpdated>")[
1
].split("</lastUpdated>")[0]
else:
raise Exception("Unsupported key: " + repr(info))
# At this point, we have a map by part1 (initially the scheme), part2 (initially a
# slash-separated string without the scheme and with potential # substitution as
# seen above), extension.
# Now, push some segments from "part2" into "part1" like this:
# https # part1
# domain1/b # part2
# domain1/c
# domain2/a
# domain2/c
# ->
# https/domain1 # part1
# b # part2
# c
# https/domain2 # part1
# a # part2
# c
# This helps reduce the lockfile size because a Gradle project will usually use lots
# of files from a single Maven repo
data = new_data
changed = True
while changed:
changed = False
new_data = {}
for part1, info1 in data.items():
starts: Set[str] = set()
# by how many bytes the file size will be increased (roughly)
lose = 0
# by how many bytes the file size will be reduced (roughly)
win = 0
# how many different initial part2 segments there are
count = 0
for part2, info2 in info1.items():
if "/" not in part2:
# can't push a segment from part2 into part1
count = 0
break
st = part2.split("/", 1)[0]
if st not in starts:
lose += len(st) + 1
count += 1
starts.add(st)
win += len(st) + 1
if count == 0:
new_data[part1] = info1
continue
# only allow pushing part2 segments into path1 if *either*:
# - the domain isn't yet part of part1
# - the initial part2 segment is always the same
if count != 1 and "." in part1:
new_data[part1] = info1
continue
# some heuristics that may or may not work well (originally this was
# used when the above if wasn't here, but perhaps it's useless now)
lose += (count - 1) * max(0, len(part1) - 4)
if win > lose or ("." not in part1 and win >= lose):
changed = True
for part2, info2 in info1.items():
st, part3 = part2.split("/", 1)
new_part1 = part1 + "/" + st
if new_part1 not in new_data.keys():
new_data[new_part1] = {}
new_data[new_part1][part3] = info2
else:
new_data[part1] = info1
data = new_data
new_data["!comment"] = "This is a nixpkgs Gradle dependency lockfile. For more details, refer to the Gradle section in the nixpkgs manual." # type: ignore
new_data["!version"] = 1 # type: ignore
with open(sys.argv[2], "wt") as f:
json.dump(new_data, f, sort_keys=True, indent=1)
f.write("\n")

View File

@@ -0,0 +1,349 @@
{
callPackage,
jdk11,
jdk17,
jdk21,
}:
let
wrapGradle =
{
lib,
callPackage,
mitm-cache,
replaceVars,
symlinkJoin,
concatTextFile,
makeSetupHook,
nix-update-script,
# This is the "current" version of gradle in nixpkgs.
# Used to define the update script.
gradle-unwrapped,
runCommand,
}:
this-gradle-unwrapped:
lib.makeOverridable (
args:
let
gradle = this-gradle-unwrapped.override args;
in
symlinkJoin {
pname = "gradle";
inherit (gradle) version;
paths = [
(makeSetupHook { name = "gradle-setup-hook"; } (concatTextFile {
name = "setup-hook.sh";
files = [
(mitm-cache.setupHook)
(replaceVars ./setup-hook.sh {
# jdk used for keytool
inherit (gradle) jdk;
init_script = "${./init-build.gradle}";
})
];
}))
gradle
mitm-cache
];
passthru = {
fetchDeps = callPackage ./fetch-deps.nix { inherit mitm-cache; };
inherit (gradle) jdk;
unwrapped = gradle;
tests = {
toolchains =
let
javaVersion = lib.getVersion jdk11;
javaMajorVersion = lib.versions.major javaVersion;
in
runCommand "detects-toolchains-from-nix-env"
{
# Use JDKs that are not the default for any of the gradle versions
nativeBuildInputs = [
(gradle.override {
javaToolchains = [
jdk11
];
})
];
src = ./tests/toolchains;
}
''
cp -a $src/* .
substituteInPlace ./build.gradle --replace-fail '@JAVA_VERSION@' '${javaMajorVersion}'
env GRADLE_USER_HOME=$TMPDIR/gradle org.gradle.native.dir=$TMPDIR/native \
gradle run --no-daemon --quiet --console plain > $out
actual="$(<$out)"
if [[ "${javaVersion}" != "$actual"* ]]; then
echo "Error: Expected '${javaVersion}', to start with '$actual'" >&2
exit 1
fi
'';
}
// gradle.tests;
}
// lib.optionalAttrs (this-gradle-unwrapped == gradle-unwrapped) {
updateScript = nix-update-script {
extraArgs = [
"--url=https://github.com/gradle/gradle"
# Gradles .0 releases are tagged as `vX.Y.0`, but the actual
# release version omits the `.0`, so well wanto to only capture
# the version up but not including the the trailing `.0`.
"--version-regex=^v(\\d+\\.\\d+(?:\\.[1-9]\\d?)?)(\\.0)?$"
];
};
};
meta = gradle.meta // {
# prefer normal gradle/mitm-cache over this wrapper, this wrapper only provides the setup hook
# and passthru
priority = (gradle.meta.priority or lib.meta.defaultPriority) + 1;
};
}
) { };
gen =
{
version,
hash,
# The default JDK/JRE that will be used for derived Gradle packages.
# A current LTS version of a JDK is a good choice.
defaultJava,
# The platforms supported by this Gradle package.
# Gradle Native-Platform ships some binaries that
# are compatible only with specific platforms.
# As of 2022-04 this affects platform compatibility
# of multiple Gradle releases, so this is used as default.
# See https://github.com/gradle/native-platform#supported-platforms
platforms ? [
"aarch64-darwin"
"aarch64-linux"
"i686-windows"
"x86_64-cygwin"
"x86_64-darwin"
"x86_64-linux"
"x86_64-windows"
],
# Extra attributes to be merged into the resulting derivation's
# meta attribute.
meta ? { },
}@genArgs:
{
lib,
stdenv,
fetchurl,
callPackage,
makeWrapper,
unzip,
ncurses5,
ncurses6,
udev,
testers,
runCommand,
writeText,
autoPatchelfHook,
buildPackages,
# The JDK/JRE used for running Gradle.
java ? defaultJava,
# Additional JDK/JREs to be registered as toolchains.
# See https://docs.gradle.org/current/userguide/toolchains.html
javaToolchains ? [ ],
}:
stdenv.mkDerivation (finalAttrs: {
pname = "gradle";
inherit version;
src = fetchurl {
inherit hash;
url = "https://services.gradle.org/distributions/gradle-${version}-bin.zip";
};
dontBuild = true;
nativeBuildInputs = [
makeWrapper
unzip
]
++ lib.optionals stdenv.hostPlatform.isLinux [
autoPatchelfHook
];
buildInputs = [
stdenv.cc.cc
ncurses5
ncurses6
];
# We only need to patchelf some libs embedded in JARs.
dontAutoPatchelf = true;
installPhase =
with builtins;
let
# set toolchains via installations.path property in gradle.properties.
# See https://docs.gradle.org/current/userguide/toolchains.html#sec:custom_loc
toolchainPaths = "org.gradle.java.installations.paths=${concatStringsSep "," javaToolchains}";
jnaLibraryPath = if stdenv.hostPlatform.isLinux then lib.makeLibraryPath [ udev ] else "";
jnaFlag =
if stdenv.hostPlatform.isLinux then "--add-flags \"-Djna.library.path=${jnaLibraryPath}\"" else "";
in
''
mkdir -pv $out/lib/gradle/
cp -rv lib/ $out/lib/gradle/
gradle_launcher_jar=$(echo $out/lib/gradle/lib/gradle-launcher-*.jar)
test -f $gradle_launcher_jar
makeWrapper ${java}/bin/java $out/bin/gradle \
--set JAVA_HOME ${java} \
${jnaFlag} \
--add-flags "-classpath $gradle_launcher_jar org.gradle.launcher.GradleMain"
echo "${toolchainPaths}" > $out/lib/gradle/gradle.properties
'';
dontFixup = !stdenv.hostPlatform.isLinux;
fixupPhase =
let
arch = if stdenv.hostPlatform.is64bit then "amd64" else "i386";
newFileEvents = toString (lib.versionAtLeast version "8.12");
in
''
# get the correct jar executable for cross
export PATH="${buildPackages.jdk}/bin:$PATH"
. ${./patching.sh}
nativeVersion="$(extractVersion native-platform $out/lib/gradle/lib/native-platform-*.jar)"
for variant in "" "-ncurses5" "-ncurses6"; do
autoPatchelfInJar \
$out/lib/gradle/lib/native-platform-linux-${arch}$variant-''${nativeVersion}.jar \
"${lib.getLib stdenv.cc.cc}/lib64:${
lib.makeLibraryPath [
stdenv.cc.cc
ncurses5
ncurses6
]
}"
done
# The file-events library _seems_ to follow the native-platform version, but
# we wont assume that.
if [ -n "${newFileEvents}" ]; then
fileEventsVersion="$(extractVersion gradle-fileevents $out/lib/gradle/lib/gradle-fileevents-*.jar)"
autoPatchelfInJar \
$out/lib/gradle/lib/gradle-fileevents-''${fileEventsVersion}.jar \
"${lib.getLib stdenv.cc.cc}/lib64:${lib.makeLibraryPath [ stdenv.cc.cc ]}"
else
fileEventsVersion="$(extractVersion file-events $out/lib/gradle/lib/file-events-*.jar)"
autoPatchelfInJar \
$out/lib/gradle/lib/file-events-linux-${arch}-''${fileEventsVersion}.jar \
"${lib.getLib stdenv.cc.cc}/lib64:${lib.makeLibraryPath [ stdenv.cc.cc ]}"
fi
# The scanner doesn't pick up the runtime dependency in the jar.
# Manually add a reference where it will be found.
mkdir $out/nix-support
echo ${stdenv.cc.cc} > $out/nix-support/manual-runtime-dependencies
# Gradle will refuse to start without _both_ 5 and 6 versions of ncurses.
echo ${ncurses5} >> $out/nix-support/manual-runtime-dependencies
echo ${ncurses6} >> $out/nix-support/manual-runtime-dependencies
${lib.optionalString stdenv.hostPlatform.isLinux "echo ${udev} >> $out/nix-support/manual-runtime-dependencies"}
'';
passthru.tests = {
version = testers.testVersion {
package = finalAttrs.finalPackage;
command = ''
env GRADLE_USER_HOME=$TMPDIR/gradle org.gradle.native.dir=$TMPDIR/native \
gradle --version
'';
};
java-application = testers.testEqualContents {
assertion = "can build and run a trivial Java application";
expected = writeText "expected" "hello\n";
actual =
runCommand "actual"
{
nativeBuildInputs = [ finalAttrs.finalPackage ];
src = ./tests/java-application;
}
''
cp -a $src/* .
env GRADLE_USER_HOME=$TMPDIR/gradle org.gradle.native.dir=$TMPDIR/native \
gradle run --no-daemon --quiet --console plain > $out
'';
};
};
passthru.jdk = defaultJava;
passthru.wrapped = callPackage wrapGradle { } (gen' genArgs);
meta =
with lib;
{
inherit platforms;
description = "Enterprise-grade build system";
longDescription = ''
Gradle is a build system which offers you ease, power and freedom.
You can choose the balance for yourself. It has powerful multi-project
build support. It has a layer on top of Ivy that provides a
build-by-convention integration for Ivy. It gives you always the choice
between the flexibility of Ant and the convenience of a
build-by-convention behavior.
'';
homepage = "https://www.gradle.org/";
changelog = "https://docs.gradle.org/${version}/release-notes.html";
downloadPage = "https://gradle.org/next-steps/?version=${version}";
sourceProvenance = with sourceTypes; [
binaryBytecode
binaryNativeCode
];
license = licenses.asl20;
maintainers = with maintainers; [
britter
liff
lorenzleutgeb
];
teams = [ lib.teams.java ];
mainProgram = "gradle";
}
// meta;
});
# Calls the generated Gradle package with default arguments.
gen' = args: callPackage (gen args) { };
in
rec {
# NOTE: Default JDKs that are hardcoded below must be LTS versions
# and respect the compatibility matrix at
# https://docs.gradle.org/current/userguide/compatibility.html
gradle_9 = gen' {
version = "9.1.0";
hash = "sha256-oX3dhaJran9d23H/iwX8UQTAICxuZHgkKXkMkzaGyAY=";
defaultJava = jdk21;
};
gradle_8 = gen' {
version = "8.14.3";
hash = "sha256-vXEQIhNJMGCVbsIp2Ua+7lcVjb2J0OYrkbyg+ixfNTE=";
defaultJava = jdk21;
};
gradle_7 = gen' {
version = "7.6.6";
hash = "sha256-Zz2XdvMDvHBI/DMp0jLW6/EFGweJO9nRFhb62ahnO+A=";
defaultJava = jdk17;
};
# Default version of Gradle in nixpkgs.
gradle = gradle_8;
}

View File

@@ -0,0 +1,277 @@
{
mitm-cache,
lib,
pkgs,
stdenv,
callPackage,
}:
let
getPkg = attrPath: lib.getAttrFromPath (lib.splitString "." (toString attrPath)) pkgs;
in
# the derivation to fetch/update deps for
{
pkg ? getPkg attrPath,
pname ? null,
attrPath ? pname,
# bwrap flags for the update script (this will be put in bash as-is)
# this is relevant for downstream users
bwrapFlags ? "--ro-bind \"$PWD\" \"$PWD\"",
# deps path (relative to the package directory, or absolute)
data,
# redirect stdout to stderr to allow the update script to be used with update script combinators
silent ? true,
useBwrap ? stdenv.hostPlatform.isLinux,
}@attrs:
let
data' =
removeAttrs
(
if builtins.isPath data then
lib.importJSON data
else if builtins.isString data then
lib.importJSON "${dirOf pkg.meta.position}/${data}"
else
data
)
[
"!comment"
"!version"
];
parseArtifactUrl =
url:
let
extension = lib.last (lib.splitString "." url);
splitUrl = lib.splitString "/" url;
artifactId = builtins.elemAt splitUrl (builtins.length splitUrl - 3);
baseVer = builtins.elemAt splitUrl (builtins.length splitUrl - 2);
filename = builtins.elemAt splitUrl (builtins.length splitUrl - 1);
filenameNoExt = lib.removeSuffix ".${extension}" filename;
verCls = lib.removePrefix "${artifactId}-" filenameNoExt;
in
rec {
inherit
artifactId
baseVer
filename
extension
;
isSnapshot = lib.hasSuffix "-SNAPSHOT" baseVer;
version =
if isSnapshot && !lib.hasPrefix "SNAPSHOT" verCls then
builtins.concatStringsSep "-" (lib.take 3 (lib.splitString "-" verCls))
else
baseVer;
classifier = if verCls == version then null else lib.removePrefix "${version}-" verCls;
# for snapshots
timestamp = builtins.elemAt (lib.splitString "-" version) 1;
buildNum = builtins.elemAt (lib.splitString "-" version) 2;
};
parseMetadataUrl =
url:
let
xmlBase = lib.removeSuffix "/maven-metadata.xml" url;
vMeta = lib.hasSuffix "-SNAPSHOT" xmlBase;
splitBase = lib.splitString "/" xmlBase;
in
if vMeta then
{
vMeta = true;
baseVer = builtins.elemAt splitBase (builtins.length splitBase - 1);
artifactId = builtins.elemAt splitBase (builtins.length splitBase - 2);
}
else
{
vMeta = false;
baseVer = null;
artifactId = builtins.elemAt splitBase (builtins.length splitBase - 1);
};
extractHashArtifact =
afterHash:
let
nameVer = builtins.match "([^/]*)/([^/]*)(/SNAPSHOT)?(/.*)?" afterHash;
artifactId = builtins.elemAt nameVer 0;
version = builtins.elemAt nameVer 1;
isSnapshot = builtins.elemAt nameVer 2 != null;
cls = builtins.elemAt nameVer 3;
in
rec {
inherit artifactId version isSnapshot;
baseVer =
if !isSnapshot then
version
else
builtins.head (builtins.match "(.*)-([^-]*)-([^-]*)" version) + "-SNAPSHOT";
classifier = if cls == null then null else lib.removePrefix "/" cls;
clsSuf = if classifier == null then "" else "-${classifier}";
};
# replace base#name/ver with base/name/ver/name-ver
decompressNameVer =
prefix:
let
splitHash = lib.splitString "#" (builtins.concatStringsSep "/" prefix);
inherit (extractHashArtifact (lib.last splitHash))
artifactId
baseVer
version
clsSuf
;
in
if builtins.length splitHash == 1 then
builtins.head splitHash
else
builtins.concatStringsSep "/${artifactId}/${baseVer}/" (
lib.init splitHash ++ [ "${artifactId}-${version}${clsSuf}" ]
);
# `visit` all elements in attrs and merge into a set
# attrs will be passed as parent1, parent1 will be passed as parent2
visitAttrs =
parent1: prefix: attrs:
builtins.foldl' (a: b: a // b) { } (lib.mapAttrsToList (visit parent1 attrs prefix) attrs);
# convert a compressed deps.json into an uncompressed json used for mitm-cache.fetch
visit =
parent2: parent1: prefix: k: v:
# groupId being present means this is a metadata xml "leaf" and we shouldn't descend further
if builtins.isAttrs v && !v ? groupId then
visitAttrs parent1 (prefix ++ [ k ]) v
else
let
url = "${decompressNameVer prefix}.${k}";
in
{
${url} =
if builtins.isString v then
{ hash = v; }
else
{
text =
let
xmlBase = lib.removeSuffix "/maven-metadata.xml" url;
meta = parseMetadataUrl url // v;
inherit (meta)
groupId
vMeta
artifactId
baseVer
;
fileList = builtins.filter (x: lib.hasPrefix xmlBase x && x != url) (builtins.attrNames finalData);
jarPomList = map parseArtifactUrl fileList;
sortByVersion = a: b: (builtins.compareVersions a.version b.version) < 0;
sortedJarPomList = lib.sort sortByVersion jarPomList;
uniqueVersionFiles = map ({ i, x }: x) (
builtins.filter (
{ i, x }: i == 0 || (builtins.elemAt sortedJarPomList (i - 1)).version != x.version
) (lib.imap0 (i: x: { inherit i x; }) sortedJarPomList)
);
uniqueVersions' = map (x: x.version) uniqueVersionFiles;
releaseVersions = map (x: x.version) (builtins.filter (x: !x.isSnapshot) uniqueVersionFiles);
latestVer = v.latest or v.release or (lib.last uniqueVersions');
releaseVer = v.release or (lib.last releaseVersions);
# The very latest version isn't necessarily used by Gradle, so it may not be present in the MITM data.
# In order to generate better metadata xml, if the latest version is known but wasn't fetched by Gradle,
# add it anyway.
uniqueVersions =
uniqueVersions'
++ lib.optional (!builtins.elem releaseVer uniqueVersions') releaseVer
++ lib.optional (!builtins.elem latestVer uniqueVersions' && releaseVer != latestVer) latestVer;
lastUpdated =
v.lastUpdated
or (if vMeta then builtins.replaceStrings [ "." ] [ "" ] snapshotTs else "20240101123456");
# the following are only used for snapshots
snapshotTsAndNum = lib.splitString "-" latestVer;
snapshotTs = builtins.elemAt snapshotTsAndNum 1;
snapshotNum = lib.last snapshotTsAndNum;
indent = x: s: builtins.concatStringsSep "\n" (map (s: x + s) (lib.splitString "\n" s));
containsSpecialXmlChars = s: builtins.match ''.*[<>"'&].*'' s != null;
in
# make sure all user-provided data is safe
assert lib.hasInfix "${builtins.replaceStrings [ "." ] [ "/" ] groupId}/${artifactId}" url;
assert !containsSpecialXmlChars groupId;
assert !containsSpecialXmlChars lastUpdated;
if vMeta then
''
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${baseVer}</version>
<versioning>
<snapshot>
<timestamp>${snapshotTs}</timestamp>
<buildNumber>${snapshotNum}</buildNumber>
</snapshot>
<lastUpdated>${lastUpdated}</lastUpdated>
<snapshotVersions>
${builtins.concatStringsSep "\n" (
map (
x:
indent " " ''
<snapshotVersion>${
lib.optionalString (x.classifier != null) "\n <classifier>${x.classifier}</classifier>"
}
<extension>${x.extension}</extension>
<value>${x.version}</value>
<updated>${builtins.replaceStrings [ "." ] [ "" ] x.timestamp}</updated>
</snapshotVersion>''
) sortedJarPomList
)}
</snapshotVersions>
</versioning>
</metadata>
''
else
assert !containsSpecialXmlChars latestVer;
assert !containsSpecialXmlChars releaseVer;
''
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<versioning>
<latest>${latestVer}</latest>
<release>${releaseVer}</release>
<versions>
${builtins.concatStringsSep "\n" (map (x: " <version>${x}</version>") uniqueVersions)}
</versions>
<lastUpdated>${lastUpdated}</lastUpdated>
</versioning>
</metadata>
'';
};
};
finalData = visitAttrs { } [ ] data';
in
mitm-cache.fetch {
name = "${pkg.pname or pkg.name}-deps";
data = finalData // {
"!version" = 1;
};
passthru = lib.optionalAttrs (!builtins.isAttrs data) {
updateScript = callPackage ./update-deps.nix { } {
inherit
pkg
pname
attrPath
bwrapFlags
data
silent
useBwrap
;
};
};
}

View File

@@ -0,0 +1,8 @@
gradle.projectsLoaded {
rootProject.allprojects {
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
}
}

View File

@@ -0,0 +1,10 @@
gradle.projectsLoaded {
rootProject.allprojects {
task nixDownloadDeps {
doLast {
configurations.findAll{it.canBeResolved}.each{it.resolve()}
buildscript.configurations.findAll{it.canBeResolved}.each{it.resolve()}
}
}
}
}

View File

@@ -0,0 +1,29 @@
extractVersion() {
local jar version
local prefix="$1"
shift
local candidates="$@"
jar="$(basename -a $candidates | sort | head -n1)"
version="${jar#$prefix-}"
echo "${version%.jar}"
}
autoPatchelfInJar() {
local file="$1" rpath="$2"
local work
work="$(mktemp -dt patching.XXXXXXXXXX)"
pushd "$work"
jar xf "$file"
rm "$file"
autoPatchelf -- .
jar cf "$file" .
popd
}

View File

@@ -0,0 +1,72 @@
gradleConfigureHook() {
if [ -z "${GRADLE_USER_HOME-}" ]; then
GRADLE_USER_HOME="$(mktemp -d)"
fi
export GRADLE_USER_HOME
export TERM=dumb
gradleFlagsArray+=(--no-daemon --console plain --init-script "${gradleInitScript:-@init_script@}")
if [ -n "${MITM_CACHE_CA-}" ]; then
if [ -z "${MITM_CACHE_KEYSTORE-}" ]; then
MITM_CACHE_KEYSTORE="$MITM_CACHE_CERT_DIR/keystore"
MITM_CACHE_KS_PWD="$(head -c10 /dev/random | base32)"
echo y | @jdk@/bin/keytool -importcert -file "$MITM_CACHE_CA" -alias alias -keystore "$MITM_CACHE_KEYSTORE" -storepass "$MITM_CACHE_KS_PWD"
fi
gradleFlagsArray+=(-Dhttp.proxyHost="$MITM_CACHE_HOST" -Dhttp.proxyPort="$MITM_CACHE_PORT")
gradleFlagsArray+=(-Dhttps.proxyHost="$MITM_CACHE_HOST" -Dhttps.proxyPort="$MITM_CACHE_PORT")
gradleFlagsArray+=(-Djavax.net.ssl.trustStore="$MITM_CACHE_KEYSTORE" -Djavax.net.ssl.trustStorePassword="$MITM_CACHE_KS_PWD")
else
gradleFlagsArray+=(--offline)
fi
if ! [[ -v enableParallelBuilding ]]; then
enableParallelBuilding=1
fi
if ! [[ -v enableParallelChecking ]]; then
enableParallelChecking=1
fi
if ! [[ -v enableParallelUpdating ]]; then
enableParallelUpdating=1
fi
}
gradle() {
local flagsArray=()
concatTo flagsArray gradleFlags gradleFlagsArray
command gradle "${flagsArray[@]}" "$@"
}
gradleBuildPhase() {
runHook preBuild
gradle ${enableParallelBuilding:+--parallel} ${gradleBuildTask:-assemble}
runHook postBuild
}
gradleCheckPhase() {
runHook preCheck
gradle ${enableParallelChecking:+--parallel} ${gradleCheckTask:-test}
runHook postCheck
}
gradleUpdateScript() {
runHook preBuild
runHook preGradleUpdate
gradle ${enableParallelUpdating:+--parallel} ${gradleUpdateTask:-nixDownloadDeps}
runHook postGradleUpdate
}
if [ -z "${dontUseGradleConfigure-}" ]; then
preConfigureHooks+=(gradleConfigureHook)
fi
if [ -z "${dontUseGradleBuild-}" ] && [ -z "${buildPhase-}" ]; then
buildPhase=gradleBuildPhase
fi
if [ -z "${dontUseGradleCheck-}" ] && [ -z "${checkPhase-}" ]; then
checkPhase=gradleCheckPhase
fi

View File

@@ -0,0 +1,7 @@
plugins {
id('application')
}
application {
mainClass = 'Main'
}

View File

@@ -0,0 +1,5 @@
public class Main {
public static void main(String[] args) {
System.out.println("hello");
}
}

View File

@@ -0,0 +1,11 @@
plugins {
id('application')
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(@JAVA_VERSION@))
}
application {
mainClass = 'Main'
}

View File

@@ -0,0 +1,5 @@
public class Main {
public static void main(String[] args) {
System.out.println(System.getProperty("java.version"));
}
}

View File

@@ -0,0 +1,157 @@
{
lib,
runtimeShell,
srcOnly,
stdenvNoCC,
writeTextFile,
writeShellScript,
path,
bubblewrap,
coreutils,
curl,
jq,
mitm-cache,
nix,
openssl,
procps,
python3,
}:
lib.makeOverridable (
{
pkg,
pname,
attrPath,
bwrapFlags,
data,
silent,
useBwrap,
}:
let
keep = [
"MITM_CACHE_HOST"
"MITM_CACHE_PORT"
"MITM_CACHE_ADDRESS"
"MITM_CACHE_CA"
"MITM_CACHE_CERT_DIR"
];
gradleScript = writeShellScript "gradle-commands.sh" ''
set -eo pipefail
export http_proxy="$MITM_CACHE_ADDRESS"
export https_proxy="$MITM_CACHE_ADDRESS"
export SSL_CERT_FILE="$MITM_CACHE_CA"
export NIX_SSL_CERT_FILE="$MITM_CACHE_CA"
export GRADLE_USER_HOME="$(${coreutils}/bin/mktemp -d)"
export IN_GRADLE_UPDATE_DEPS=1
trap "${coreutils}/bin/rm -rf '$GRADLE_USER_HOME'" SIGINT SIGTERM ERR EXIT
cd "$(${coreutils}/bin/mktemp -d)"
${coreutils}/bin/mkdir out
export out="$PWD/out"
trap "${coreutils}/bin/rm -rf '$PWD'" SIGINT SIGTERM ERR EXIT
source "$stdenv/setup"
phases="''${prePhases[*]:-} unpackPhase patchPhase ''${preConfigurePhases[*]:-} configurePhase gradleUpdateScript" genericBuild
'';
source = srcOnly (
pkg.overrideAttrs (old: {
mitmCache = "";
gradleInitScript = ./init-deps.gradle;
stdenv = old.stdenv or stdenvNoCC;
})
);
sourceDrvPath = builtins.unsafeDiscardOutputDependency source.drvPath;
nixShellKeep = lib.concatMapStringsSep " " (x: "--keep ${x}") keep;
in
writeTextFile {
name = "fetch-deps.sh";
executable = true;
# see pkgs/common-updater/combinators.nix
derivationArgs.passthru = {
supportedFeatures = lib.optional silent "silent";
}
// lib.optionalAttrs (attrPath != null) { inherit attrPath; };
text = ''
#!${runtimeShell}
set -eo pipefail
export PATH="${
lib.makeBinPath (
[
coreutils
curl
jq
mitm-cache
openssl
procps
python3.pkgs.ephemeral-port-reserve
]
++ lib.optional useBwrap bubblewrap
)
}:$PATH"
outPath="${
# if this is an absolute path in nix store, use path relative to the store path
if lib.hasPrefix "${builtins.storeDir}/" (toString data) then
builtins.concatStringsSep "/" (
lib.drop 1 (lib.splitString "/" (lib.removePrefix "${builtins.storeDir}/" (toString data)))
)
# if this is an absolute path anywhere else, just use that path
else if lib.hasPrefix "/" (toString data) then
toString data
# otherwise, use a path relative to the package
else
"${dirOf pkg.meta.position}/${data}"
}"
pushd "$(mktemp -d)" >/dev/null
MITM_CACHE_DIR="$PWD"
trap "rm -rf '$MITM_CACHE_DIR'" SIGINT SIGTERM ERR EXIT
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 1 -out ca.cer -subj "/C=AL/ST=a/L=a/O=a/OU=a/CN=example.org"
export MITM_CACHE_HOST=127.0.0.1
export MITM_CACHE_PORT="''${mitmCachePort:-$(ephemeral-port-reserve "$MITM_CACHE_HOST")}"
export MITM_CACHE_ADDRESS="$MITM_CACHE_HOST:$MITM_CACHE_PORT"
# forget all redirects - this makes the lockfiles predictable
# not only does this strip CDN URLs, but it also improves security - since the redirects aren't
# stored in the lockfile, a malicious actor can't change the redirect URL stored in the lockfile
mitm-cache \
-l"$MITM_CACHE_ADDRESS" \
record \
--reject '\.(md5|sha(1|256|512:?):?)$' \
--forget-redirects-from '.*' \
--record-text '/maven-metadata\.xml$' >/dev/null 2>/dev/null &
MITM_CACHE_PID="$!"
# wait for mitm-cache to fully start
for i in {0..20}; do
kill -0 "$MITM_CACHE_PID" 2>/dev/null || (echo "Failed to start mitm-cache" && exit 1)
curl -so/dev/null "$MITM_CACHE_ADDRESS" && break
[[ "$i" -eq 20 ]] && (echo "Failed to start mitm-cache" && exit 1)
sleep 0.5
done
trap "kill '$MITM_CACHE_PID'" SIGINT SIGTERM ERR EXIT
export MITM_CACHE_CERT_DIR="$PWD"
export MITM_CACHE_CA="$MITM_CACHE_CERT_DIR/ca.cer"
popd >/dev/null
useBwrap="''${USE_BWRAP:-${toString useBwrap}}"
if [ -n "$useBwrap" ]; then
# bwrap isn't necessary, it's only used to prevent messy build scripts from touching ~
bwrap \
--unshare-all --share-net --clearenv --chdir / --setenv HOME /homeless-shelter \
--tmpfs /home --bind /tmp /tmp --ro-bind /nix /nix --ro-bind /run /run --proc /proc --dev /dev \
--ro-bind ${toString path} ${toString path} --bind "$MITM_CACHE_CERT_DIR" "$MITM_CACHE_CERT_DIR" \
${builtins.concatStringsSep " " (map (x: "--setenv ${x} \"\$${x}\"") keep)} \
--setenv NIX_BUILD_SHELL ${runtimeShell} ${bwrapFlags} ''${BWRAP_FLAGS:-} \
-- ${nix}/bin/nix-shell --pure --run ${gradleScript} ${nixShellKeep} ${sourceDrvPath}
else
NIX_BUILD_SHELL=${runtimeShell} nix-shell --pure --run ${gradleScript} ${nixShellKeep} ${sourceDrvPath}
fi${lib.optionalString silent " >&2"}
kill -s SIGINT "$MITM_CACHE_PID"
for i in {0..20}; do
# check for valid json
if jq -e 1 "$MITM_CACHE_DIR/out.json" >/dev/null 2>/dev/null; then
exec ${python3.interpreter} ${./compress-deps-json.py} "$MITM_CACHE_DIR/out.json" "$outPath"
fi
sleep 1
done
exit 1
'';
}
)

View File

@@ -0,0 +1,57 @@
{
stdenv,
fetchFromGitHub,
nix-update-source,
lib,
python3,
which,
runtimeShell,
pylint,
}:
stdenv.mkDerivation rec {
version = "0.9.2";
src = fetchFromGitHub {
hash = "sha256-bV5HauM0xmRI/9Pxp1cYLPLA8PbFvPER2y4mAMmgchs=";
owner = "timbertson";
repo = "gup";
rev = "version-${version}";
};
pname = "gup";
nativeBuildInputs = [
python3
which
pylint
];
buildInputs = [ python3 ];
strictDeps = true;
buildPhase = "make python";
installPhase = ''
mkdir $out
cp -r python/bin $out/bin
'';
passthru.updateScript = [
runtimeShell
"-c"
''
set -e
echo
cd ${toString ./.}
${nix-update-source}/bin/nix-update-source \
--prompt version \
--replace-attr version \
--set owner timbertson \
--set repo gup \
--set type fetchFromGitHub \
--set rev 'version-{version}' \
--nix-literal rev 'version-''${version}'\
--modify-nix default.nix
''
];
meta = {
inherit (src.meta) homepage;
description = "Better make, inspired by djb's redo";
license = lib.licenses.lgpl2Plus;
maintainers = [ lib.maintainers.timbertson ];
platforms = lib.platforms.all;
};
}

View File

@@ -0,0 +1,43 @@
{
lib,
stdenv,
fetchFromGitHub,
qmake,
qtbase,
qtscript,
}:
stdenv.mkDerivation rec {
pname = "qbs";
version = "1.24.1";
src = fetchFromGitHub {
owner = "qbs";
repo = "qbs";
rev = "v${version}";
sha256 = "sha256-nL7UZh29Oecu3RvXYg5xsin2IvPWpApleLH37sEdSAI=";
};
nativeBuildInputs = [ qmake ];
dontWrapQtApps = true;
qmakeFlags = [
"QBS_INSTALL_PREFIX=$(out)"
"qbs.pro"
];
buildInputs = [
qtbase
qtscript
];
meta = with lib; {
description = "Tool that helps simplify the build process for developing projects across multiple platforms";
homepage = "https://wiki.qt.io/Qbs";
license = licenses.lgpl3;
maintainers = [ ];
platforms = platforms.linux;
};
}

View File

@@ -0,0 +1,46 @@
{
lib,
stdenv,
fetchFromGitHub,
erlang,
}:
stdenv.mkDerivation rec {
pname = "rebar";
version = "2.6.4";
src = fetchFromGitHub {
owner = "rebar";
repo = "rebar";
rev = version;
sha256 = "sha256-okvG7X2uHtZ1p+HUoFOmslrWvYjk0QWBAvAMAW2E40c=";
};
buildInputs = [ erlang ];
buildPhase = "escript bootstrap";
installPhase = ''
mkdir -p $out/bin
cp rebar $out/bin/rebar
'';
meta = {
homepage = "https://github.com/rebar/rebar";
description = "Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases";
mainProgram = "rebar";
longDescription = ''
rebar is a self-contained Erlang script, so it's easy to
distribute or even embed directly in a project. Where possible,
rebar uses standard Erlang/OTP conventions for project
structures, thus minimizing the amount of build configuration
work. rebar also provides dependency management, enabling
application writers to easily re-use common libraries from a
variety of locations (git, hg, etc).
'';
platforms = lib.platforms.unix;
license = lib.licenses.asl20;
teams = [ lib.teams.beam ];
};
}

View File

@@ -0,0 +1,179 @@
{
lib,
stdenv,
fetchFromGitHub,
fetchgit,
fetchHex,
erlang,
makeWrapper,
writeScript,
common-updater-scripts,
coreutils,
git,
gnused,
nix,
nixfmt,
rebar3-nix,
}:
let
version = "3.25.1";
owner = "erlang";
deps = import ./rebar-deps.nix { inherit fetchFromGitHub fetchgit fetchHex; };
rebar3 = stdenv.mkDerivation rec {
pname = "rebar3";
inherit version erlang;
# How to obtain `sha256`:
# nix-prefetch-url --unpack https://github.com/erlang/rebar3/archive/${version}.tar.gz
src = fetchFromGitHub {
inherit owner;
repo = pname;
rev = version;
sha256 = "Wpg8MDVwum/cBpwbcY3Cjt2JkuQHEp7wxbZKgyP6crc=";
};
buildInputs = [ erlang ];
postPatch = ''
mkdir -p _checkouts _build/default/lib/
${toString (
lib.mapAttrsToList (k: v: ''
cp -R --no-preserve=mode ${v} _checkouts/${k}
'') deps
)}
# Bootstrap script expects the dependencies in _build/default/lib
# TODO: Make it accept checkouts?
for i in _checkouts/* ; do
ln -s $(pwd)/$i $(pwd)/_build/default/lib/
done
'';
buildPhase = ''
HOME=. escript bootstrap
'';
checkPhase = ''
HOME=. escript ./rebar3 ct
'';
doCheck = true;
installPhase = ''
mkdir -p $out/bin
cp rebar3 $out/bin/rebar3
'';
meta = {
homepage = "https://github.com/rebar/rebar3";
description = "Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases";
mainProgram = "rebar3";
longDescription = ''
rebar is a self-contained Erlang script, so it's easy to distribute or
even embed directly in a project. Where possible, rebar uses standard
Erlang/OTP conventions for project structures, thus minimizing the amount
of build configuration work. rebar also provides dependency management,
enabling application writers to easily re-use common libraries from a
variety of locations (hex.pm, git, hg, and so on).
'';
platforms = lib.platforms.unix;
teams = [ lib.teams.beam ];
license = lib.licenses.asl20;
};
passthru.updateScript = writeScript "update.sh" ''
#!${stdenv.shell}
set -ox errexit
PATH=${
lib.makeBinPath [
common-updater-scripts
coreutils
git
gnused
nix
nixfmt
(rebar3WithPlugins { globalPlugins = [ rebar3-nix ]; })
]
}
latest=$(list-git-tags | sed -n '/[\d\.]\+/p' | sort -V | tail -1)
if [ "$latest" != "${version}" ]; then
nixpkgs="$(git rev-parse --show-toplevel)"
nix_path="$nixpkgs/pkgs/development/tools/build-managers/rebar3"
update-source-version rebar3 "$latest" --version-key=version --print-changes --file="$nix_path/default.nix"
tmpdir=$(mktemp -d)
cp -R $(nix-build $nixpkgs --no-out-link -A rebar3.src)/* "$tmpdir"
(cd "$tmpdir" && rebar3 as test nix lock -o "$nix_path/rebar-deps.nix")
nixfmt "$nix_path/rebar-deps.nix"
else
echo "rebar3 is already up-to-date"
fi
'';
};
# Alias rebar3 so we can use it as default parameter below
_rebar3 = rebar3;
rebar3WithPlugins =
{
plugins ? [ ],
globalPlugins ? [ ],
rebar3 ? _rebar3,
}:
let
pluginLibDirs = map (p: "${p}/lib/erlang/lib") (lib.unique (plugins ++ globalPlugins));
globalPluginNames = lib.unique (map (p: p.packageName) globalPlugins);
rebar3Patched = (
rebar3.overrideAttrs (old: {
# skip-plugins.patch is necessary because otherwise rebar3 will always
# try to fetch plugins if they are not already present in _build.
#
# global-deps.patch makes it possible to use REBAR_GLOBAL_PLUGINS to
# instruct rebar3 to always load a certain plugin. It is necessary since
# REBAR_GLOBAL_CONFIG_DIR doesn't seem to work for this.
patches = [
./skip-plugins.patch
./global-plugins.patch
];
# our patches cause the tests to fail
doCheck = false;
})
);
in
stdenv.mkDerivation {
pname = "rebar3-with-plugins";
inherit (rebar3) version;
nativeBuildInputs = [
erlang
makeWrapper
];
unpackPhase = "true";
# Here we extract the rebar3 escript (like `rebar3_prv_local_install.erl`) and
# add plugins to the code path.
installPhase = ''
erl -noshell -eval '
{ok, Escript} = escript:extract("${rebar3Patched}/bin/rebar3", []),
{archive, Archive} = lists:keyfind(archive, 1, Escript),
{ok, _} = zip:extract(Archive, [{cwd, "'$out/lib'"}]),
init:stop(0)
'
cp ${./rebar_ignore_deps.erl} rebar_ignore_deps.erl
erlc -o $out/lib/rebar/ebin rebar_ignore_deps.erl
mkdir -p $out/bin
makeWrapper ${erlang}/bin/erl $out/bin/rebar3 \
--set REBAR_GLOBAL_PLUGINS "${toString globalPluginNames} rebar_ignore_deps" \
--suffix-each ERL_LIBS ":" "$out/lib ${toString pluginLibDirs}" \
--add-flags "+sbtu +A1 -noshell -boot start_clean -s rebar3 main -extra"
'';
};
in
{
inherit rebar3 rebar3WithPlugins;
}

View File

@@ -0,0 +1,14 @@
diff --git a/apps/rebar/src/rebar_plugins.erl b/apps/rebar/src/rebar_plugins.erl
index 469be42e..3a901cbe 100644
--- a/apps/rebar/src/rebar_plugins.erl
+++ b/apps/rebar/src/rebar_plugins.erl
@@ -31,7 +31,8 @@ project_plugins_install(State) ->
top_level_install(State) ->
Profiles = rebar_state:current_profiles(State),
lists:foldl(fun(Profile, StateAcc) ->
- Plugins = rebar_state:get(State, {plugins, Profile}, []),
+ Plugins = rebar_state:get(State, {plugins, Profile}, [])
+ ++ [list_to_atom(P) || P <- string:lexemes(os:getenv("REBAR_GLOBAL_PLUGINS", ""), " ")],
handle_plugins(Profile, Plugins, StateAcc)
end, State, Profiles).

View File

@@ -0,0 +1,27 @@
# Generated by rebar3_nix
let
fetchOnly = { src, ... }: src;
in
{
builder ? fetchOnly,
fetchHex,
fetchgit,
fetchFromGitHub,
overrides ? (x: y: { }),
}:
let
self = packages // (overrides self packages);
packages = {
meck = builder {
name = "meck";
version = "0.8.13";
src = fetchHex {
pkg = "meck";
version = "0.8.13";
sha256 = "sha256-008BPBVttRrVfMVWiRuXIOahwd9f4uFa+ZnITWzr6xo=";
};
beamDeps = [ ];
};
};
in
self

View File

@@ -0,0 +1,43 @@
%% This module, when loaded as a plugin, overrides the default `install_deps`
%% provider and erases the dependencies from the rebar3 state, when
%% REBAR_IGNORE_DEPS is true.
-module(rebar_ignore_deps).
-export([init/1, do/1, format_error/1]).
init(State0) ->
case os:getenv("REBAR_IGNORE_DEPS", "") of
"" ->
{ok, State0};
_ ->
do_init(State0)
end.
do_init(State0) ->
State1 = rebar_state:allow_provider_overrides(State0, true),
Provider = providers:create(
[
{name, install_deps}, %% override the default install_deps provider
{module, ?MODULE},
{bare, false},
{deps, [app_discovery]},
{example, undefined},
{opts, []},
{short_desc, ""},
{desc, ""}
]),
State2 = rebar_state:add_provider(State1, Provider),
{ok, rebar_state:allow_provider_overrides(State2, false)}.
do(State0) ->
io:format("Ignoring deps...~n"),
Profiles = rebar_state:current_profiles(State0),
State = lists:foldl(fun(P, Acc0) ->
Acc = rebar_state:set(Acc0, {deps, P}, []),
rebar_state:set(Acc, {parsed_deps, P}, [])
end, State0, Profiles),
{ok, State}.
format_error(Reason) ->
io_lib:format("~p", [Reason]).

View File

@@ -0,0 +1,80 @@
diff --git a/apps/rebar/src/rebar_plugins.erl b/apps/rebar/src/rebar_plugins.erl
index cd5f377c..469be42e 100644
--- a/apps/rebar/src/rebar_plugins.erl
+++ b/apps/rebar/src/rebar_plugins.erl
@@ -108,41 +108,9 @@ handle_plugins(Profile, Plugins, State, Upgrade) ->
State3 = rebar_state:set(State2, deps_dir, DepsDir),
rebar_state:lock(State3, Locks).
-handle_plugin(Profile, Plugin, State, SrcPlugins, Upgrade) ->
+handle_plugin(_Profile, Plugin, State, _SrcPlugins, _Upgrade) ->
try
- %% Inject top-level src plugins as project apps, so that they get skipped
- %% by the installation as already seen
- ProjectApps = rebar_state:project_apps(State),
- State0 = rebar_state:project_apps(State, SrcPlugins),
- %% We however have to pick the deps of top-level apps and promote them
- %% directly to make sure they are installed if they were not also at the top level
- TopDeps = top_level_deps(State, SrcPlugins),
- %% Install the plugins
- {Apps, State1} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State0, [Plugin|TopDeps], Upgrade),
- {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(SrcPlugins++Apps),
- ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []),
- %% Return things to normal
- State2 = rebar_state:project_apps(State1, ProjectApps),
-
- %% Add already built plugin deps to the code path
- ToBuildPaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild],
- PreBuiltPaths = [Ebin || A <- Sorted,
- Ebin <- [rebar_app_info:ebin_dir(A)],
- not lists:member(Ebin, ToBuildPaths)],
- code:add_pathsa(PreBuiltPaths),
-
- %% Build plugin and its deps
- build_plugins(ToBuild, Sorted, State2),
-
- %% Add newly built deps and plugin to code path
- State3 = rebar_state:update_all_plugin_deps(State2, Sorted),
- NewCodePaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild],
-
- %% Store plugin code paths so we can remove them when compiling project apps
- State4 = rebar_state:update_code_paths(State3, all_plugin_deps, PreBuiltPaths++NewCodePaths),
- rebar_paths:set_paths([plugins], State4),
-
- {plugin_providers(Plugin), State4}
+ {plugin_providers(Plugin), State}
catch
?WITH_STACKTRACE(C,T,S)
?DEBUG("~p ~p ~p", [C, T, S]),
@@ -150,15 +118,6 @@ handle_plugin(Profile, Plugin, State, SrcPlugins, Upgrade) ->
{[], State}
end.
-build_plugins(MustBuildApps, AllApps, State) ->
- State1 = rebar_state:deps_to_build(State, MustBuildApps),
- State2 = rebar_state:all_deps(State1, AllApps),
- State3 = rebar_state:set(State2, deps_dir, ?DEFAULT_PLUGINS_DIR),
- {Args, Extra} = rebar_state:command_parsed_args(State),
- State4 = rebar_state:command_parsed_args(State3, {[{deps_only, true}|Args], Extra}),
- rebar_prv_compile:do(State4),
- ok.
-
plugin_providers({Plugin, _, _, _}) when is_atom(Plugin) ->
validate_plugin(Plugin);
plugin_providers({Plugin, _, _}) when is_atom(Plugin) ->
@@ -251,15 +210,6 @@ prepare_plugin(AppInfo) ->
false -> rebar_app_info:valid(Relocated, undefined) % force revalidation
end.
-top_level_deps(State, Apps) ->
- CurrentProfiles = rebar_state:current_profiles(State),
- Keys = lists:append([[{plugins, P}, {deps, P}] || P <- CurrentProfiles]),
- RawDeps = lists:foldl(fun(App, Acc) ->
- %% Only support the profiles we would with regular plugins?
- lists:append([rebar_app_info:get(App, Key, []) || Key <- Keys]) ++ Acc
- end, [], Apps),
- rebar_utils:tup_dedup(RawDeps).
-
needs_rebuild(AppInfo) ->
%% if source files are newer than built files then the code was edited
%% and can't be considered valid -- force a rebuild.

View File

@@ -0,0 +1,68 @@
{
lib,
stdenv,
fetchurl,
jre,
autoPatchelfHook,
zlib,
ncurses,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "sbt";
version = "1.11.7";
src = fetchurl {
url = "https://github.com/sbt/sbt/releases/download/v${finalAttrs.version}/sbt-${finalAttrs.version}.tgz";
hash = "sha256-EjKBj5HDljmpO74RCOEtlMcESmRqeEfxo5d7nkZxbNY=";
};
postPatch = ''
echo -java-home ${jre.home} >>conf/sbtopts
'';
nativeBuildInputs = lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ];
buildInputs = lib.optionals stdenv.hostPlatform.isLinux [
stdenv.cc.cc # libstdc++.so.6
zlib
];
propagatedBuildInputs = [
# for infocmp
ncurses
];
installPhase = ''
runHook preInstall
mkdir -p $out/share/sbt $out/bin
cp -ra . $out/share/sbt
ln -sT ../share/sbt/bin/sbt $out/bin/sbt
ln -sT ../share/sbt/bin/sbtn-${
if (stdenv.hostPlatform.isDarwin) then
"universal-apple-darwin"
else if (stdenv.hostPlatform.isAarch64) then
"aarch64-pc-linux"
else
"x86_64-pc-linux"
} $out/bin/sbtn
runHook postInstall
'';
meta = with lib; {
homepage = "https://www.scala-sbt.org/";
license = licenses.bsd3;
sourceProvenance = with sourceTypes; [
binaryBytecode
binaryNativeCode
];
description = "Build tool for Scala, Java and more";
maintainers = with maintainers; [
nequissimus
kashw2
];
platforms = platforms.unix;
};
})

View File

@@ -0,0 +1,39 @@
{
lib,
sbt,
makeWrapper,
boehmgc,
libunwind,
re2,
llvmPackages,
zlib,
}:
sbt.overrideAttrs (previousAttrs: {
nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [ makeWrapper ];
postFixup = (previousAttrs.postFixup or "") + ''
wrapProgram $out/bin/sbt \
--set CLANG_PATH "${llvmPackages.clang}/bin/clang" \
--set CLANGPP_PATH "${llvmPackages.clang}/bin/clang" \
--set CPATH "${
lib.makeSearchPathOutput "dev" "include" [
re2
zlib
boehmgc
libunwind
llvmPackages.libcxx
]
}/c++/v1" \
--set LIBRARY_PATH "${
lib.makeLibraryPath [
re2
zlib
boehmgc
libunwind
llvmPackages.libcxx
]
}" \
--set NIX_CFLAGS_LINK "-lc++"
'';
})