diff --git a/ChangeLog.md b/ChangeLog.md index a88af3542a..a2a93b2166 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -16,6 +16,7 @@ * Don't require cabal-install to upload [#313](https://github.com/commercialhaskell/stack/issues/313) * Generate indexes for all deps and all installed snapshot packages [#143](https://github.com/fpco/commercialhaskell/issues/143) * Provide `--resolver global` option [#645](https://github.com/commercialhaskell/stack/issues/645) +* Make `stack build --flag` error when flag or package is unknown [#617](https://github.com/commercialhaskell/stack/issues/617) Bug fixes: diff --git a/src/Stack/Build/Source.hs b/src/Stack/Build/Source.hs index 7b8bbc9e58..074292543d 100644 --- a/src/Stack/Build/Source.hs +++ b/src/Stack/Build/Source.hs @@ -14,6 +14,7 @@ module Stack.Build.Source ) where import Control.Applicative ((<|>), (<$>), (<*>)) +import Control.Arrow ((&&&)) import Control.Exception (catch) import Control.Monad import Control.Monad.Catch (MonadCatch) @@ -247,6 +248,34 @@ loadLocals bopts latestVersion = do let known = Set.fromList $ map (packageName . lpPackage) lps unknown = Set.difference (Map.keysSet names) known + -- Check if flags specified in stack.yaml and the command line are + -- used, see https://github.com/commercialhaskell/stack/issues/617 + flags = map (, FSCommandLine) [(k, v) | (Just k, v) <- Map.toList $ boptsFlags bopts] + ++ map (, FSStackYaml) (Map.toList $ bcFlags bconfig) + + localNameMap = Map.fromList $ map (packageName . lpPackage &&& lpPackage) lps + checkFlagUsed ((name, userFlags), source) = + case Map.lookup name localNameMap of + -- Package is not available locally + Nothing -> + case Map.lookup name $ bcExtraDeps bconfig of + -- Also not in extra-deps, it's an error + Nothing -> Just $ UFNoPackage source name + -- We don't check for flag presence for extra deps + Just _ -> Nothing + -- Package exists locally, let's check if the flags are defined + Just pkg -> + let unused = Set.difference (Map.keysSet userFlags) (packageDefinedFlags pkg) + in if Set.null unused + -- All flags are defined, nothing to do + then Nothing + -- Error about the undefined flags + else Just $ UFFlagsNotDefined source name unused + + unusedFlags = mapMaybe checkFlagUsed flags + + unless (null unusedFlags) $ throwM $ InvalidFlagSpecification $ Set.fromList unusedFlags + return (lps, unknown, idents) where -- Attempt to parse a TargetSpec based on its textual form and on diff --git a/src/Stack/Build/Types.hs b/src/Stack/Build/Types.hs index d2a80fbf6a..936b59783c 100644 --- a/src/Stack/Build/Types.hs +++ b/src/Stack/Build/Types.hs @@ -11,6 +11,8 @@ module Stack.Build.Types (StackBuildException(..) + ,FlagSource(..) + ,UnusedFlags(..) ,InstallLocation(..) ,ModTime ,modTime @@ -95,8 +97,16 @@ data StackBuildException Version -- local version Version -- version specified on command line | NoSetupHsFound (Path Abs Dir) + | InvalidFlagSpecification (Set UnusedFlags) deriving Typeable +data FlagSource = FSCommandLine | FSStackYaml + deriving (Show, Eq, Ord) + +data UnusedFlags = UFNoPackage FlagSource PackageName + | UFFlagsNotDefined FlagSource PackageName (Set FlagName) + deriving (Show, Eq, Ord) + instance Show StackBuildException where show (Couldn'tFindPkgId name) = ("After installing " <> packageNameString name <> @@ -224,6 +234,29 @@ instance Show StackBuildException where ] show (NoSetupHsFound dir) = "No Setup.hs or Setup.lhs file found in " ++ toFilePath dir + show (InvalidFlagSpecification unused) = unlines + $ "Invalid flag specification:" + : map go (Set.toList unused) + where + goS :: FlagSource -> String + goS FSCommandLine = " (specified on command line)" + goS FSStackYaml = " (specified in stack.yaml)" + + go :: UnusedFlags -> String + go (UFNoPackage src name) = concat + [ "- Package '" + , packageNameString name + , "' not found" + , goS src + ] + go (UFFlagsNotDefined src name flags) = concat + [ "- Package '" + , packageNameString name + , "' does not define the following flags" + , goS src + , ": " + , intercalate ", " $ map flagNameString $ Set.toList flags + ] instance Exception StackBuildException diff --git a/src/Stack/Package.hs b/src/Stack/Package.hs index 2b223705cf..19736c9b93 100644 --- a/src/Stack/Package.hs +++ b/src/Stack/Package.hs @@ -125,6 +125,7 @@ data Package = ,packageOpts :: !GetPackageOpts -- ^ Args to pass to GHC. ,packageHasExposedModules :: !Bool -- ^ Does the package have exposed modules? ,packageSimpleType :: !Bool -- ^ Does the package of build-type: Simple + ,packageDefinedFlags :: !(Set FlagName) -- ^ All flags defined in the .cabal file } deriving (Show,Typeable) @@ -259,6 +260,7 @@ resolvePackage packageConfig gpkg = Package generatePkgDescOpts locals cabalfp pkg , packageHasExposedModules = maybe False (not . null . exposedModules) (library pkg) , packageSimpleType = buildType (packageDescription gpkg) == Just Simple + , packageDefinedFlags = S.fromList $ map (fromCabalFlagName . flagName) $ genPackageFlags gpkg } where diff --git a/test/integration/tests/617-extra-dep-flag/Main.hs b/test/integration/tests/617-extra-dep-flag/Main.hs new file mode 100644 index 0000000000..e61b083aa4 --- /dev/null +++ b/test/integration/tests/617-extra-dep-flag/Main.hs @@ -0,0 +1,4 @@ +import StackTest + +main :: IO () +main = stack ["build"] diff --git a/test/integration/tests/617-extra-dep-flag/files/stack.yaml b/test/integration/tests/617-extra-dep-flag/files/stack.yaml new file mode 100644 index 0000000000..eab516925b --- /dev/null +++ b/test/integration/tests/617-extra-dep-flag/files/stack.yaml @@ -0,0 +1,7 @@ +resolver: ghc-7.8 +flags: + text: + integer-simple: false +extra-deps: +- text-1.2.0.3 +packages: [] diff --git a/test/integration/tests/617-unused-flag-cli/Main.hs b/test/integration/tests/617-unused-flag-cli/Main.hs new file mode 100644 index 0000000000..f9a2b093ea --- /dev/null +++ b/test/integration/tests/617-unused-flag-cli/Main.hs @@ -0,0 +1,8 @@ +import StackTest + +main :: IO () +main = do + stack ["build"] + stackErr ["build", "--flag", "foo:bar"] + stackErr ["build", "--flag", "files:bar"] + stack ["build", "--flag", "*:bar"] diff --git a/test/integration/tests/617-unused-flag-cli/files/files.cabal b/test/integration/tests/617-unused-flag-cli/files/files.cabal new file mode 100644 index 0000000000..6ec3b1e471 --- /dev/null +++ b/test/integration/tests/617-unused-flag-cli/files/files.cabal @@ -0,0 +1,10 @@ +name: files +version: 0.1.0.0 +build-type: Simple +cabal-version: >=1.10 + +library + hs-source-dirs: src + exposed-modules: Lib + build-depends: base >= 4.7 && < 5 + default-language: Haskell2010 diff --git a/test/integration/tests/617-unused-flag-cli/files/src/Lib.hs b/test/integration/tests/617-unused-flag-cli/files/src/Lib.hs new file mode 100644 index 0000000000..d36ff2714d --- /dev/null +++ b/test/integration/tests/617-unused-flag-cli/files/src/Lib.hs @@ -0,0 +1,6 @@ +module Lib + ( someFunc + ) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/test/integration/tests/617-unused-flag-cli/files/stack.yaml b/test/integration/tests/617-unused-flag-cli/files/stack.yaml new file mode 100644 index 0000000000..2cb0fdb76e --- /dev/null +++ b/test/integration/tests/617-unused-flag-cli/files/stack.yaml @@ -0,0 +1 @@ +resolver: ghc-7.8 diff --git a/test/integration/tests/617-unused-flag-name-yaml/Main.hs b/test/integration/tests/617-unused-flag-name-yaml/Main.hs new file mode 100644 index 0000000000..b16d43227b --- /dev/null +++ b/test/integration/tests/617-unused-flag-name-yaml/Main.hs @@ -0,0 +1,4 @@ +import StackTest + +main :: IO () +main = stackErr ["build"] diff --git a/test/integration/tests/617-unused-flag-name-yaml/files/files.cabal b/test/integration/tests/617-unused-flag-name-yaml/files/files.cabal new file mode 100644 index 0000000000..6ec3b1e471 --- /dev/null +++ b/test/integration/tests/617-unused-flag-name-yaml/files/files.cabal @@ -0,0 +1,10 @@ +name: files +version: 0.1.0.0 +build-type: Simple +cabal-version: >=1.10 + +library + hs-source-dirs: src + exposed-modules: Lib + build-depends: base >= 4.7 && < 5 + default-language: Haskell2010 diff --git a/test/integration/tests/617-unused-flag-name-yaml/files/src/Lib.hs b/test/integration/tests/617-unused-flag-name-yaml/files/src/Lib.hs new file mode 100644 index 0000000000..d36ff2714d --- /dev/null +++ b/test/integration/tests/617-unused-flag-name-yaml/files/src/Lib.hs @@ -0,0 +1,6 @@ +module Lib + ( someFunc + ) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/test/integration/tests/617-unused-flag-name-yaml/files/stack.yaml b/test/integration/tests/617-unused-flag-name-yaml/files/stack.yaml new file mode 100644 index 0000000000..018bb09125 --- /dev/null +++ b/test/integration/tests/617-unused-flag-name-yaml/files/stack.yaml @@ -0,0 +1,4 @@ +resolver: ghc-7.8 +flags: + files: + does-not-exist: false diff --git a/test/integration/tests/617-unused-flag-yaml/Main.hs b/test/integration/tests/617-unused-flag-yaml/Main.hs new file mode 100644 index 0000000000..b16d43227b --- /dev/null +++ b/test/integration/tests/617-unused-flag-yaml/Main.hs @@ -0,0 +1,4 @@ +import StackTest + +main :: IO () +main = stackErr ["build"] diff --git a/test/integration/tests/617-unused-flag-yaml/files/files.cabal b/test/integration/tests/617-unused-flag-yaml/files/files.cabal new file mode 100644 index 0000000000..6ec3b1e471 --- /dev/null +++ b/test/integration/tests/617-unused-flag-yaml/files/files.cabal @@ -0,0 +1,10 @@ +name: files +version: 0.1.0.0 +build-type: Simple +cabal-version: >=1.10 + +library + hs-source-dirs: src + exposed-modules: Lib + build-depends: base >= 4.7 && < 5 + default-language: Haskell2010 diff --git a/test/integration/tests/617-unused-flag-yaml/files/src/Lib.hs b/test/integration/tests/617-unused-flag-yaml/files/src/Lib.hs new file mode 100644 index 0000000000..d36ff2714d --- /dev/null +++ b/test/integration/tests/617-unused-flag-yaml/files/src/Lib.hs @@ -0,0 +1,6 @@ +module Lib + ( someFunc + ) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/test/integration/tests/617-unused-flag-yaml/files/stack.yaml b/test/integration/tests/617-unused-flag-yaml/files/stack.yaml new file mode 100644 index 0000000000..0936b63b8f --- /dev/null +++ b/test/integration/tests/617-unused-flag-yaml/files/stack.yaml @@ -0,0 +1,4 @@ +resolver: ghc-7.8 +flags: + does-not-exist: + foo: false