Implement `MIN_VERSION_GLASGOW_HASKELL()` macro
authorHerbert Valerio Riedel <hvr@gnu.org>
Sun, 5 Oct 2014 20:35:22 +0000 (22:35 +0200)
committerHerbert Valerio Riedel <hvr@gnu.org>
Sun, 5 Oct 2014 20:36:18 +0000 (22:36 +0200)
This exposes the `cProjectPatchLevel{1,2}` value at the CPP level to
allow it to be used in CPP conditionals. Concretely, GHC 7.10.2.20150623
would result in

  #define __GLASGOW_HASKELL__             710
  #define __GLASGOW_HASKELL_PATCHLEVEL1__ 2
  #define __GLASGOW_HASKELL_PATCHLEVEL2__ 20150623

while GHC 7.10.3 results in

  #define __GLASGOW_HASKELL__             710
  #define __GLASGOW_HASKELL_PATCHLEVEL1__ 3

and finally GHC 7.9.20141009 results in

  #define __GLASGOW_HASKELL__             709
  #define __GLASGOW_HASKELL_PATCHLEVEL1__ 20141009

As it's error-prone to properly express CPP conditionals for testing GHC
multi-component versions, a new macro `MIN_VERSION_GLASGOW_HASKELL()` is
provided (also via the new CPP include file `ghcversion.h`)

Finally, in order to make it easier to define the new CPP macro
`MIN_VERSION_GLASGOW_HASKELL()`, a new default-included
`include/ghcversion.h` is used for the new CPP definitions.

Reviewed By: ekmett, austin, #ghc

Differential Revision: https://phabricator.haskell.org/D66

.gitignore
aclocal.m4
compiler/ghc.mk
compiler/main/DriverPipeline.hs
docs/users_guide/phases.xml
ghc.mk
includes/ghc.mk
mk/project.mk.in

index d578d5c..19b55b2 100644 (file)
@@ -103,6 +103,7 @@ _darcs/
 /ghc/ghc-bin.cabal
 /includes/ghcautoconf.h
 /includes/ghcplatform.h
+/includes/ghcversion.h
 /index.html
 /inplace/
 /libffi/build/
index 62cf6fe..a98691e 100644 (file)
@@ -1604,6 +1604,13 @@ AC_SUBST([ProjectVersionInt])
 # The project patchlevel is zero unless stated otherwise
 test -z "$ProjectPatchLevel" && ProjectPatchLevel=0
 
+# Save split version of ProjectPatchLevel
+ProjectPatchLevel1=`echo $ProjectPatchLevel | sed 's/^\(@<:@^.@:>@*\)\(\.\{0,1\}\(.*\)\)$/\1/'`
+ProjectPatchLevel2=`echo $ProjectPatchLevel | sed 's/^\(@<:@^.@:>@*\)\(\.\{0,1\}\(.*\)\)$/\3/'`
+
+AC_SUBST([ProjectPatchLevel1])
+AC_SUBST([ProjectPatchLevel2])
+
 # Remove dots from the patch level; this allows us to have versions like 6.4.1.20050508
 ProjectPatchLevel=`echo $ProjectPatchLevel | sed 's/\.//'`
 
index 8e00149..0f98960 100644 (file)
@@ -71,6 +71,10 @@ compiler/stage%/build/Config.hs : mk/config.mk mk/project.mk | $$(dir $$@)/.
        @echo 'cProjectVersionInt    = "$(ProjectVersionInt)"'              >> $@
        @echo 'cProjectPatchLevel    :: String'                             >> $@
        @echo 'cProjectPatchLevel    = "$(ProjectPatchLevel)"'              >> $@
+       @echo 'cProjectPatchLevel1   :: String'                             >> $@
+       @echo 'cProjectPatchLevel1   = "$(ProjectPatchLevel1)"'             >> $@
+       @echo 'cProjectPatchLevel2   :: String'                             >> $@
+       @echo 'cProjectPatchLevel2   = "$(ProjectPatchLevel2)"'             >> $@
        @echo 'cBooterVersion        :: String'                             >> $@
        @echo 'cBooterVersion        = "$(GhcVersion)"'                     >> $@
        @echo 'cStage                :: String'                             >> $@
index ca72007..562c7d7 100644 (file)
@@ -1089,6 +1089,8 @@ runPhase (RealPhase cc_phase) input_fn dflags
                 -- very weakly typed, being derived from C--.
                 ["-fno-strict-aliasing"]
 
