commit 9b0d3d17b04a59bb591c06273d2d5a975f8c5d58
Author: Lawrence Velázquez <vq@larryv.me>
Date:   Fri Nov 14 17:52:32 2014 -0500

    Handle OS X deployment targets correctly

    As described in PR target/63810, this addresses several problems with
    the validation and encoding of deployment target version strings for the
    __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro.  There are
    currently four testcases exercising inputs to -mmacosx-version-min
    (gcc/testsuite/gcc.dg/darwin-minversion-*), but they provide minimal
    coverage, and one is incorrect.

    * The tiny number is now respected, if present.  Thus "10.9.4"
      correctly becomes "1094" instead of "1090", and "10.10.1" becomes
      "101001" instead of "101000".
    * Zero padding is now ignored.  Thus "10.09" correctly becomes "1090"
      instead of "100900", and "10.00010" becomes "101000" instead of being
      treated as invalid.
    * Deployment targets that are missing minor and tiny numbers are no
      longer considered invalid.  Thus "10" is treated as "10.0.0", which
      becomes "1000" without causing an error.

    With this change, trunk matches the behavior of Apple LLVM Compiler
    6.1.0 on 8,451 of 8,464 generated test inputs.  (The discrepancies are
    due to a bug in Clang.)  I don't notice any testsuite regressions on
    OS X 10.10 Yosemite x86-64.

    2015-05-15  Lawrence Velázquez  <vq@larryv.me>

    	PR target/63810
    	* gcc/config/darwin-c.c (version_components): New global enum.
    	(parse_version, version_as_legacy_macro)
    	(version_as_modern_macro, macosx_version_as_macro): New functions.
    	(version_as_macro): Remove.
    	(darwin_cpp_builtins): Use new function.
    	* gcc/testsuite/gcc.dg/darwin-minversion-3.c: Update testcase.
    	* gcc/testsuite/gcc.dg/darwin-minversion-4.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-5.c: New testcase.
    	* gcc/testsuite/gcc.dg/darwin-minversion-6.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-7.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-8.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-9.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-10.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-11.c: Ditto.
    	* gcc/testsuite/gcc.dg/darwin-minversion-12.c: Ditto.
---
Adapted for GCC 4.7.4 from the upstream trunk fix. The text above is
from my email to the gcc-patches mailing list; some minor details may
not apply to the patch below.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63810
https://gcc.gnu.org/ml/gcc-patches/2015-05/msg01888.html
https://gcc.gnu.org/viewcvs/gcc?limit_changes=0&view=rev&rev=223808

 gcc/config/darwin-c.c                       | 155 +++++++++++++++++++++++---
 gcc/testsuite/gcc.dg/darwin-minversion-10.c |  16 +++
 gcc/testsuite/gcc.dg/darwin-minversion-11.c |  16 +++
 gcc/testsuite/gcc.dg/darwin-minversion-12.c |  16 +++
 gcc/testsuite/gcc.dg/darwin-minversion-3.c  |   6 +-
 gcc/testsuite/gcc.dg/darwin-minversion-4.c  |  12 ++
 gcc/testsuite/gcc.dg/darwin-minversion-5.c  |  16 +++
 gcc/testsuite/gcc.dg/darwin-minversion-6.c  |  15 +++
 gcc/testsuite/gcc.dg/darwin-minversion-7.c  |  15 +++
 gcc/testsuite/gcc.dg/darwin-minversion-8.c  |  16 +++
 gcc/testsuite/gcc.dg/darwin-minversion-9.c  |  15 +++
 11 files changed, 282 insertions(+), 16 deletions(-)

diff --git gcc/config/darwin-c.c gcc/config/darwin-c.c
index 211d141..3a7e3ee 100644
--- gcc/config/darwin-c.c
+++ gcc/config/darwin-c.c
@@ -570,25 +570,154 @@ find_subframework_header (cpp_reader *pfile, const char *header, cpp_dir **dirp)
   return 0;
 }
 
