Skip to content

Commit ee2e171

Browse files
committed
nix backend: init
1 parent bf0b6f6 commit ee2e171

File tree

7 files changed

+639
-2
lines changed

7 files changed

+639
-2
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (C) 2025 Victor Fuentes <[email protected]>
3+
*
4+
* Based on the archlinux and alpinelinux backends, which are:
5+
* Copyright (C) 2016 Matthias Klumpp <[email protected]>
6+
* Copyright (C) 2020 Rasmus Thomsen <[email protected]>
7+
*
8+
* Licensed under the GNU Lesser General Public License Version 3
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Lesser General Public License as published by
12+
* the Free Software Foundation, either version 3 of the license, or
13+
* (at your option) any later version.
14+
*
15+
* This software is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Lesser General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Lesser General Public License
21+
* along with this software. If not, see <http://www.gnu.org/licenses/>.
22+
*/
23+
24+
module asgen.backends.nix.nixindexutils;
25+
26+
import std.file : exists;
27+
import std.format : format;
28+
import std.json : parseJSON, JSONValue;
29+
import std.path : buildPath;
30+
import std.process : execute, pipeProcess, wait;
31+
import std.range : empty;
32+
import std.string : strip;
33+
34+
import glib.Util : Util;
35+
36+
import asgen.downloader : Downloader, DownloadException;
37+
import asgen.logging : logDebug, logError, logInfo;
38+
39+
immutable(string) downloadNixPackagesIfNecessary (const string channel, const string destFilePath)
40+
{
41+
if (destFilePath.exists) {
42+
return destFilePath;
43+
}
44+
45+
auto brotliExe = Util.findProgramInPath("brotli");
46+
if (brotliExe.empty) {
47+
throw new Exception("brotli binary not found. Cannot extract packages.json.br");
48+
}
49+
50+
auto downloader = Downloader.get;
51+
52+
immutable filePath = buildPath("https://channels.nixos.org/", channel, "packages.json.br");
53+
54+
try {
55+
downloader.downloadFile(filePath, destFilePath ~ ".br");
56+
auto result = execute([brotliExe, "-d", destFilePath ~ ".br", "-o", destFilePath]);
57+
if (result.status == 0) {
58+
return destFilePath;
59+
}
60+
} catch (DownloadException e) {
61+
logDebug("Unable to download: %s", e.msg);
62+
}
63+
64+
throw new Exception("Failed to download and extract packages.json.br");
65+
}
66+
67+
immutable(string) buildNixIndexIfNecessary (const string channel, const string arch, const string destFilePath)
68+
{
69+
if ((buildPath(destFilePath, "files")).exists) {
70+
return destFilePath;
71+
}
72+
73+
auto nixExe = Util.findProgramInPath("nix");
74+
if (nixExe.empty) {
75+
throw new Exception("nix binary not found. Cannot extract packages.json.br");
76+
}
77+
auto nixIndexExe = Util.findProgramInPath("nix-index");
78+
if (nixIndexExe.empty) {
79+
throw new Exception("nix-index binary not found. Cannot extract packages.json.br");
80+
}
81+
82+
string nixpkgsPath;
83+
{
84+
auto pipes = pipeProcess([
85+
nixExe,
86+
"--extra-experimental-features",
87+
"nix-command flakes",
88+
"eval",
89+
format("nixpkgs/%s#path", channel),
90+
"--quiet",
91+
]);
92+
string stdout_output;
93+
string stderr_output;
94+
foreach (line; pipes.stdout.byLine) {
95+
stdout_output ~= line ~ "\n";
96+
}
97+
foreach (line; pipes.stderr.byLine) {
98+
stderr_output ~= line ~ "\n";
99+
}
100+
auto result = wait(pipes.pid);
101+
102+
if (result != 0) {
103+
throw new Exception(format("Failed to get nixpkgs path: %s", stderr_output));
104+
}
105+
nixpkgsPath = stdout_output.strip();
106+
}
107+
108+
logInfo("Building nix-index db, this may take a while");
109+
auto pipes = pipeProcess([
110+
nixIndexExe,
111+
"-f",
112+
nixpkgsPath,
113+
"-s",
114+
arch,
115+
"-d",
116+
destFilePath,
117+
]);
118+
foreach (line; pipes.stderr.byLine) {
119+
logDebug("nix-index: %s", line);
120+
}
121+
auto result = wait(pipes.pid);
122+
123+
if (result != 0) {
124+
throw new Exception("Failed to populate nix index");
125+
}
126+
127+
return destFilePath;
128+
}
129+
130+
ubyte[] nixStoreCat (const string nixExe, const string storeUrl, const string path)
131+
{
132+
auto pipes = pipeProcess([
133+
nixExe,
134+
"--extra-experimental-features",
135+
"nix-command",
136+
"store",
137+
"cat",
138+
"--store",
139+
storeUrl,
140+
"--quiet",
141+
path,
142+
]);
143+
string stdout_output;
144+
string stderr_output;
145+
foreach (line; pipes.stdout.byLine) {
146+
stdout_output ~= line ~ "\n";
147+
}
148+
foreach (line; pipes.stderr.byLine) {
149+
stderr_output ~= line ~ "\n";
150+
}
151+
auto result = wait(pipes.pid);
152+
if (result != 0) {
153+
logError("nix store cat failed: %s", stderr_output);
154+
return [' '];
155+
}
156+
return cast(ubyte[]) stdout_output;
157+
}
158+
159+
immutable(JSONValue) nixStoreLs (const string nixExe, const string storeUrl, const string path)
160+
{
161+
auto pipes = pipeProcess([
162+
nixExe,
163+
"--extra-experimental-features",
164+
"nix-command",
165+
"store",
166+
"ls",
167+
"--store",
168+
storeUrl,
169+
"--recursive",
170+
"--json",
171+
"--quiet",
172+
path,
173+
]);
174+
string stdout_output;
175+
string stderr_output;
176+
foreach (line; pipes.stdout.byLine) {
177+
stdout_output ~= line ~ "\n";
178+
}
179+
foreach (line; pipes.stderr.byLine) {
180+
stderr_output ~= line ~ "\n";
181+
}
182+
auto result = wait(pipes.pid);
183+
if (result != 0) {
184+
throw new Exception(format("nix store ls failed: %s", stderr_output));
185+
}
186+
187+
return parseJSON(stdout_output);
188+
}

0 commit comments

Comments
 (0)