diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,74 @@
+# Revision history for distribution-nixpkgs
+
+## 1.7.1.1
+
+* Adapt test suite to added representation of `RISCV64` and
+  `LoongArch64` architectures in Cabal 3.12.
+
+## 1.7.1
+
+* GHC and nixpkgs now
+  [use the system double `javascript-ghcjs`](https://github.com/NixOS/nixpkgs/commit/471b9cab41b218080f5e9f4abbc83eaaa60c6abf)
+  instead of `js-ghcjs` to match Cabal's default rendering. With this release,
+  `distribution-nixpkgs` does the same.
+  Note that this means that this release needs to be used with nixpkgs unstable
+  after [97d55ec](https://github.com/NixOS/nixpkgs/commit/97d55ec923b0cd3798a6a84e2e0a6b2c6b54f6a9)
+  or nixpkgs stable 23.05 or newer.
+
+## 1.7.0.1
+
+* Adapt test suite to changed representation of some `Platform`s in Cabal 3.8.
+* Update test data to include new nixpkgs architectures `rx`, `microblaze` and
+  `microblazeel`.
+* The distribution-nixpkgs repository has been merged into the
+  [cabal2nix](https://github.com/NixOS/cabal2nix) repository.
+  All URLs in the cabal file have been updated and the distribution-nixpkgs
+  repository will be archived.
+
+## 1.7.0
+
+* `Distribution.Nixpkgs.Meta`
+  * `pPrint (x :: Meta)` now renders every maintainer as a full attribute
+    path instead of using `with`.
+  * **API breaking change**: Changed types for all platform related fields /
+    lenses of `Meta`, i.e. `platforms` and `hydraPlatforms`.
+    * They are now wrapped in a `Maybe`. If `Nothing`, the respective attribute
+      is not rendered.
+    * Instead of `Platform` we now use `NixpkgsPlatform` which may also be a
+      `NixpkgsPlatformGroup`, like `lib.platforms.linux`, in addition to the
+      wrapped `Platform` in `NixpkgsPlatformSingle`.
+  * **API breaking change**: Add new fields / lenses to `Meta`:
+    * `badPlatforms`: Allows setting a list of unsupported platforms, works
+      similarly to the `platform` / `hydraPlatform` lenses.
+    * `mainProgram`: Allows setting the (base-)name of the main binary of a
+      derivation. Not rendered if `Nothing`.
+  * **API breaking change**: Remove `allKnownPlatforms`, its previous use can be
+    replaced using `badPlatforms`.
+  * Added `nixpkgsPlatformFromString` which parses a specific format used by
+    hackage2nix's config files into either `NixpkgsPlatformSingle` or
+    `NixpkgsPlatformGroup`.
+* `Language.Nix.PrettyPrinting`: Added new helpers, `listattrDoc` and
+  `toAscListSortedOn`.
+
+## 1.6.2
+
+* Expect `MonadFailDesugaring` (or equivalent behavior) to be the default,
+  requiring GHC >= 8.6.1. Adds support for GHC 9.2.1 (rc1).
+
+## 1.6.1
+
+* `Distribution.Nixpkgs.Meta`: support `aarch64-darwin`
+  * Render `Platform AArch64 OSX` to `"aarch64-darwin"`
+  * Add `Platform AArch64 OSX` to `allKnownPlatforms`
+
+## 1.6.0
+
+* `Distribution.Nixpkgs.PackageMap`
+  * `readNixpkgPackageMap`: instead of a list of arguments to pass to
+    `nix-env`, take path to nixpkgs and an optional nix expression
+    to pass to it as arguments.
+  * `readNixpkgPackageMap`: populate `PackageMap` with *all* attribute
+    paths that point to derivations instead of just one per derivation.
+    This fixes `resolve` not finding certain identifiers if there were
+    two attributes pointing to the same derivation in nixpkgs. See also
+    [#9](https://github.com/NixOS/distribution-nixpkgs/issues/9).
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+# distribution-nixpkgs
+
+[![hackage release](https://img.shields.io/hackage/v/distribution-nixpkgs.svg?label=hackage)](http://hackage.haskell.org/package/distribution-nixpkgs)
+[![stackage LTS package](http://stackage.org/package/distribution-nixpkgs/badge/lts)](http://stackage.org/lts/package/distribution-nixpkgs)
+
+## Maintainer's Notes
+
+### Keeping platform test cases up to date
+
+As nixpkgs expands its list of supported platforms, new system tuples are added.
+Therefore we need to adjust the test suite regularly in order to cover newly
+added platforms. For this purpose, the test suite checks the platforms it verifies
+against a list of system tuples generated from nixpkgs. This list needs to be
+updated manually, so running the test suite doesn't depend on Nix being available.
+
+To check if any new platforms need to be covered, do the following:
+
+1. Run `./test/data/regenerate-all-system-tuples.sh /path/to/nixpkgs/checkout`.
+   If no local nixpkgs is given, `<nixpkgs>` will be used.
+
+2. Run the test suite: `cabal v2-test`.
+
+3. If the test suite fails, add test cases for all missing system tuples.
+
+4. In all cases, change the date of last update in
+   `src/Distribution/Nixpkgs/Meta.hs` to the current day.
diff --git a/derivation-attr-paths.nix b/derivation-attr-paths.nix
new file mode 100644
--- /dev/null
+++ b/derivation-attr-paths.nix
@@ -0,0 +1,42 @@
+{ nixpkgsPath ? <nixpkgs>
+, nixpkgsArgs ? {}
+}@args:
+
+let
+  pkgs = import nixpkgsPath nixpkgsArgs;
+
+  inherit (pkgs) lib;
+
+  /* Condition for us to recurse:
+     Either we are at the top-level or
+     recurseForDerivations is true.
+
+     Type :: list any -> any -> bool
+  */
+  recurseInto = path: x: path == [] ||
+    (lib.isAttrs x && (x.recurseForDerivations or false));
+
+  /* Takes the nixpkgs set and returns all attribute paths
+     to reachable derivations within it as a list of lists of
+     strings.
+
+     Type :: attrs -> list (list string)
+  */
+  derivationPaths =
+    let
+      go = path: x:
+        let
+          inherit (builtins.tryEval x)
+            value
+            success
+            ;
+        in
+          if !success then []
+          else if lib.isDerivation value then [
+            path
+          ] else if recurseInto path value then lib.concatLists (
+            lib.mapAttrsToList (n: go (path ++ [ n ])) value
+          ) else [];
+    in go [];
+in
+  derivationPaths pkgs
diff --git a/distribution-nixpkgs.cabal b/distribution-nixpkgs.cabal
--- a/distribution-nixpkgs.cabal
+++ b/distribution-nixpkgs.cabal
@@ -1,20 +1,26 @@
 name:          distribution-nixpkgs
-version:       1.2
+version:       1.7.1.1
 synopsis:      Types and functions to manipulate the Nixpkgs distribution
 description:   Types and functions to represent, query, and manipulate the Nixpkgs distribution.
 license:       BSD3
 license-file:  LICENSE
-maintainer:    Peter Simons <simons@cryp.to>
-tested-with:   GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.3
+author:        Peter Simons <simons@cryp.to>
+maintainer:    sternenseemann <sternenseemann@systemli.org>
+tested-with:   GHC == 8.10.7 || == 9.0.2 || == 9.2.8 || == 9.4.8 || == 9.6.6 || == 9.8.3 || == 9.10.1
 category:      Distribution, Nix
-homepage:      https://github.com/peti/distribution-nixpkgs#readme
-bug-reports:   https://github.com/peti/distribution-nixpkgs/issues
+homepage:      https://github.com/NixOS/cabal2nix/tree/master/distribution-nixpkgs#readme
+bug-reports:   https://github.com/NixOS/cabal2nix/issues
 build-type:    Simple
-cabal-version: >= 1.10
+cabal-version: 1.18
+data-files:    derivation-attr-paths.nix
+extra-doc-files:    CHANGELOG.md
+                    README.md
+extra-source-files: test/data/all-system-tuples.json
 
 source-repository head
   type:     git
-  location: https://github.com/peti/distribution-nixpkgs
+  location: https://github.com/NixOS/cabal2nix
+  subdir:   distribution-nixpkgs
 
 library
   exposed-modules:    Distribution.Nixpkgs.Hashes
@@ -22,9 +28,10 @@
                       Distribution.Nixpkgs.Meta
                       Distribution.Nixpkgs.PackageMap
                       Language.Nix.PrettyPrinting
+  other-modules:      Paths_distribution_nixpkgs
   hs-source-dirs:     src
   build-depends:      base         > 4.2 && < 5
-                    , Cabal        > 2.2
+                    , Cabal        >= 2.4
                     , aeson
                     , bytestring
                     , containers
@@ -33,9 +40,7 @@
                     , lens
                     , pretty       >= 1.1.2
                     , process
-                    , split
   default-language:   Haskell2010
-  default-extensions: MonadFailDesugaring
   other-extensions:   CPP
                       TemplateHaskell
                       RecordWildCards
@@ -48,8 +53,15 @@
   type:               exitcode-stdio-1.0
   main-is:            hspec.hs
   hs-source-dirs:     test
-  build-depends:      base, deepseq, distribution-nixpkgs, hspec, lens
+  build-depends:      base
+                    , deepseq
+                    , distribution-nixpkgs
+                    , language-nix
+                    , hspec
+                    , lens
+                    , Cabal
+                    , aeson
+                    , directory
   default-language:   Haskell2010
-  default-extensions: MonadFailDesugaring
   ghc-options:        -Wall -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates
                       -Wredundant-constraints
diff --git a/src/Distribution/Nixpkgs/License.hs b/src/Distribution/Nixpkgs/License.hs
--- a/src/Distribution/Nixpkgs/License.hs
+++ b/src/Distribution/Nixpkgs/License.hs
@@ -2,8 +2,8 @@
 
 {- |
    Known licenses in Nix expressions are represented using the
-   attributes defined in @pkgs\/lib\/licenses.nix@, and unknown licenses
-   are represented as a literal string.
+   attributes defined in nixpkgs' @lib\/licenses.nix@, and
+   unknown licenses are represented as a literal string.
  -}
 
 module Distribution.Nixpkgs.License ( License(..) ) where
@@ -14,27 +14,28 @@
 import Language.Nix.PrettyPrinting
 
 -- | The representation for licenses used in Nix derivations. Known
--- licenses are Nix expressions -- such as @stdenv.lib.licenses.bsd3@
--- --, so their exact \"name\" is not generally known, because the path
--- to @stdenv@ depends on the context defined in the expression. In
--- Cabal expressions, for example, the BSD3 license would have to be
--- referred to as @self.stdenv.lib.licenses.bsd3@. Other expressions,
--- however, use different paths to the @licenses@ record. Because of tat
+-- licenses are Nix expressions — such as @lib.licenses.bsd3@ —,
+-- so their exact name is not generally known, because the path
+-- to @lib@ depends on the context defined in the expression.
+--
+-- In Cabal expressions, for example, the BSD3 license would have to be
+-- referred to as @self.lib.licenses.bsd3@. Other expressions,
+-- however, use different paths to the @licenses@ record. Because of that
 -- situation, the library cannot provide an abstract data type that
--- encompasses all known licenses. Instead, the @License@ type just
--- distinguishes references to known and unknown licenses. The
--- difference between the two is in the way they are pretty-printed:
+-- encompasses all known licenses.
 --
--- > > putStrLn (display (Known "stdenv.lib.license.gpl2"))
--- > stdenv.lib.license.gpl2
--- >
--- > > putStrLn (display (Unknown (Just "GPL")))
--- > "GPL"
--- >
--- > > putStrLn (display (Unknown Nothing))
--- > "unknown"
+-- Instead, the @License@ type just distinguishes references to known
+-- and unknown licenses. The difference between the two is in the way
+-- they are pretty-printed:
 --
--- Note that the "Text" instance definition provides pretty-printing,
+-- >>> putStrLn (prettyShow (Known "lib.license.gpl2"))
+-- lib.license.gpl2
+-- >>> putStrLn (prettyShow (Unknown (Just "GPL")))
+-- "GPL"
+-- >>> putStrLn (prettyShow (Unknown Nothing))
+-- "unknown"
+--
+-- Note that the 'Pretty' instance definition provides pretty-printing,
 -- but no parsing as of now!
 
 data License = Known String
diff --git a/src/Distribution/Nixpkgs/Meta.hs b/src/Distribution/Nixpkgs/Meta.hs
--- a/src/Distribution/Nixpkgs/Meta.hs
+++ b/src/Distribution/Nixpkgs/Meta.hs
@@ -10,51 +10,265 @@
  -}
 
 module Distribution.Nixpkgs.Meta
-  ( Meta, nullMeta
-  , homepage, description, license, platforms, hydraPlatforms, maintainers, broken
-  , allKnownPlatforms
+  ( -- * Representation of the Nixpkgs Meta Set
+    Meta, nullMeta
+    -- ** Lenses for 'Meta'
+  , homepage, description, license, platforms, badPlatforms, hydraPlatforms, mainProgram, maintainers, broken
+    -- * Representation of Nixpkgs Platform Descriptions
+  , NixpkgsPlatform (..)
+  , nixpkgsPlatformFromString
   ) where
 
 -- Avoid name clash with Prelude.<> exported by post-SMP versions of base.
 #if MIN_VERSION_base(4,11,0)
 import Prelude hiding ( (<>) )
 #endif
+import Control.Applicative ( (<|>) )
 import Control.DeepSeq
-import Control.Lens
+import Control.Lens hiding ( Strict )
+import Data.List ( stripPrefix )
 import Data.Set ( Set )
 import qualified Data.Set as Set
 import Distribution.Nixpkgs.License
+import qualified Distribution.Pretty as CabalPretty
 import Distribution.System
 import GHC.Generics ( Generic )
 import Language.Nix.Identifier
+import Language.Nix.Path ( path )
 import Language.Nix.PrettyPrinting
 
+-- | Representation of platform(s) as supported by nixpkgs:
+--
+--     * 'NixpkgsPlatformGroup' represents the name of a platform
+--       list as found in @lib.platforms@. For example, at the
+--       time of writing @NixpkgsPlatformGroup "darwin"@ would
+--       represent the platform tuples @x86_64-darwin@, @aarch64-darwin@,
+--       @i686-darwin@ and @armv7a-darwin@. Naturally, this is
+--       subject to change as nixpkgs updates @lib.platforms@.
+--     * 'NixpkgsPlatformSingle' indicates a single platform tuple
+--       represented using Cabal's 'Platform'.
+--
+--   The former is useful to express related groups of
+--   platforms which have similar properties. The latter
+--   can be used to, for example, exclude a single, specific
+--   platform.
+--
+--   @hackage2nix@ has used the latter approach historically
+--   and is being extended to support nixpkgs' platform
+--   groups as well for increased maintainer convenience.
+--
+--   The 'Pretty' instance allows for converting a 'NixpkgsPlatform'
+--   into a Nix expression compatible with @meta.platforms@:
+--
+--   >>> pPrint $ NixpkgsPlatformSingle $ Platform X86_64 NetBSD
+--   "x86_64-netbsd"
+--
+--   For 'NixpkgsPlatformGroup' we assume that the @lib@ attribute set is in
+--   scope:
+--
+--   >>> pPrint $ NixpkgsPlatformGroup $ ident # "riscv"
+--   lib.platforms.riscv
+data NixpkgsPlatform
+  = NixpkgsPlatformSingle Platform
+  -- ^ Single platform represented as a Cabal platform. Can be understood as
+  -- equivalent to Nix's system strings and will be converted to one usually.
+  | NixpkgsPlatformGroup Identifier
+  -- ^ 'Identifier' of the attribute name of a platform
+  --   group in nixpkgs' @lib.platforms@.
+  deriving (Show, Eq, Ord, Generic)
+
+nixpkgsPlatformFromCabal :: Platform -> String
+nixpkgsPlatformFromCabal (Platform arch os) = "\"" ++ nixArch ++ "-" ++ nixOs ++ "\""
+  where nixArch =
+          case arch of
+            I386 -> "i686" -- rendered as i386 by default
+            PPC -> "powerpc" -- rendered as ppc by default
+            PPC64 -> "powerpc64" -- rendered as ppc64 by default
+            _    -> CabalPretty.prettyShow arch
+        nixOs =
+          case os of
+            OSX -> "darwin" -- rendered as osx by default
+            _   -> CabalPretty.prettyShow os
+
+-- | Obtain a 'NixpkgsPlatform' from a string representation intended for config
+--   files.
+--
+--   * Every string starting with @lib.platforms.@ or @platforms.@ is
+--     parsed into 'NixpkgsPlatformGroup'.
+--   * All other strings are attempted to be interpreted as a nix(pkgs) style
+--     system tuple and parsed into 'NixpkgsPlatformSingle'.
+--
+--   If none of these formats match the input 'String', 'Nothing' is returned.
+--   A 'Just' result thus only indicates that the format of the platform is
+--   sound — 'nixpkgsPlatformFromString' does /not/ check if the parsed platform
+--   actually exists.
+--
+--   'NixpkgsPlatformSingle' is parsed from system tuples as understood by Nix
+--   and nixpkgs. System tuples are derived from autoconf's
+--   [target triplets](https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.70/autoconf.html#Manual-Configuration),
+--   dropping the vendor part. They have the form @cpu-os@ where @os@ can either
+--   be a single component or of the form @kernel-system@ (system is an autoconf
+--   term here, not a Nix system). Note that three component systems are very
+--   rare. The two main candidates @x86_64-linux-musl@ and @x86_64-linux-gnu@
+--   are [prohibited for historical reasons](https://github.com/NixOS/nix/blob/ec07a70979a86cc436de7e46e03789b4606d25ab/configure.ac#L26-L28)
+--   and represented as plain @x86_64-linux@ instead.
+--
+--   Note that 'nixpkgsPlatformFromString' expects to receive a /valid/ system
+--   tuple, i.e. it will accept all system tuples that have a sound format
+--   (with the caveat that it will accept n-tuples for @n >= 4@ even though
+--   they are technically invalid). This is done because the ambiguity of
+--   system tuples requires knowledge over its legal contents in order to check
+--   their validity properly. Since @lib.systems.elaborate@ from nixpkgs is the
+--   source of truth in this case, we want to avoid the need to continuously
+--   update @distribution-nixpkgs@ to reflect its inner workings.
+--
+--   'nixpkgsPlatformFromString' does, however, some conversions to alleviate some
+--   discrepancies between Cabal and nixpkgs. Parsing and rendering system tuples
+--   using 'nixpkgsPlatformFromString' and rendering them via the 'Pretty'
+--   instance of 'NixpkgsPlatform' should not change the system tuple
+--   for tuples accepted by nixpkgs. This has been tested for all known tuples
+--   (from @lib.platforms@ and @lib.systems.examples@) as of 2024-12-01.
+--   Please open an issue if any newly added ones are not recognized properly.
+--
+--   __Warning__: 'nixpkgsPlatformFromString' consequently tries to preserve all
+--   information of the passed system tuple. This means that it distinguishes
+--   between things that Cabal wouldn't, e.g. `powerpc64` and `powerpc64le`. If
+--   you use this function to obtain a 'Platform' that is later used to evaluate
+--   a @.cabal@ file, it will behave unexpectedly in some situation. It is
+--   recommended to use Cabal's own facilities or
+--   @Distribution.Nixpkgs.Haskell.Platform@, provided by @cabal2nix@, instead.
+--
+--   'nixpkgsPlatformFromString' is also /not/ the inverse operation for
+--   'NixpkgsPlatform'\'s 'Pretty' instance. It is not intended for parsing Nix
+--   expressions.
+--
+--   >>> nixpkgsPlatformFromString "x86_64-netbsd"
+--   Just (NixpkgsPlatformSingle (Platform X86_64 NetBSD))
+--   >>> nixpkgsPlatformFromString "platforms.riscv"
+--   Just (NixpkgsPlatformGroup (Identifier "riscv"))
+--   >>> nixpkgsPlatformFromString "garbage"
+--   Nothing
+nixpkgsPlatformFromString :: String -> Maybe NixpkgsPlatform
+nixpkgsPlatformFromString s = platformGroup <|> singlePlatform
+  where platformGroup = do
+          -- also accept "platform." as prefix to save some typing
+          name <- stripPrefix "lib.platforms." s <|> stripPrefix "platforms." s
+          Just $ NixpkgsPlatformGroup (ident # name)
+
+        singlePlatform = NixpkgsPlatformSingle <$> cabalPlatformFromSystem s
+
+-- | Parse a system tuple as understood by Nix and nixpkgs to a Cabal 'Platform'.
+--   Used internally by 'nixpkgsPlatformFromString'.
+--
+--   >>> cabalPlatformFromSystem "x86_64-linux"
+--   Just (Platform X86_64 Linux)
+--   >>> cabalPlatformFromSystem "x86_64-linux-musl"
+--   Just (Platform X86_64 (OtherOS "linux-musl"))
+--   >>> cabalPlatformFromSystem "powerpc-darwin"
+--   Just (Platform PPC OSX)
+--   >>> cabalPlatformFromSystem "powerpc64le-linux"
+--   Just (Platform (OtherArch "powerpc64le") Linux)
+--   >>> cabalPlatformFromSystem "javascript-ghcjs"
+--   Just (Platform JavaScript Ghcjs)
+cabalPlatformFromSystem :: String -> Maybe Platform
+cabalPlatformFromSystem s =
+  case break (== '-') s of
+    (arch, '-':os) ->
+      if null arch || null os
+      then Nothing
+      else Just $ Platform (parseArch arch) (parseOS os)
+    _ -> Nothing
+  where -- Use permissive classification to also recognize autoconf / nixpkgs
+        -- style OS strings, e.g. "darwin" where Cabal would expect "osx".
+        --
+        -- Note that we don't reimplement GHC_CONVERT_OS from GHC's configure
+        -- file here at the moment. Our goal is to recognize all /well formed/
+        -- nixpkgs system strings, nothing more. That this works correctly for
+        -- nixpkgs systems currently in use is confirmed by the test suite.
+        parseOS = classifyOS Permissive
+
+        -- Use Strict for arch classification and add specific guards for cases
+        -- where Cabal's naming expectations (which seems to be LLVM oriented)
+        -- clash with what nixpkgs / autoconf use (e.g. Cabal uses "ppc" instead
+        -- of "powerpc"). Using Permissive is not possible because Cabal is a bit
+        -- overzealous in arch recognition: For example, it ignores endianess in
+        -- the case of the POWER architectures, parsing "powerpcle" to "ppc" and
+        -- "powerpc64le" to "ppc64" when they are clearly different platforms.
+        --
+        -- We also don't implement GHC_CONVERT_ARCH here, for the reasons stated
+        -- above.
+        parseArch as =
+          case classifyArch Strict as of
+            OtherArch "i686" -> I386
+            OtherArch "powerpc" -> PPC
+            OtherArch "powerpc64" -> PPC64
+            a -> a
+
+instance Pretty NixpkgsPlatform where
+  pPrint (NixpkgsPlatformSingle p) = text $ nixpkgsPlatformFromCabal p
+  pPrint (NixpkgsPlatformGroup p)  = pPrint
+    $ path # [ ident # "lib", ident # "platforms", p ]
+
+instance NFData NixpkgsPlatform
+
 -- | A representation of the @meta@ section used in Nix expressions.
 --
 -- >>> :set -XOverloadedStrings
 -- >>> :{
---   print (pPrint (Meta "http://example.org" "an example package" (Unknown Nothing)
---                  (Set.singleton (Platform X86_64 Linux))
---                  Set.empty
---                  (Set.fromList ["joe","jane"])
---                  True))
+--   let meta = nullMeta
+--         & homepage .~ "http://example.org"
+--         & description .~ "An example package"
+--         & license .~ Unknown Nothing
+--         & platforms .~ Just (Set.singleton (NixpkgsPlatformSingle (Platform X86_64 Linux)))
+--         & badPlatforms .~ Nothing
+--         & hydraPlatforms .~ Just Set.empty
+--         & mainProgram .~ Just "example-binary"
+--         & maintainers .~ Set.fromList ["joe", "jane"]
+--         & broken .~ True
+--   in print $ pPrint meta
 -- :}
 -- homepage = "http://example.org";
--- description = "an example package";
+-- description = "An example package";
 -- license = "unknown";
 -- platforms = [ "x86_64-linux" ];
--- hydraPlatforms = stdenv.lib.platforms.none;
--- maintainers = with stdenv.lib.maintainers; [ jane joe ];
+-- hydraPlatforms = lib.platforms.none;
+-- mainProgram = "example-binary";
+-- maintainers = [ lib.maintainers.jane lib.maintainers.joe ];
 -- broken = true;
-
 data Meta = Meta
-  { _homepage       :: String           -- ^ URL of the package homepage
-  , _description    :: String           -- ^ short description of the package
-  , _license        :: License          -- ^ licensing terms
-  , _platforms      :: Set Platform     -- ^ We re-use the Cabal type for convenience, but render it to conform to @pkgs\/lib\/platforms.nix@.
-  , _hydraPlatforms :: Set Platform     -- ^ list of platforms built by Hydra (render to conform to @pkgs\/lib\/platforms.nix@)
-  , _maintainers    :: Set Identifier   -- ^ list of maintainers from @pkgs\/lib\/maintainers.nix@
-  , _broken         :: Bool             -- ^ set to @true@ if the build is known to fail
+  { _homepage       :: String
+  -- ^ URL of the package homepage
+  , _description    :: String
+  -- ^ short description of the package
+  , _license        :: License
+  -- ^ licensing terms
+  , _platforms      :: Maybe (Set NixpkgsPlatform)
+  -- ^ List of platforms that are supported by the package.
+  --   'Nothing' prevents the attribute from being rendered.
+  --   See 'NixpkgsPlatform' on the precise representation of platforms.
+  , _badPlatforms   :: Maybe (Set NixpkgsPlatform)
+  -- ^ List of platforms that are known to be unsupported. This is semantically
+  --   equivalent to setting the following:
+  --
+  --   @
+  --     platforms = lib.subtractLists
+  --       (initialMeta.badPlatforms or []);
+  --       (initialMeta.platforms or lib.platforms.all)
+  --   @
+  --
+  --   'Nothing' prevents the attribute from being rendered.
+  --   See 'NixpkgsPlatform' on the precise representation of platforms.
+  , _hydraPlatforms :: Maybe (Set NixpkgsPlatform)
+  -- ^ Platforms for which the package should be tested, built and added to the
+  --   binary cache by Hydra. 'Nothing' prevents the attribute from being rendered.
+  --  See 'NixpkgsPlatform' on the precise representation of platforms.
+  , _mainProgram    :: Maybe String
+  -- ^ Filename (as in basename) of the main executable provided by the described
+  --   package. @Nothing@ if it is a library or no obvious default can be chosen.
+  , _maintainers    :: Set Identifier
+  -- ^ list of maintainers from @pkgs\/lib\/maintainers.nix@
+  , _broken         :: Bool
+  -- ^ set to @true@ if the build is known to fail
   }
   deriving (Show, Eq, Ord, Generic)
 
@@ -67,38 +281,153 @@
     [ onlyIf (not (null _homepage)) $ attr "homepage" $ string _homepage
     , onlyIf (not (null _description)) $ attr "description" $ string _description
     , attr "license" $ pPrint _license
-    , onlyIf (_platforms /= allKnownPlatforms) $ renderPlatforms "platforms" _platforms
-    , onlyIf (_hydraPlatforms /= _platforms) $ renderPlatforms "hydraPlatforms" _hydraPlatforms
-    , setattr "maintainers" (text "with stdenv.lib.maintainers;") (Set.map (view ident) _maintainers)
+    , maybe mempty (renderPlatforms "platforms") _platforms
+    , maybe mempty (renderPlatforms "badPlatforms") _badPlatforms
+    , maybe mempty (renderPlatforms "hydraPlatforms") _hydraPlatforms
+    , maybe mempty (attr "mainProgram" . string) _mainProgram
+    , listattrDoc "maintainers" mempty $ renderMaintainers _maintainers
     , boolattr "broken" _broken _broken
     ]
 
-renderPlatforms :: String -> Set Platform -> Doc
+-- | This function renders an Nix attribute binding suitable for use in
+--   an attribute set representing the given set of 'NixpkgsPlatform's.
+--
+--   The @field@ argument is the name of the binding, usually either
+--   @platforms@, @hydraPlatforms@ or @badPlatforms@.
+--
+--   Platforms are rendered in the following way:
+--
+--   * If the given 'Set' is empty, the binding's value is @lib.platforms.none@.
+--     This has been preserved for “backwards compatibility” since changing this
+--     would generate a huge diff for nixpkgs' @hackage-packages.nix@ file.
+--
+--   * First we render all 'NixpkgsPlatformSingle' values. This is done by
+--     printing a Nix list containing Nix system strings. If there are no
+--     platform groups, but the 'Set' is not empty, an empty list (@[ ]@) is
+--     rendered.
+--
+--   * Finally we render all 'NixpkgsPlatformGroup's. Since these are lists, we
+--     need to append them to the (cabal) platform list via the @++@ operator.
+--     For each group we render @++ lib.platforms.<group name>@.
+--
+--   For example:
+--
+--   >>> :{
+--     renderPlatforms "badPlatforms"
+--       $ Set.fromList [
+--         NixpkgsPlatformSingle (Platform (OtherArch "armv7l") Linux),
+--         NixpkgsPlatformGroup (ident # "darwin"),
+--         NixpkgsPlatformGroup (ident # "windows")
+--       ]
+--   :}
+--   badPlatforms = [
+--     "armv7l-linux"
+--   ] ++ lib.platforms.darwin
+--     ++ lib.platforms.windows;
+--
+--   >>> :{
+--     renderPlatforms "platforms"
+--       $ Set.fromList [
+--         NixpkgsPlatformGroup (ident # "x86"),
+--         NixpkgsPlatformGroup (ident # "riscv"),
+--         NixpkgsPlatformGroup (ident # "freebsd")
+--       ]
+--   :}
+--   platforms =
+--        lib.platforms.freebsd
+--     ++ lib.platforms.riscv
+--     ++ lib.platforms.x86;
+--
+--   >>> :{
+--     renderPlatforms "platforms"
+--       $ Set.fromList [
+--         NixpkgsPlatformGroup (ident # "x86")
+--       ]
+--   :}
+--   platforms = lib.platforms.x86;
+--
+--   >>> :{
+--     renderPlatforms "platforms"
+--       $ Set.fromList [
+--         NixpkgsPlatformSingle (Platform I386 Linux),
+--         NixpkgsPlatformSingle (Platform X86_64 Linux),
+--         NixpkgsPlatformSingle (Platform AArch64 Linux),
+--         NixpkgsPlatformSingle (Platform X86_64 OSX),
+--         NixpkgsPlatformSingle (Platform AArch64 OSX)
+--       ]
+--   :}
+--   platforms = [
+--     "i686-linux" "x86_64-linux" "x86_64-darwin" "aarch64-linux"
+--     "aarch64-darwin"
+--   ];
+--
+--   >>> :{
+--     renderPlatforms "platforms"
+--       $ Set.fromList [
+--         NixpkgsPlatformSingle (Platform I386 Linux),
+--         NixpkgsPlatformSingle (Platform X86_64 Linux),
+--         NixpkgsPlatformSingle (Platform AArch64 Linux),
+--         NixpkgsPlatformSingle (Platform X86_64 OSX),
+--         NixpkgsPlatformSingle (Platform AArch64 OSX),
+--         NixpkgsPlatformGroup (ident # "riscv"),
+--         NixpkgsPlatformGroup (ident # "arm")
+--       ]
+--   :}
+--   platforms = [
+--     "i686-linux" "x86_64-linux" "x86_64-darwin" "aarch64-linux"
+--     "aarch64-darwin"
+--   ] ++ lib.platforms.arm
+--     ++ lib.platforms.riscv;
+renderPlatforms :: String -> Set NixpkgsPlatform -> Doc
 renderPlatforms field ps
-  | Set.null ps = sep [ text field <+> equals <+> text "stdenv.lib.platforms.none" <> semi ]
-  | otherwise   = sep [ text field <+> equals <+> lbrack
-                      , nest 2 $ fsep $ map text (toAscList (Set.map fromCabalPlatform ps))
-                      , rbrack <> semi
-                      ]
+  -- preserve old behavior: no platforms -> lib.platforms.none
+  | Set.null ps = sep [ text field <+> equals <+> text "lib.platforms.none" <> semi ]
+  | otherwise =
+    sep [ text field <+> equals <+> unless (Set.null cabalPs) lbrack
+        , unless (Set.null cabalPs) (nest 2 $ fsep renderedCabalPs)
+        , unless (Set.null cabalPs) rbrack
+        , unless (Set.null nixpkgsPs) (nest 2 renderedNixpkgsPs)
+        ]
+    <> semi
+  where -- render nixpkgs platforms and cabal platform tuples separately
+        -- since the former represents multiple platforms and meta doesn't
+        -- support nested lists.
+        (cabalPs, nixpkgsPs) = Set.partition isSinglePlatform ps
+        isSinglePlatform (NixpkgsPlatformSingle _) = True
+        isSinglePlatform _ = False
 
+        renderedCabalPs = map pPrint $ Set.toAscList cabalPs
+
+        -- append lib.platforms list via nix's ++ at the end
+        -- if there is no cabal platforms list, don't emit leading ++
+        appendNixpkgsP acc elem = acc $$
+          if isEmpty acc && Set.null cabalPs
+          then nest 3 $ pPrint elem
+          else text "++" <+> pPrint elem
+        renderedNixpkgsPs = Set.foldl' appendNixpkgsP mempty nixpkgsPs
+
+        -- Helper function, roughly the inverse of nixpkgs' optionals
+        unless False x = x
+        unless True _  = mempty
+
+renderMaintainers :: Set Identifier -> [Doc]
+renderMaintainers = map (pPrint . toPath) . toAscListSortedOn (view ident)
+  where toPath m = path # [ ident # "lib", ident # "maintainers", m]
+
+-- | 'Meta' record with no field set, i.e. evaluating any will throw:
+--
+--   >>> nullMeta ^. homepage
+--   "*** Exception: undefined Meta.homepage
+--   ...
 nullMeta :: Meta
 nullMeta = Meta
   { _homepage = error "undefined Meta.homepage"
   , _description = error "undefined Meta.description"
   , _license = error "undefined Meta.license"
   , _platforms = error "undefined Meta.platforms"
+  , _badPlatforms = error "undefined Meta.badPlatforms"
   , _hydraPlatforms = error "undefined Meta.hydraPlatforms"
+  , _mainProgram = error "undefined Meta.mainProgram"
   , _maintainers = error "undefined Meta.maintainers"
   , _broken = error "undefined Meta.broken"
   }
-
-allKnownPlatforms :: Set Platform
-allKnownPlatforms = Set.fromList [ Platform I386 Linux, Platform X86_64 Linux
-                                 , Platform X86_64 OSX
-                                 ]
-
-fromCabalPlatform :: Platform -> String
-fromCabalPlatform (Platform I386 Linux)   = "\"i686-linux\""
-fromCabalPlatform (Platform X86_64 Linux) = "\"x86_64-linux\""
-fromCabalPlatform (Platform X86_64 OSX)   = "\"x86_64-darwin\""
-fromCabalPlatform p                       = error ("fromCabalPlatform: invalid Nix platform" ++ show p)
diff --git a/src/Distribution/Nixpkgs/PackageMap.hs b/src/Distribution/Nixpkgs/PackageMap.hs
--- a/src/Distribution/Nixpkgs/PackageMap.hs
+++ b/src/Distribution/Nixpkgs/PackageMap.hs
@@ -1,3 +1,4 @@
+-- | Build a lookup table from package identifiers to full attribute paths in nixpkgs.
 module Distribution.Nixpkgs.PackageMap
   ( PackageMap, readNixpkgPackageMap
   , resolve
@@ -7,45 +8,78 @@
 import qualified Data.Aeson as JSON
 import qualified Data.ByteString.Lazy as LBS
 import Data.Function
-import Data.List as List
-import Data.List.Split
+import Data.List (minimumBy)
 import Data.Map.Strict ( Map )
 import qualified Data.Map.Strict as Map
 import Data.Maybe
 import Data.Set ( Set )
 import qualified Data.Set as Set
 import Language.Nix
+import Paths_distribution_nixpkgs (getDataFileName)
 import System.Process
 
 type PackageMap = Map Identifier (Set Path)
 
-readNixpkgPackageMap :: [String] -> IO PackageMap
-readNixpkgPackageMap = fmap identifierSet2PackageMap . readNixpkgSet
+-- | Evaluate nixpkgs at a given (nix) path and build a 'Map'
+--   keeping track of all 'Path's that end in a given 'Identifier'
+--   and evaluate to a derivation.
+--   This can be used to find an attribute 'Path' for an arbitrary
+--   package name using 'resolve'.
+--
+--   Note: Evaluation of nixpkgs is very expensive (takes multiple
+--   seconds), so cache the result of this function if possible.
+--
+--   >>> readNixpkgPackageMap "<nixpkgs>" (Just "{ config = { allowAliases = false; }; }")
+--   fromList [ … ]
+readNixpkgPackageMap :: String
+                     -- ^ Path to nixpkgs, must be a valid nix path
+                     --   (absolute, relative or @NIX_PATH@ lookup)
+                     -> Maybe String
+                     -- ^ (Optional) argument attribute set to pass to
+                     --   nixpkgs. Must be a valid nix attribute set.
+                     -> IO PackageMap
+readNixpkgPackageMap nixpkgsPath nixpkgsArgs =
+  identifierSet2PackageMap <$> readNixpkgSet nixpkgsPath nixpkgsArgs
 
-readNixpkgSet :: [String] -> IO (Set String)
-readNixpkgSet extraArgs = do
-  (_, Just h, _, _) <- createProcess (proc "nix-env" (["-qaP", "--json"] ++ extraArgs))
-                       { std_out = CreatePipe, env = Nothing }  -- TODO: ensure that overrides don't screw up our results
+readNixpkgSet :: String -> Maybe String -> IO (Set [String])
+readNixpkgSet nixpkgsPath nixpkgsArgs = do
+  pathsExpr <- getDataFileName "derivation-attr-paths.nix"
+  let nixInstantiate = proc "nix-instantiate"
+        [ "--strict"
+        , "--json"
+        , "--eval"
+        , pathsExpr
+        , "--arg", "nixpkgsPath", nixpkgsPath
+        , "--arg", "nixpkgsArgs", fromMaybe "{}" nixpkgsArgs
+        ]
+  (_, Just h, _, _) <- -- TODO: ensure that overrides don't screw up our results
+    createProcess nixInstantiate { std_out = CreatePipe, env = Nothing }
   buf <- LBS.hGetContents h
-  let pkgmap :: Either String (Map String JSON.Object)
-      pkgmap = JSON.eitherDecode buf
-  either fail (return . Map.keysSet) pkgmap
+  either fail return $ JSON.eitherDecode buf
 
-identifierSet2PackageMap :: Set String -> PackageMap
-identifierSet2PackageMap pkgset = foldr (uncurry insertIdentifier) Map.empty pkglist
+identifierSet2PackageMap :: Set [String] -> PackageMap
+identifierSet2PackageMap = foldr insertIdentifier Map.empty
   where
-    pkglist :: [(Identifier, Path)]
-    pkglist = mapMaybe parsePackage (Set.toList pkgset)
-
-    insertIdentifier :: Identifier -> Path -> PackageMap -> PackageMap
-    insertIdentifier i = Map.insertWith Set.union i . Set.singleton
+    insertIdentifier :: [String] -> (PackageMap -> PackageMap)
+    insertIdentifier rawPath =
+      case parsePackage rawPath of
+        Nothing -> id
+        Just (i, p) -> Map.insertWith Set.union i $ Set.singleton p
 
-parsePackage :: String -> Maybe (Identifier, Path)
-parsePackage x | null x                 = error "Distribution.Nixpkgs.PackageMap.parsepackage: empty string is no valid identifier"
-               | xs <- splitOn "." x    = if needsQuoting (head xs)
-                                             then Nothing
-                                             else Just (ident # last xs, path # map (review ident) xs)
+parsePackage :: [String] -> Maybe (Identifier, Path)
+parsePackage x
+  | null x = -- this case would be a bug in derivation-attr-paths.nix
+      error "Distribution.Nixpkgs.PackageMap.parsepackage: empty path is no valid identifier"
+  | otherwise =
+      if any needsQuoting x
+        then Nothing
+        else Just (ident # last x, path # map (review ident) x)
 
+-- | Finds the shortest 'Path' in a 'PackageMap' that has the
+--   given 'Identifier' as its last component.
+--
+--   >>> resolve nixpkgs (ident # "pam")
+--   Just (Bind (Identifier "pam") (Path [Identifier "pam"]))
 resolve :: PackageMap -> Identifier -> Maybe Binding
 resolve nixpkgs i = case Map.lookup i nixpkgs of
                       Nothing -> Nothing
diff --git a/src/Language/Nix/PrettyPrinting.hs b/src/Language/Nix/PrettyPrinting.hs
--- a/src/Language/Nix/PrettyPrinting.hs
+++ b/src/Language/Nix/PrettyPrinting.hs
@@ -5,15 +5,15 @@
 
 module Language.Nix.PrettyPrinting
   ( onlyIf
-  , setattr, toAscList
-  , listattr
+  , toAscList, toAscListSortedOn
+  , setattr
+  , listattr, listattrDoc
   , boolattr
   , attr
   , string
   , funargs
   -- * Re-exports from other modules
   , module Text.PrettyPrint.HughesPJClass
-  , Text, disp
   )
   where
 
@@ -23,10 +23,9 @@
 #endif
 import Data.Char
 import Data.Function
-import Data.List
+import Data.List (sortBy)
 import Data.Set ( Set )
 import qualified Data.Set as Set
-import Distribution.Text ( Text, disp )
 import "pretty" Text.PrettyPrint.HughesPJClass
 
 attr :: String -> Doc -> Doc
@@ -38,25 +37,40 @@
 boolattr :: String -> Bool -> Bool -> Doc
 boolattr n p v = if p then attr n (bool v) else empty
 
+listattrDoc :: String -> Doc -> [Doc] -> Doc
+listattrDoc n prefix vs = onlyIf (not (null vs)) $
+  sep [ text n <+> equals <+> prefix <+> lbrack,
+        nest 2 $ fsep vs,
+        rbrack <> semi
+      ]
+
 listattr :: String -> Doc -> [String] -> Doc
-listattr n prefix vs = onlyIf (not (null vs)) $
-                sep [ text n <+> equals <+> prefix <+> lbrack,
-                      nest 2 $ fsep $ map text vs,
-                      rbrack <> semi
-                    ]
+listattr n p = listattrDoc n p . map text
 
 setattr :: String -> Doc -> Set String -> Doc
-setattr name prefix set = listattr name prefix (toAscList set)
+setattr name prefix set = listattrDoc name prefix
+  $ map text $ toAscList set
 
+toAscListSortedOn :: (a -> String) -> Set a -> [a]
+toAscListSortedOn f = sortBy (compare `on` map toLower . f) . Set.toList
+
 toAscList :: Set String -> [String]
-toAscList = sortBy (compare `on` map toLower) . Set.toList
+toAscList = toAscListSortedOn id
 
 bool :: Bool -> Doc
 bool True  = text "true"
 bool False = text "false"
 
 string :: String -> Doc
-string = doubleQuotes . text
+string = doubleQuotes . quoteString
+
+quoteString :: String -> Doc
+quoteString []          = mempty
+quoteString ['\\']      = text "\\\\"
+quoteString ('\\':x:xs)
+  | x `elem` "\"rn$\\t" = text ['\\',x] <> quoteString xs
+  | otherwise           = text "\\\\" <> quoteString (x:xs)
+quoteString (x:xs)      = char x <> quoteString xs
 
 prepunctuate :: Doc -> [Doc] -> [Doc]
 prepunctuate _ []     = []
diff --git a/test/data/all-system-tuples.json b/test/data/all-system-tuples.json
new file mode 100644
--- /dev/null
+++ b/test/data/all-system-tuples.json
@@ -0,0 +1,1 @@
+["aarch64-darwin","aarch64-freebsd","aarch64-genode","aarch64-linux","aarch64-netbsd","aarch64-none","aarch64-windows","aarch64_be-none","arm-none","armv5tel-linux","armv6l-linux","armv6l-netbsd","armv6l-none","armv7a-darwin","armv7a-linux","armv7a-netbsd","armv7l-linux","armv7l-netbsd","avr-none","i686-cygwin","i686-darwin","i686-freebsd","i686-genode","i686-linux","i686-netbsd","i686-none","i686-openbsd","i686-windows","javascript-ghcjs","loongarch64-linux","m68k-linux","m68k-netbsd","m68k-none","microblaze-linux","microblaze-none","microblazeel-linux","microblazeel-none","mips-linux","mips-none","mips64-linux","mips64-none","mips64el-linux","mipsel-linux","mipsel-netbsd","mmix-mmixware","msp430-none","or1k-none","powerpc-netbsd","powerpc-none","powerpc64-linux","powerpc64le-linux","powerpcle-none","riscv32-linux","riscv32-netbsd","riscv32-none","riscv64-linux","riscv64-netbsd","riscv64-none","rx-none","s390-linux","s390-none","s390x-linux","s390x-none","vc4-none","wasm32-none","wasm32-wasi","wasm64-wasi","x86_64-cygwin","x86_64-darwin","x86_64-freebsd","x86_64-genode","x86_64-linux","x86_64-netbsd","x86_64-none","x86_64-openbsd","x86_64-redox","x86_64-solaris","x86_64-windows"]
diff --git a/test/hspec.hs b/test/hspec.hs
--- a/test/hspec.hs
+++ b/test/hspec.hs
@@ -1,14 +1,23 @@
+{-# LANGUAGE CPP #-}
 module Main ( main ) where
 
 import Control.DeepSeq
 import Control.Exception
 import Control.Lens
+import Control.Monad (forM_)
+import Data.Aeson (decodeFileStrict)
+import Data.Bifunctor (second)
+import Data.Maybe (fromJust)
 import Distribution.Nixpkgs.License
 import Distribution.Nixpkgs.Meta
+import Distribution.System (Platform (..), OS (..), Arch (..))
+import Language.Nix.Identifier
+import Language.Nix.PrettyPrinting
+import System.Directory (doesFileExist)
 import Test.Hspec
 
 main :: IO ()
-main = hspec $
+main = hspec $ do
   describe "DeepSeq instances work properly for" $ do
     it "License" $ mapM_ hitsBottom [Known undefined, Unknown (Just undefined)]
     it "Meta" $ mapM_ hitsBottom
@@ -19,6 +28,144 @@
                   , nullMeta & maintainers .~ undefined
                   , nullMeta & broken .~ undefined
                   ]
+
+  describe "Platform rendering and parsing:" $ do
+    it "Checks cover all systems from all-system-tuples.json" $ do
+      let allSystemTuplesJson = "test/data/all-system-tuples.json"
+      available <- doesFileExist allSystemTuplesJson
+      if not available
+      then pendingWith $ "System tuple list not found at: " ++ allSystemTuplesJson
+      else do
+        allSystemTuples <- decodeFileStrict allSystemTuplesJson
+        map fst nixpkgsSystemMapping `shouldMatchList` fromJust allSystemTuples
+
+    forM_ platformMapping $ \(str, plat) -> describe str $ do
+      it "is rendered correctly" $ checkRendering plat str
+      it "is parsed correctly" $
+        nixpkgsPlatformFromString str `shouldBe` Just plat
+
+-- All system tuples from lib.platforms. Testing these allows us to
+-- get notified about behavior change in Cabal as early as possible.
+-- You can check if the list is up to date by running
+-- regenerate-all-system-tuples.sh.
+nixpkgsSystemMapping :: [(String, Platform)]
+nixpkgsSystemMapping =
+  [ -- lib.platforms.all
+    ("aarch64-darwin", Platform AArch64 OSX)
+  , ("aarch64-freebsd", Platform AArch64 FreeBSD)
+  , ("aarch64-genode", Platform AArch64 (OtherOS "genode"))
+  , ("aarch64-linux", Platform AArch64 Linux)
+  , ("aarch64-netbsd", Platform AArch64 NetBSD)
+  , ("aarch64-none", Platform AArch64 (OtherOS "none"))
+  , ("aarch64-windows", Platform AArch64 Windows)
+  , ("aarch64_be-none", Platform (OtherArch "aarch64_be") (OtherOS "none"))
+  , ("arm-none", Platform Arm (OtherOS "none"))
+  , ("armv5tel-linux", Platform (OtherArch "armv5tel") Linux)
+  , ("armv6l-linux", Platform (OtherArch "armv6l") Linux)
+  , ("armv6l-netbsd", Platform (OtherArch "armv6l") NetBSD)
+  , ("armv6l-none", Platform (OtherArch "armv6l") (OtherOS "none"))
+  , ("armv7a-darwin", Platform (OtherArch "armv7a") OSX)
+  , ("armv7a-linux", Platform (OtherArch "armv7a") Linux)
+  , ("armv7a-netbsd", Platform (OtherArch "armv7a") NetBSD)
+  , ("armv7l-linux", Platform (OtherArch "armv7l") Linux)
+  , ("armv7l-netbsd", Platform (OtherArch "armv7l") NetBSD)
+  , ("avr-none", Platform (OtherArch "avr") (OtherOS "none"))
+  , ("i686-cygwin", Platform I386 (OtherOS "cygwin"))
+  , ("i686-darwin", Platform I386 OSX)
+  , ("i686-freebsd", Platform I386 FreeBSD)
+  , ("i686-genode", Platform I386 (OtherOS "genode"))
+  , ("i686-linux", Platform I386 Linux)
+  , ("i686-netbsd", Platform I386 NetBSD)
+  , ("i686-none", Platform I386 (OtherOS "none"))
+  , ("i686-openbsd", Platform I386 OpenBSD)
+  , ("i686-windows", Platform I386 Windows)
+  , ("javascript-ghcjs", Platform JavaScript Ghcjs)
+#if MIN_VERSION_Cabal(3,12,0)
+  , ("loongarch64-linux", Platform LoongArch64 Linux)
+#else
+  , ("loongarch64-linux", Platform (OtherArch "loongarch64") Linux)
+#endif
+  , ("m68k-linux", Platform M68k Linux)
+  , ("m68k-netbsd", Platform M68k NetBSD)
+  , ("m68k-none", Platform M68k (OtherOS "none"))
+  , ("microblaze-linux", Platform (OtherArch "microblaze") Linux)
+  , ("microblazeel-linux", Platform (OtherArch "microblazeel") Linux)
+  , ("microblaze-none", Platform (OtherArch "microblaze") (OtherOS "none"))
+  , ("microblazeel-none", Platform (OtherArch "microblazeel") (OtherOS "none"))
+  , ("mips-linux", Platform Mips Linux)
+  , ("mips-none", Platform Mips (OtherOS "none"))
+  , ("mips64-linux", Platform (OtherArch "mips64") Linux)
+  , ("mips64-none", Platform (OtherArch "mips64") (OtherOS "none"))
+  , ("mips64el-linux", Platform (OtherArch "mips64el") Linux)
+  , ("mipsel-linux", Platform (OtherArch "mipsel") Linux)
+  , ("mipsel-netbsd", Platform (OtherArch "mipsel") NetBSD)
+  , ("mmix-mmixware", Platform (OtherArch "mmix") (OtherOS "mmixware"))
+  , ("msp430-none", Platform (OtherArch "msp430") (OtherOS "none"))
+  , ("or1k-none", Platform (OtherArch "or1k") (OtherOS "none"))
+  , ("powerpc-netbsd", Platform PPC NetBSD)
+  , ("powerpc-none", Platform PPC (OtherOS "none"))
+  , ("powerpc64-linux", Platform PPC64 Linux)
+  , ("powerpc64le-linux", Platform (OtherArch "powerpc64le") Linux)
+  , ("powerpcle-none", Platform (OtherArch "powerpcle") (OtherOS "none"))
+  , ("riscv32-linux", Platform (OtherArch "riscv32") Linux)
+  , ("riscv32-netbsd", Platform (OtherArch "riscv32") NetBSD)
+  , ("riscv32-none", Platform (OtherArch "riscv32") (OtherOS "none"))
+#if MIN_VERSION_Cabal(3,12,0)
+  , ("riscv64-linux", Platform RISCV64 Linux)
+  , ("riscv64-netbsd", Platform RISCV64 NetBSD)
+  , ("riscv64-none", Platform RISCV64 (OtherOS "none"))
+#else
+  , ("riscv64-linux", Platform (OtherArch "riscv64") Linux)
+  , ("riscv64-netbsd", Platform (OtherArch "riscv64") NetBSD)
+  , ("riscv64-none", Platform (OtherArch "riscv64") (OtherOS "none"))
+#endif
+  , ("rx-none", Platform (OtherArch "rx") (OtherOS "none"))
+  , ("s390-linux", Platform S390 Linux)
+  , ("s390-none", Platform S390 (OtherOS "none"))
+#if MIN_VERSION_Cabal(3,8,1)
+  , ("s390x-linux", Platform S390X Linux)
+  , ("s390x-none", Platform S390X (OtherOS "none"))
+#else
+  , ("s390x-linux", Platform (OtherArch "s390x") Linux)
+  , ("s390x-none", Platform (OtherArch "s390x") (OtherOS "none"))
+#endif
+  , ("vc4-none", Platform (OtherArch "vc4") (OtherOS "none"))
+#if MIN_VERSION_Cabal(3,8,1)
+  , ("wasm32-none", Platform Wasm32 (OtherOS "none"))
+  , ("wasm32-wasi", Platform Wasm32 Wasi)
+  , ("wasm64-wasi", Platform (OtherArch "wasm64") Wasi)
+#else
+  , ("wasm32-none", Platform (OtherArch "wasm32") (OtherOS "none"))
+  , ("wasm32-wasi", Platform (OtherArch "wasm32") (OtherOS "wasi"))
+  , ("wasm64-wasi", Platform (OtherArch "wasm64") (OtherOS "wasi"))
+#endif
+  , ("x86_64-cygwin", Platform X86_64 (OtherOS "cygwin"))
+  , ("x86_64-darwin", Platform X86_64 OSX)
+  , ("x86_64-freebsd", Platform X86_64 FreeBSD)
+  , ("x86_64-genode", Platform X86_64 (OtherOS "genode"))
+  , ("x86_64-linux", Platform X86_64 Linux)
+  , ("x86_64-netbsd", Platform X86_64 NetBSD)
+  , ("x86_64-none", Platform X86_64 (OtherOS "none"))
+  , ("x86_64-openbsd", Platform X86_64 OpenBSD)
+  , ("x86_64-redox", Platform X86_64 (OtherOS "redox"))
+  , ("x86_64-solaris", Platform X86_64 Solaris)
+  , ("x86_64-windows", Platform X86_64 Windows)
+  -- lib.systems.examples
+  -- Currently no tuples are unique to the example list.
+  ]
+
+platformMapping :: [(String, NixpkgsPlatform)]
+platformMapping = map (second NixpkgsPlatformSingle) nixpkgsSystemMapping
+  ++ map platformGroupMapping [ "riscv", "x86", "linux", "darwin" ]
+
+platformGroupMapping :: String -> (String, NixpkgsPlatform)
+platformGroupMapping name =
+  ("lib.platforms." ++ name, NixpkgsPlatformGroup (ident # name))
+
+checkRendering :: NixpkgsPlatform -> String -> Expectation
+checkRendering plat@(NixpkgsPlatformSingle _) str =
+  prettyShow plat `shouldBe` "\"" ++ str ++ "\""
+checkRendering plat str = prettyShow plat `shouldBe` str
 
 hitsBottom :: NFData a => a -> Expectation
 hitsBottom x = evaluate (rnf x) `shouldThrow` anyErrorCall
