From a9c93bdd8b027d6de09a3eada7721e7fd2d3e050 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" <ezyang@cs.stanford.edu> Date: Mon, 26 Oct 2015 20:48:36 +0100 Subject: [PATCH] Implement MIN_VERSION and VERSION macros natively in GHC. Test Plan: validate Reviewers: austin, thomie, bgamari Reviewed By: thomie Subscribers: thomie Differential Revision: https://phabricator.haskell.org/D1349 GHC Trac Issues: #10970 --- compiler/main/DriverPipeline.hs | 45 +++++++++++++++++++++++++++ compiler/main/Packages.hs | 11 ++++++- docs/users_guide/phases.rst | 16 ++++++++++ testsuite/.gitignore | 3 +- testsuite/tests/driver/T10970.hs | 6 ++++ testsuite/tests/driver/T10970.stdout | 2 ++ testsuite/tests/driver/T10970a.hs | 6 ++++ testsuite/tests/driver/T10970a.stderr | 6 ++++ testsuite/tests/driver/all.T | 3 ++ 9 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 testsuite/tests/driver/T10970.hs create mode 100644 testsuite/tests/driver/T10970.stdout create mode 100644 testsuite/tests/driver/T10970a.hs create mode 100644 testsuite/tests/driver/T10970a.stderr diff --git a/compiler/main/DriverPipeline.hs b/compiler/main/DriverPipeline.hs index 373afba3faa7..697353e5c3c1 100644 --- a/compiler/main/DriverPipeline.hs +++ b/compiler/main/DriverPipeline.hs @@ -74,6 +74,7 @@ import Data.List ( isSuffixOf ) import Data.Maybe import Data.Char import Data.Time +import Data.Version -- --------------------------------------------------------------------------- -- Pre-process @@ -2049,6 +2050,20 @@ doCpp dflags raw input_fn output_fn = do , "-include", ghcVersionH ] + -- MIN_VERSION macros + let uids = explicitPackages (pkgState dflags) + pkgs = catMaybes (map (lookupPackage dflags) uids) + mb_macro_include <- + -- Only generate if we have (1) we have set -hide-all-packages + -- (so we don't generate a HUGE macro file of things we don't + -- care about but are exposed) and (2) we actually have packages + -- to write macros for! + if gopt Opt_HideAllPackages dflags && not (null pkgs) + then do macro_stub <- newTempName dflags "h" + writeFile macro_stub (generatePackageVersionMacros pkgs) + return [SysTools.FileOption "-include" macro_stub] + else return [] + cpp_prog ( map SysTools.Option verbFlags ++ map SysTools.Option include_paths ++ map SysTools.Option hsSourceCppOpts @@ -2058,6 +2073,7 @@ doCpp dflags raw input_fn output_fn = do ++ map SysTools.Option hscpp_opts ++ map SysTools.Option sse_defs ++ map SysTools.Option avx_defs + ++ mb_macro_include -- Set the language mode to assembler-with-cpp when preprocessing. This -- alleviates some of the C99 macro rules relating to whitespace and the hash -- operator, which we tend to abuse. Clang in particular is not very happy @@ -2087,6 +2103,35 @@ getBackendDefs dflags | hscTarget dflags == HscLlvm = do getBackendDefs _ = return [] +-- --------------------------------------------------------------------------- +-- Macros (cribbed from Cabal) + +generatePackageVersionMacros :: [PackageConfig] -> String +generatePackageVersionMacros pkgs = concat + [ "/* package " ++ sourcePackageIdString pkg ++ " */\n" + ++ generateMacros "" pkgname version + | pkg <- pkgs + , let version = packageVersion pkg + pkgname = map fixchar (packageNameString pkg) + ] + +fixchar :: Char -> Char +fixchar '-' = '_' +fixchar c = c + +generateMacros :: String -> String -> Version -> String +generateMacros prefix name version = + concat + ["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n" + ,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n" + ," (major1) < ",major1," || \\\n" + ," (major1) == ",major1," && (major2) < ",major2," || \\\n" + ," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")" + ,"\n\n" + ] + where + (major1:major2:minor:_) = map show (versionBranch version ++ repeat 0) + -- --------------------------------------------------------------------------- -- join object files into a single relocatable object file, using ld -r diff --git a/compiler/main/Packages.hs b/compiler/main/Packages.hs index 0e32947b31e7..9f60c1cc28be 100644 --- a/compiler/main/Packages.hs +++ b/compiler/main/Packages.hs @@ -7,7 +7,7 @@ module Packages ( module PackageConfig, -- * Reading the package config, and processing cmdline args - PackageState(preloadPackages), + PackageState(preloadPackages, explicitPackages), emptyPackageState, initPackages, readPackageConfigs, @@ -245,6 +245,10 @@ data PackageState = PackageState { -- is always mentioned before the packages it depends on. preloadPackages :: [UnitId], + -- | Packages which we explicitly depend on (from a command line flag). + -- We'll use this to generate version macros. + explicitPackages :: [UnitId], + -- | This is a full map from 'ModuleName' to all modules which may possibly -- be providing it. These providers may be hidden (but we'll still want -- to report them in error messages), or it may be an ambiguous import. @@ -255,6 +259,7 @@ emptyPackageState :: PackageState emptyPackageState = PackageState { pkgIdMap = emptyUFM, preloadPackages = [], + explicitPackages = [], moduleToPkgConfAll = Map.empty } @@ -961,6 +966,10 @@ mkPackageState dflags0 pkgs0 preload0 = do let pstate = PackageState{ preloadPackages = dep_preload, + explicitPackages = foldUFM (\pkg xs -> + if elemUFM (packageConfigId pkg) vis_map + then packageConfigId pkg : xs + else xs) [] pkg_db, pkgIdMap = pkg_db, moduleToPkgConfAll = mkModuleToPkgConfAll dflags pkg_db vis_map } diff --git a/docs/users_guide/phases.rst b/docs/users_guide/phases.rst index e7a46950b555..8945e3bbe707 100644 --- a/docs/users_guide/phases.rst +++ b/docs/users_guide/phases.rst @@ -340,6 +340,22 @@ defined by your local GHC installation, the following trick is useful: architecture, where⟨arch⟩ is the name of the current architecture (eg. ``i386``, ``x86_64``, ``powerpc``, ``sparc``, etc.). +``VERSION_pkgname`` + This macro is available starting GHC 8.0. It is defined for every + exposed package, but only if the ``-hide-all-packages`` flag + is set. This macro expands to a string recording the + version of ``pkgname`` that is exposed for module import. + It is identical in behavior to the ``VERSION_pkgname`` macros + that Cabal defines. + +``MIN_VERSION_pkgname(x,y,z)`` + This macro is available starting GHC 8.0. It is defined for every + exposed package, but only if the ``-hide-all-packages`` flag + is set. This macro is provided for convenience to write CPP + conditionals testing if a package version is ``x.y.z`` or + less. It is identical in behavior to the ``MIN_VERSION_pkgname`` + macros that Cabal defines. + .. _cpp-string-gaps: CPP and string gaps diff --git a/testsuite/.gitignore b/testsuite/.gitignore index a2b645429b5c..d9f7dccabd91 100644 --- a/testsuite/.gitignore +++ b/testsuite/.gitignore @@ -532,6 +532,7 @@ mk/ghcconfig*_bin_ghc*.exe.mk /tests/driver/Hello062a.hs /tests/driver/Hello062b.hs /tests/driver/Hello062c.hs +/tests/driver/T10970 /tests/driver/T1959/E.hs /tests/driver/T1959/prog /tests/driver/T3007/A/Setup @@ -631,8 +632,8 @@ mk/ghcconfig*_bin_ghc*.exe.mk /tests/driver/recomp013/C.hs /tests/driver/recomp014/A.hs /tests/driver/recomp014/A1.hs -/tests/driver/recomp014/B.hsig /tests/driver/recomp014/B.hs-boot +/tests/driver/recomp014/B.hsig /tests/driver/recomp014/C.hs /tests/driver/recomp014/recomp014 /tests/driver/retc001/B.hs diff --git a/testsuite/tests/driver/T10970.hs b/testsuite/tests/driver/T10970.hs new file mode 100644 index 000000000000..9de4f80650f4 --- /dev/null +++ b/testsuite/tests/driver/T10970.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE CPP #-} +main = do + putStrLn VERSION_containers +#if MIN_VERSION_base(3,0,0) + putStrLn "OK" +#endif diff --git a/testsuite/tests/driver/T10970.stdout b/testsuite/tests/driver/T10970.stdout new file mode 100644 index 000000000000..2a2835658b19 --- /dev/null +++ b/testsuite/tests/driver/T10970.stdout @@ -0,0 +1,2 @@ +0.5.6.2 +OK diff --git a/testsuite/tests/driver/T10970a.hs b/testsuite/tests/driver/T10970a.hs new file mode 100644 index 000000000000..9de4f80650f4 --- /dev/null +++ b/testsuite/tests/driver/T10970a.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE CPP #-} +main = do + putStrLn VERSION_containers +#if MIN_VERSION_base(3,0,0) + putStrLn "OK" +#endif diff --git a/testsuite/tests/driver/T10970a.stderr b/testsuite/tests/driver/T10970a.stderr new file mode 100644 index 000000000000..74c68211685e --- /dev/null +++ b/testsuite/tests/driver/T10970a.stderr @@ -0,0 +1,6 @@ + +T10970a.hs:4:0: error: + error: missing binary operator before token "(" + #if MIN_VERSION_base(3,0,0) + ^ +`gcc' failed in phase `C pre-processor'. (Exit code: 1) diff --git a/testsuite/tests/driver/all.T b/testsuite/tests/driver/all.T index 08fdc1f6f6c4..f946055c21a3 100644 --- a/testsuite/tests/driver/all.T +++ b/testsuite/tests/driver/all.T @@ -456,3 +456,6 @@ test('T365', test('T9360a', normal, run_command, ['{compiler} --interactive -e ""']) test('T9360b', normal, run_command, ['{compiler} -e "" --interactive']) + +test('T10970', normal, compile_and_run, ['-hide-all-packages -package base -package containers']) +test('T10970a', normal, compile_fail, ['']) -- GitLab