From e1c1e2173248f39c1b15fca7b2a31ad7b5199ce7 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Wed, 6 Feb 2013 14:52:29 -0500
Subject: [PATCH] Enable building with Microsoft Visual Studio 2012.

Backpatch to release 9.2

Brar Piening and Noah Misch, reviewed by Craig Ringer.
---
 doc/src/sgml/install-windows.sgml | 22 ++++++----
 src/backend/utils/adt/pg_locale.c | 68 ++++++++++++++++++++++++++++---
 src/bin/initdb/initdb.c           | 12 +++++-
 src/port/chklocale.c              | 43 ++++++++++++++-----
 src/port/win32env.c               |  6 +++
 src/tools/msvc/MSBuildProject.pm  | 44 +++++++++++++++++++-
 src/tools/msvc/README             |  9 ++--
 src/tools/msvc/Solution.pm        | 31 ++++++++++++--
 src/tools/msvc/VSObjectFactory.pm | 14 +++++--
 src/tools/msvc/build.pl           |  2 +-
 src/tools/msvc/gendef.pl          |  1 +
 11 files changed, 213 insertions(+), 39 deletions(-)

diff --git a/doc/src/sgml/install-windows.sgml b/doc/src/sgml/install-windows.sgml
index 452cf3195a..a330381954 100644
--- a/doc/src/sgml/install-windows.sgml
+++ b/doc/src/sgml/install-windows.sgml
@@ -19,8 +19,8 @@
  <para>
   There are several different ways of building PostgreSQL on
   <productname>Windows</productname>. The simplest way to build with
-  Microsoft tools is to install a supported version of the
-  <productname>Microsoft Windows SDK</productname> and use the included
+  Microsoft tools is to install <productname>Visual Studio Express 2012
+  for Windows Desktop</productname> and use the included
   compiler. It is also possible to build with the full
   <productname>Microsoft Visual C++ 2005, 2008 or 2010</productname>. In some cases
   that requires the installation of the <productname>Windows SDK</productname>
@@ -77,17 +77,18 @@
   <productname>Visual Studio Express</productname> or some versions of the
   <productname>Microsoft Windows SDK</productname>. If you do not already have a
   <productname>Visual Studio</productname> environment set up, the easiest
-  way is to use the compilers in the <productname>Windows SDK</productname>,
-  which is a free download from Microsoft.
+  ways are to use the compilers in the <productname>Windows SDK 7.1</productname>
+  or those from <productname>Visual Studio Express 2012 for Windows
+  Desktop</productname>, which are both free downloads from Microsoft.
  </para>
 
  <para>
   PostgreSQL is known to support compilation using the compilers shipped with
   <productname>Visual Studio 2005</productname> to
-  <productname>Visual Studio 2010</productname> (including Express editions),
+  <productname>Visual Studio 2012</productname> (including Express editions),
   as well as standalone Windows SDK releases 6.0 to 7.1.
   64-bit PostgreSQL builds are only supported with
-  <productname>Microsoft Windows SDK</productname> version 6.0a and above or
+  <productname>Microsoft Windows SDK</productname> version 6.0a to 7.1 or
   <productname>Visual Studio 2008</productname> and above.
  </para>
 
@@ -149,17 +150,20 @@ $ENV{PATH}=$ENV{PATH} . ';c:\some\where\bison\bin';
     <varlistentry>
      <term><productname>Microsoft Windows SDK</productname></term>
      <listitem><para>