-/* Return the value of darwin_macosx_version_min suitable for the
-   __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro,
-   so '10.4.2' becomes 1040.  The lowest digit is always zero.
-   Print a warning if the version number can't be understood.  */
+/* Given an OS X version VERSION_STR, return it as a statically-allocated array
+   of three integers. If VERSION_STR is invalid, return NULL.
+
+   VERSION_STR must consist of one, two, or three tokens, each separated by
+   a single period.  Each token must contain only the characters '0' through
+   '9' and is converted to an equivalent non-negative decimal integer. Omitted
+   tokens become zeros.  For example:
+
+        "10"              becomes       {10,0,0}
+        "10.10"           becomes       {10,10,0}
+        "10.10.1"         becomes       {10,10,1}
+        "10.000010.1"     becomes       {10,10,1}
+        "10.010.001"      becomes       {10,10,1}
+        "000010.10.00001" becomes       {10,10,1}
+        ".9.1"            is invalid
+        "10..9"           is invalid
+        "10.10."          is invalid  */
+
+enum version_components { MAJOR, MINOR, TINY };
+
+static const unsigned long *
+parse_version (const char *version_str)
+{
+  size_t version_len;
+  char *end;
+  static unsigned long version_array[3];
+
+  version_len = strlen (version_str);
+  if (version_len < 1)
+    return NULL;
+
+  /* Version string must consist of digits and periods only.  */
+  if (strspn (version_str, "0123456789.") != version_len)
+    return NULL;
+
+  if (!ISDIGIT (version_str[0]) || !ISDIGIT (version_str[version_len - 1]))
+    return NULL;
+
+  version_array[MAJOR] = strtoul (version_str, &end, 10);
+  version_str = end + ((*end == '.') ? 1 : 0);
+
+  /* Version string must not contain adjacent periods.  */
+  if (*version_str == '.')
+    return NULL;
+
+  version_array[MINOR] = strtoul (version_str, &end, 10);
+  version_str = end + ((*end == '.') ? 1 : 0);
+
+  version_array[TINY] = strtoul (version_str, &end, 10);
+
+  /* Version string must contain no more than three tokens.  */
+  if (*end != '\0')
+    return NULL;
+
+  return version_array;
+}
+
+/* Given VERSION -- a three-component OS X version represented as an array of
+   non-negative integers -- return a statically-allocated string suitable for
+   the legacy __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro.  If VERSION
+   is invalid and cannot be coerced into a valid form, return NULL.
+
+   The legacy format is a four-character string -- two chars for the major
+   number and one each for the minor and tiny numbers.  Minor and tiny numbers
+   from 10 through 99 are permitted but are clamped to 9 (for example, {10,9,10}
+   produces "1099").  If VERSION contains numbers greater than 99, it is
+   rejected.  */
+
 static const char *
-version_as_macro (void)
+version_as_legacy_macro (const unsigned long *version)
 {
-  static char result[] = "1000";
+  unsigned long major, minor, tiny;
+  static char result[5];
+
+  major = version[MAJOR];
+  minor = version[MINOR];
+  tiny = version[TINY];
+
+  if (major > 99 || minor > 99 || tiny > 99)
+    return NULL;
+
+  minor = ((minor > 9) ? 9 : minor);
+  tiny = ((tiny > 9) ? 9 : tiny);
+
+  if (sprintf (result, "%lu%lu%lu", major, minor, tiny) != 4)
+    return NULL;
+
+  return result;
+}
 
