-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnix-clj.nix
139 lines (121 loc) · 4.61 KB
/
nix-clj.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{ stdenvNoCC, runCommand, callPackage, makeBinaryWrapper, applyPatches, jdk, clojure, ...}:
let
buildClojureLibrary = {
pname
,version
,src
,path ? [ "src" ]
,resources ? [ ]
,ns ? [ "${pname}.core" ]
,deps ? []
,patches ? []
, ...
}@args : stdenvNoCC.mkDerivation (rec {
inherit pname version;
dontUnpack = true;
nativeBuildInputs = [ jdk clojure ];
propagatedBuildInputs = [ clojure ] ++ deps;
# Clojure libraries seem to be usually built with deps.edn or Leiningen.
#
# I don't want to work with deps.edn, because it is hard-coded to make
# network requests on runtime. (Tried patching that out, but gave up, didn't
# dig too deep and don't remember what was blocking me... only remember it
# being something seemingly arbitrary - and some problems with it's fetching
# of git repositories).
#
# I remeber considering Leiningen, but discarding it because of
# bootstrapability issues - Leiningen is build using Leiningen. I tried
# compiling old versions from scratch but gave up after getting no real
# results.
#
# But maybe that's not a problem? Since the main thing that these tools are
# doing is exporting a classpath (after pulling binary blobs from maven), I
# can easily do it by hand. Here is a summary of the buildPhase:
#
# 1. Some libraries contain a little Java source code. Compile that first
# (since the Clojure code might depend on that)
# 2. Now compile each namespace with clojure.core/compile.
#
# For that to work, some things need to happen:
#
# - A target directory for bytecode must exist. Default for Clojure is
# "classes" - It's no problem to share it with any Java-produced code.
# - Any classes referenced in code must be present on the class path. Same
# for required Clojure namespaces - clojure.core/require looks for that on
# the classpath.
#
# Notes on the installPhase:
#
# - my Clojure doesn't seem to use the precompiled bytecode. It insists on
# always loading the source files, even when the corresponding bytecode
# exists on the filesystem. I deduce this from the clojure.core/require
# calls being just as slow. I might be doing something wrong
# here... didn't dig into it too much. But decided to just prune the
# sources from the uberjar, leaving only the bytecode. This was
# immediately visible by loading being an order of magnitude faster.
# - Put the jar in $out/share/java for it to be found by nixpkgs setup hooks.
buildPhase = let
src' = if builtins.length patches > 0
then applyPatches { inherit src patches; }
else src;
in ''
mkdir classes
addToSearchPath CLASSPATH classes
for p in ${toString resources}; do
cp --no-preserve=mode -r ${src'}/$p/* classes
done
for p in ${toString path}; do
addToSearchPath CLASSPATH ${src'}/$p
find ${src'}/$p -name '*.java' >> sources.txt
done
if test -s sources.txt; then javac -d classes @sources.txt; fi
java -Dclojure.compile.path=classes clojure.lang.Compile ${toString ns}
'';
installPhase = ''
mkdir -p $out/share/java
(cd classes; jar --create --date=1980-01-01T00:00:02Z -f $out/share/java/${pname}-${version}.jar *)
'';
} // args);
clojureWithPackages = selectPackages: stdenvNoCC.mkDerivation {
pname = "clojure";
version = "with-packages";
dontUnpack = true;
buildInputs = [ makeBinaryWrapper ];
propagatedBuildInputs = selectPackages packages;
installPhase = ''
makeWrapper ${jdk}/bin/java $out/bin/clojure \
--add-flags clojure.main \
--prefix CLASSPATH : "$CLASSPATH"
'';
};
packages = callPackage ./packages.nix { inherit buildClojureLibrary; };
buildUberjar = name: cljpkgs: runCommand name { inherit jdk; } ''
mkdir -p $out/share/java
mkdir classes
declare -A jars
unpack_jars () {
for jar in $1/share/java/*.jar; do
(cd classes; $jdk/bin/jar -xf $jar)
done
local prop=$1/nix-support/propagated-build-inputs
if [ ! -f $prop ]; then return; fi
for pkg in $(cat $prop); do
if [ -z ''${jars[$pkg]} ]; then
jars[$pkg]=1
unpack_jars $pkg
fi
done
}
for pkg in ${toString cljpkgs}; do
unpack_jars $pkg
done
(cd classes; $jdk/bin/jar --create --date=1980-01-01T00:00:02Z -f $out/share/java/$name.jar *)
'';
in clojure // {
pkgs = packages;
withPackages = clojureWithPackages;
inherit
buildClojureLibrary
buildUberjar
;
}