2022-04-28 00:42:02 +02:00
|
|
|
{ pkgs ? import <nixpkgs> {}
|
|
|
|
# OpenWRT release
|
|
|
|
, release ? "21.02.3"
|
|
|
|
# OpenWRT target
|
|
|
|
, target
|
|
|
|
, variant ? "generic"
|
|
|
|
# Checksum of the `sha256sums` file
|
|
|
|
, sha256
|
|
|
|
# Checksum of a feed's `Packages` file
|
|
|
|
, feedsSha256
|
2022-04-29 21:29:29 +02:00
|
|
|
# Manually specify packages' arch for OpenWRT<19 releases without profiles.json
|
2022-04-29 21:33:58 +02:00
|
|
|
, packagesArch ? null
|
2022-04-28 00:42:02 +02:00
|
|
|
}:
|
|
|
|
let
|
2022-06-05 21:51:01 +02:00
|
|
|
inherit (pkgs) lib fetchurl;
|
|
|
|
|
2022-04-29 21:33:58 +02:00
|
|
|
sanitizeFilename = fileName:
|
|
|
|
builtins.replaceStrings [ "~" ] [ "-" ] (
|
|
|
|
builtins.baseNameOf fileName
|
|
|
|
);
|
2022-04-29 21:30:44 +02:00
|
|
|
|
2022-04-28 00:42:02 +02:00
|
|
|
fetchSums = url: sha256:
|
|
|
|
let
|
|
|
|
sumsFile = fetchurl {
|
|
|
|
url = "${url}/sha256sums";
|
|
|
|
inherit sha256;
|
|
|
|
};
|
|
|
|
|
|
|
|
filesSha256 =
|
|
|
|
builtins.foldl' (filesSha256: line:
|
|
|
|
let
|
|
|
|
m = builtins.match "([0-9a-f]+) \\*(.+)" line;
|
|
|
|
in
|
|
|
|
if builtins.isList m && builtins.length m == 2
|
|
|
|
then filesSha256 // {
|
|
|
|
${builtins.elemAt m 1} = builtins.elemAt m 0;
|
|
|
|
} else filesSha256
|
|
|
|
) {} (lib.splitString "\n" (builtins.readFile sumsFile));
|
|
|
|
|
|
|
|
in
|
|
|
|
builtins.mapAttrs (file: sha256:
|
|
|
|
fetchurl {
|
|
|
|
url = "${url}/${file}";
|
2022-04-29 21:30:44 +02:00
|
|
|
name = sanitizeFilename file;
|
2022-04-28 00:42:02 +02:00
|
|
|
inherit sha256;
|
|
|
|
}
|
|
|
|
) filesSha256;
|
|
|
|
|
2022-06-05 23:00:32 +02:00
|
|
|
parsePackages = url: packagesContent:
|
2022-04-28 00:42:02 +02:00
|
|
|
let
|
2022-06-05 23:00:32 +02:00
|
|
|
parsedRaw = map (section:
|
2022-04-28 00:42:02 +02:00
|
|
|
builtins.foldl' (data: line:
|
|
|
|
let
|
|
|
|
m = builtins.match "(.+): (.+)" line;
|
|
|
|
in
|
|
|
|
if builtins.isList m && builtins.length m == 2
|
|
|
|
then data // {
|
|
|
|
${builtins.elemAt m 0} = builtins.elemAt m 1;
|
|
|
|
} else data
|
|
|
|
) {} (lib.splitString "\n" section)
|
|
|
|
) (lib.splitString "\n\n" packagesContent);
|
|
|
|
|
2022-06-05 23:00:32 +02:00
|
|
|
parseDepends = depStr:
|
|
|
|
map (dep: builtins.elemAt (lib.splitString " " dep) 0)
|
|
|
|
(lib.splitString ", " depStr);
|
2022-04-28 00:42:02 +02:00
|
|
|
in
|
2022-06-05 23:00:32 +02:00
|
|
|
builtins.foldl'
|
|
|
|
(variantFiles: parsed:
|
2022-04-28 00:42:02 +02:00
|
|
|
if parsed ? Filename && parsed ? SHA256sum
|
|
|
|
then
|
|
|
|
variantFiles // {
|
2022-06-05 23:00:32 +02:00
|
|
|
${parsed.Package} = {
|
|
|
|
filename = parsed.Filename;
|
|
|
|
file = fetchurl {
|
|
|
|
url = "${url}/${parsed.Filename}";
|
|
|
|
sha256 = parsed.SHA256sum;
|
|
|
|
name = sanitizeFilename parsed.Filename;
|
|
|
|
};
|
|
|
|
depends = if parsed ? Depends then parseDepends parsed.Depends else [];
|
|
|
|
provides = if parsed ? Provides then parsed.Provides else null;
|
|
|
|
type = "real";
|
2022-04-28 00:42:02 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
variantFiles
|
2022-06-05 23:00:32 +02:00
|
|
|
) {} parsedRaw;
|
2022-04-28 00:42:02 +02:00
|
|
|
|
|
|
|
baseUrl = "https://downloads.openwrt.org/releases/${release}";
|
|
|
|
|
|
|
|
variantFiles = fetchSums "${baseUrl}/targets/${target}/${variant}" sha256;
|
|
|
|
|
|
|
|
feedsPackagesFile = builtins.mapAttrs (feed: sha256:
|
|
|
|
fetchurl {
|
|
|
|
url = "${baseUrl}/packages/${arch}/${feed}/Packages";
|
|
|
|
inherit sha256;
|
|
|
|
}
|
|
|
|
) feedsSha256;
|
|
|
|
|
2022-06-05 23:00:32 +02:00
|
|
|
packagesByFeed = builtins.mapAttrs (feed: packagesFile:
|
|
|
|
parsePackages "${baseUrl}/packages/${arch}/${feed}" (builtins.readFile packagesFile)
|
2022-04-28 00:42:02 +02:00
|
|
|
) feedsPackagesFile;
|
|
|
|
|
2022-06-05 23:00:32 +02:00
|
|
|
corePackages =
|
|
|
|
parsePackages
|
|
|
|
"${baseUrl}/targets/${target}/${variant}/packages"
|
|
|
|
(builtins.readFile variantFiles."packages/Packages");
|
|
|
|
|
|
|
|
realPackages =
|
|
|
|
(builtins.foldl' (a: b: a // b) { } (builtins.attrValues packagesByFeed))
|
|
|
|
// corePackages;
|
|
|
|
|
|
|
|
# for each package that 'provides' something, register that package as a
|
|
|
|
# dependency of the provided package. if there is no real package with that
|
|
|
|
# name, then a 'virtual' one is created. Using this, when a real package
|
|
|
|
# depends on something provided by several real packages, all possible
|
|
|
|
# providers will be downloaded
|
|
|
|
addVirtual = packages:
|
|
|
|
builtins.foldl'
|
|
|
|
(packages: pn:
|
|
|
|
let
|
|
|
|
p = packages.${pn};
|
|
|
|
in
|
|
|
|
if p.provides != null
|
|
|
|
then
|
|
|
|
let
|
|
|
|
vp = if packages ? ${p.provides} then packages.${p.provides} else {
|
|
|
|
type = "virtual";
|
|
|
|
depends = [ ];
|
|
|
|
};
|
|
|
|
in
|
|
|
|
packages // {
|
|
|
|
${p.provides} = vp // { depends = vp.depends ++ [ pn ]; };
|
|
|
|
}
|
|
|
|
else
|
|
|
|
packages
|
|
|
|
)
|
|
|
|
packages
|
|
|
|
(builtins.attrNames packages);
|
|
|
|
|
|
|
|
# packages which aren't available in feeds but are provided by imagebuilders
|
|
|
|
dummyPackages =
|
|
|
|
let
|
|
|
|
dummyPackage = { depends = [ ]; provides = null; type = "dummy"; };
|
|
|
|
in
|
|
|
|
{
|
|
|
|
libc = dummyPackage;
|
|
|
|
kernel = dummyPackage;
|
|
|
|
};
|
|
|
|
|
|
|
|
# all packages, including dummy and virtual
|
|
|
|
allPackages = addVirtual (realPackages // dummyPackages);
|
|
|
|
|
|
|
|
# remove package names starting with '-' from deps
|
|
|
|
#
|
|
|
|
# this should be used on the dependencies before expanding their
|
|
|
|
# requirements, and assumes that '-' deps come after the non-'-' version in
|
|
|
|
# the list
|
|
|
|
applyMinusDeps = deps:
|
|
|
|
builtins.foldl'
|
|
|
|
(deps: dep:
|
|
|
|
if lib.hasPrefix "-" dep
|
|
|
|
then
|
|
|
|
lib.remove (lib.removePrefix "-" dep) deps
|
|
|
|
else
|
|
|
|
deps ++ [ dep ]
|
|
|
|
) [ ]
|
|
|
|
deps;
|
|
|
|
|
|
|
|
# given a package set and a list of package names to install (including names
|
|
|
|
# starting with - to be removed), find all possible required package names
|
|
|
|
expandDeps = packages:
|
|
|
|
let
|
|
|
|
addDep = current_deps: new_dep:
|
|
|
|
if builtins.hasAttr new_dep current_deps
|
|
|
|
then
|
|
|
|
current_deps
|
|
|
|
else
|
|
|
|
let
|
|
|
|
with_new_dep = current_deps // { ${new_dep} = true; };
|
|
|
|
deps = packages.${new_dep}.depends;
|
|
|
|
in
|
|
|
|
builtins.foldl' addDep with_new_dep deps;
|
|
|
|
in
|
|
|
|
deps: builtins.attrNames (builtins.foldl' addDep { } (applyMinusDeps deps));
|
2022-04-28 00:42:02 +02:00
|
|
|
|
2022-04-28 01:17:11 +02:00
|
|
|
profiles =
|
|
|
|
if variantFiles ? "profiles.json"
|
|
|
|
then builtins.fromJSON (
|
|
|
|
builtins.readFile (
|
|
|
|
variantFiles."profiles.json"
|
|
|
|
)
|
2022-04-28 00:42:02 +02:00
|
|
|
)
|
2022-04-28 01:17:11 +02:00
|
|
|
else null;
|
|
|
|
|
2022-04-29 21:29:29 +02:00
|
|
|
arch = if packagesArch == null
|
|
|
|
then profiles.arch_packages
|
|
|
|
else packagesArch;
|
2022-04-28 00:42:02 +02:00
|
|
|
|
|
|
|
in {
|
2022-06-05 23:00:32 +02:00
|
|
|
inherit allPackages;
|
2022-06-10 21:54:09 +02:00
|
|
|
inherit corePackages packagesByFeed;
|
2022-06-05 23:00:32 +02:00
|
|
|
inherit expandDeps;
|
|
|
|
inherit variantFiles;
|
2022-04-28 01:17:11 +02:00
|
|
|
inherit profiles arch;
|
2022-04-28 00:42:02 +02:00
|
|
|
}
|