From 1aa65000568422f0187d2eb4fef00bcdca0cc125 Mon Sep 17 00:00:00 2001
From: Thomas Van Lenten <thomasvl@google.com>
Date: Thu, 15 Sep 2016 13:27:17 -0400
Subject: [PATCH] Update the ObjC version checks to support a min and current
 version.

- Capture the version used to generated.
- Check at compile time and runtime that generated code isn't from a newer
  version, also check that the min version required is also supported.
- Keep the old constants/macros/functions to special case the last version
  that was working so those generated sources still work until we decide
  otherwise.
---
 objectivec/DevTools/check_version_stamps.sh   | 67 +++++++++----------
 objectivec/GPBBootstrap.h                     | 29 ++++++--
 objectivec/GPBUtilities.m                     | 44 +++++++++++-
 objectivec/GPBUtilities_PackagePrivate.h      | 11 +++
 objectivec/google/protobuf/Any.pbobjc.h       |  7 +-
 objectivec/google/protobuf/Any.pbobjc.m       |  2 +-
 objectivec/google/protobuf/Api.pbobjc.h       |  7 +-
 objectivec/google/protobuf/Api.pbobjc.m       |  2 +-
 objectivec/google/protobuf/Duration.pbobjc.h  |  7 +-
 objectivec/google/protobuf/Duration.pbobjc.m  |  2 +-
 objectivec/google/protobuf/Empty.pbobjc.h     |  7 +-
 objectivec/google/protobuf/Empty.pbobjc.m     |  2 +-
 objectivec/google/protobuf/FieldMask.pbobjc.h |  7 +-
 objectivec/google/protobuf/FieldMask.pbobjc.m |  2 +-
 .../google/protobuf/SourceContext.pbobjc.h    |  7 +-
 .../google/protobuf/SourceContext.pbobjc.m    |  2 +-
 objectivec/google/protobuf/Struct.pbobjc.h    |  7 +-
 objectivec/google/protobuf/Struct.pbobjc.m    |  2 +-
 objectivec/google/protobuf/Timestamp.pbobjc.h |  7 +-
 objectivec/google/protobuf/Timestamp.pbobjc.m |  2 +-
 objectivec/google/protobuf/Type.pbobjc.h      |  7 +-
 objectivec/google/protobuf/Type.pbobjc.m      |  2 +-
 objectivec/google/protobuf/Wrappers.pbobjc.h  |  7 +-
 objectivec/google/protobuf/Wrappers.pbobjc.m  |  2 +-
 .../compiler/objectivec/objectivec_file.cc    | 24 ++++---
 25 files changed, 184 insertions(+), 81 deletions(-)

diff --git a/objectivec/DevTools/check_version_stamps.sh b/objectivec/DevTools/check_version_stamps.sh
index 325b71dd9a..1acbe2a2b7 100755
--- a/objectivec/DevTools/check_version_stamps.sh
+++ b/objectivec/DevTools/check_version_stamps.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/bash -eu
 
 # This script checks that the runtime version number constant in the compiler
 # source and in the runtime source is the same.
@@ -8,8 +8,6 @@
 # builds would break. At the same time, we don't want the runtime source
 # depending on the compiler sources; so two copies of the constant are needed.
 
-set -eu
-
 readonly ScriptDir=$(dirname "$(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")")
 readonly ProtoRootDir="${ScriptDir}/../.."
 
@@ -18,39 +16,40 @@ die() {
     exit 1
 }
 
-readonly ConstantName=GOOGLE_PROTOBUF_OBJC_GEN_VERSION
-
-# Collect version from plugin sources.
-
-readonly PluginSrc="${ProtoRootDir}/src/google/protobuf/compiler/objectivec/objectivec_file.cc"
-readonly PluginVersion=$( \
-    cat "${PluginSrc}" \
-        | sed -n -e "s:const int32 ${ConstantName} = \([0-9]*\);:\1:p"
-)
-
-if [[ -z "${PluginVersion}" ]] ; then
-    die "Failed to find ${ConstantName} in the plugin source (${PluginSrc})."
-fi
-
-# Collect version from runtime sources.
-
+readonly GeneratorSrc="${ProtoRootDir}/src/google/protobuf/compiler/objectivec/objectivec_file.cc"
 readonly RuntimeSrc="${ProtoRootDir}/objectivec/GPBBootstrap.h"