+        ghcVersionH <- liftIO $ getGhcVersionPathName dflags
+
         let gcc_lang_opt | cc_phase `eqPhase` Ccpp    = "c++"
                          | cc_phase `eqPhase` Cobjc   = "objective-c"
                          | cc_phase `eqPhase` Cobjcpp = "objective-c++"
@@ -1138,7 +1140,9 @@ runPhase (RealPhase cc_phase) input_fn dflags
                        ++ verbFlags
                        ++ [ "-S" ]
                        ++ cc_opt
-                       ++ [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt ]
+                       ++ [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt
+                          , "-include", ghcVersionH
+                          ]
                        ++ framework_paths
                        ++ split_opt
                        ++ include_paths
@@ -2092,6 +2096,13 @@ doCpp dflags raw input_fn output_fn = do
 
     backend_defs <- getBackendDefs dflags
 
+    -- Default CPP defines in Haskell source
+    ghcVersionH <- getGhcVersionPathName dflags
+    let hsSourceCppOpts =
+          [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt
+          , "-include", ghcVersionH
+          ]
+
     cpp_prog       (   map SysTools.Option verbFlags
                     ++ map SysTools.Option include_paths
                     ++ map SysTools.Option hsSourceCppOpts
@@ -2129,11 +2140,6 @@ getBackendDefs dflags | hscTarget dflags == HscLlvm = do
 getBackendDefs _ =
     return []
 
-hsSourceCppOpts :: [String]
--- Default CPP defines in Haskell source
-hsSourceCppOpts =
-        [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt ]
-
 -- ---------------------------------------------------------------------------
 -- join object files into a single relocatable object file, using ld -r
 
@@ -2213,6 +2219,16 @@ haveRtsOptsFlags dflags =
                                         RtsOptsSafeOnly -> False
                                         _ -> True
 
+-- | Find out path to @ghcversion.h@ file
+getGhcVersionPathName :: DynFlags -> IO FilePath
+getGhcVersionPathName dflags = do
+  dirs <- getPackageIncludePath dflags [rtsPackageKey]
+
+  found <- filterM doesFileExist (map (</> "ghcversion.h") dirs)
+  case found of
+      []    -> throwGhcExceptionIO (InstallationError ("ghcversion.h missing"))
+      (x:_) -> return x
+
 -- Note [-fPIC for assembler]
 -- When compiling .c source file GHC's driver pipeline basically
 -- does the following two things:
index 095de32..085ebbf 100644 (file)
@@ -389,6 +389,85 @@ $ cat foo.hspp</screen>
 
       <varlistentry>
         <term>
+          <constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL1&lowbar;&lowbar;</constant>
+          <indexterm><primary><constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL1&lowbar;&lowbar;</constant></primary></indexterm>
+        </term>
+        <term>
+          <constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL2&lowbar;&lowbar;</constant>
+          <indexterm><primary><constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL2&lowbar;&lowbar;</constant></primary></indexterm>
+        </term>
+        <listitem>
+          <para>These macros are available starting with GHC 7.10.1.</para>
+
+          <para>For three-part GHC version numbers
+          <literal><replaceable>x</replaceable>.<replaceable>y</replaceable>.<replaceable>z</replaceable></literal>,
+          the value of
+          <constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL1&lowbar;&lowbar;</constant>
+          is the integer <replaceable>z</replaceable>.</para>
+
+          <para>For four-part GHC version numbers
+          <literal><replaceable>x</replaceable>.<replaceable>y</replaceable>.<replaceable>z</replaceable>.<replaceable>z'</replaceable></literal>,
+          the value of
+          <constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL1&lowbar;&lowbar;</constant>
+          is the integer <replaceable>z</replaceable> while the value of
+          <constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;PATCHLEVEL2&lowbar;&lowbar;</constant>
+          is set to the integer <replaceable>z'</replaceable>.</para>
+
+          <para>These macros are provided for allowing finer
+          granularity than is provided by
+          <literal>__GLASGOW_HASKELL__</literal>. Usually, this should
+          not be necessary as it's expected for most APIs to remain
+          stable between patchlevel releases, but occasionally
+          internal API changes are necessary to fix bugs.  Also
+          conditional compilation on the patchlevel can be useful for
+          working around bugs in older releases.</para>
+
+          <para>NB. These macros are set when pre-processing both
+          Haskell source and C source, including the C source
+          generated from a Haskell module
+          (i.e. <filename>.hs</filename>, <filename>.lhs</filename>,
+          <filename>.c</filename> and <filename>.hc</filename>
+          files).</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <constant>MIN&lowbar;VERSION&lowbar;GLASGOW&lowbar;HASKELL(<replaceable>x</replaceable>,<replaceable>y</replaceable>,<replaceable>z</replaceable>,<replaceable>z'</replaceable>)</constant>
+          <indexterm><primary><constant>MIN&lowbar;VERSION&lowbar;GLASGOW&lowbar;HASKELL</constant></primary></indexterm>
+        </term>
+        <listitem>
+          <para>This macro is available starting with GHC 7.10.1.</para>
+
+          <para>This macro is provided for convenience to write CPP
+          conditionals testing whether the GHC version used is version
+          <literal><replaceable>x</replaceable>.<replaceable>y</replaceable>.<replaceable>z</replaceable>.<replaceable>z'</replaceable></literal>
+          or later.</para>
+
+          <para>If compatibility with Haskell compilers (including GHC
+          prior to version 7.10.1) which do not define
+          <literal>MIN_VERSION_GLASGOW_HASKELL</literal> is required,
+          the presence of the
+          <literal>MIN_VERSION_GLASGOW_HASKELL</literal> macro needs
+          to be ensured before it is called, e.g.:</para>
+
+<programlisting>&num;ifdef MIN_VERSION_GLASGOW_HASKELL
+&num;if MIN_VERSION_GLASGOW_HASKELL(7,10,2,0)
+/* code that applies only to GHC 7.10.2 or later */
+&num;endif
+&num;endif</programlisting>
+
+          <para>NB. This macro is set when pre-processing both
+          Haskell source and C source, including the C source
+          generated from a Haskell module
+          (i.e. <filename>.hs</filename>, <filename>.lhs</filename>,
+          <filename>.c</filename> and <filename>.hc</filename>
+          files).</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
           <constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;LLVM&lowbar;&lowbar;</constant>
           <indexterm><primary><constant>&lowbar;&lowbar;GLASGOW&lowbar;HASKELL&lowbar;LLVM&lowbar;&lowbar;</constant></primary></indexterm>
         </term>
diff --git a/ghc.mk b/ghc.mk
index eedb023..b75049f 100644 (file)
--- a/ghc.mk
+++ b/ghc.mk
@@ -1223,6 +1223,7 @@ CLEAN_FILES += includes/GHCConstants.h
 CLEAN_FILES += includes/DerivedConstants.h
 CLEAN_FILES += includes/ghcautoconf.h
 CLEAN_FILES += includes/ghcplatform.h
+CLEAN_FILES += includes/ghcversion.h
 CLEAN_FILES += utils/ghc-pkg/Version.hs
 CLEAN_FILES += compiler/parser/Parser.y
 CLEAN_FILES += compiler/prelude/primops.txt
index 5342cc8..f0bfbec 100644 (file)
@@ -16,6 +16,7 @@
 # XXX: these should go in includes/dist/build?
 includes_H_CONFIG   = includes/ghcautoconf.h
 includes_H_PLATFORM = includes/ghcplatform.h
+includes_H_VERSION  = includes/ghcversion.h
 
 #
 # All header files are in includes/{one of these subdirectories}
@@ -53,6 +54,34 @@ ifeq "$(DYNAMIC_BY_DEFAULT)" "YES"
 includes_CC_OPTS += -DDYNAMIC_BY_DEFAULT
 endif
 
+
+$(includes_H_VERSION) : mk/project.mk | $$(dir $$@)/.
+       @echo "Creating $@..."
+       @echo "#ifndef __GHCVERSION_H__"  > $@
+       @echo "#define __GHCVERSION_H__" >> $@
+       @echo >> $@
+       @echo "#ifndef __GLASGOW_HASKELL__" >> $@
+       @echo "# define __GLASGOW_HASKELL__ $(ProjectVersionInt)" >> $@
+       @echo "#endif" >> $@
+       @echo >> $@
+       @if [ -n "$(ProjectPatchLevel1)" ]; then \
+         echo "#define __GLASGOW_HASKELL_PATCHLEVEL1__ $(ProjectPatchLevel1)" >> $@; \
+       fi
+       @if [ -n "$(ProjectPatchLevel2)" ]; then \
+         echo "#define __GLASGOW_HASKELL_PATCHLEVEL1__ $(ProjectPatchLevel2)" >> $@; \
+       fi
+       @echo >> $@
+       @echo '#define MIN_VERSION_GLASGOW_HASKELL(ma,mi,pl1,pl2) (\\'     >> $@
+       @echo '   ((ma)*100+(mi)) <  __GLASGOW_HASKELL__ || \\'            >> $@
+       @echo '   ((ma)*100+(mi)) == __GLASGOW_HASKELL__    \\'            >> $@
+       @echo '          && (pl1) <  __GLASGOW_HASKELL_PATCHLEVEL1__ || \\'>> $@
+       @echo '   ((ma)*100+(mi)) == __GLASGOW_HASKELL__    \\'            >> $@
+       @echo '          && (pl1) == __GLASGOW_HASKELL_PATCHLEVEL1__ \\'   >> $@
+       @echo '          && (pl2) <= __GLASGOW_HASKELL_PATCHLEVEL2__ )'    >> $@
+       @echo >> $@
+       @echo "#endif /* __GHCVERSION_H__ */"          >> $@
+       @echo "Done."
+
 ifneq "$(BINDIST)" "YES"
 
 ifeq "$(PORTING_HOST)" "YES"
@@ -160,8 +189,8 @@ DERIVE_CONSTANTS_FLAGS += $(addprefix --gcc-flag$(space),$(includes_CC_OPTS) -fc
 DERIVE_CONSTANTS_FLAGS += --nm-program "$(NM)"
 
 ifneq "$(BINDIST)" "YES"
-$(includes_DERIVEDCONSTANTS):           $$(includes_H_CONFIG) $$(includes_H_PLATFORM) $$(includes_H_FILES) $$(rts_H_FILES)
-$(includes_GHCCONSTANTS_HASKELL_VALUE): $$(includes_H_CONFIG) $$(includes_H_PLATFORM) $$(includes_H_FILES) $$(rts_H_FILES)
+$(includes_DERIVEDCONSTANTS):           $$(includes_H_CONFIG) $$(includes_H_PLATFORM) $$(includes_H_VERSION) $$(includes_H_FILES) $$(rts_H_FILES)
+$(includes_GHCCONSTANTS_HASKELL_VALUE): $$(includes_H_CONFIG) $$(includes_H_PLATFORM) $$(includes_H_VERSION) $$(includes_H_FILES) $$(rts_H_FILES)
 
 $(includes_DERIVEDCONSTANTS): $(deriveConstants_INPLACE) | $$(dir $$@)/.
        $< --gen-header -o $@ --tmpdir $(dir $@) $(DERIVE_CONSTANTS_FLAGS)
@@ -183,10 +212,10 @@ endif
 # Install all header files
 
 $(eval $(call clean-target,includes,,\
-  $(includes_H_CONFIG) $(includes_H_PLATFORM)))
+  $(includes_H_CONFIG) $(includes_H_PLATFORM) $(includes_H_VERSION)))
 
 $(eval $(call all-target,includes,\
-  $(includes_H_CONFIG) $(includes_H_PLATFORM) \
+  $(includes_H_CONFIG) $(includes_H_PLATFORM) $(includes_H_VERSION) \
   $(includes_GHCCONSTANTS_HASKELL_TYPE) \
   $(includes_GHCCONSTANTS_HASKELL_VALUE) \
   $(includes_GHCCONSTANTS_HASKELL_WRAPPERS) \
@@ -202,5 +231,5 @@ install_includes :
            $(call INSTALL_DIR,"$(DESTDIR)$(ghcheaderdir)/$d") && \
            $(call INSTALL_HEADER,$(INSTALL_OPTS),includes/$d/*.h,"$(DESTDIR)$(ghcheaderdir)/$d/") && \
        ) true
-       $(call INSTALL_HEADER,$(INSTALL_OPTS),$(includes_H_CONFIG) $(includes_H_PLATFORM) $(includes_DERIVEDCONSTANTS),"$(DESTDIR)$(ghcheaderdir)/")
+       $(call INSTALL_HEADER,$(INSTALL_OPTS),$(includes_H_CONFIG) $(includes_H_PLATFORM) $(includes_H_VERSION) $(includes_DERIVEDCONSTANTS),"$(DESTDIR)$(ghcheaderdir)/")
 
index 69ed885..129b540 100644 (file)
@@ -29,6 +29,8 @@ ProjectTags       =
 ProjectVersion    = @ProjectVersion@$(ProjectTags)
 ProjectVersionInt = @ProjectVersionInt@
 ProjectPatchLevel = @ProjectPatchLevel@
+ProjectPatchLevel1 = @ProjectPatchLevel1@
+ProjectPatchLevel2 = @ProjectPatchLevel2@
 
 ################################################################################
 #