nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 stdenv,
4 python312Packages,
5 fetchFromGitHub,
6 cargo,
7 curl,
8 gettext,
9 libclang,
10 pkg-config,
11 openssl,
12 rustPlatform,
13 rustc,
14 fetchYarnDeps,
15 yarn,
16 nodejs,
17 fixup-yarn-lock,
18 glibcLocales,
19 libiconv,
20 versionCheckHook,
21
22 enableMinimal ? false,
23}:
24
25let
26 version = "0.2.20250521-115337+25ed6ac4";
27
28 # Sapling sets a Cargo config containing lines like so:
29 # [target.aarch64-apple-darwin]
30 # rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"]
31 #
32 # The default cargo config that's set by the build hook will set
33 # unstable.host-config and unstable.target-applies-to-host which seems to
34 # result in the link arguments above being ignored and thus link failures.
35 # All it is there to do anyway is just to do stuff with musl and cross
36 # compilation, which doesn't work on macOS anyway so we can just stub it
37 # on macOS.
38 #
39 # See https://github.com/NixOS/nixpkgs/pull/198311#issuecomment-1326894295
40 myCargoSetupHook = rustPlatform.cargoSetupHook.overrideAttrs (old: {
41 cargoConfig = lib.optionalString (!stdenv.hostPlatform.isDarwin) old.cargoConfig;
42 });
43
44 src = fetchFromGitHub {
45 owner = "facebook";
46 repo = "sapling";
47 tag = version;
48 hash = "sha256-NvfSx6BMbwOFY+y6Yb/tyUNYeuL8WCoc+HSVys8Ko0Y=";
49 };
50
51 addonsSrc = "${src}/addons";
52
53 # Fetches the Yarn modules in Nix to to be used as an offline cache
54 yarnOfflineCache = fetchYarnDeps {
55 yarnLock = "${addonsSrc}/yarn.lock";
56 sha256 = "sha256-9l4lSzFTF5rSByO388tosJCxOb65Nnua6HaDD7F62No=";
57 };
58
59 # Builds the NodeJS server that runs with `sl web`
60 isl = stdenv.mkDerivation {
61 pname = "sapling-isl";
62 src = addonsSrc;
63 inherit version;
64
65 nativeBuildInputs = [
66 fixup-yarn-lock
67 nodejs
68 yarn
69 ];
70
71 buildPhase = ''
72 runHook preBuild
73
74 export HOME=$(mktemp -d)
75 fixup-yarn-lock yarn.lock
76 yarn config --offline set yarn-offline-mirror ${yarnOfflineCache}
77 yarn install --offline --frozen-lockfile --ignore-engines --ignore-scripts --no-progress
78 patchShebangs node_modules
79 patchShebangs isl/node_modules
80
81 substituteInPlace build-tar.py \
82 --replace-fail 'run(yarn + ["--cwd", src_join(), "install", "--prefer-offline"])' 'pass'
83
84 ${python312Packages.python}/bin/python3 build-tar.py \
85 --output isl-dist.tar.xz \
86 --yarn 'yarn --offline --frozen-lockfile --ignore-engines --ignore-scripts --no-progress'
87
88 runHook postBuild
89 '';
90
91 installPhase = ''
92 runHook preInstall
93
94 mkdir -p $out
95 install isl-dist.tar.xz $out/isl-dist.tar.xz
96
97 runHook postInstall
98 '';
99 };
100in
101# Builds the main `sl` binary and its Python extensions
102python312Packages.buildPythonApplication {
103 format = "setuptools";
104 pname = "sapling";
105 inherit src version;
106
107 sourceRoot = "${src.name}/eden/scm";
108
109 # Upstream does not commit Cargo.lock
110 cargoDeps = rustPlatform.importCargoLock {
111 lockFile = ./Cargo.lock;
112 outputHashes = {
113 "abomonation-0.7.3+smallvec1" = "sha256-AxEXR6GC8gHjycIPOfoViP7KceM29p2ZISIt4iwJzvM=";
114 "cloned-0.1.0" = "sha256-026OKsszbF2aPWpA8JBc6KwZHxEqwnKIluzDjO/opgc=";
115 "fb303_core-0.0.0" = "sha256-IJKAWgBLrLnWItw6UTNdwjuTDO6dUfqyKsVv2aW6Kyo=";
116 "fbthrift-0.0.1+unstable" = "sha256-FuUo1cZG7Ed+TAXY53MpylBPGzFruIsWaxKPR26TxVk=";
117 "serde_bser-0.4.0" = "sha256-OY+IZh4nz5ICrDKYr8pPfORW4i8KBULhGC5YyXb5Ulg=";
118 "watchman_client-0.9.0" = "sha256-OY+IZh4nz5ICrDKYr8pPfORW4i8KBULhGC5YyXb5Ulg=";
119 };
120 };
121
122 postPatch = ''
123 cp ${./Cargo.lock} Cargo.lock
124
125 substituteInPlace sapling/thirdparty/pysocks/setup.py \
126 --replace-fail 'os.path.dirname(__file__)' "\"$out/lib/${python312Packages.python.libPrefix}/site-packages/sapling/thirdparty/pysocks\""
127 ''
128 + lib.optionalString (!enableMinimal) ''
129 # If asked, we optionally patch in a hardcoded path to the
130 # 'nodejs' package, so that 'sl web' always works. Without the
131 # patch, 'sl web' will still work if 'nodejs' is in $PATH.
132 substituteInPlace lib/config/loader/src/builtin_static/core.rs \
133 --replace '"#);' $'[web]\nnode-path=${nodejs}/bin/node\n"#);'
134 '';
135
136 postInstall = ''
137 install ${isl}/isl-dist.tar.xz $out/lib/isl-dist.tar.xz
138 '';
139
140 postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
141 wrapProgram $out/bin/sl \
142 --set LOCALE_ARCHIVE "${glibcLocales}/lib/locale/locale-archive"
143 '';
144
145 nativeBuildInputs = [
146 curl
147 pkg-config
148 myCargoSetupHook
149 cargo
150 rustc
151 ]
152 ++ lib.optionals stdenv.hostPlatform.isLinux [ gettext ];
153
154 buildInputs = [
155 openssl
156 ]
157 ++ lib.optionals stdenv.hostPlatform.isDarwin [
158 curl
159 libiconv
160 ];
161
162 HGNAME = "sl";
163 LIBCLANG_PATH = "${lib.getLib libclang}/lib";
164 SAPLING_OSS_BUILD = "true";
165 SAPLING_VERSION = version;
166 SAPLING_VERSION_HASH =
167 let
168 sha1Hash = builtins.hashString "sha1" version;
169 hexSubstring = builtins.substring 0 16 sha1Hash;
170 in
171 lib.trivial.fromHexString hexSubstring;
172
173 nativeInstallCheckInputs = [
174 versionCheckHook
175 ];
176 versionCheckProgramArg = "version";
177 doInstallCheck = true;
178
179 passthru = {
180 # Expose isl to nix repl as sapling.isl.
181 isl = isl;
182 updateScript = ./update.sh;
183 };
184
185 meta = {
186 description = "Scalable, User-Friendly Source Control System";
187 homepage = "https://sapling-scm.com";
188 license = lib.licenses.gpl2Only;
189 maintainers = with lib.maintainers; [
190 pbar
191 thoughtpolice
192 shikanime
193 ];
194 platforms = lib.platforms.unix;
195 mainProgram = "sl";
196 };
197}