-readonly RuntimeVersion=$( \
-    cat "${RuntimeSrc}" \
-        | sed -n -e "s:#define ${ConstantName} \([0-9]*\):\1:p"
-)
-
-if [[ -z "${RuntimeVersion}" ]] ; then
-    die "Failed to find ${ConstantName} in the runtime source (${RuntimeSrc})."
-fi
-
-# Compare them.
 
-if [[ "${PluginVersion}" != "${RuntimeVersion}" ]] ; then
-    die "Versions don't match!
-   Plugin: ${PluginVersion} from ${PluginSrc}
-  Runtime: ${RuntimeVersion} from ${RuntimeSrc}
+check_constant() {
+  local ConstantName="$1"
+
+  # Collect version from generator sources.
+  local GeneratorVersion=$( \
+      cat "${GeneratorSrc}" \
+          | sed -n -e "s:const int32 ${ConstantName} = \([0-9]*\);:\1:p"
+  )
+  if [[ -z "${GeneratorVersion}" ]] ; then
+      die "Failed to find ${ConstantName} in the generator source (${GeneratorSrc})."
+  fi
+
+  # Collect version from runtime sources.
+  local RuntimeVersion=$( \
+      cat "${RuntimeSrc}" \
+          | sed -n -e "s:#define ${ConstantName} \([0-9]*\):\1:p"
+  )
+  if [[ -z "${RuntimeVersion}" ]] ; then
+      die "Failed to find ${ConstantName} in the runtime source (${RuntimeSrc})."
+  fi
+
+  # Compare them.
+  if [[ "${GeneratorVersion}" != "${RuntimeVersion}" ]] ; then
+      die "${ConstantName} values don't match!
+  Generator: ${GeneratorVersion} from ${GeneratorSrc}
+    Runtime: ${RuntimeVersion} from ${RuntimeSrc}
 "
-fi
+  fi
+}
+
+# Do the check.
+check_constant GOOGLE_PROTOBUF_OBJC_VERSION
 
 # Success
diff --git a/objectivec/GPBBootstrap.h b/objectivec/GPBBootstrap.h
index 7dc943d40d..f14dd8aa84 100644
--- a/objectivec/GPBBootstrap.h
+++ b/objectivec/GPBBootstrap.h
@@ -93,10 +93,27 @@
 // Meant to be used internally by generated code.
 #define GPB_METHOD_FAMILY_NONE __attribute__((objc_method_family(none)))
 
-// The protoc-gen-objc version which works with the current version of the
-// generated Objective C sources.  In general we don't want to change the
-// runtime interfaces (or this version) as it means everything has to be
-// regenerated.
+// ----------------------------------------------------------------------------
+// These version numbers are all internal to the ObjC Protobuf runtime; they
+// are used to ensure compatibility between the generated sources and the
+// headers being compiled against and/or the version of sources being run
+// against.
 //
-// Meant to be used internally by generated code.
-#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30002
+// They are all #defines so the values are captured into every .o file they
+// are used in and to allow comparisons in the preprocessor.
+
+// Current library runtime version.
+// - Gets bumped when the runtime makes changes to the interfaces between the
+//   generated code and runtime (things added/removed, etc).
+#define GOOGLE_PROTOBUF_OBJC_VERSION 30002
+
+// Minimum runtime version supported for compiling/running against.
+// - Gets changed when support for the older generated code is dropped.
+#define GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION 30001
+
+
+// This is a legacy constant now frozen in time for old generated code. If
+// GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION ever gets moved above 30001 then
+// this should also change to break code compiled with an old runtime that
+// can't be supported any more.
+#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30001
diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m
index 80b85d07d4..d4538598f7 100644
--- a/objectivec/GPBUtilities.m
+++ b/objectivec/GPBUtilities.m
@@ -58,8 +58,50 @@ NSData *GPBEmptyNSData(void) {
   return defaultNSData;
 }
 
