Commit e7621b8e authored by John Ericson's avatar John Ericson Committed by John Ericson
Browse files

Clamp down on dependencies:

Previously:

 - "build-depends" entries on internal libraries with an explicit version
   range are an error.

Now for all three of "build-tools", "tool-depends", and "build-depends":

 - Explicit versions ranges on internal components that are satisfied by
   the current package cause a warning.

 - Explicit version ranges on internal components are are not satisfied by
   the current package are an error.

And additionally for "tool-depends":

 - dependencies on a non-existent executable in the current package are an
   error.
parent c0b63100
......@@ -159,6 +159,7 @@ library
Distribution.Simple.Build.PathsModule
Distribution.Simple.BuildPaths
Distribution.Simple.BuildTarget
Distribution.Simple.BuildToolDepends
Distribution.Simple.CCompiler
Distribution.Simple.Command
Distribution.Simple.Compiler
......
......@@ -42,9 +42,11 @@ import Distribution.Compiler
import Distribution.System
import Distribution.License
import Distribution.Simple.BuildPaths (autogenPathsModuleName)
import Distribution.Simple.BuildToolDepends
import Distribution.Simple.CCompiler
import Distribution.Types.ComponentRequestedSpec
import Distribution.Types.Dependency
import Distribution.Types.ExeDependency
import Distribution.Types.UnqualComponentName
import Distribution.Types.CondTree
import Distribution.Simple.Utils hiding (findPackageDesc, notice)
......@@ -527,13 +529,42 @@ checkFields pkg =
++ "for example 'tested-with: GHC==6.10.4, GHC==6.12.3' and not "
++ "'tested-with: GHC==6.10.4 && ==6.12.3'."
, check (not (null buildDependsRangeOnInternalLibrary)) $
, check (not (null depInternalLibraryWithExtraVersion)) $
PackageBuildWarning $
"The package has a version range for a dependency on an "
"The package has an extraneous version range for a dependency on an "
++ "internal library: "
++ commaSep (map display buildDependsRangeOnInternalLibrary)
++ ". This version range has no semantic meaning and can be "
++ "removed."
++ commaSep (map display depInternalLibraryWithExtraVersion)
++ ". This version range includes the current package but isn't needed "
++ "as the current package's library will always be used."
, check (not (null depInternalLibraryWithImpossibleVersion)) $
PackageBuildImpossible $
"The package has an impossible version range for a dependency on an "
++ "internal library: "
++ commaSep (map display depInternalLibraryWithImpossibleVersion)
++ ". This version range does not include the current package, and must "
++ "be removed as the current package's library will always be used."
, check (not (null depInternalExecutableWithExtraVersion)) $
PackageBuildWarning $
"The package has an extraneous version range for a dependency on an "
++ "internal executable: "
++ commaSep (map display depInternalExecutableWithExtraVersion)
++ ". This version range includes the current package but isn't needed "
++ "as the current package's executable will always be used."
, check (not (null depInternalExecutableWithImpossibleVersion)) $
PackageBuildImpossible $
"The package has an impossible version range for a dependency on an "
++ "internal executable: "
++ commaSep (map display depInternalExecutableWithImpossibleVersion)
++ ". This version range does not include the current package, and must "
++ "be removed as the current package's executable will always be used."
, check (not (null depMissingInternalExecutable)) $
PackageBuildImpossible $
"The package depends on a missing internal executable: "
++ commaSep (map display depInternalExecutableWithImpossibleVersion)
]
where
unknownCompilers = [ name | (OtherCompiler name, _) <- testedWith pkg ]
......@@ -559,14 +590,55 @@ checkFields pkg =
internalLibraries =
map (maybe (packageName pkg) (unqualComponentNameToPackageName) . libName)
(allLibraries pkg)
buildDependsRangeOnInternalLibrary =
internalExecutables = map exeName $ executables pkg
internalLibDeps =
[ dep
| bi <- allBuildInfo pkg
, dep@(Dependency name versionRange) <- targetBuildDepends bi
, not (isAnyVersion versionRange)
, dep@(Dependency name _) <- targetBuildDepends bi
, name `elem` internalLibraries
]
internalExeDeps =
[ dep
| bi <- allBuildInfo pkg
, dep <- getAllToolDependencies pkg bi
, isInternal pkg dep
]
depInternalLibraryWithExtraVersion =
[ dep
| dep@(Dependency _ versionRange) <- internalLibDeps
, not $ isAnyVersion versionRange
, packageVersion pkg `withinRange` versionRange
]
depInternalLibraryWithImpossibleVersion =
[ dep
| dep@(Dependency _ versionRange) <- internalLibDeps
, not $ packageVersion pkg `withinRange` versionRange
]
depInternalExecutableWithExtraVersion =
[ dep
| dep@(ExeDependency _ _ versionRange) <- internalExeDeps
, not $ isAnyVersion versionRange
, packageVersion pkg `withinRange` versionRange
]
depInternalExecutableWithImpossibleVersion =
[ dep
| dep@(ExeDependency _ _ versionRange) <- internalExeDeps
, not $ packageVersion pkg `withinRange` versionRange
]
depMissingInternalExecutable =
[ dep
| dep@(ExeDependency _ eName _) <- internalExeDeps
, not $ eName `elem` internalExecutables
]
checkLicense :: PackageDescription -> [PackageCheck]
checkLicense pkg =
......
-- |
--
-- This modules provides functions for working with both the legacy
-- "build-tools" field, and its replacement, "build-tool-depends". Prefer using
-- the functions contained to access those fields directly.
module Distribution.Simple.BuildToolDepends where
import Data.Maybe
import qualified Data.Map as Map
import Distribution.Package
import Distribution.PackageDescription
import Distribution.Types.ExeDependency
import Distribution.Types.LegacyExeDependency
import Distribution.Types.UnqualComponentName
-- | Desugar a "build-tools" entry into proper a executable dependency if
-- possible.
--
-- An entry can be so desguared in two cases:
--
-- 1. The name in build-tools matches a locally defined executable. The
-- executable dependency produced is on that exe in the current package.
--
-- 2. The name in build-tools matches a hard-coded set of known tools. For now,
-- the executable dependency produced is one an executable in a package of
-- the same, but the hard-coding could just as well be per-key.
--
-- The first cases matches first.
desugarBuildTool :: PackageDescription
-> LegacyExeDependency
-> Maybe ExeDependency
desugarBuildTool pkg led =
if foundLocal
then Just $ ExeDependency (packageName pkg) toolName reqVer
else Map.lookup name whiteMap
where
LegacyExeDependency name reqVer = led
toolName = mkUnqualComponentName name
foundLocal = toolName `elem` map exeName (executables pkg)
whitelist = [ "hscolour", "haddock", "happy", "alex", "hsc2hs", "c2hs"
, "cpphs", "greencard"]
whiteMap = Map.fromList $ flip map whitelist $ \n ->
(n, ExeDependency (mkPackageName n) (mkUnqualComponentName n) reqVer)
-- | Get everything from "build-tool-depends", along with entries from
-- "build-tools" that we know how to desugar.
--
-- This should almost always be used instead of just accessing the
-- `toolDepends` field directly.
getAllToolDependencies :: PackageDescription
-> BuildInfo
-> [ExeDependency]
getAllToolDependencies pkg bi =
toolDepends bi ++ mapMaybe (desugarBuildTool pkg) (buildTools bi)
-- | Does the given executable dependency map to this current package?
--
-- This is a tiny function, but used in a number of places.
--
-- This function is only sound to call on `BuildInfo`s from the given package
-- description. This is because it just filters the package names of each
-- dependency, and does not check whether version bounds in fact exclude the
-- current package, or the referenced components in fact exist in the current
-- package.
--
-- This is OK because when a package is loaded, it is checked (in
-- `Distribution.Package.Check`) that dependencies matching internal components
-- do indeed have version bounds accepting the current package, and any
-- depended-on component in the current package actually exists. In fact this
-- check is performed by gathering the internal tool dependencies of each
-- component of the package according to this module, and ensuring those
-- properties on each so-gathered dependency.
--
-- version bounds and components of the package are unchecked. This is because
-- we sanitize exe deps so that the matching name implies these other
-- conditions.
isInternal :: PackageDescription -> ExeDependency -> Bool
isInternal pkg (ExeDependency n _ _) = n == packageName pkg
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment