lol
1{
2 lib,
3 stdenv,
4 python311Packages,
5 fetchFromGitHub,
6 fetchurl,
7 cargo,
8 curl,
9 pkg-config,
10 openssl,
11 rustPlatform,
12 rustc,
13 fetchYarnDeps,
14 yarn,
15 nodejs,
16 fixup-yarn-lock,
17 glibcLocales,
18 libiconv,
19
20 enableMinimal ? false,
21}:
22
23let
24 inherit (lib.importJSON ./deps.json) links version versionHash;
25 # Sapling sets a Cargo config containing lines like so:
26 # [target.aarch64-apple-darwin]
27 # rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"]
28 #
29 # The default cargo config that's set by the build hook will set
30 # unstable.host-config and unstable.target-applies-to-host which seems to
31 # result in the link arguments above being ignored and thus link failures.
32 # All it is there to do anyway is just to do stuff with musl and cross
33 # compilation, which doesn't work on macOS anyway so we can just stub it
34 # on macOS.
35 #
36 # See https://github.com/NixOS/nixpkgs/pull/198311#issuecomment-1326894295
37 myCargoSetupHook = rustPlatform.cargoSetupHook.overrideAttrs (old: {
38 cargoConfig = lib.optionalString (!stdenv.hostPlatform.isDarwin) old.cargoConfig;
39 });
40
41 src = fetchFromGitHub {
42 owner = "facebook";
43 repo = "sapling";
44 rev = version;
45 hash = "sha256-4pOpJ91esTSH90MvvMu74CnlLULLUawqxcniUeqnLwA=";
46 };
47
48 addonsSrc = "${src}/addons";
49
50 # Fetches the Yarn modules in Nix to to be used as an offline cache
51 yarnOfflineCache = fetchYarnDeps {
52 yarnLock = "${addonsSrc}/yarn.lock";
53 sha256 = "sha256-jCtrflwDrwql6rY1ff1eXLKdwmnXhg5bCJPlCczBCIk=";
54 };
55
56 # Builds the NodeJS server that runs with `sl web`
57 isl = stdenv.mkDerivation {
58 pname = "sapling-isl";
59 src = addonsSrc;
60 inherit version;
61
62 nativeBuildInputs = [
63 fixup-yarn-lock
64 nodejs
65 yarn
66 ];
67
68 buildPhase = ''
69 runHook preBuild
70
71 export HOME=$(mktemp -d)
72 fixup-yarn-lock yarn.lock
73 yarn config --offline set yarn-offline-mirror ${yarnOfflineCache}
74 yarn install --offline --frozen-lockfile --ignore-engines --ignore-scripts --no-progress
75 patchShebangs node_modules
76 patchShebangs isl/node_modules
77
78 substituteInPlace build-tar.py \
79 --replace-fail 'run(yarn + ["--cwd", src_join(), "install", "--prefer-offline"])' 'pass'
80
81 ${python311Packages.python}/bin/python3 build-tar.py \
82 --output isl-dist.tar.xz \
83 --yarn 'yarn --offline --frozen-lockfile --ignore-engines --ignore-scripts --no-progress'
84
85 runHook postBuild
86 '';
87
88 installPhase = ''
89 runHook preInstall
90
91 mkdir -p $out
92 install isl-dist.tar.xz $out/isl-dist.tar.xz
93
94 runHook postInstall
95 '';
96 };
97in
98# Builds the main `sl` binary and its Python extensions
99python311Packages.buildPythonApplication {
100 format = "setuptools";
101 pname = "sapling";
102 inherit src version;
103
104 sourceRoot = "${src.name}/eden/scm";
105
106 # Upstream does not commit Cargo.lock
107 cargoDeps = rustPlatform.importCargoLock {
108 lockFile = ./Cargo.lock;
109 outputHashes = {
110 "abomonation-0.7.3+smallvec1" = "sha256-AxEXR6GC8gHjycIPOfoViP7KceM29p2ZISIt4iwJzvM=";
111 "cloned-0.1.0" = "sha256-2BaNR/pQmR7pHtRf6VBQLcZgLHbj2JCxeX4auAB0efU=";
112 "fb303_core-0.0.0" = "sha256-PDGdKjR6KPv1uH1JSTeoG5Rs0ZkmNJLqqSXtvV3RWic=";
113 "fbthrift-0.0.1+unstable" = "sha256-J4REXGuLjHyN3SHilSWhMoqpRcn1QnEtsTsZF4Z3feU=";
114 "serde_bser-0.4.0" = "sha256-Su1IP3NzQu/87p/+uQaG8JcICL9hit3OV1O9oFiACsQ=";
115 };
116 };
117 postPatch = ''
118 cp ${./Cargo.lock} Cargo.lock
119 ''
120 + lib.optionalString (!enableMinimal) ''
121 # If asked, we optionally patch in a hardcoded path to the
122 # 'nodejs' package, so that 'sl web' always works. Without the
123 # patch, 'sl web' will still work if 'nodejs' is in $PATH.
124 substituteInPlace lib/config/loader/src/builtin_static/core.rs \
125 --replace '"#);' $'[web]\nnode-path=${nodejs}/bin/node\n"#);'
126 '';
127
128 # Since the derivation builder doesn't have network access to remain pure,
129 # fetch the artifacts manually and link them. Then replace the hardcoded URLs
130 # with filesystem paths for the curl calls.
131 postUnpack = ''
132 mkdir $sourceRoot/hack_pydeps
133 ${lib.concatStrings (
134 map (li: "ln -s ${fetchurl li} $sourceRoot/hack_pydeps/${baseNameOf li.url}\n") links
135 )}
136 sed -i "s|https://files.pythonhosted.org/packages/[[:alnum:]]*/[[:alnum:]]*/[[:alnum:]]*/|file://$NIX_BUILD_TOP/$sourceRoot/hack_pydeps/|g" $sourceRoot/setup.py
137 '';
138
139 postInstall = ''
140 install ${isl}/isl-dist.tar.xz $out/lib/isl-dist.tar.xz
141 '';
142
143 postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
144 wrapProgram $out/bin/sl \
145 --set LOCALE_ARCHIVE "${glibcLocales}/lib/locale/locale-archive"
146 '';
147
148 nativeBuildInputs = [
149 curl
150 pkg-config
151 myCargoSetupHook
152 cargo
153 rustc
154 ];
155
156 buildInputs = [
157 openssl
158 ]
159 ++ lib.optionals stdenv.hostPlatform.isDarwin [
160 curl
161 libiconv
162 ];
163
164 HGNAME = "sl";
165 SAPLING_OSS_BUILD = "true";
166 SAPLING_VERSION_HASH = versionHash;
167
168 # Python setuptools version 66 and newer does not support upstream Sapling's
169 # version numbers (e.g. "0.2.20230124-180750-hf8cd450a"). Change the version
170 # number to something supported by setuptools (e.g. "0.2.20230124").
171 # https://github.com/facebook/sapling/issues/571
172 SAPLING_VERSION = builtins.elemAt (builtins.split "-" version) 0;
173
174 # just a simple check phase, until we have a running test suite. this should
175 # help catch issues like lack of a LOCALE_ARCHIVE setting (see GH PR #202760)
176 doCheck = true;
177 installCheckPhase = ''
178 echo -n "testing sapling version; should be \"$SAPLING_VERSION\"... "
179 $out/bin/sl version | grep -qw "$SAPLING_VERSION"
180 echo "OK!"
181 '';
182
183 # Expose isl to nix repl as sapling.isl.
184 passthru.isl = isl;
185
186 meta = with lib; {
187 description = "Scalable, User-Friendly Source Control System";
188 homepage = "https://sapling-scm.com";
189 license = licenses.gpl2Only;
190 maintainers = with maintainers; [
191 pbar
192 thoughtpolice
193 ];
194 platforms = platforms.unix;
195 mainProgram = "sl";
196 };
197}