+// -- About Version Checks --
+// There's actually 3 places these checks all come into play:
+// 1. When the generated source is compile into .o files, the header check
+//    happens. This is checking the protoc used matches the library being used
+//    when making the .o.
+// 2. Every place a generated proto header is included in a developer's code,
+//    the header check comes into play again. But this time it is checking that
+//    the current library headers being used still support/match the ones for
+//    the generated code.
+// 3. At runtime the final check here (GPBCheckRuntimeVersionsInternal), is
+//    called from the generated code passing in values captured when the
+//    generated code's .o was made. This checks that at runtime the generated
+//    code and runtime library match.
+
+void GPBCheckRuntimeVersionSupport(int32_t objcRuntimeVersion) {
+  // NOTE: This is passing the value captured in the compiled code to check
+  // against the values captured when the runtime support was compiled. This
+  // ensures the library code isn't in a different framework/library that
+  // was generated with a non matching version.
+  if (GOOGLE_PROTOBUF_OBJC_VERSION < objcRuntimeVersion) {
+    // Library is too old for headers.
+    [NSException raise:NSInternalInconsistencyException
+                format:@"Linked to ProtocolBuffer runtime version %d,"
+                       @" but code compiled needing atleast %d!",
+                       GOOGLE_PROTOBUF_OBJC_VERSION, objcRuntimeVersion];
+  }
+  if (objcRuntimeVersion < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION) {
+    // Headers are too old for library.
+    [NSException raise:NSInternalInconsistencyException
+                format:@"Proto generation source compiled against runtime"
+                       @" version %d, but this version of the runtime only"
+                       @" supports back to %d!",
+                       objcRuntimeVersion,
+                       GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION];
+  }
+}
+
+// This api is no longer used for version checks. 30001 is the last version
+// using this old versioning model. When that support is removed, this function
+// can be removed (along with the declaration in GPBUtilities_PackagePrivate.h).
 void GPBCheckRuntimeVersionInternal(int32_t version) {
-  if (version != GOOGLE_PROTOBUF_OBJC_GEN_VERSION) {
+  GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION == 30001,
+                           time_to_remove_this_old_version_shim);
+  if (version != GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION) {
     [NSException raise:NSInternalInconsistencyException
                 format:@"Linked to ProtocolBuffer runtime version %d,"
                        @" but code compiled with version %d!",
diff --git a/objectivec/GPBUtilities_PackagePrivate.h b/objectivec/GPBUtilities_PackagePrivate.h
index a41ee2ab61..274351b712 100644
--- a/objectivec/GPBUtilities_PackagePrivate.h
+++ b/objectivec/GPBUtilities_PackagePrivate.h
@@ -50,6 +50,17 @@ CF_EXTERN_C_BEGIN
 
 // These two are used to inject a runtime check for version mismatch into the
 // generated sources to make sure they are linked with a supporting runtime.
+void GPBCheckRuntimeVersionSupport(int32_t objcRuntimeVersion);
+GPB_INLINE void GPB_DEBUG_CHECK_RUNTIME_VERSIONS() {
+  // NOTE: By being inline here, this captures the value from the library's
+  // headers at the time the generated code was compiled.
+#if defined(DEBUG) && DEBUG
+  GPBCheckRuntimeVersionSupport(GOOGLE_PROTOBUF_OBJC_VERSION);
+#endif
+}
+
+// Legacy version of the checks, remove when GOOGLE_PROTOBUF_OBJC_GEN_VERSION
+// goes away (see more info in GPBBootstrap.h).
 void GPBCheckRuntimeVersionInternal(int32_t version);
 GPB_INLINE void GPBDebugCheckRuntimeVersion() {
 #if defined(DEBUG) && DEBUG
diff --git a/objectivec/google/protobuf/Any.pbobjc.h b/objectivec/google/protobuf/Any.pbobjc.h
index b88b786a16..d236e4b20e 100644
--- a/objectivec/google/protobuf/Any.pbobjc.h
+++ b/objectivec/google/protobuf/Any.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Any.pbobjc.m b/objectivec/google/protobuf/Any.pbobjc.m
index e105492602..d210643f05 100644
--- a/objectivec/google/protobuf/Any.pbobjc.m
+++ b/objectivec/google/protobuf/Any.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBAnyRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Api.pbobjc.h b/objectivec/google/protobuf/Api.pbobjc.h
index 3750e093cb..742a812211 100644
--- a/objectivec/google/protobuf/Api.pbobjc.h
+++ b/objectivec/google/protobuf/Api.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Api.pbobjc.m b/objectivec/google/protobuf/Api.pbobjc.m
index 70437d496a..58b47157de 100644
--- a/objectivec/google/protobuf/Api.pbobjc.m
+++ b/objectivec/google/protobuf/Api.pbobjc.m
@@ -43,7 +43,7 @@ static GPBFileDescriptor *GPBApiRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Duration.pbobjc.h b/objectivec/google/protobuf/Duration.pbobjc.h
index 090eb0024e..67380704a4 100644
--- a/objectivec/google/protobuf/Duration.pbobjc.h
+++ b/objectivec/google/protobuf/Duration.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Duration.pbobjc.m b/objectivec/google/protobuf/Duration.pbobjc.m
index f87d35dba3..bafb64a04f 100644
--- a/objectivec/google/protobuf/Duration.pbobjc.m
+++ b/objectivec/google/protobuf/Duration.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBDurationRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Empty.pbobjc.h b/objectivec/google/protobuf/Empty.pbobjc.h
index e0ed3e16cf..bd49cfdbc3 100644
--- a/objectivec/google/protobuf/Empty.pbobjc.h
+++ b/objectivec/google/protobuf/Empty.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Empty.pbobjc.m b/objectivec/google/protobuf/Empty.pbobjc.m
index 9b60f36d61..506b500e04 100644
--- a/objectivec/google/protobuf/Empty.pbobjc.m
+++ b/objectivec/google/protobuf/Empty.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBEmptyRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/FieldMask.pbobjc.h b/objectivec/google/protobuf/FieldMask.pbobjc.h
index 14ed537958..6434ac1862 100644
--- a/objectivec/google/protobuf/FieldMask.pbobjc.h
+++ b/objectivec/google/protobuf/FieldMask.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/FieldMask.pbobjc.m b/objectivec/google/protobuf/FieldMask.pbobjc.m
index 4dc8409cf3..b0915af450 100644
--- a/objectivec/google/protobuf/FieldMask.pbobjc.m
+++ b/objectivec/google/protobuf/FieldMask.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBFieldMaskRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/SourceContext.pbobjc.h b/objectivec/google/protobuf/SourceContext.pbobjc.h
index 417562c0eb..799d190ac6 100644
--- a/objectivec/google/protobuf/SourceContext.pbobjc.h
+++ b/objectivec/google/protobuf/SourceContext.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/SourceContext.pbobjc.m b/objectivec/google/protobuf/SourceContext.pbobjc.m
index 648d736c54..83bfa3460b 100644
--- a/objectivec/google/protobuf/SourceContext.pbobjc.m
+++ b/objectivec/google/protobuf/SourceContext.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBSourceContextRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Struct.pbobjc.h b/objectivec/google/protobuf/Struct.pbobjc.h
index 163b39ba7a..3fc80caa5a 100644
--- a/objectivec/google/protobuf/Struct.pbobjc.h
+++ b/objectivec/google/protobuf/Struct.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Struct.pbobjc.m b/objectivec/google/protobuf/Struct.pbobjc.m
index bc9f23ff61..f36ec582f9 100644
--- a/objectivec/google/protobuf/Struct.pbobjc.m
+++ b/objectivec/google/protobuf/Struct.pbobjc.m
@@ -40,7 +40,7 @@ static GPBFileDescriptor *GPBStructRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Timestamp.pbobjc.h b/objectivec/google/protobuf/Timestamp.pbobjc.h
index 094e9b6b6e..898f88e313 100644
--- a/objectivec/google/protobuf/Timestamp.pbobjc.h
+++ b/objectivec/google/protobuf/Timestamp.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Timestamp.pbobjc.m b/objectivec/google/protobuf/Timestamp.pbobjc.m
index 1506dff3aa..4ab159fb9c 100644
--- a/objectivec/google/protobuf/Timestamp.pbobjc.m
+++ b/objectivec/google/protobuf/Timestamp.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBTimestampRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Type.pbobjc.h b/objectivec/google/protobuf/Type.pbobjc.h
index da923c3031..06e7548051 100644
--- a/objectivec/google/protobuf/Type.pbobjc.h
+++ b/objectivec/google/protobuf/Type.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Type.pbobjc.m b/objectivec/google/protobuf/Type.pbobjc.m
index a36f1cd47d..7a949388f8 100644
--- a/objectivec/google/protobuf/Type.pbobjc.m
+++ b/objectivec/google/protobuf/Type.pbobjc.m
@@ -43,7 +43,7 @@ static GPBFileDescriptor *GPBTypeRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/objectivec/google/protobuf/Wrappers.pbobjc.h b/objectivec/google/protobuf/Wrappers.pbobjc.h
index 2bf6fd2936..3cb9fe77dc 100644
--- a/objectivec/google/protobuf/Wrappers.pbobjc.h
+++ b/objectivec/google/protobuf/Wrappers.pbobjc.h
@@ -13,8 +13,11 @@
  #import "GPBProtocolBuffers.h"
 #endif
 
-#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
-#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
 #endif
 
 // @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Wrappers.pbobjc.m b/objectivec/google/protobuf/Wrappers.pbobjc.m
index c8fdeb0884..5479eb127b 100644
--- a/objectivec/google/protobuf/Wrappers.pbobjc.m
+++ b/objectivec/google/protobuf/Wrappers.pbobjc.m
@@ -39,7 +39,7 @@ static GPBFileDescriptor *GPBWrappersRoot_FileDescriptor(void) {
   // about thread safety of the singleton.
   static GPBFileDescriptor *descriptor = NULL;
   if (!descriptor) {
-    GPBDebugCheckRuntimeVersion();
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
                                                  objcPrefix:@"GPB"
                                                      syntax:GPBFileSyntaxProto3];
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
index 878a374313..7ad127bb3b 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_file.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
@@ -51,10 +51,8 @@ namespace objectivec {
 
 namespace {
 
-// This is also found in GPBBootstrap.h, and needs to be kept in sync.  It
-// is the version check done to ensure generated code works with the current
-// runtime being used.
-const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30002;
+// This is also found in GPBBootstrap.h, and needs to be kept in sync.
+const int32 GOOGLE_PROTOBUF_OBJC_VERSION = 30002;
 
 const char* kHeaderExtension = ".pbobjc.h";
 
@@ -192,13 +190,19 @@ void FileGenerator::GenerateHeader(io::Printer *printer) {
 
   // Add some verification that the generated code matches the source the
   // code is being compiled with.
+  // NOTE: This captures the raw numeric values at the time the generator was
+  // compiled, since that will be the versions for the ObjC runtime at that
+  // time.  The constants in the generated code will then get their values at
+  // at compile time (so checking against the headers being used to compile).
   printer->Print(
-      "#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n"
-      "#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.\n"
+      "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n"
+      "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n"
+      "#endif\n"
+      "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n"
+      "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n"
       "#endif\n"
       "\n",
-      "protoc_gen_objc_version",
-      SimpleItoa(GOOGLE_PROTOBUF_OBJC_GEN_VERSION));
+      "google_protobuf_objc_version", SimpleItoa(GOOGLE_PROTOBUF_OBJC_VERSION));
 
   // #import any headers for "public imports" in the proto file.
   {
@@ -392,7 +396,7 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
         "  // about thread safety and initialization of registry.\n"
         "  static GPBExtensionRegistry* registry = nil;\n"
         "  if (!registry) {\n"
-        "    GPBDebugCheckRuntimeVersion();\n"
+        "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"
         "    registry = [[GPBExtensionRegistry alloc] init];\n");
 
     printer->Indent();
@@ -487,7 +491,7 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
         "  // about thread safety of the singleton.\n"
         "  static GPBFileDescriptor *descriptor = NULL;\n"
         "  if (!descriptor) {\n"
-        "    GPBDebugCheckRuntimeVersion();\n");
+        "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n");
     if (vars["objc_prefix"].size() > 0) {
       printer->Print(
           vars,