diff --git a/cabal2nix/hackage2nix/Main.hs b/cabal2nix/hackage2nix/Main.hs index 370f3324..231c0f1e 100644 --- a/cabal2nix/hackage2nix/Main.hs +++ b/cabal2nix/hackage2nix/Main.hs @@ -81,12 +81,7 @@ main = do config <- sconcat <$> mapM (\file -> readConfiguration (nixpkgsRepository file)) configFiles nixpkgs <- readNixpkgPackageMap nixpkgsRepository (Just "{ config = { allowAliases = false; }; }") preferredVersions <- readPreferredVersions (fromMaybe (hackageRepository "preferred-versions") preferredVersionsFile) - let fixup = Map.delete "acme-everything" -- TODO: https://github.com/NixOS/cabal2nix/issues/164 - . Map.delete "type" -- TODO: https://github.com/NixOS/cabal2nix/issues/163 - . Map.delete "control-invariants" -- TODO: depends on "assert" - . Map.delete "ConcurrentUtils" -- TODO: depends on "assert" - . Map.delete "with" -- TODO: https://github.com/NixOS/cabal2nix/issues/164 - . over (at "hermes") (fmap (set (contains "1.3.4.3") False)) -- TODO: https://github.com/haskell/hackage-server/issues/436 + let fixup = over (at "hermes") (fmap (set (contains "1.3.4.3") False)) -- TODO: https://github.com/haskell/hackage-server/issues/436 hackage <- fixup <$> readHackage hackageRepository let hackagePackagesFile :: FilePath diff --git a/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs b/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs index 8212e358..c97aa1f6 100644 --- a/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs +++ b/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs @@ -8,10 +8,81 @@ import Distribution.Package import Distribution.Text import Language.Nix --- | Map Cabal names to Nix attribute names. +-- | Map Cabal names to Nix identifiers that don't need to be quoted. +-- +-- Currently this only supports 'PackageName's that consist of nothing but ASCII +-- characters (as needs to be the case with all Hackage packages). +-- Cabal package names are not changed if they already are a Nix identifier +-- that doesn't need quoting (with some notable exceptions). If they would need +-- quoting, they are prefixed with an underscore. +-- +-- >>> toNixName $ mkPackageName "cabal2nix" +-- Identifier "cabal2nix" +-- >>> toNixName $ mkPackageName "4Blocks" +-- Identifier "_4Blocks" +-- >>> toNixName $ mkPackageName "assert" +-- Identifier "_assert" +-- +-- Package names that clash with attribute names that have a special meaning +-- to the Nix evaluator are also prefixed (e.g. +-- [@type@ is evaluated eagerly]((https://github.com/NixOS/cabal2nix/issues/163)). +-- +-- The mapping is intended to be reversible, but this isn't implemented by +-- @cabal2nix@ yet (and untested). It also should not be considered +-- stable yet, in particular the following may be changed: +-- +-- - Future versions of @cabal2nix@ may prefix more 'PackageName's. +-- - The mapping may be extended to support all possible 'PackageName's. +-- +-- See also: +-- +-- - [Cabal documentation on the package name field](https://cabal.readthedocs.io/en/stable/cabal-package-description-file.html#pkg-field-name) +-- - "Language.Nix.Identifier" +-- - [Nix documentation on identifiers](https://nix.dev/manual/nix/2.30/language/identifiers.html#identifier) toNixName :: PackageName -> Identifier -toNixName "" = error "toNixName: invalid empty package name" -toNixName n = fromString (unPackageName n) +toNixName n = fromString $ + case unPackageName n of + "" -> error "toNixName: BUG: received empty package name" + '_':_ -> error "toNixName: BUG: PackageName starts with an underscore, but shouldn't" + name + -- From the Cabal documentation: + -- + -- A valid package name comprises an alphanumeric ‘word’; or two or more + -- such words separated by a hyphen character (-). A word cannot be + -- comprised only of the digits 0 to 9. + -- + -- Cabal also latin unicode characters while Hackage enforces that package + -- names are ASCII. + -- + -- If the package name comes from Hackage, the set of legal characters + -- ([a-zA-Z0-9-]) is a subset of those permissible as a Nix identifier + -- without quoting ([a-zA-Z0-9_'-]). The main difference are the rules + -- governing what may go where. In the following cases a Hackage package + -- name is not a simple identifier and 'needsQuoting' returns True: + -- + -- - if the first “word” of the package name starts with a number, e.g. 4Blocks. + -- - if the package name is the same one of the 'nixKeywords'. + -- + -- If we prefix these strings with an underscore, they no longer need quoting. + -- Because Cabal 'PackageName's may not contain underscores this mapped name + -- can never clash. (Reversing the mapping is very simple at the moment as + -- a result.) + -- + -- We additionally prefix perfectly usable identifiers like type and + -- recurseForDerivations if they have special meaning to the Nix evaluator + -- (or Hydra etc.) since it may cause evaluation failures if we expose a + -- package under haskellPackages instead of whatever value(s) Nix may + -- expect. + -- + -- TODO: Add mapping for non-ASCII 'PackageName's, using __ prefix (?) + | needsQuoting name || name `elem` haveSpecialSemantics -> '_':name + | otherwise -> name + where + -- Special attributes that affect the behavior of the Nix evaluator in some way. + -- See https://github.com/NixOS/cabal2nix/issues/163. + -- We can ignore underscore prefixed attrs like __toString, __functor. + -- Only type is the name of a real package at the moment. + haveSpecialSemantics = [ "type", "outPath", "recurseForDerivations" ] -- | Map library names specified in Cabal files to Nix package identifiers. -- diff --git a/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Normalize.hs b/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Normalize.hs index 9b405049..a59aafcd 100644 --- a/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Normalize.hs +++ b/cabal2nix/src/Distribution/Nixpkgs/Haskell/FromCabal/Normalize.hs @@ -6,6 +6,7 @@ import Control.Lens import qualified Data.Set as Set import Data.String import Distribution.Nixpkgs.Haskell +import Distribution.Nixpkgs.Haskell.FromCabal.Name (toNixName) import Distribution.Nixpkgs.Meta import Distribution.Package import Language.Nix hiding ( quote ) @@ -21,8 +22,8 @@ normalize drv = drv normalizeBuildInfo :: PackageName -> BuildInfo -> BuildInfo normalizeBuildInfo pname bi = bi - & haskell %~ Set.filter (\b -> view localName b /= fromString (unPackageName pname)) - & tool %~ Set.filter (\b -> view localName b /= fromString (unPackageName pname)) + & haskell %~ Set.filter (\b -> view localName b /= toNixName pname) + & tool %~ Set.filter (\b -> view localName b /= toNixName pname) normalizeMeta :: Meta -> Meta normalizeMeta meta = meta