Repository: conan-io/conan

Issue:
Getting an x64 build with VS2019, but expecting an x86 build
I'm trying to get my project ready for VS2019, but I'm getting an x64 build where I used to get an x86 build in VS2017.  I noticed VS2019 support hasn't been announced in the changelog, so apologies if I'm reporting on an unfinished feature!

Tools I'm using:
Conan 1.13.1
CMake 3.14.0
Visual Studio Community 2019 16.0.0 RC.2
Visual Studio Community 2017 15.9.9
Windows 10 1809 64-bit

Steps to reproduce:
git clone https://gitlab.com/ssrobins/conan-sdl2.git
cd conan-sdl2
build_windows.bat

Notice that when the CMake output shows the selected compiler it shows Hostx86/x86/cl.exe, as expected.

Open build_windows.bat in a text editor and change:
compiler.version=15
To:
compiler.version=16

Re-run build_windows.bat.  CMake output shows Hostx64/x64/cl.exe as the compiler.  Make sure you have a finished sdl2 lib build so the lib is ready for the next step.

Now try to build a program using the sdl2 VS2019 lib in your local cache:
git clone https://gitlab.com/ssrobins/sdl2-example.git
cd sdl2-example

Open build_windows.bat in a text editor and change:
compiler.version=15
To:
compiler.version=16

Also, change the cmake command from this:
cmake -G "Visual Studio 15 2017" .. || goto :error
To this:
cmake -G "Visual Studio 16 2019" -A Win32 .. || goto :error

Run build_windows.bat.  I get linker errors because it's trying to link the 32-bit program with the 64-bit sdl2 lib.  I think the removal of architecture from the CMake generator name in favor of a separate -A parameter might be causing the problem.



Discussion:

Resolving patch:
diff --git a/conans/client/build/cmake.py b/conans/client/build/cmake.py
--- a/conans/client/build/cmake.py
+++ b/conans/client/build/cmake.py
@@ -7,7 +7,7 @@
 from conans.client.build.cmake_flags import CMakeDefinitionsBuilder, \
     get_generator, is_multi_configuration, verbose_definition, verbose_definition_name, \
     cmake_install_prefix_var_name, get_toolset, build_type_definition, \
-    cmake_in_local_cache_var_name, runtime_definition_var_name
+    cmake_in_local_cache_var_name, runtime_definition_var_name, get_generator_platform
 from conans.errors import ConanException
 from conans.model.conan_file import ConanFile
 from conans.model.version import Version
@@ -20,7 +20,8 @@ class CMake(object):
 
     def __init__(self, conanfile, generator=None, cmake_system_name=True,
                  parallel=True, build_type=None, toolset=None, make_program=None,
-                 set_cmake_flags=False, msbuild_verbosity=None, cmake_program=None):
+                 set_cmake_flags=False, msbuild_verbosity=None, cmake_program=None,
+                 generator_platform=None):
         """
         :param conanfile: Conanfile instance
         :param generator: Generator name to use or none to autodetect
@@ -35,6 +36,7 @@ def __init__(self, conanfile, generator=None, cmake_system_name=True,
                 (e.g. using CMAKE_SIZEOF_VOID_P or CMAKE_LIBRARY_ARCHITECTURE)
         :param msbuild_verbosity: verbosity level for MSBuild (in case of Visual Studio generator)
         :param cmake_program: Path to the custom cmake executable
+        :param generator_platform: Generator platform name or none to autodetect (-A cmake option)
         """
         if not isinstance(conanfile, ConanFile):
             raise ConanException("First argument of CMake() has to be ConanFile. Use CMake(self)")
