From d4df4381a10cd03999b1f03b5767f8bb7cb106f2 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Fri, 24 Dec 2021 16:19:40 +0300 Subject: [PATCH] flek --- .gitignore | 1 + default.nix | 264 +--------------------------------- examples/0install.nix | 8 +- examples/default.nix | 8 ++ examples/frama-c.nix | 8 +- examples/mina.nix | 16 ++- examples/opam-repository.json | 7 - examples/opam2json.nix | 10 +- examples/tezos.nix | 13 +- flake.lock | 128 +++++++++++++++++ flake.nix | 39 +++++ opam.nix | 259 +++++++++++++++++++++++++++++++++ opam2json.nix | 9 +- 13 files changed, 473 insertions(+), 297 deletions(-) create mode 100644 .gitignore create mode 100644 examples/default.nix delete mode 100644 examples/opam-repository.json create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 opam.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62cd3e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +result* \ No newline at end of file diff --git a/default.nix b/default.nix index 9835919..80aeb43 100644 --- a/default.nix +++ b/default.nix @@ -1,258 +1,6 @@ -# Pkgset = { ${name} = { ${version} = Pkgdef; ... } ... } -# Pkgdef = { name = String; version = String; depends = [OpamVar]; build = ?[[String]]; install = ?[[String]]; ... } - -pkgs: -let - inherit (builtins) - readDir mapAttrs concatStringsSep concatMap all isString isList elem - attrValues filter attrNames head elemAt splitVersion foldl' fromJSON - listToAttrs readFile getAttr toFile match isAttrs pathExists; - inherit (pkgs) lib; - inherit (lib) - versionAtLeast splitString tail mapAttrs' nameValuePair zipAttrsWith collect - filterAttrs unique subtractLists concatMapStringsSep concatLists reverseList - fileContents pipe makeScope optionalAttrs filterAttrsRecursive hasSuffix - converge mapAttrsRecursive hasAttr composeManyExtensions; - - readDirRecursive = dir: - mapAttrs (name: type: - if type == "directory" then readDirRecursive "${dir}/${name}" else type) - (readDir dir); - - # [Pkgset] -> Pkgset - mergePackageSets = zipAttrsWith (_: foldl' (a: b: a // b) { }); - - bootstrapPackagesStub = import ./bootstrapPackages.nix { }; - - bootstrapPackageNames = attrNames bootstrapPackagesStub; -in rec { - # filterRelevant (traverseOPAMRepository ../../opam-repository) "opam-ed" - opam2json = pkgs.ocaml-ng.ocamlPackages_4_09.callPackage ./opam2json.nix { }; - - # Path -> {...} - fromOPAM = opamFile: - let - json = pkgs.runCommandNoCC "opam.json" { - preferLocalBuild = true; - allowSubstitutes = false; - } "${opam2json}/bin/opam2json ${opamFile} > $out"; - in fromJSON (readFile json); - - fromOPAM' = opamText: fromOPAM (toFile "opam" opamText); - - # Pkgdef -> Derivation - pkgdef2drv = import ./pkgdef2drv.nix pkgs; - - # Path -> Derivation - opam2nix = { opamFile, name ? null, version ? null }: - pkgdef2drv (fromOPAM opamFile // { inherit name version; }); - - splitNameVer = nameVer: - let nv = nameVerToValuePair nameVer; - in { inherit (nv) name version; }; - - nameVerToValuePair = nameVer: - let split = splitString "." nameVer; - in nameValuePair (head split) (concatStringsSep "." (tail split)); - - ops = { - eq = "="; - gt = ">"; - lt = "<"; - geq = ">="; - leq = "<="; - neq = "!="; - }; - - global-variables = import ./global-variables.nix pkgs; - - opamListToQuery = list: listToAttrs (map nameVerToValuePair list); - - opamList = repos: packages: - let - opam-root = pkgs.runCommand "opamroot" { - nativeBuildInputs = [ pkgs.opam ]; - OPAMNO = "true"; - } '' - export OPAMROOT=$out - - mkdir -p $NIX_BUILD_TOP/repos - - ${concatStringsSep "\n" (attrValues (mapAttrs (name: repo: - "cp -R --no-preserve=all ${repo} $NIX_BUILD_TOP/repos/${name}") - repos))} - - opam init --bare default $NIX_BUILD_TOP/repos/default --disable-sandboxing --disable-completion -n --bypass-checks - ${concatStringsSep "\n" (attrValues (mapAttrs (name: repo: - "opam repository add --set-default ${name} $NIX_BUILD_TOP/repos/${name}") - (lib.filterAttrs (name: _: name != "default") repos)))} - ''; - - pkgRequest = name: version: - if isNull version then name else "${name}.${version}"; - - resolve-drv = pkgs.runCommand "resolve" { - nativeBuildInputs = [ pkgs.opam ]; - OPAMNO = "true"; - OPAMCLI = "2.0"; - } '' - export OPAMROOT=$NIX_BUILD_TOP/opam - - cp -R --no-preserve=all ${opam-root} $OPAMROOT - - opam list --resolve=${ - concatStringsSep "," (attrValues (mapAttrs pkgRequest packages)) - } --no-switch --short --with-test --depopts --columns=package > $out - ''; - solution = fileContents resolve-drv; - - lines = s: splitString "\n" s; - - in lines solution; - - makeOpamRepo = dir: - let - files = readDirRecursive dir; - opamFiles = filterAttrsRecursive - (name: value: isAttrs value || hasSuffix "opam" name) files; - opamFilesOnly = - converge (filterAttrsRecursive (_: v: v != { })) opamFiles; - packages = collect isList (mapAttrsRecursive (path: _: - let - fileName = lib.last path; - dirName = lib.last (lib.init path); - # We try to avoid reading this opam file if possible - name = if fileName == "opam" then - (fromOPAM "${dir + ("/" + concatStringsSep "/" path)}").name or dirName - else - lib.removeSuffix ".opam" (lib.last path); - - in [ - { - name = "sources/${name}/${name}.local"; - path = "${dir + ("/" + concatStringsSep "/" (lib.init path))}"; - } - { - name = "packages/${name}/${name}.local/opam"; - path = "${dir + ("/" + concatStringsSep "/" path)}"; - } - ]) opamFilesOnly); - repo-description = { - name = "repo"; - path = toFile "repo" ''opam-version: "2.0"''; - }; - repo = pkgs.linkFarm "opam-repo" - ([ repo-description ] ++ concatLists packages); - in repo; - - queryToDefs = repos: packages: - let - # default has the lowest prio - repos' = (attrValues (filterAttrs (name: _: name != "default") repos)) - ++ [ repos.default ]; - - findPackage = name: version: - head (filter ({ opamFile, ... }: pathExists opamFile) (map (repo: - let - sourcePath = "${repo}/sources/${name}/${name}.local"; - isLocal = pathExists sourcePath; - pkgDir = "${repo}/packages/${name}/${name}.${version}"; - filesPath = "${pkgDir}/files"; - in { - opamFile = "${pkgDir}/opam"; - inherit name version isLocal repo; - src = if isLocal then - pkgs.runCommand "source-copy" { } - "cp --no-preserve=all -R ${sourcePath}/ $out" - else - pkgs.emptyDirectory; - } // optionalAttrs (pathExists filesPath) { files = filesPath; }) - repos')); - - packageFiles = mapAttrs findPackage packages; - in mapAttrs - (_: { opamFile, name, version, ... }@args: args // (fromOPAM opamFile)) - packageFiles; - - queryToDefs' = repos: packages: - let - # default has the lowest prio - repos' = (attrValues (filterAttrs (name: _: name != "default") repos)) - ++ [ repos.default ]; - - findPackage = name: version: - head (filter ({ dir, ... }: pathExists dir) (map (repo: { - dir = "${repo}/packages/${name}/${name}.${version}"; - inherit name version; - src = repo.passthru.origSrc or pkgs.emptyDirectory; - }) repos')); - - packageFiles = mapAttrs (_: - { dir, name, version, src }: - { - inherit name version src; - opamFile = "${dir}/opam"; - } // optionalAttrs (pathExists "${dir}/files") { - files = "${dir}/files"; - }) (mapAttrs findPackage packages); - - readPackageFiles = '' - ( - echo '[' - ${concatMapStringsSep '' - - echo ',' - '' ({ opamFile, name, version, src, files ? null }: - '' - opam2json ${opamFile} | jq '.name = "${name}" | .version = "${version}" | .opamFile = "${opamFile}" | .src = "${src}"${ - lib.optionalString (!isNull files) ''| .files = "${files}"'' - }' '') (attrValues packageFiles)} - echo ']' - ) > $out - ''; - - pkgdefs = pkgs.runCommand "opam2json-many.json" { - nativeBuildInputs = [ opam2json pkgs.jq ]; - } readPackageFiles; - - listToAttrsByName = lst: - listToAttrs (map (x: nameValuePair x.name x) lst); - in listToAttrsByName (fromJSON (readFile pkgdefs)); - - defsToScope = repos: pkgs: packages: - makeScope pkgs.newScope (self: - (mapAttrs (name: pkg: self.callPackage (pkgdef2drv pkg) { }) packages) - // (import ./bootstrapPackages.nix pkgs - packages.ocaml.version or packages.ocaml-base-compiler.version)); - - defaultOverlay = import ./overlay.nix; - - applyOverlays = overlays: scope: - scope.overrideScope' (composeManyExtensions overlays); - - queryToScope = { repos, pkgs, overlays ? [ defaultOverlay ] }: - query: - pipe query [ - (opamList repos) - (opamListToQuery) - (queryToDefs repos) - (defsToScope repos pkgs) - (applyOverlays overlays) - ]; - - opamImport = { repos, pkgs }: - export: - let - installedList = (fromOPAM export).installed; - set = pipe installedList [ - opamListToQuery - (queryToDefs repos) - (defsToScope repos pkgs) - ]; - installedPackageNames = map (x: (splitNameVer x).name) installedList; - combined = pkgs.symlinkJoin { - name = "opam-switch"; - paths = attrValues (lib.getAttrs installedPackageNames set); - }; - in combined; -} +(import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock); +in fetchTarball { + url = + "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; +}) { src = ./.; }).defaultNix diff --git a/examples/0install.nix b/examples/0install.nix index 7cc720a..9a620d3 100644 --- a/examples/0install.nix +++ b/examples/0install.nix @@ -1,8 +1,10 @@ +inputs: +pkgs: let - pkgs = import { }; - opam-nix = import ../. pkgs; + opam-nix = inputs.self.lib.${pkgs.system}; + repos = { - default = pkgs.fetchFromGitHub (pkgs.lib.importJSON ./opam-repository.json); + default = inputs.opam-repository; }; scope = opam-nix.queryToScope { inherit repos pkgs; } { "0install" = null; diff --git a/examples/default.nix b/examples/default.nix new file mode 100644 index 0000000..5593fc2 --- /dev/null +++ b/examples/default.nix @@ -0,0 +1,8 @@ +inputs: pkgs: +{ + inherit (import ./0install.nix inputs pkgs) "0install"; + inherit (import ./frama-c.nix inputs pkgs) frama-c; + inherit (import ./mina.nix inputs pkgs) mina; + inherit (import ./opam2json.nix inputs pkgs) opam2json; + inherit (import ./tezos.nix inputs pkgs) tezos; +} diff --git a/examples/frama-c.nix b/examples/frama-c.nix index 39e62c8..4df2453 100644 --- a/examples/frama-c.nix +++ b/examples/frama-c.nix @@ -1,8 +1,10 @@ +inputs: +pkgs: let - pkgs = import { }; - opam-nix = import ../. pkgs; + opam-nix = inputs.self.lib.${pkgs.system}; + repos = { - default = pkgs.fetchFromGitHub (pkgs.lib.importJSON ./opam-repository.json); + default = inputs.opam-repository; }; scope = opam-nix.queryToScope { inherit repos pkgs; } { frama-c = null; diff --git a/examples/mina.nix b/examples/mina.nix index 5ebd3ca..497b45f 100644 --- a/examples/mina.nix +++ b/examples/mina.nix @@ -1,6 +1,7 @@ +inputs: +pkgs: let - pkgs = import { }; - opam-nix = import ../. pkgs; + opam-nix = inputs.self.lib.${pkgs.system}; mina = pkgs.fetchFromGitHub { owner = "minaprotocol"; @@ -11,7 +12,7 @@ let }; repos = { default = pkgs.runCommand "opam-repository-depext-fix" { - src = pkgs.fetchFromGitHub (pkgs.lib.importJSON ./opam-repository.json); + src = inputs.opam-repository; } '' cp --no-preserve=all -R $src $out sed 's/available: .*//' -i $out/packages/depext/depext.transition/opam @@ -77,10 +78,13 @@ let version = "dev"; src = mina; buildInputs = unique' (deps ++ propagatedExternalBuildInputs); - nativeBuildInputs = [ self.dune self.ocamlfind ]; + nativeBuildInputs = [ self.dune self.ocamlfind pkgs.cargo pkgs.rustc ]; - buildPhase = - "dune build src/app/logproc/logproc.exe src/app/cli/src/mina.exe"; + buildPhase = '' + sed 's/mina_version.normal/mina_version.dummy/' -i src/lib/mina_version/dune + sed 's,/usr/local/lib/librocksdb_coda.a,${pkgs.rocksdb}/lib/librocksdb.a,' -i src/external/ocaml-rocksdb/dune + dune build src/app/logproc/logproc.exe src/app/cli/src/mina.exe + ''; installPhase = '' mkdir -p $out/bin mv src/app/logproc/logproc.exe src/app/cli/src/mina.exe $out/bin diff --git a/examples/opam-repository.json b/examples/opam-repository.json deleted file mode 100644 index aef1fb3..0000000 --- a/examples/opam-repository.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "owner": "ocaml", - "repo": "opam-repository", - "rev": "ef416cdfe8d909b9ee4b9df995691452f8fe5c0f", - "sha256": "9gix8qtGQlHdBpE2lBuWSrOlu0cT6g3Mxentv/5r5t8=", - "fetchSubmodules": true -} diff --git a/examples/opam2json.nix b/examples/opam2json.nix index 1e27f9d..fca8712 100644 --- a/examples/opam2json.nix +++ b/examples/opam2json.nix @@ -1,10 +1,10 @@ +inputs: +pkgs: let - pkgs = import { }; - opam-nix = import ../. pkgs; + opam-nix = inputs.self.lib.${pkgs.system}; repos = { - default = pkgs.fetchFromGitHub (pkgs.lib.importJSON ./opam-repository.json); - # Only uses the source, not the derivation itself - opam2json = opam-nix.makeOpamRepo (pkgs.ocamlPackages.callPackage ../opam2json.nix {}).src; + default = inputs.opam-repository; + opam2json = opam-nix.makeOpamRepo inputs.opam2json; }; scope = opam-nix.queryToScope { inherit repos pkgs; } { opam2json = null; diff --git a/examples/tezos.nix b/examples/tezos.nix index fc52bd7..0f4d664 100644 --- a/examples/tezos.nix +++ b/examples/tezos.nix @@ -1,14 +1,11 @@ +inputs: +pkgs: let - pkgs = import { }; - opam-nix = import ../. pkgs; + opam-nix = inputs.self.lib.${pkgs.system}; repos = { - default = pkgs.fetchFromGitHub (pkgs.lib.importJSON ./opam-repository.json); + default = inputs.opam-repository; - tezos = opam-nix.makeOpamRepo (pkgs.fetchgit { - url = "https://gitlab.com/tezos/tezos.git"; - rev = "7a8c3312f7f02d8c143164352ce8564f856ddcd5"; # v12.0-rc1 - sha256 = "sha256-1/u8tWP0yuB0omCq54Lfp+Rkr/IUUV21RjUt7hGdU+8="; - }); + tezos = opam-nix.makeOpamRepo inputs.tezos; }; scope = opam-nix.queryToScope { inherit repos pkgs; } { tezos = null; diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..dbaf510 --- /dev/null +++ b/flake.lock @@ -0,0 +1,128 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1627913399, + "narHash": "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "12c64ca55c1014cdc1b16ed5a804aa8576601ff2", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1638122382, + "narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "74f7e4319258e287b0f9cb95426c9853b282730b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "mina": { + "flake": false, + "locked": { + "lastModified": 1636041719, + "narHash": "sha256-nl3Z3EFftaCinrhY1UKn3fqXGOJeHUcoE9wbHQCvMnw=", + "owner": "minaprotocol", + "repo": "mina", + "rev": "af76cb7980d5e81e704120290a850ea9c6f8522e", + "type": "github" + }, + "original": { + "owner": "minaprotocol", + "repo": "mina", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1640319671, + "narHash": "sha256-ZkKmakwaOaLiZOpIZWbeJZwap5CzJ30s4UJTfydYIYc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "eac07edbd20ed4908b98790ba299250b5527ecdf", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "opam-repository": { + "flake": false, + "locked": { + "lastModified": 1640349521, + "narHash": "sha256-8ooQFVDFg1WOlGivMueU2HH0E1qfthJMkOMhUAAqjyA=", + "owner": "ocaml", + "repo": "opam-repository", + "rev": "c7b45b3cbcfbe22a7b3b8af1e5bb9998f3aa0e8a", + "type": "github" + }, + "original": { + "owner": "ocaml", + "repo": "opam-repository", + "type": "github" + } + }, + "opam2json": { + "flake": false, + "locked": { + "lastModified": 1639407772, + "narHash": "sha256-r55h5B9mRQ9JM/82XZNrkO4/0HyiP2Wt1S7IZtcupCc=", + "owner": "tweag", + "repo": "opam2json", + "rev": "db0cecf937f5f57ec1149120e56652816d8a1b51", + "type": "github" + }, + "original": { + "owner": "tweag", + "repo": "opam2json", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "mina": "mina", + "nixpkgs": "nixpkgs", + "opam-repository": "opam-repository", + "opam2json": "opam2json", + "tezos": "tezos" + } + }, + "tezos": { + "flake": false, + "locked": { + "lastModified": 1640270007, + "narHash": "sha256-zwten69tEsrMNr0zXdoAaj/j9o3Zsh8y5UcE004+nmc=", + "owner": "tezos", + "repo": "tezos", + "rev": "1a27b35a255f095cbda2efb98464637d00694ef1", + "type": "gitlab" + }, + "original": { + "owner": "tezos", + "repo": "tezos", + "type": "gitlab" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4346bfa --- /dev/null +++ b/flake.nix @@ -0,0 +1,39 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + opam2json = { + url = "github:tweag/opam2json"; + flake = false; + }; + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + + # Used for examples/tests + opam-repository = { + url = "github:ocaml/opam-repository"; + flake = false; + }; + tezos = { + url = "gitlab:tezos/tezos"; + flake = false; + }; + mina = { + url = "github:minaprotocol/mina"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, flake-utils, ... }@inputs: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + opam-nix = import ./opam.nix inputs pkgs; + in { + lib = opam-nix; + + checks = import ./examples inputs pkgs; + }); +} diff --git a/opam.nix b/opam.nix new file mode 100644 index 0000000..c5db38d --- /dev/null +++ b/opam.nix @@ -0,0 +1,259 @@ +# Pkgset = { ${name} = { ${version} = Pkgdef; ... } ... } +# Pkgdef = { name = String; version = String; depends = [OpamVar]; build = ?[[String]]; install = ?[[String]]; ... } + +inputs: +pkgs: +let + inherit (builtins) + readDir mapAttrs concatStringsSep concatMap all isString isList elem + attrValues filter attrNames head elemAt splitVersion foldl' fromJSON + listToAttrs readFile getAttr toFile match isAttrs pathExists; + inherit (pkgs) lib; + inherit (lib) + versionAtLeast splitString tail mapAttrs' nameValuePair zipAttrsWith collect + filterAttrs unique subtractLists concatMapStringsSep concatLists reverseList + fileContents pipe makeScope optionalAttrs filterAttrsRecursive hasSuffix + converge mapAttrsRecursive hasAttr composeManyExtensions; + + readDirRecursive = dir: + mapAttrs (name: type: + if type == "directory" then readDirRecursive "${dir}/${name}" else type) + (readDir dir); + + # [Pkgset] -> Pkgset + mergePackageSets = zipAttrsWith (_: foldl' (a: b: a // b) { }); + + bootstrapPackagesStub = import ./bootstrapPackages.nix { }; + + bootstrapPackageNames = attrNames bootstrapPackagesStub; +in rec { + # filterRelevant (traverseOPAMRepository ../../opam-repository) "opam-ed" + opam2json = pkgs.ocaml-ng.ocamlPackages_4_09.callPackage (import ./opam2json.nix inputs.opam2json) { }; + + # Path -> {...} + fromOPAM = opamFile: + let + json = pkgs.runCommandNoCC "opam.json" { + preferLocalBuild = true; + allowSubstitutes = false; + } "${opam2json}/bin/opam2json ${opamFile} > $out"; + in fromJSON (readFile json); + + fromOPAM' = opamText: fromOPAM (toFile "opam" opamText); + + # Pkgdef -> Derivation + pkgdef2drv = import ./pkgdef2drv.nix pkgs; + + # Path -> Derivation + opam2nix = { opamFile, name ? null, version ? null }: + pkgdef2drv (fromOPAM opamFile // { inherit name version; }); + + splitNameVer = nameVer: + let nv = nameVerToValuePair nameVer; + in { inherit (nv) name version; }; + + nameVerToValuePair = nameVer: + let split = splitString "." nameVer; + in nameValuePair (head split) (concatStringsSep "." (tail split)); + + ops = { + eq = "="; + gt = ">"; + lt = "<"; + geq = ">="; + leq = "<="; + neq = "!="; + }; + + global-variables = import ./global-variables.nix pkgs; + + opamListToQuery = list: listToAttrs (map nameVerToValuePair list); + + opamList = repos: packages: + let + opam-root = pkgs.runCommand "opamroot" { + nativeBuildInputs = [ pkgs.opam ]; + OPAMNO = "true"; + } '' + export OPAMROOT=$out + + mkdir -p $NIX_BUILD_TOP/repos + + ${concatStringsSep "\n" (attrValues (mapAttrs (name: repo: + "cp -R --no-preserve=all ${repo} $NIX_BUILD_TOP/repos/${name}") + repos))} + + opam init --bare default $NIX_BUILD_TOP/repos/default --disable-sandboxing --disable-completion -n --bypass-checks + ${concatStringsSep "\n" (attrValues (mapAttrs (name: repo: + "opam repository add --set-default ${name} $NIX_BUILD_TOP/repos/${name}") + (lib.filterAttrs (name: _: name != "default") repos)))} + ''; + + pkgRequest = name: version: + if isNull version then name else "${name}.${version}"; + + resolve-drv = pkgs.runCommand "resolve" { + nativeBuildInputs = [ pkgs.opam ]; + OPAMNO = "true"; + OPAMCLI = "2.0"; + } '' + export OPAMROOT=$NIX_BUILD_TOP/opam + + cp -R --no-preserve=all ${opam-root} $OPAMROOT + + opam list --resolve=${ + concatStringsSep "," (attrValues (mapAttrs pkgRequest packages)) + } --no-switch --short --with-test --depopts --columns=package > $out + ''; + solution = fileContents resolve-drv; + + lines = s: splitString "\n" s; + + in lines solution; + + makeOpamRepo = dir: + let + files = readDirRecursive dir; + opamFiles = filterAttrsRecursive + (name: value: isAttrs value || hasSuffix "opam" name) files; + opamFilesOnly = + converge (filterAttrsRecursive (_: v: v != { })) opamFiles; + packages = collect isList (mapAttrsRecursive (path: _: + let + fileName = lib.last path; + dirName = lib.last (lib.init path); + # We try to avoid reading this opam file if possible + name = if fileName == "opam" then + (fromOPAM "${dir + ("/" + concatStringsSep "/" path)}").name or dirName + else + lib.removeSuffix ".opam" (lib.last path); + + in [ + { + name = "sources/${name}/${name}.local"; + path = "${dir + ("/" + concatStringsSep "/" (lib.init path))}"; + } + { + name = "packages/${name}/${name}.local/opam"; + path = "${dir + ("/" + concatStringsSep "/" path)}"; + } + ]) opamFilesOnly); + repo-description = { + name = "repo"; + path = toFile "repo" ''opam-version: "2.0"''; + }; + repo = pkgs.linkFarm "opam-repo" + ([ repo-description ] ++ concatLists packages); + in repo; + + queryToDefs = repos: packages: + let + # default has the lowest prio + repos' = (attrValues (filterAttrs (name: _: name != "default") repos)) + ++ [ repos.default ]; + + findPackage = name: version: + head (filter ({ opamFile, ... }: pathExists opamFile) (map (repo: + let + sourcePath = "${repo}/sources/${name}/${name}.local"; + isLocal = pathExists sourcePath; + pkgDir = "${repo}/packages/${name}/${name}.${version}"; + filesPath = "${pkgDir}/files"; + in { + opamFile = "${pkgDir}/opam"; + inherit name version isLocal repo; + src = if isLocal then + pkgs.runCommand "source-copy" { } + "cp --no-preserve=all -R ${sourcePath}/ $out" + else + pkgs.emptyDirectory; + } // optionalAttrs (pathExists filesPath) { files = filesPath; }) + repos')); + + packageFiles = mapAttrs findPackage packages; + in mapAttrs + (_: { opamFile, name, version, ... }@args: args // (fromOPAM opamFile)) + packageFiles; + + queryToDefs' = repos: packages: + let + # default has the lowest prio + repos' = (attrValues (filterAttrs (name: _: name != "default") repos)) + ++ [ repos.default ]; + + findPackage = name: version: + head (filter ({ dir, ... }: pathExists dir) (map (repo: { + dir = "${repo}/packages/${name}/${name}.${version}"; + inherit name version; + src = repo.passthru.origSrc or pkgs.emptyDirectory; + }) repos')); + + packageFiles = mapAttrs (_: + { dir, name, version, src }: + { + inherit name version src; + opamFile = "${dir}/opam"; + } // optionalAttrs (pathExists "${dir}/files") { + files = "${dir}/files"; + }) (mapAttrs findPackage packages); + + readPackageFiles = '' + ( + echo '[' + ${concatMapStringsSep '' + + echo ',' + '' ({ opamFile, name, version, src, files ? null }: + '' + opam2json ${opamFile} | jq '.name = "${name}" | .version = "${version}" | .opamFile = "${opamFile}" | .src = "${src}"${ + lib.optionalString (!isNull files) ''| .files = "${files}"'' + }' '') (attrValues packageFiles)} + echo ']' + ) > $out + ''; + + pkgdefs = pkgs.runCommand "opam2json-many.json" { + nativeBuildInputs = [ opam2json pkgs.jq ]; + } readPackageFiles; + + listToAttrsByName = lst: + listToAttrs (map (x: nameValuePair x.name x) lst); + in listToAttrsByName (fromJSON (readFile pkgdefs)); + + defsToScope = repos: pkgs: packages: + makeScope pkgs.newScope (self: + (mapAttrs (name: pkg: self.callPackage (pkgdef2drv pkg) { }) packages) + // (import ./bootstrapPackages.nix pkgs + packages.ocaml.version or packages.ocaml-base-compiler.version)); + + defaultOverlay = import ./overlay.nix; + + applyOverlays = overlays: scope: + scope.overrideScope' (composeManyExtensions overlays); + + queryToScope = { repos, pkgs, overlays ? [ defaultOverlay ] }: + query: + pipe query [ + (opamList repos) + (opamListToQuery) + (queryToDefs repos) + (defsToScope repos pkgs) + (applyOverlays overlays) + ]; + + opamImport = { repos, pkgs }: + export: + let + installedList = (fromOPAM export).installed; + set = pipe installedList [ + opamListToQuery + (queryToDefs repos) + (defsToScope repos pkgs) + ]; + installedPackageNames = map (x: (splitNameVer x).name) installedList; + combined = pkgs.symlinkJoin { + name = "opam-switch"; + paths = attrValues (lib.getAttrs installedPackageNames set); + }; + in combined; +} diff --git a/opam2json.nix b/opam2json.nix index 8f75508..d166288 100644 --- a/opam2json.nix +++ b/opam2json.nix @@ -1,15 +1,10 @@ +src: { stdenv, fetchFromGitHub, opam-installer, ocaml, findlib, yojson, opam-file-format, cmdliner }: stdenv.mkDerivation { pname = "opam2json"; version = "0.1"; - src = fetchFromGitHub { - owner = "tweag"; - repo = "opam2json"; - rev = "db0cecf937f5f57ec1149120e56652816d8a1b51"; - sha256 = "r55h5B9mRQ9JM/82XZNrkO4/0HyiP2Wt1S7IZtcupCc="; - fetchSubmodules = true; - }; + inherit src; buildInputs = [ yojson opam-file-format cmdliner ]; nativeBuildInputs = [ ocaml findlib opam-installer ];