-      It is recommended that you upgrade to the latest supported version
-      of the <productname>Microsoft Windows SDK</productname> (currently
+      If your build environment doesn't ship with a supported version of the
+      <productname>Microsoft Windows SDK</productname> it
+      is recommended that you upgrade to the latest version (currently
       version 7.1), available for download from
       <ulink url="http://www.microsoft.com/downloads/"></>.
      </para>
      <para>
       You must always include the
       <application>Windows Headers and Libraries</application> part of the SDK.
-      If you install the <productname>Windows SDK</productname>
+      If you install a <productname>Windows SDK</productname>
       including the <application>Visual C++ Compilers</application>,
       you don't need <productname>Visual Studio</productname> to build.
+      Note that as of Version 8.0a the Windows SDK no longer ships with a
+      complete command-line build environment.
      </para></listitem>
     </varlistentry>
 
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 8fe824b426..890aa19816 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -715,12 +715,41 @@ cache_locale_time(void)
 
 #if defined(WIN32) && defined(LC_MESSAGES)
 /*
- *	Convert Windows locale name to the ISO formatted one
- *	if possible.
+ * Convert a Windows setlocale() argument to a Unix-style one.
  *
- *	This function returns NULL if conversion is impossible,
- *	otherwise returns the pointer to a static area which
- *	contains the iso formatted locale name.
+ * Regardless of platform, we install message catalogs under a Unix-style
+ * LL[_CC][.ENCODING][@VARIANT] naming convention.  Only LC_MESSAGES settings
+ * following that style will elicit localized interface strings.
+ *
+ * Before Visual Studio 2012 (msvcr110.dll), Windows setlocale() accepted "C"
+ * (but not "c") and strings of the form <Language>[_<Country>][.<CodePage>],
+ * case-insensitive.  setlocale() returns the fully-qualified form; for
+ * example, setlocale("thaI") returns "Thai_Thailand.874".  Internally,
+ * setlocale() and _create_locale() select a "locale identifier"[1] and store
+ * it in an undocumented _locale_t field.  From that LCID, we can retrieve the
+ * ISO 639 language and the ISO 3166 country.  Character encoding does not
+ * matter, because the server and client encodings govern that.
+ *
+ * Windows Vista introduced the "locale name" concept[2], closely following
+ * RFC 4646.  Locale identifiers are now deprecated.  Starting with Visual
+ * Studio 2012, setlocale() accepts locale names in addition to the strings it
+ * accepted historically.  It does not standardize them; setlocale("Th-tH")
+ * returns "Th-tH".  setlocale(category, "") still returns a traditional
+ * string.  Furthermore, msvcr110.dll changed the undocumented _locale_t
+ * content to carry locale names instead of locale identifiers.
+ *
+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
+ * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
+ * Unix-style values of the lc_messages GUC can elicit localized messages.  In
+ * particular, every lc_messages setting that initdb can select automatically
+ * will yield only C-locale messages.  XXX This could be fixed by running the
+ * fully-qualified locale name through a lookup table.
+ *
+ * This function returns a pointer to a static buffer bearing the converted
+ * name or NULL if conversion fails.
+ *
+ * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
+ * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
  */
 static char *
 IsoLocaleName(const char *winlocname)
@@ -739,6 +768,34 @@ IsoLocaleName(const char *winlocname)
 	loct = _create_locale(LC_CTYPE, winlocname);
 	if (loct != NULL)
 	{
+#if (_MSC_VER >= 1700)			/* Visual Studio 2012 or later */
+		size_t		rc;
+		char	   *hyphen;
+
+		/* Locale names use only ASCII, any conversion locale suffices. */
+		rc = wchar2char(iso_lc_messages, loct->locinfo->locale_name[LC_CTYPE],
+						sizeof(iso_lc_messages), NULL);
+		_free_locale(loct);
+		if (rc == -1 || rc == sizeof(iso_lc_messages))
+			return NULL;
+
+		/*
+		 * Since the message catalogs sit on a case-insensitive filesystem, we
+		 * need not standardize letter case here.  So long as we do not ship
+		 * message catalogs for which it would matter, we also need not
+		 * translate the script/variant portion, e.g. uz-Cyrl-UZ to
+		 * uz_UZ@cyrillic.  Simply replace the hyphen with an underscore.
+		 *
+		 * Note that the locale name can be less-specific than the value we
+		 * would derive under earlier Visual Studio releases.  For example,
+		 * French_France.1252 yields just "fr".  This does not affect any of
+		 * the country-specific message catalogs available as of this writing
+		 * (pt_BR, zh_CN, zh_TW).
+		 */
+		hyphen = strchr(iso_lc_messages, '-');
+		if (hyphen)
+			*hyphen = '_';
+#else
 		char		isolang[32],
 					isocrty[32];
 		LCID		lcid;
@@ -753,6 +810,7 @@ IsoLocaleName(const char *winlocname)
 		if (!GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty, sizeof(isocrty)))
 			return NULL;
 		snprintf(iso_lc_messages, sizeof(iso_lc_messages) - 1, "%s_%s", isolang, isocrty);
+#endif
 		return iso_lc_messages;
 	}
 	return NULL;
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1bba4268a5..b75d976856 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -935,14 +935,22 @@ find_matching_ts_config(const char *lc_type)
 
 	/*
 	 * Convert lc_ctype to a language name by stripping everything after an
-	 * underscore.	Just for paranoia, we also stop at '.' or '@'.
+	 * underscore (usual case) or a hyphen (Windows "locale name"; see
+	 * comments at IsoLocaleName()).
+	 *
+	 * XXX Should ' ' be a stop character?  This would select "norwegian" for
+	 * the Windows locale "Norwegian (Nynorsk)_Norway.1252".  If we do so, we
+	 * should also accept the "nn" and "nb" Unix locales.
+	 *
+	 * Just for paranoia, we also stop at '.' or '@'.
 	 */
 	if (lc_type == NULL)
 		langname = pg_strdup("");
 	else
 	{
 		ptr = langname = pg_strdup(lc_type);
-		while (*ptr && *ptr != '_' && *ptr != '.' && *ptr != '@')
+		while (*ptr &&
+			   *ptr != '_' && *ptr != '-' && *ptr != '.' && *ptr != '@')
 			ptr++;
 		*ptr = '\0';
 	}