@@ -45,6 +47,7 @@ def __init__(self, conanfile, generator=None, cmake_system_name=True,
         self._cmake_program = os.getenv("CONAN_CMAKE_PROGRAM") or cmake_program or "cmake"
 
         self.generator = generator or get_generator(conanfile.settings)
+        self.generator_platform = generator_platform or get_generator_platform(conanfile.settings)
         if not self.generator:
             self._conanfile.output.warn("CMake generator could not be deduced from settings")
         self.parallel = parallel
@@ -109,6 +112,8 @@ def is_multi_configuration(self):
     @property
     def command_line(self):
         args = ['-G "%s"' % self.generator] if self.generator else []
+        if self.generator_platform:
+            args.append('-A "%s"' % self.generator_platform)
         args.append(self.flags)
         args.append('-Wno-dev')
 
diff --git a/conans/client/build/cmake_flags.py b/conans/client/build/cmake_flags.py
--- a/conans/client/build/cmake_flags.py
+++ b/conans/client/build/cmake_flags.py
@@ -8,6 +8,7 @@
 from conans.client.tools.oss import get_cross_building_settings
 from conans.errors import ConanException
 from conans.model.build_info import DEFAULT_BIN, DEFAULT_INCLUDE, DEFAULT_LIB, DEFAULT_SHARE
+from conans.model.version import Version
 from conans.util.env_reader import get_env
 from conans.util.log import logger
 
@@ -51,12 +52,12 @@ def get_generator(settings):
                     '16': '16 2019'}
         base = "Visual Studio %s" % _visuals.get(compiler_version,
                                                  "UnknownVersion %s" % compiler_version)
-        if arch == "x86_64":
-            return base + " Win64"
-        elif "arm" in arch:
-            return base + " ARM"
-        else:
-            return base
+        if Version(compiler_version) < "16":
+            if arch == "x86_64":
+                base += " Win64"
+            elif "arm" in arch:
+                base += " ARM"
+        return base
 
     # The generator depends on the build machine, not the target
     if os_build == "Windows":
@@ -65,6 +66,22 @@ def get_generator(settings):
     return "Unix Makefiles"
 
 
+def get_generator_platform(settings):
+    if "CONAN_CMAKE_GENERATOR_PLATFORM" in os.environ:
+        return os.environ["CONAN_CMAKE_GENERATOR_PLATFORM"]
+
+    compiler = settings.get_safe("compiler")
+    arch = settings.get_safe("arch")
+    compiler_version = settings.get_safe("compiler.version")
+
+    if compiler == "Visual Studio" and Version(compiler_version) >= "16":
+        return {"x86": "Win32",
+                "x86_64": "x64",
+                "armv7": "ARM",
+                "armv8": "ARM64"}.get(arch)
+    return None
+
+
 def is_multi_configuration(generator):
     if not generator:
         return False
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -108,6 +108,7 @@
 # conan_cmake_program = cmake         # environment CONAN_CMAKE_PROGRAM (overrides the make program used in CMake.cmake_program)
 
 # cmake_generator                     # environment CONAN_CMAKE_GENERATOR
+# cmake generator platform            # environment CONAN_CMAKE_GENERATOR_PLATFORM
 # http://www.vtk.org/Wiki/CMake_Cross_Compiling
 # cmake_toolchain_file                # environment CONAN_CMAKE_TOOLCHAIN_FILE
 # cmake_system_name                   # environment CONAN_CMAKE_SYSTEM_NAME
@@ -185,6 +186,7 @@ def env_vars(self):
                "CONAN_VERBOSE_TRACEBACK": self._env_c("general.verbose_traceback", "CONAN_VERBOSE_TRACEBACK", None),
                # http://www.vtk.org/Wiki/CMake_Cross_Compiling
                "CONAN_CMAKE_GENERATOR": self._env_c("general.cmake_generator", "CONAN_CMAKE_GENERATOR", None),
+               "CONAN_CMAKE_GENERATOR_PLATFORM": self._env_c("general.cmake_generator_platform", "CONAN_CMAKE_GENERATOR_PLATFORM", None),
                "CONAN_CMAKE_TOOLCHAIN_FILE": self._env_c("general.cmake_toolchain_file", "CONAN_CMAKE_TOOLCHAIN_FILE", None),
                "CONAN_CMAKE_SYSTEM_NAME": self._env_c("general.cmake_system_name", "CONAN_CMAKE_SYSTEM_NAME", None),
                "CONAN_CMAKE_SYSTEM_VERSION": self._env_c("general.cmake_system_version", "CONAN_CMAKE_SYSTEM_VERSION", None),


