aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/environment.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-09-30 15:30:57 -0400
committerGitHub <noreply@github.com>2016-09-30 15:30:57 -0400
commit295b67df518805a1120671e3b248ff0672a89b93 (patch)
treed0b3a80cb6af908c77e4fadf685046a0313efb51 /mesonbuild/environment.py
parent338dbc964cf2927d476d1fb89a5751e8cfce39bd (diff)
parent89753ecc27c9857be2554b1af7fe1f5162b8e761 (diff)
downloadmeson-295b67df518805a1120671e3b248ff0672a89b93.zip
meson-295b67df518805a1120671e3b248ff0672a89b93.tar.gz
meson-295b67df518805a1120671e3b248ff0672a89b93.tar.bz2
Merge pull request #814 from centricular/heavy-cleanup-compilers-buildtargets
Heavy cleanup in compilers and BuildTarget
Diffstat (limited to 'mesonbuild/environment.py')
-rw-r--r--mesonbuild/environment.py185
1 files changed, 139 insertions, 46 deletions
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 341e5e8..e411687 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -57,26 +57,89 @@ def detect_ninja():
if p.returncode == 0 and mesonlib.version_compare(version, ">=1.6"):
return n
-def detect_cpu_family():
+def detect_native_windows_arch():
+ """
+ The architecture of Windows itself: x86 or amd64
+ """
+ # These env variables are always available. See:
+ # https://msdn.microsoft.com/en-us/library/aa384274(VS.85).aspx
+ # https://blogs.msdn.microsoft.com/david.wang/2006/03/27/howto-detect-process-bitness/
+ arch = os.environ.get('PROCESSOR_ARCHITEW6432', '').lower()
+ if not arch:
+ try:
+ # If this doesn't exist, something is messing with the environment
+ arch = os.environ['PROCESSOR_ARCHITECTURE'].lower()
+ except KeyError:
+ raise InterpreterException('Unable to detect native OS architecture')
+ return arch
+
+def detect_windows_arch(compilers):
+ """
+ Detecting the 'native' architecture of Windows is not a trivial task. We
+ cannot trust that the architecture that Python is built for is the 'native'
+ one because you can run 32-bit apps on 64-bit Windows using WOW64 and
+ people sometimes install 32-bit Python on 64-bit Windows.
+
+ We also can't rely on the architecture of the OS itself, since it's
+ perfectly normal to compile and run 32-bit applications on Windows as if
+ they were native applications. It's a terrible experience to require the
+ user to supply a cross-info file to compile 32-bit applications on 64-bit
+ Windows. Thankfully, the only way to compile things with Visual Studio on
+ Windows is by entering the 'msvc toolchain' environment, which can be
+ easily detected.
+
+ In the end, the sanest method is as follows:
+ 1. Check if we're in an MSVC toolchain environment, and if so, return the
+ MSVC toolchain architecture as our 'native' architecture.
+ 2. If not, check environment variables that are set by Windows and WOW64 to
+ find out the architecture that Windows is built for, and use that as our
+ 'native' architecture.
+ """
+ os_arch = detect_native_windows_arch()
+ if os_arch != 'amd64':
+ return os_arch
+ # If we're on 64-bit Windows, 32-bit apps can be compiled without
+ # cross-compilation. So if we're doing that, just set the native arch as
+ # 32-bit and pretend like we're running under WOW64. Else, return the
+ # actual Windows architecture that we deduced above.
+ for compiler in compilers.values():
+ # Check if we're using and inside an MSVC toolchain environment
+ if compiler.id == 'msvc' and 'VCINSTALLDIR' in os.environ:
+ # 'Platform' is only set when the target arch is not 'x86'.
+ # It's 'x64' when targetting x86_64 and 'arm' when targetting ARM.
+ platform = os.environ.get('Platform', 'x86').lower()
+ if platform == 'x86':
+ return platform
+ if compiler.id == 'gcc' and compiler.has_define('__i386__'):
+ return 'x86'
+ return os_arch
+
+def detect_cpu_family(compilers):
"""
Python is inconsistent in its platform module.
It returns different values for the same cpu.
For x86 it might return 'x86', 'i686' or somesuch.
Do some canonicalization.
"""
- trial = platform.machine().lower()
+ if mesonlib.is_windows():
+ trial = detect_windows_arch(compilers)
+ else:
+ trial = platform.machine().lower()
if trial.startswith('i') and trial.endswith('86'):
return 'x86'
if trial.startswith('arm'):
return 'arm'
- if trial == 'amd64':
+ if trial in ('amd64', 'x64'):
return 'x86_64'
# Add fixes here as bugs are reported.
return trial
-def detect_cpu():
- trial = platform.machine().lower()
- if trial == 'amd64':
+def detect_cpu(compilers):
+ if mesonlib.is_windows():
+ trial = detect_windows_arch(compilers)
+ else:
+ trial = platform.machine().lower()
+ if trial in ('amd64', 'x64'):
return 'x86_64'
# Add fixes here as bugs are reported.
return trial
@@ -225,6 +288,45 @@ class Environment():
if type(oldval) != type(value):
self.coredata.user_options[name] = value
+ @staticmethod
+ def get_gnu_compiler_defines(compiler):
+ """
+ Detect GNU compiler platform type (Apple, MinGW, Unix)
+ """
+ # Arguments to output compiler pre-processor defines to stdout
+ # gcc, g++, and gfortran all support these arguments
+ args = compiler + ['-E', '-dM', '-']
+ p = subprocess.Popen(args, universal_newlines=True,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ output = p.communicate('')[0]
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to detect GNU compiler type:\n' + output)
+ # Parse several lines of the type:
+ # `#define ___SOME_DEF some_value`
+ # and extract `___SOME_DEF`
+ defines = {}
+ for line in output.split('\n'):
+ if not line:
+ continue
+ d, *rest = line.split(' ', 2)
+ if d != '#define':
+ continue
+ if len(rest) == 1:
+ defines[rest] = True
+ if len(rest) == 2:
+ defines[rest[0]] = rest[1]
+ return defines
+
+ @staticmethod
+ def get_gnu_compiler_type(defines):
+ # Detect GCC type (Apple, MinGW, Cygwin, Unix)
+ if '__APPLE__' in defines:
+ return GCC_OSX
+ elif '__MINGW32__' in defines or '__MINGW64__' in defines:
+ return GCC_MINGW
+ # We ignore Cygwin for now, and treat it as a standard GCC
+ return GCC_STANDARD
+
def detect_c_compiler(self, want_cross):
evar = 'CC'
if self.is_cross_build() and want_cross:
@@ -266,16 +368,13 @@ class Environment():
version = vmatch.group(0)
else:
version = 'unknown version'
- if 'apple' in out and 'Free Software Foundation' in out:
- return GnuCCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap)
- if (out.startswith('cc') or 'gcc' in out.lower()) and \
- 'Free Software Foundation' in out:
- lowerout = out.lower()
- if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower():
- gtype = GCC_MINGW
- else:
- gtype = GCC_STANDARD
- return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap)
+ if 'Free Software Foundation' in out:
+ defines = self.get_gnu_compiler_defines([compiler])
+ if not defines:
+ popen_exceptions[compiler] = 'no pre-processor defines'
+ continue
+ gtype = self.get_gnu_compiler_type(defines)
+ return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines)
if 'clang' in out:
if 'Apple' in out:
cltype = CLANG_OSX
@@ -331,13 +430,12 @@ class Environment():
version = vmatch.group(0)
if 'GNU Fortran' in out:
- if mesonlib.is_osx():
- gcctype = GCC_OSX
- elif mesonlib.is_windows():
- gcctype = GCC_MINGW
- else:
- gcctype = GCC_STANDARD
- return GnuFortranCompiler([compiler], version, gcctype, is_cross, exe_wrap)
+ defines = self.get_gnu_compiler_defines([compiler])
+ if not defines:
+ popen_exceptions[compiler] = 'no pre-processor defines'
+ continue
+ gtype = self.get_gnu_compiler_type(defines)
+ return GnuFortranCompiler([compiler], version, gtype, is_cross, exe_wrap, defines)
if 'G95' in out:
return G95FortranCompiler([compiler], version, is_cross, exe_wrap)
@@ -419,16 +517,13 @@ class Environment():
version = vmatch.group(0)
else:
version = 'unknown version'
- if 'apple' in out and 'Free Software Foundation' in out:
- return GnuCPPCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap)
- if (out.startswith('c++ ') or 'g++' in out or 'GCC' in out) and \
- 'Free Software Foundation' in out:
- lowerout = out.lower()
- if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower():
- gtype = GCC_MINGW
- else:
- gtype = GCC_STANDARD
- return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap)
+ if 'Free Software Foundation' in out:
+ defines = self.get_gnu_compiler_defines([compiler])
+ if not defines:
+ popen_exceptions[compiler] = 'no pre-processor defines'
+ continue
+ gtype = self.get_gnu_compiler_type(defines)
+ return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines)
if 'clang' in out:
if 'Apple' in out:
cltype = CLANG_OSX
@@ -469,13 +564,11 @@ class Environment():
version = vmatch.group(0)
else:
version = 'unknown version'
- if (out.startswith('cc ') or 'gcc' in out) and \
- 'Free Software Foundation' in out:
- return GnuObjCCompiler(exelist, version, is_cross, exe_wrap)
+ if 'Free Software Foundation' in out:
+ defines = self.get_gnu_compiler_defines(exelist)
+ return GnuObjCCompiler(exelist, version, is_cross, exe_wrap, defines)
if out.startswith('Apple LLVM'):
return ClangObjCCompiler(exelist, version, CLANG_OSX, is_cross, exe_wrap)
- if 'apple' in out and 'Free Software Foundation' in out:
- return GnuObjCCompiler(exelist, version, is_cross, exe_wrap)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
def detect_objcpp_compiler(self, want_cross):
@@ -502,13 +595,11 @@ class Environment():
version = vmatch.group(0)
else:
version = 'unknown version'
- if (out.startswith('c++ ') or out.startswith('g++')) and \
- 'Free Software Foundation' in out:
- return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap)
+ if 'Free Software Foundation' in out:
+ defines = self.get_gnu_compiler_defines(exelist)
+ return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap, defines)
if out.startswith('Apple LLVM'):
return ClangObjCPPCompiler(exelist, version, CLANG_OSX, is_cross, exe_wrap)
- if 'apple' in out and 'Free Software Foundation' in out:
- return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
def detect_java_compiler(self):
@@ -846,10 +937,12 @@ class CrossBuildInfo():
return 'host_machine' in self.config
def need_exe_wrapper(self):
- if self.has_host() and detect_cpu_family() == 'x86_64' and \
+ # Can almost always run 32-bit binaries on 64-bit natively if the host
+ # and build systems are the same. We don't pass any compilers to
+ # detect_cpu_family() here because we always want to know the OS
+ # architecture, not what the compiler environment tells us.
+ if self.has_host() and detect_cpu_family({}) == 'x86_64' and \
self.config['host_machine']['cpu_family'] == 'x86' and \
self.config['host_machine']['system'] == detect_system():
- # Can almost always run 32-bit binaries on 64-bit natively if the
- # host and build systems are the same
return False
return True