diff --git a/src/port/chklocale.c b/src/port/chklocale.c
index 7e2062c6bb..9e889383f2 100644
--- a/src/port/chklocale.c
+++ b/src/port/chklocale.c
@@ -189,26 +189,49 @@ static const struct encoding_match encoding_match_list[] = {
 
 #ifdef WIN32
 /*
- * On Windows, use CP<codepage number> instead of the nl_langinfo() result
+ * On Windows, use CP<code page number> instead of the nl_langinfo() result
+ *
+ * Visual Studio 2012 expanded the set of valid LC_CTYPE values, so have its
+ * locale machinery determine the code page.  See comments at IsoLocaleName().
+ * For other compilers, follow the locale's predictable format.
+ *
+ * Returns a malloc()'d string for the caller to free.
  */
 static char *
 win32_langinfo(const char *ctype)
 {
-	char	   *r;
+	char	   *r = NULL;
+
+#if (_MSC_VER >= 1700)
+	_locale_t	loct = NULL;
+
+	loct = _create_locale(LC_CTYPE, ctype);
+	if (loct != NULL)
+	{
+		r = malloc(16);			/* excess */
+		if (r != NULL)
+			sprintf(r, "CP%u", loct->locinfo->lc_codepage);
+		_free_locale(loct);
+	}
+#else
 	char	   *codepage;
-	int			ln;
 
 	/*
 	 * Locale format on Win32 is <Language>_<Country>.<CodePage> . For
-	 * example, English_USA.1252.
+	 * example, English_United States.1252.
 	 */
 	codepage = strrchr(ctype, '.');
-	if (!codepage)
-		return NULL;
-	codepage++;
-	ln = strlen(codepage);
-	r = malloc(ln + 3);
-	sprintf(r, "CP%s", codepage);
+	if (codepage != NULL)
+	{
+		int			ln;
+
+		codepage++;
+		ln = strlen(codepage);
+		r = malloc(ln + 3);
+		if (r != NULL)
+			sprintf(r, "CP%s", codepage);
+	}
+#endif
 
 	return r;
 }
diff --git a/src/port/win32env.c b/src/port/win32env.c
index 5f0f6f9859..b5f4e8e6d6 100644
--- a/src/port/win32env.c
+++ b/src/port/win32env.c
@@ -60,6 +60,12 @@ pgwin32_putenv(const char *envval)
 		{
 			"msvcr90", 0, NULL
 		},						/* Visual Studio 2008 */
+		{
+			"msvcr100", 0, NULL
+		},						/* Visual Studio 2010 */
+		{
+			"msvcr110", 0, NULL
+		},						/* Visual Studio 2012 */
 		{
 			NULL, 0, NULL
 		}
diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm
index 2e3eab6599..0cafd717a2 100644
--- a/src/tools/msvc/MSBuildProject.pm
+++ b/src/tools/msvc/MSBuildProject.pm
@@ -1,7 +1,7 @@
 package MSBuildProject;
 
 #
-# Package that encapsulates a MSBuild (Visual C++ 2010) project file
+# Package that encapsulates a MSBuild project file (Visual C++ 2010 or greater)
 #
 # src/tools/msvc/MSBuildProject.pm
 #
@@ -397,4 +397,46 @@ sub new
 	return $self;
 }
 