-  if (strncmp (darwin_macosx_version_min, "10.", 3) != 0)
+/* Given VERSION -- a three-component OS X version represented as an array of
+   non-negative integers -- return a statically-allocated string suitable for
+   the modern __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro.  If VERSION
+   is invalid, return NULL.
+
+   The modern format is a six-character string -- two chars for each component,
+   with zero-padding if necessary (for example, {10,10,1} produces "101001"). If
+   VERSION contains numbers greater than 99, it is rejected.  */
+
+static const char *
+version_as_modern_macro (const unsigned long *version)
+{
+  unsigned long major, minor, tiny;
+  static char result[7];
+
+  major = version[MAJOR];
+  minor = version[MINOR];
+  tiny = version[TINY];
+
+  if (major > 99 || minor > 99 || tiny > 99)
+    return NULL;
+
+  if (sprintf (result, "%02lu%02lu%02lu", major, minor, tiny) != 6)
+    return NULL;
+
+  return result;
+}
+
+/* Return the value of darwin_macosx_version_min, suitably formatted for the
+   __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro.  Values representing
+   OS X 10.9 and earlier are encoded using the legacy four-character format,
+   while 10.10 and later use a modern six-character format.  (For example,
+   "10.9" produces "1090", and "10.10.1" produces "101001".)  If
+   darwin_macosx_version_min is invalid and cannot be coerced into a valid
+   form, print a warning and return "1000".  */
+
+static const char *
+macosx_version_as_macro (void)
+{
+  const unsigned long *version_array;
+  const char *version_macro;
+
+  version_array = parse_version (darwin_macosx_version_min);
+  if (!version_array)
     goto fail;
-  if (! ISDIGIT (darwin_macosx_version_min[3]))
+
+  if (version_array[MAJOR] != 10)
     goto fail;
-  result[2] = darwin_macosx_version_min[3];
-  if (darwin_macosx_version_min[4] != '\0'
-      && darwin_macosx_version_min[4] != '.')
+
+  if (version_array[MINOR] < 10)
+    version_macro = version_as_legacy_macro (version_array);
+  else
+    version_macro = version_as_modern_macro (version_array);
+
+  if (!version_macro)
     goto fail;
 
-  return result;
+  return version_macro;
 
  fail:
   error ("unknown value %qs of -mmacosx-version-min",
@@ -614,7 +743,7 @@ darwin_cpp_builtins (cpp_reader *pfile)
     builtin_define ("__CONSTANT_CFSTRINGS__");
 
   builtin_define_with_value ("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__",
-			     version_as_macro(), false);
+			     macosx_version_as_macro(), false);
 
   /* Since we do not (at 4.6) support ObjC gc for the NeXT runtime, the
      following will cause a syntax error if one tries to compile gc attributed
diff --git gcc/testsuite/gcc.dg/darwin-minversion-10.c gcc/testsuite/gcc.dg/darwin-minversion-10.c
new file mode 100644
index 0000000..bf95d46
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-10.c
@@ -0,0 +1,16 @@
+/* PR target/63810: Test that an OS X minimum version with zero-padded
+   minor and tiny numbers less than 10 produces the correct
+   four-character macro.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=10.07.02" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1072
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-11.c gcc/testsuite/gcc.dg/darwin-minversion-11.c
new file mode 100644
index 0000000..a03e707
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-11.c
@@ -0,0 +1,16 @@
+/* PR target/63810: Test that an OS X minimum version with outrageous
+   zero-padding and a minor number greater than 9 still produces
+   a six-character macro.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=00010.010.0000098" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 101098
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-12.c gcc/testsuite/gcc.dg/darwin-minversion-12.c
new file mode 100644
index 0000000..4c2ce38
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-12.c
@@ -0,0 +1,16 @@
+/* PR target/63810: Test that an OS X minimum version with outrageous
+   zero-padding and a minor number less than 10 still produces
+   a four-character macro.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=010.008.000031" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1089
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-3.c gcc/testsuite/gcc.dg/darwin-minversion-3.c
index d0c5934..76886dd 100644
--- gcc/testsuite/gcc.dg/darwin-minversion-3.c
+++ gcc/testsuite/gcc.dg/darwin-minversion-3.c
@@ -1,10 +1,10 @@
-/* Test that most-minor versions greater than 9 work.  */
-/* { dg-options "-mmacosx-version-min=10.4.10" } */
+/* Test that most minor versions less than 10 work.  */
+/* { dg-options "-mmacosx-version-min=10.4.1" } */
 /* { dg-do compile { target *-*-darwin* } } */
 
 int main(void)
 {
-#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1040
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1041
   fail me;
 #endif
   return 0;
diff --git gcc/testsuite/gcc.dg/darwin-minversion-4.c gcc/testsuite/gcc.dg/darwin-minversion-4.c
new file mode 100644
index 0000000..1481aa5
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-4.c
@@ -0,0 +1,12 @@
+/* Test that minor versions greater than 9 produce a six-character macro.  */
+/* { dg-options "-mmacosx-version-min=10.10.1" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 101001
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-5.c gcc/testsuite/gcc.dg/darwin-minversion-5.c
new file mode 100644
index 0000000..09a9d72
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-5.c
@@ -0,0 +1,16 @@
+/* PR target/63810: Test that an OS X minimum version with minor number
+   less than 10 and tiny number greater than 9 produces a four-character
+   macro with the tiny number clamped to 9.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=10.9.10" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1099
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-6.c gcc/testsuite/gcc.dg/darwin-minversion-6.c
new file mode 100644
index 0000000..e01fa72
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-6.c
@@ -0,0 +1,15 @@
+/* PR target/63810: Test that tiny numbers are preserved in
+   six-character macros.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=10.10.11" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 101011
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-7.c gcc/testsuite/gcc.dg/darwin-minversion-7.c
new file mode 100644
index 0000000..72f495b
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-7.c
@@ -0,0 +1,15 @@
+/* PR target/63810: Test that tiny numbers less than 10 are preserved in
+   four-character macros.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=10.9.1" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1091
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-8.c gcc/testsuite/gcc.dg/darwin-minversion-8.c
new file mode 100644
index 0000000..5982df5
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-8.c
@@ -0,0 +1,16 @@
+/* PR target/63810: Test that an OS X minimum version with minor number
+   greater than 9 and no tiny number produces a six-character macro
+   ending in "00".  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=10.11" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 101100
+  fail me;
+#endif
+  return 0;
+}
diff --git gcc/testsuite/gcc.dg/darwin-minversion-9.c gcc/testsuite/gcc.dg/darwin-minversion-9.c
new file mode 100644
index 0000000..d781783
--- /dev/null
+++ gcc/testsuite/gcc.dg/darwin-minversion-9.c
@@ -0,0 +1,15 @@
+/* PR target/63810: Test that an OS X minimum version with a zero-padded
+   minor number less than 10 produces a four-character macro.  */
+/* Added by Lawrence Velázquez <vq@larryv.me>.  */
+
+/* { dg-options "-mmacosx-version-min=10.08.4" } */
+/* { dg-do compile { target *-*-darwin* } } */
+
+int
+main ()
+{
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ != 1084
+  fail me;
+#endif
+  return 0;
+}
