diff --git a/cabal-install/src/Distribution/Client/CmdClean.hs b/cabal-install/src/Distribution/Client/CmdClean.hs index b4c5a122a2d..28121071044 100644 --- a/cabal-install/src/Distribution/Client/CmdClean.hs +++ b/cabal-install/src/Distribution/Client/CmdClean.hs @@ -75,7 +75,7 @@ cleanCommand = CommandUI cleanProjectDir (\path flags -> flags {cleanProjectDir = path}) (reqArg "DIR" (succeedReadE Flag) flagToList) , option [] ["project-file"] - "Set the path of the cabal.project file (relative to the project directory)" + "Set the path of the cabal.project file (relative to the project directory when relative)" cleanProjectFile (\pf flags -> flags {cleanProjectFile = pf}) (reqArg "FILE" (succeedReadE Flag) flagToList) , option ['s'] ["save-config"] diff --git a/cabal-install/src/Distribution/Client/DistDirLayout.hs b/cabal-install/src/Distribution/Client/DistDirLayout.hs index fa064caef79..3b878adb9bb 100644 --- a/cabal-install/src/Distribution/Client/DistDirLayout.hs +++ b/cabal-install/src/Distribution/Client/DistDirLayout.hs @@ -168,6 +168,10 @@ data ProjectRoot = -- | An explicit project root. It contains the absolute project -- root dir and the relative @cabal.project@ file (or explicit override) | ProjectRootExplicit FilePath FilePath + + -- | An explicit, absolute project root dir and an explicit, absolute + -- @cabal.project@ file. + | ProjectRootExplicitAbsolute FilePath FilePath deriving (Eq, Show) defaultProjectFile :: FilePath @@ -185,8 +189,9 @@ defaultDistDirLayout projectRoot mdistDirectory = DistDirLayout {..} where (projectRootDir, projectFile) = case projectRoot of - ProjectRootImplicit dir -> (dir, dir defaultProjectFile) - ProjectRootExplicit dir file -> (dir, dir file) + ProjectRootImplicit dir -> (dir, dir defaultProjectFile) + ProjectRootExplicit dir file -> (dir, dir file) + ProjectRootExplicitAbsolute dir file -> (dir, file) distProjectRootDirectory :: FilePath distProjectRootDirectory = projectRootDir diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 8627a17d08c..ce328bc7ba7 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -434,7 +434,9 @@ findProjectRoot verbosity mprojectDir mprojectFile = do Nothing -> pure $ Right (ProjectRootExplicit projectDir defaultProjectFile) Just projectFile - | isAbsolute projectFile -> left (BadProjectRootAbsoluteFile projectFile) + | isAbsolute projectFile -> doesFileExist projectFile >>= \case + False -> left (BadProjectRootAbsoluteFile projectFile) + True -> Right . ProjectRootExplicitAbsolute dir <$> canonicalizePath projectFile | otherwise -> doesFileExist (projectDir projectFile) >>= \case False -> left (BadProjectRootDirFile dir projectFile) @@ -500,7 +502,7 @@ renderBadProjectRoot = \case "The given project directory '" <> dir <> "' does not exist." BadProjectRootAbsoluteFile file -> - "The given project file'" <> file <> "' is an absolute path: it must be relative to the project directory." + "The given project file '" <> file <> "' does not exist." BadProjectRootDirFile dir file -> "The given project directory/file combination '" <> dir file <> "' does not exist." diff --git a/cabal-install/src/Distribution/Client/ProjectFlags.hs b/cabal-install/src/Distribution/Client/ProjectFlags.hs index 9084f061863..8959f60aefc 100644 --- a/cabal-install/src/Distribution/Client/ProjectFlags.hs +++ b/cabal-install/src/Distribution/Client/ProjectFlags.hs @@ -21,7 +21,8 @@ data ProjectFlags = ProjectFlags -- ^ The project directory. , flagProjectFile :: Flag FilePath - -- ^ The cabal project file path, relative to the project directory; defaults to @cabal.project@. + -- ^ The cabal project file path; defaults to @cabal.project@. + -- This path, when relative, is relative to the project directory. -- The filename portion of the path denotes the cabal project file name, but it also -- is the base of auxiliary project files, such as -- @cabal.project.local@ and @cabal.project.freeze@ which are also @@ -51,7 +52,7 @@ projectFlagsOptions showOrParseArgs = flagProjectDir (\path flags -> flags { flagProjectDir = path }) (reqArg "DIR" (succeedReadE Flag) flagToList) , option [] ["project-file"] - "Set the path of the cabal.project file (relative to the project directory)" + "Set the path of the cabal.project file (relative to the project directory when relative)" flagProjectFile (\pf flags -> flags { flagProjectFile = pf }) (reqArg "FILE" (succeedReadE Flag) flagToList) , option ['z'] ["ignore-project"] diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index a0ba8857ded..6eb3b125bab 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -59,12 +59,12 @@ import Distribution.Client.ProjectConfig.Legacy import UnitTests.Distribution.Client.ArbitraryInstances import UnitTests.Distribution.Client.TreeDiffInstances () -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.QuickCheck import Data.TreeDiff.Class import Data.TreeDiff.QuickCheck +import Test.Tasty +import Test.Tasty.HUnit +import Test.Tasty.QuickCheck tests :: [TestTree] tests = @@ -108,27 +108,27 @@ tests = testFindProjectRoot :: TestTree testFindProjectRoot = testGroup "findProjectRoot" - [ test "defaults" (cd dir) Nothing Nothing (succeeds dir file) - , test "defaults in lib" (cd libDir) Nothing Nothing (succeeds dir file) + [ test "defaults" (cd dir) Nothing Nothing (succeeds dir file) + , test "defaults in lib" (cd libDir) Nothing Nothing (succeeds dir file) - , test "explicit file" (cd dir) Nothing (Just file) (succeeds dir file) - , test "explicit file in lib" (cd libDir) Nothing (Just file) (succeeds dir file) + , test "explicit file" (cd dir) Nothing (Just file) (succeeds dir file) + , test "explicit file in lib" (cd libDir) Nothing (Just file) (succeeds dir file) - , test "other file" (cd dir) Nothing (Just fileOther) (succeeds dir fileOther) - , test "other file in lib" (cd libDir) Nothing (Just fileOther) (succeeds dir fileOther) + , test "other file" (cd dir) Nothing (Just fileOther) (succeeds dir fileOther) + , test "other file in lib" (cd libDir) Nothing (Just fileOther) (succeeds dir fileOther) -- Deprecated use-case - , test "absolute file" Nothing Nothing (Just (dir file)) (succeeds dir file) + , test "absolute file" Nothing Nothing (Just absFile) (succeeds dir file) - , test "nested file" (cd dir) Nothing (Just nixFile) (succeeds dir nixFile) - , test "nested file in lib" (cd libDir) Nothing (Just nixFile) (succeeds dir nixFile) + , test "nested file" (cd dir) Nothing (Just nixFile) (succeeds dir nixFile) + , test "nested file in lib" (cd libDir) Nothing (Just nixFile) (succeeds dir nixFile) - , test "explicit dir" Nothing (Just dir) Nothing (succeeds dir file) - , test "explicit dir & file" Nothing (Just dir) (Just file) (succeeds dir file) - , test "explicit dir & nested file" Nothing (Just dir) (Just nixFile) (succeeds dir nixFile) - , test "explicit dir & nested other file" Nothing (Just dir) (Just nixOther) (succeeds dir nixOther) + , test "explicit dir" Nothing (Just dir) Nothing (succeeds dir file) + , test "explicit dir & file" Nothing (Just dir) (Just file) (succeeds dir file) + , test "explicit dir & nested file" Nothing (Just dir) (Just nixFile) (succeeds dir nixFile) + , test "explicit dir & nested other file" Nothing (Just dir) (Just nixOther) (succeeds dir nixOther) - , test "explicit dir & absolute file" Nothing (Just dir) (Just (dir file)) fails + , test "explicit dir & absolute file" Nothing (Just dir) (Just absFile) (succeedsWith ProjectRootExplicitAbsolute dir absFile) ] where dir = fixturesDir "project-root" @@ -136,6 +136,7 @@ testFindProjectRoot = testGroup "findProjectRoot" file = defaultProjectFile fileOther = file <.> "other" + absFile = dir file nixFile = "nix" file nixOther = nixFile <.> "other" @@ -158,9 +159,11 @@ testFindProjectRoot = testGroup "findProjectRoot" cd d = Just (withCurrentDirectory d) - succeeds projectDir projectFile result = case result of + succeeds = succeedsWith ProjectRootExplicit + + succeedsWith mk projectDir projectFile result = case result of Left err -> assertFailure $ "Expected ProjectRoot, but found " <> show err - Right pr -> pr @?= ProjectRootExplicit projectDir projectFile + Right pr -> pr @?= mk projectDir projectFile fails result = case result of Left _ -> pure () diff --git a/changelog.d/pr-8454 b/changelog.d/pr-8454 index 99e8b48b5bc..915caf61cc9 100644 --- a/changelog.d/pr-8454 +++ b/changelog.d/pr-8454 @@ -7,6 +7,6 @@ significance: significant description: { - Added --project-dir flag for specifying the project's root directory -- Deprecated using --project-file with an absolute filepath +- Deprecated using --project-file with an absolute filepath without also using --project-dir } diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index 51396a166e0..cf08059de68 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -286,9 +286,11 @@ package, and thus apply globally: .. _cmdoption-project-dir: .. option:: --project-dir=DIR - Specifies the path of the project directory. - When :ref:`project-file` is also specified, it is - relative to this directory. + Specifies the path of the project directory. If a relative + :ref:`project-file` path is also specified, + it will be resolved relative to this directory. + + The project directory need not contain a ``cabal.project`` file. This option cannot be specified via a ``cabal.project`` file. @@ -303,7 +305,8 @@ package, and thus apply globally: ``--project-file=my.project``, then the other files that will be probed are ``my.project.freeze`` and ``my.project.local``. - If :ref:`project-dir` is not specified, we will + If :ref:`project-dir` is not specified, + and the path is relative, we will look for the file relative to the current working directory, and then for the parent directory, until the project file is found or we have hit the top of the user's home directory.