+package VC2012Project;
+
+#
+# Package that encapsulates a Visual C++ 2012 project file
+#
+
+use strict;
+use warnings;
+use base qw(MSBuildProject);
+
+sub new
+{
+    my $classname = shift;
+    my $self = $classname->SUPER::_new(@_);
+    bless($self, $classname);
+
+    $self->{vcver} = '11.00';
+
+    return $self;
+}
+
+# This override adds the <PlatformToolset> element
+# to the PropertyGroup labeled "Configuration"
+sub WriteConfigurationPropertyGroup
+{
+    my ($self, $f, $cfgname, $p) = @_;
+    my $cfgtype =
+      ($self->{type} eq "exe")
+      ?'Application'
+      :($self->{type} eq "dll"?'DynamicLibrary':'StaticLibrary');
+
+    print $f <<EOF;
+  <PropertyGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="Configuration">
+    <ConfigurationType>$cfgtype</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <CharacterSet>MultiByte</CharacterSet>
+    <WholeProgramOptimization>$p->{wholeopt}</WholeProgramOptimization>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+EOF
+}
+
 1;
diff --git a/src/tools/msvc/README b/src/tools/msvc/README
index 3b2939ad68..b61ddb8791 100644
--- a/src/tools/msvc/README
+++ b/src/tools/msvc/README
@@ -92,10 +92,11 @@ These configuration arguments are passed over to Mkvcbuild::mkvcbuild
 (Mkvcbuild.pm) which creates the Visual Studio project and solution files.
 It does this by using VSObjectFactory::CreateSolution to create an object
 implementing the Solution interface (this could be either a VS2005Solution,
-a VS2008Solution or a VS2010Solution, all in Solution.pm, depending on the
-user's build environment) and adding objects implementing the corresponding
-Project interface (VC2005Project or VC2008Project from VCBuildProject.pm or
-VC2010Project from MSBuildProject.pm) to it.
+a VS2008Solution, a VS2010Solution or a VS2012Solution, all in Solution.pm,
+depending on the user's build environment) and adding objects implementing
+the corresponding Project interface (VC2005Project or VC2008Project from
+VCBuildProject.pm or VC2010Project or VC2012Project from MSBuildProject.pm)
+to it.
 When Solution::Save is called, the implementations of Solution and Project
 save their content in the appropriate format.
 The final step of starting the appropriate build program (msbuild or vcbuild)
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 850a1dfabc..e271ac8d9b 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -63,13 +63,12 @@ sub DeterminePlatform
 {
 	my $self = shift;
 
-	# Determine if we are in 32 or 64-bit mode. Do this by seeing if CL has
-	# 64-bit only parameters.
+	# Examine CL help output to determine if we are in 32 or 64-bit mode.
 	$self->{platform} = 'Win32';
-	open(P, "cl /? 2>NUL|") || die "cl command not found";
+	open(P, "cl /? 2>&1 |") || die "cl command not found";
 	while (<P>)
 	{
-		if (/^\/favor:</)
+		if (/^\/favor:<.+AMD64/)
 		{
 			$self->{platform} = 'x64';
 			last;
@@ -700,4 +699,28 @@ sub new
 	return $self;
 }
 
+package VS2012Solution;
+
+#
+# Package that encapsulates a Visual Studio 2012 solution file
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Solution);
+
+sub new
+{
+    my $classname = shift;
+    my $self = $classname->SUPER::_new(@_);
+    bless($self, $classname);
+
+    $self->{solutionFileVersion} = '12.00';
+    $self->{vcver} = '11.00';
+    $self->{visualStudioName} = 'Visual Studio 2012';
+
+    return $self;
+}
+
 1;
diff --git a/src/tools/msvc/VSObjectFactory.pm b/src/tools/msvc/VSObjectFactory.pm
index c3aa33ec24..0fbf3faa0e 100644
--- a/src/tools/msvc/VSObjectFactory.pm
+++ b/src/tools/msvc/VSObjectFactory.pm
@@ -41,6 +41,10 @@ sub CreateSolution
 	{
 		return new VS2010Solution(@_);
 	}
+	elsif ($visualStudioVersion eq '11.00')
+	{
+		return new VS2012Solution(@_);
+	}
 	else
 	{
 		croak "The requested Visual Studio version is not supported.";
@@ -68,6 +72,10 @@ sub CreateProject
 	{
 		return new VC2010Project(@_);
 	}
+	elsif ($visualStudioVersion eq '11.00')
+	{
+		return new VC2012Project(@_);
+	}
 	else
 	{
 		croak "The requested Visual Studio version is not supported.";
@@ -82,7 +90,7 @@ sub DetermineVisualStudioVersion
 	{
 
 # Determine version of nmake command, to set proper version of visual studio
-# we use nmake as it has existed for a long time and still exists in visual studio 2010
+# we use nmake as it has existed for a long time and still exists in current visual studio versions
 		open(P, "nmake /? 2>&1 |")
 		  || croak
 "Unable to determine Visual Studio version: The nmake command wasn't found.";
@@ -107,11 +115,11 @@ sub DetermineVisualStudioVersion
 sub _GetVisualStudioVersion
 {
 	my ($major, $minor) = @_;
-	if ($major > 10)
+	if ($major > 11)
 	{
 		carp
 "The determined version of Visual Studio is newer than the latest supported version. Returning the latest supported version instead.";
-		return '10.00';
+		return '11.00';
 	}
 	elsif ($major < 6)
 	{
diff --git a/src/tools/msvc/build.pl b/src/tools/msvc/build.pl
index 8979402d4c..c947bbe318 100644
--- a/src/tools/msvc/build.pl
+++ b/src/tools/msvc/build.pl
@@ -50,7 +50,7 @@ elsif ($ARGV[0] ne "RELEASE")
 
 # ... and do it
 
-if ($buildwhat and $vcver eq '10.00')
+if ($buildwhat and $vcver >= 10.00)
 {
 	system(
 "msbuild $buildwhat.vcxproj /verbosity:detailed /p:Configuration=$bconf");
diff --git a/src/tools/msvc/gendef.pl b/src/tools/msvc/gendef.pl
index ab65c46cfa..8ef0422df9 100644
--- a/src/tools/msvc/gendef.pl
+++ b/src/tools/msvc/gendef.pl
@@ -40,6 +40,7 @@ while (<$ARGV[0]/*.obj>)
 		next if $pieces[6] =~ /^\(/;
 		next if $pieces[6] =~ /^__real/;
 		next if $pieces[6] =~ /^__imp/;
+		next if $pieces[6] =~ /^__xmm/;
 		next if $pieces[6] =~ /NULL_THUNK_DATA$/;
 		next if $pieces[6] =~ /^__IMPORT_DESCRIPTOR/;
 		next if $pieces[6] =~ /^__NULL_IMPORT/;
-- 
2.40.0