@@ -457,7 +457,7 @@ final: prev: {
457 hackage-package =
457 hackage-package =
458 { name, compiler-nix-name, ... }@args':
458 { name, compiler-nix-name, ... }@args':
459 let args = { caller = "hackage-package"; } // args';
459 let args = { caller = "hackage-package"; } // args';
460− in (hackage-project args).hsPkgs.${name};
460+ in (hackage-project args).getPackage name;
461 hackage-project =
461 hackage-project =
463 , compiler-nix-name
463 , compiler-nix-name
@@ -500,11 +500,19 @@ final: prev: {
501 args = { caller = "cabalProject'"; } // args';
501 args = { caller = "cabalProject'"; } // args';
502 callProjectResults = callCabalProjectToNix args;
502 callProjectResults = callCabalProjectToNix args;
503− in let pkg-set = mkCabalProjectPkgSet
504− { inherit compiler-nix-name;
505− plan-pkgs = importAndFilterProject {
503+ plan-pkgs = importAndFilterProject {
506− inherit (callProjectResults) projectNix sourceRepos src;
504+ inherit (callProjectResults) projectNix sourceRepos src;
506+ pkg-set = if plan-pkgs ? configurationError
508+ inherit (plan-pkgs) configurationError;
510+ compiler.nix-name = compiler-nix-name;
514+ else mkCabalProjectPkgSet
515+ { inherit compiler-nix-name plan-pkgs;
508 pkg-def-extras = args.pkg-def-extras or [];
516 pkg-def-extras = args.pkg-def-extras or [];
509 modules = (args.modules or [])
517 modules = (args.modules or [])
510 ++ final.lib.optional (args ? ghcOverride || args ? ghc)
518 ++ final.lib.optional (args ? ghcOverride || args ? ghc)
@@ -514,7 +522,7 @@ final: prev: {
514 extra-hackages = args.extra-hackages or [];
522 extra-hackages = args.extra-hackages or [];
517− project = addProjectAndPackageAttrs rec {
525+ project = addProjectAndPackageAttrs rec {
518 inherit (pkg-set.config) hsPkgs;
526 inherit (pkg-set.config) hsPkgs;
519 inherit pkg-set;
527 inherit pkg-set;
520 plan-nix = callProjectResults.projectNix;
528 plan-nix = callProjectResults.projectNix;
@@ -535,7 +543,7 @@ final: prev: {
535 final.lib.fix (project':
543 final.lib.fix (project':
536 let project = project' // { recurseForDerivations = false; };
544 let project = project' // { recurseForDerivations = false; };
537 in rawProject // rec {
545 in rawProject // rec {
538− hsPkgs = final.lib.mapAttrs (n: package':
546+ hsPkgs = final.lib.mapAttrs (packageName: package':
539 if package' == null
547 if package' == null
540 then null
548 then null
@@ -548,6 +556,16 @@ final: prev: {
548 ) package'.components;
556 ) package'.components;
549 inherit project;
557 inherit project;
559+ # Look up a component in the package based on ctype:name
560+ getComponent = componentName:
561+ let m = builtins.match "(lib|flib|exe|test|bench):([^:]*)" componentName;
563+ assert final.lib.asserts.assertMsg (m != null)
564+ "Invalid package component name ${componentName}. Expected it to start with one of lib: flib: exe: test: or bench:";
565+ if builtins.elemAt m 0 == "lib" && builtins.elemAt m 1 == packageName
566+ then components.library
567+ else components.${haskellLib.prefixComponent.${builtins.elemAt m 0}}.${builtins.elemAt m 1};
551 coverageReport = haskellLib.coverageReport (rec {
569 coverageReport = haskellLib.coverageReport (rec {
552 name = package.identifier.name + "-" + package.identifier.version;
570 name = package.identifier.name + "-" + package.identifier.version;
553 library = if components ? library then components.library else null;
571 library = if components ? library then components.library else null;
@@ -568,6 +586,48 @@ final: prev: {
568 rawProject.projectFunction pkgs.haskell-nix rawProject.projectArgs
586 rawProject.projectFunction pkgs.haskell-nix rawProject.projectArgs
569 ) final.pkgsCross) // { recurseForDerivations = false; };
587 ) final.pkgsCross) // { recurseForDerivations = false; };
589+ # Like `.hsPkgs.${packageName}` but when compined with `getComponent` any
590+ # cabal configure errors are defered until the components derivation builds.
591+ getPackage = packageName:
592+ if rawProject.pkg-set ? configurationError
594+ # A minimal proxy for a package when cabal configure failed
596+ # Including the project so that things like:
597+ # (p.getPackage "hello").project.tool "hlint" "latest"
598+ # will still work even if "hello" failed to configure.
601+ # Defer configure time errors for the library component
602+ # (p.getPackage "hello").components.library
603+ components.library = package.getComponent "lib:${packageName}";
605+ # This procide a derivation (even though the component may
606+ # not exist at all). The derivation will never build
607+ # and simple outputs the result of cabal configure.
608+ getComponent = componentName:
609+ final.evalPackages.runCommand "cabal-configure-error" {
611+ inherit project package;
614+ cat ${rawProject.pkg-set.configurationError}
615+ echo Unable to find component ${packageName}:${componentName} \
616+ due to the above cabal configuration error
621+ else project.hsPkgs.${packageName};
623+ # Look a component in the project based on `pkg:ctype:name`
624+ getComponent = componentName:
625+ let m = builtins.match "([^:]*):(lib|flib|exe|test|bench):([^:]*)" componentName;
627+ assert final.lib.asserts.assertMsg (m != null)
628+ "Invalid package component name ${componentName}. Expected package:ctype:component (where ctype is one of lib, flib, exe, test, or bench)";
629+ (getPackage (builtins.elemAt m 0)).getComponent "${builtins.elemAt m 1}:${builtins.elemAt m 2}";
571 # Helper function that can be used to make a Nix Flake out of a project
631 # Helper function that can be used to make a Nix Flake out of a project
572 # by including a flake.nix. See docs/tutorials/getting-started-flakes.md
632 # by including a flake.nix. See docs/tutorials/getting-started-flakes.md
573 # for an example flake.nix file.
633 # for an example flake.nix file.