Test patch:
diff --git a/conans/test/unittests/client/build/cmake_test.py b/conans/test/unittests/client/build/cmake_test.py
--- a/conans/test/unittests/client/build/cmake_test.py
+++ b/conans/test/unittests/client/build/cmake_test.py
@@ -188,6 +188,91 @@ def cmake_generator_test(self):
             cmake = CMake(conan_file)
             self.assertIn('-G "My CMake Generator"', cmake.command_line)
 
+    def cmake_generator_platform_test(self):
+        conan_file = ConanFileMock()
+        conan_file.settings = Settings()
+
+        with tools.environment_append({"CONAN_CMAKE_GENERATOR": "My CMake Generator",
+                                       "CONAN_CMAKE_GENERATOR_PLATFORM": "My CMake Platform"}):
+            cmake = CMake(conan_file)
+            self.assertIn('-G "My CMake Generator" -A "My CMake Platform"', cmake.command_line)
+
+    def cmake_generator_platform_override_test(self):
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "15"
+        settings.arch = "x86"
+
+        conan_file = ConanFileMock()
+        conan_file.settings = settings
+
+        with tools.environment_append({"CONAN_CMAKE_GENERATOR_PLATFORM": "Win64"}):
+            cmake = CMake(conan_file)
+            self.assertIn('-G "Visual Studio 15 2017" -A "Win64"', cmake.command_line)
+
+    def cmake_generator_platform_gcc_test(self):
+            settings = Settings.loads(default_settings_yml)
+            settings.os = "Linux"
+            settings.os_build = "Linux"
+            settings.compiler = "gcc"
+            settings.compiler.version = "8"
+            settings.compiler.libcxx = "libstdc++"
+            settings.arch = "x86"
+
+            conan_file = ConanFileMock()
+            conan_file.settings = settings
+
+            cmake = CMake(conan_file)
+            self.assertIn('-G "Unix Makefiles"', cmake.command_line)
+            self.assertNotIn('-A', cmake.command_line)
+
+    @parameterized.expand([('x86', 'Visual Studio 15 2017'),
+                           ('x86_64', 'Visual Studio 15 2017 Win64'),
+                           ('armv7', 'Visual Studio 15 2017 ARM')])
+    def cmake_generator_platform_vs2017_test(self, arch, generator):
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "15"
+        settings.arch = arch
+
+        conan_file = ConanFileMock()
+        conan_file.settings = settings
+
+        cmake = CMake(conan_file)
+        self.assertIn('-G "%s"' % generator, cmake.command_line)
+        self.assertNotIn('-A', cmake.command_line)
+
+    @parameterized.expand([('x86', 'Win32'),
+                           ('x86_64', 'x64'),
+                           ('armv7', 'ARM'),
+                           ('armv8', 'ARM64')])
+    def cmake_generator_platform_vs2019_test(self, arch, platform):
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "16"
+        settings.arch = arch
+
+        conan_file = ConanFileMock()
+        conan_file.settings = settings
+
+        cmake = CMake(conan_file)
+        self.assertIn('-G "Visual Studio 16 2019" -A "%s"' % platform, cmake.command_line)
+
+    @parameterized.expand([('arm',),
+                           ('ppc',),
+                           ('86',)])
+    def cmake_generator_platform_other_test(self, platform):
+        conan_file = ConanFileMock()
+        conan_file.settings = Settings()
+
+        with tools.environment_append({"CONAN_CMAKE_GENERATOR": "Green Hills MULTI",
+                                       "CONAN_CMAKE_GENERATOR_PLATFORM": platform}):
+            cmake = CMake(conan_file)
+            self.assertIn('-G "Green Hills MULTI" -A "%s"' % platform, cmake.command_line)
+
     def cmake_fpic_test(self):
         settings = Settings.loads(default_settings_yml)
         settings.os = "Linux"