From 6326ab29c052070761f8111c20730b4e31f3d2f5 Mon Sep 17 00:00:00 2001 From: Gautier DI FOLCO Date: Sat, 10 Oct 2026 16:02:08 +0200 Subject: [PATCH 1/2] fix: `cabal sdist` target selection (#11385) --- .../src/Distribution/Client/CmdSdist.hs | 16 +++++----------- .../MultiTarget/valid-and-test-suite.out | 5 +++-- .../MultiTarget/valid-and-test-suite.test.hs | 6 +++--- .../NewSdist/SubDirPackageTarget/cabal.project | 1 + .../NewSdist/SubDirPackageTarget/p/P.hs | 1 + .../NewSdist/SubDirPackageTarget/p/p.cabal | 7 +++++++ .../SubDirPackageTarget/subdir-target.out | 8 ++++++++ .../SubDirPackageTarget/subdir-target.test.hs | 10 ++++++++++ changelog.d/11385.md | 15 +++++++++++++++ 9 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/cabal.project create mode 100644 cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/P.hs create mode 100644 cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/p.cabal create mode 100644 cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.out create mode 100644 cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.test.hs create mode 100644 changelog.d/11385.md diff --git a/cabal-install/src/Distribution/Client/CmdSdist.hs b/cabal-install/src/Distribution/Client/CmdSdist.hs index 6ac25d527c1..414979622cd 100644 --- a/cabal-install/src/Distribution/Client/CmdSdist.hs +++ b/cabal-install/src/Distribution/Client/CmdSdist.hs @@ -118,10 +118,6 @@ import Distribution.Simple.Utils , withOutputMarker , wrapText ) -import Distribution.Types.ComponentName - ( ComponentName - , showComponentName - ) import Distribution.Types.GenericPackageDescription (GenericPackageDescription) import Distribution.Types.PackageName ( PackageName @@ -401,12 +397,15 @@ reifyTargetSelectors pkgs sels = go (TargetAllPackages (Just kind)) = [Left (AllComponentsOnly kind)] go (TargetPackageNamed pname _) = [Left (NonlocalPackageNotAllowed pname)] go (TargetComponentUnknown pname _ _) = [Left (NonlocalPackageNotAllowed pname)] - go (TargetComponent _ cname _) = [Left (ComponentsNotAllowed cname)] + -- sdist only packages whole packages, so any target selector referring to a + -- component (or a module/file within a component) of a local package selects + -- that package. (#11385: a bare package name typed inside the package directory + -- resolves to its primary library component, which shadowed the package match.) + go (TargetComponent pid _ _) = [getPkg pid] data TargetProblem = AllComponentsOnly ComponentKind | NonlocalPackageNotAllowed PackageName - | ComponentsNotAllowed ComponentName renderTargetProblem :: TargetProblem -> String renderTargetProblem (AllComponentsOnly kind) = @@ -414,11 +413,6 @@ renderTargetProblem (AllComponentsOnly kind) = ++ renderComponentKind Plural kind ++ " from a package " ++ "for distribution. Only entire packages may be packaged for distribution." -renderTargetProblem (ComponentsNotAllowed cname) = - "The component " - ++ showComponentName cname - ++ " cannot be packaged for distribution on its own. " - ++ "Only entire packages may be packaged for distribution." renderTargetProblem (NonlocalPackageNotAllowed pname) = "The package " ++ unPackageName pname diff --git a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out index 6c1e881f806..fe66a6058ee 100644 --- a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out +++ b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out @@ -1,3 +1,4 @@ # cabal v2-sdist -Error: [Cabal-7151] -The component test suite 'a-tests' cannot be packaged for distribution on its own. Only entire packages may be packaged for distribution. +Wrote tarball sdist to /dist-newstyle/sdist/a-0.1.tar.gz +Wrote tarball sdist to /dist-newstyle/sdist/b-0.1.tar.gz +Wrote tarball sdist to /dist-newstyle/sdist/a-0.1.tar.gz diff --git a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs index 4950fde708a..68cc5c52f75 100644 --- a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs +++ b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs @@ -1,6 +1,6 @@ import Test.Cabal.Prelude main = cabalTest $ do cwd <- fmap testCurrentDir getTestEnv - fails $ cabal "v2-sdist" ["a", "b", "a-tests"] - shouldNotExist $ cwd "dist-newstyle/sdist/a-0.1.tar.gz" - shouldNotExist $ cwd "dist-newstyle/sdist/b-0.1.tar.gz" + cabal "v2-sdist" ["a", "b", "a-tests"] + shouldExist $ cwd "dist-newstyle/sdist/a-0.1.tar.gz" + shouldExist $ cwd "dist-newstyle/sdist/b-0.1.tar.gz" diff --git a/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/cabal.project b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/cabal.project new file mode 100644 index 00000000000..a61b094f388 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/cabal.project @@ -0,0 +1 @@ +packages: p/ diff --git a/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/P.hs b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/P.hs new file mode 100644 index 00000000000..fc4877ad85e --- /dev/null +++ b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/P.hs @@ -0,0 +1 @@ +module P where diff --git a/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/p.cabal b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/p.cabal new file mode 100644 index 00000000000..ed1f50f41a7 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/p/p.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.2 +name: p +version: 0.1 + +library + default-language: Haskell2010 + exposed-modules: P diff --git a/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.out b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.out new file mode 100644 index 00000000000..e1f64f03707 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.out @@ -0,0 +1,8 @@ +# cabal v2-sdist +Configuration is affected by cabal.project at ''. +Wrote tarball sdist to /dist-newstyle/sdist/p-0.1.tar.gz +# cabal v2-sdist +Configuration is affected by cabal.project at ''. +Wrote tarball sdist to /dist-newstyle/sdist/p-0.1.tar.gz +# cabal v2-sdist +Wrote tarball sdist to /dist-newstyle/sdist/p-0.1.tar.gz diff --git a/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.test.hs b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.test.hs new file mode 100644 index 00000000000..b1816f9ae1f --- /dev/null +++ b/cabal-testsuite/PackageTests/NewSdist/SubDirPackageTarget/subdir-target.test.hs @@ -0,0 +1,10 @@ +import Test.Cabal.Prelude +main = cabalTest $ do + cwd <- fmap testCurrentDir getTestEnv + -- #11385: bare package name typed inside the package directory + withDirectory "p" $ cabal "v2-sdist" ["p"] + -- explicit component form selects the containing package + withDirectory "p" $ cabal "v2-sdist" ["p:lib:p"] + -- package name from the project root keeps working + cabal "v2-sdist" ["p"] + shouldExist $ cwd "dist-newstyle/sdist/p-0.1.tar.gz" diff --git a/changelog.d/11385.md b/changelog.d/11385.md new file mode 100644 index 00000000000..4b84eed5ba5 --- /dev/null +++ b/changelog.d/11385.md @@ -0,0 +1,15 @@ +--- +synopsis: "`cabal sdist ` works when run inside the package directory" +packages: [cabal-install] +prs: 12440 +issues: 11385 +--- + +`cabal sdist ` failed with `[Cabal-7151] The component library cannot be +packaged for distribution on its own` when run inside the package directory, +because a bare package name resolves to the package's primary library component. +Any target selector referring to a component (or a module or file within a +component) of a local package now selects that package, so `cabal sdist `, +`cabal sdist :lib:` and similar forms package the containing package. +Explicit component-kind filters such as `cabal sdist tests` still fail with +`[Cabal-7151]`. From a07707c76e41133241be2dfa962bee7e488fa925 Mon Sep 17 00:00:00 2001 From: Gautier DI FOLCO Date: Sat, 10 Oct 2026 16:50:53 +0200 Subject: [PATCH 2/2] Deduplicate sdist targets resolving to one package Multiple target selectors matching the same package (e.g. 'cabal sdist a a-tests') generated duplicate archives and made the stdout output guard reject a single tarball with [Cabal-7152]. Deduplicate the packages produced by reifyTargetSelectors by packageId. Addresses the Copilot review on #12440. --- cabal-install/src/Distribution/Client/CmdSdist.hs | 3 ++- .../NewSdist/MultiTarget/valid-and-test-suite.out | 3 ++- .../NewSdist/MultiTarget/valid-and-test-suite.test.hs | 9 +++++++++ changelog.d/11385.md | 5 ++++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cabal-install/src/Distribution/Client/CmdSdist.hs b/cabal-install/src/Distribution/Client/CmdSdist.hs index 414979622cd..07707d6362e 100644 --- a/cabal-install/src/Distribution/Client/CmdSdist.hs +++ b/cabal-install/src/Distribution/Client/CmdSdist.hs @@ -115,6 +115,7 @@ import Distribution.Simple.Utils ( dieWithException , notice , ordNub + , ordNubBy , withOutputMarker , wrapText ) @@ -372,7 +373,7 @@ packageToSdist verbosity projectRootDir format outputFile pkg = do reifyTargetSelectors :: [PackageSpecifier UnresolvedSourcePackage] -> [TargetSelector] -> Either [TargetProblem] [UnresolvedSourcePackage] reifyTargetSelectors pkgs sels = case partitionEithers (foldMap go sels) of - ([], sels') -> Right sels' + ([], sels') -> Right (ordNubBy packageId sels') (errs, _) -> Left errs where -- there can be pkgs which are in extra-packages: diff --git a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out index fe66a6058ee..a8c658cf147 100644 --- a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out +++ b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.out @@ -1,4 +1,5 @@ # cabal v2-sdist Wrote tarball sdist to /dist-newstyle/sdist/a-0.1.tar.gz Wrote tarball sdist to /dist-newstyle/sdist/b-0.1.tar.gz -Wrote tarball sdist to /dist-newstyle/sdist/a-0.1.tar.gz +# cabal v2-sdist +Wrote source list to /lists/a-0.1.list diff --git a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs index 68cc5c52f75..e5d37479aa8 100644 --- a/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs +++ b/cabal-testsuite/PackageTests/NewSdist/MultiTarget/valid-and-test-suite.test.hs @@ -1,6 +1,15 @@ import Test.Cabal.Prelude +import System.Directory (createDirectoryIfMissing) +import System.FilePath (normalise) main = cabalTest $ do cwd <- fmap testCurrentDir getTestEnv + liftIO $ createDirectoryIfMissing False $ cwd "lists" cabal "v2-sdist" ["a", "b", "a-tests"] shouldExist $ cwd "dist-newstyle/sdist/a-0.1.tar.gz" shouldExist $ cwd "dist-newstyle/sdist/b-0.1.tar.gz" + + -- Duplicate selectors resolving to the same package must yield a single + -- package, not two copies of the archive (#11385). With --list-only the + -- deduped package is written to a single .list file. + cabal "v2-sdist" ["a", "a-tests", "--list-only", "--output-dir", "lists"] + assertFindInFile (normalise "a/a.cabal") (cwd "lists/a-0.1.list") diff --git a/changelog.d/11385.md b/changelog.d/11385.md index 4b84eed5ba5..d17549685b0 100644 --- a/changelog.d/11385.md +++ b/changelog.d/11385.md @@ -12,4 +12,7 @@ Any target selector referring to a component (or a module or file within a component) of a local package now selects that package, so `cabal sdist `, `cabal sdist :lib:` and similar forms package the containing package. Explicit component-kind filters such as `cabal sdist tests` still fail with -`[Cabal-7151]`. +`[Cabal-7151]`. Multiple selectors resolving to the same package (e.g. +`cabal sdist a a-tests`) now produce a single archive per package instead of +duplicates, and `cabal sdist a a-tests --output-dir -` writes one tarball to +standard output rather than failing with `[Cabal-7152]`.