From e3698e76a2172ff7ab4c69347d3ec3741b7f4679 Mon Sep 17 00:00:00 2001 From: Teo Camarasu Date: Thu, 18 Apr 2024 14:57:25 +0100 Subject: [PATCH] Support hpack include directives Add a `generateCabalFile` argument that allows us to pre-genereate the cabal file, thus allowing us to create an environment where hpack can successfully find included files. We also just read the package.yaml file directly to get the name rather than by calling readYAML, since include directives lead to unparseable YAML. Resolves #47 --- nix/build-support/stacklock2nix/default.nix | 49 ++++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/nix/build-support/stacklock2nix/default.nix b/nix/build-support/stacklock2nix/default.nix index 6c1e3f5..4d83640 100644 --- a/nix/build-support/stacklock2nix/default.nix +++ b/nix/build-support/stacklock2nix/default.nix @@ -133,7 +133,15 @@ # defaultLocalPkgFilter: pkgName: path: type: true # ``` localPkgFilter ? - defaultLocalPkgFilter: pkgName: path: type: defaultLocalPkgFilter path type + defaultLocalPkgFilter: pkgName: path: type: defaultLocalPkgFilter path type, + # A function to generate a cabal file for a local package. + # Normally this is handled by `cabal2nix`, but in special cases we might want + # to do this ourselves, eg, when using hpack `!include`s. + # + # + # generateCabalFile :: String -> String -> Path -> Path + generateCabalFile ? + pkgName: subdir: filteredSrc: filteredSrc }: # The stack.yaml path can be computed from the stack.yaml.lock path, or @@ -558,15 +566,19 @@ let # Example: `"my-cool-pkg"` pkgNameFromPackageYaml = let - packageYaml = readYAML (justCabalFilePath + "/package.yaml"); - in - if packageYaml ? name then - packageYaml.name - else - throw - ("Could not find read a .name field from the package.yaml file in package ${localPkgPathStr}. " + - "This is unexpected. All package.yaml files should have a top-level .name field. " + - "package.yaml file: ${builtins.toJSON packageYaml}"); + packageYamlName = builtins.readFile + (runCommand "read-package-name" {} + '' + function fail { + echo "Could not find read a .name field from the package.yaml file in package ${localPkgPathStr}." + echo "This is unexpected. All package.yaml files should have a top-level .name field." + echo "package.yaml file: " + cat package.yaml + exit 1 + } + (sed -nr 's/name:\s*\"?(.*)\"?\s*/\1/p' ${justCabalFilePath + "/package.yaml"} | tr -d '\n' >$out) || fail'' + ); + in packageYamlName; # Whether or not this package has at least one .cabal file. # @@ -626,12 +638,25 @@ let # Path to the Haskell package. # - # pkgPath :: Path - pkgPath = lib.cleanSourceWith { + # filteredPkgPath :: Path + filteredPkgPath = lib.cleanSourceWith { src = rawPkgPath; filter = path: type: localPkgFilter localPkgDefaultFilter pkgName path type; name = "stacklock2nix-pkg-sources-" + pkgName; }; + + # Path to the Haskell package, + # but if it doesn't contain a .cabal file + # then we call the `generateCabalFile` function to potentially pre-generate + # the .cabal file. This is useful if the `package.yaml` file needs special + # treatment, eg, because of includes. + # + # pkgPath :: Path + pkgPath = + if hasSingleCabalFile then + filteredPkgPath + else + generateCabalFile pkgName localPkgPathStr filteredPkgPath; in { inherit pkgPath pkgName; };