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