subsurface/subsurfacesysinfo.cpp
Dirk Hohndel 5e345fba0a Make system detection compile on Mac
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-06-14 13:29:13 -07:00

592 lines
16 KiB
C++

#include "subsurfacesysinfo.h"
#include <QString>
#include "qplatformdefs.h"
#ifdef Q_OS_UNIX
#include <sys/utsname.h>
#endif
#ifdef __APPLE__
#include <MacTypes.h>
#include <CoreServices/CoreServices.h>
#endif
// main part: processor type
#if defined(Q_PROCESSOR_ALPHA)
# define ARCH_PROCESSOR "alpha"
#elif defined(Q_PROCESSOR_ARM)
# define ARCH_PROCESSOR "arm"
#elif defined(Q_PROCESSOR_AVR32)
# define ARCH_PROCESSOR "avr32"
#elif defined(Q_PROCESSOR_BLACKFIN)
# define ARCH_PROCESSOR "bfin"
#elif defined(Q_PROCESSOR_X86_32)
# define ARCH_PROCESSOR "i386"
#elif defined(Q_PROCESSOR_X86_64)
# define ARCH_PROCESSOR "x86_64"
#elif defined(Q_PROCESSOR_IA64)
# define ARCH_PROCESSOR "ia64"
#elif defined(Q_PROCESSOR_MIPS)
# define ARCH_PROCESSOR "mips"
#elif defined(Q_PROCESSOR_POWER)
# define ARCH_PROCESSOR "power"
#elif defined(Q_PROCESSOR_S390)
# define ARCH_PROCESSOR "s390"
#elif defined(Q_PROCESSOR_SH)
# define ARCH_PROCESSOR "sh"
#elif defined(Q_PROCESSOR_SPARC)
# define ARCH_PROCESSOR "sparc"
#else
# define ARCH_PROCESSOR "unknown"
#endif
// endinanness
#if defined(Q_LITTLE_ENDIAN)
# define ARCH_ENDIANNESS "little_endian"
#elif defined(Q_BIG_ENDIAN)
# define ARCH_ENDIANNESS "big_endian"
#endif
// pointer type
#if defined(Q_OS_WIN64) || (defined(Q_OS_WINRT) && defined(_M_X64))
# define ARCH_POINTER "llp64"
#elif defined(__LP64__) || QT_POINTER_SIZE - 0 == 8
# define ARCH_POINTER "lp64"
#else
# define ARCH_POINTER "ilp32"
#endif
// secondary: ABI string (includes the dash)
#if defined(__ARM_EABI__) || defined(__mips_eabi)
# define ARCH_ABI1 "-eabi"
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _ABIO32
# define ARCH_ABI1 "-o32"
# elif _MIPS_SIM == _ABIN32
# define ARCH_ABI1 "-n32"
# elif _MIPS_SIM == _ABI64
# define ARCH_ABI1 "-n64"
# elif _MIPS_SIM == _ABIO64
# define ARCH_ABI1 "-o64"
# endif
#else
# define ARCH_ABI1 ""
#endif
#if defined(__ARM_PCS_VFP) || defined(__mips_hard_float)
# define ARCH_ABI2 "-hardfloat"
#else
# define ARCH_ABI2 ""
#endif
#define ARCH_ABI ARCH_ABI1 ARCH_ABI2
#define ARCH_FULL ARCH_PROCESSOR "-" ARCH_ENDIANNESS "-" ARCH_POINTER ARCH_ABI
#if defined(Q_OS_OSX)
SubsurfaceSysInfo::MacVersion SubsurfaceSysInfo::macVersion()
{
#if defined(Q_OS_OSX)
SInt32 gestalt_version;
if (Gestalt(gestaltSystemVersionMinor, &gestalt_version) == noErr) {
// add 2 because OS X 10.0 is 0x02 in the enum
return SubsurfaceSysInfo::MacVersion(gestalt_version + 2);
}
#elif defined(Q_OS_IOS)
return qt_ios_version(); // qtcore_mac_objc.mm
#endif
return SubsurfaceSysInfo::MV_Unknown;
}
const SubsurfaceSysInfo::MacVersion SubsurfaceSysInfo::MacintoshVersion = SubsurfaceSysInfo::macVersion();
#elif defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
QT_BEGIN_INCLUDE_NAMESPACE
#include "qt_windows.h"
QT_END_INCLUDE_NAMESPACE
#ifndef Q_OS_WINRT
static inline OSVERSIONINFO winOsVersion()
{
OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0, {'\0'}};
// GetVersionEx() has been deprecated in Windows 8.1 and will return
// only Windows 8 from that version on.
# if defined(_MSC_VER) && _MSC_VER >= 1800
# pragma warning( push )
# pragma warning( disable : 4996 )
# endif
GetVersionEx(&result);
# if defined(_MSC_VER) && _MSC_VER >= 1800
# pragma warning( pop )
# endif
# ifndef Q_OS_WINCE
if (result.dwMajorVersion == 6 && result.dwMinorVersion == 2) {
// This could be Windows 8.1 or higher. Note that as of Windows 9,
// the major version needs to be checked as well.
DWORDLONG conditionMask = 0;
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_PLATFORMID, VER_EQUAL);
OSVERSIONINFOEX checkVersion = { sizeof(OSVERSIONINFOEX), result.dwMajorVersion, result.dwMinorVersion,
result.dwBuildNumber, result.dwPlatformId, {'\0'}, 0, 0, 0, 0, 0 };
for ( ; VerifyVersionInfo(&checkVersion, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, conditionMask); ++checkVersion.dwMinorVersion)
result.dwMinorVersion = checkVersion.dwMinorVersion;
}
# endif // !Q_OS_WINCE
return result;
}
#endif // !Q_OS_WINRT
SubsurfaceSysInfo::WinVersion SubsurfaceSysInfo::windowsVersion()
{
#ifndef VER_PLATFORM_WIN32s
#define VER_PLATFORM_WIN32s 0
#endif
#ifndef VER_PLATFORM_WIN32_WINDOWS
#define VER_PLATFORM_WIN32_WINDOWS 1
#endif
#ifndef VER_PLATFORM_WIN32_NT
#define VER_PLATFORM_WIN32_NT 2
#endif
#ifndef VER_PLATFORM_WIN32_CE
#define VER_PLATFORM_WIN32_CE 3
#endif
static SubsurfaceSysInfo::WinVersion winver;
if (winver)
return winver;
#ifdef Q_OS_WINRT
winver = SubsurfaceSysInfo::WV_WINDOWS8;
#else
winver = SubsurfaceSysInfo::WV_NT;
const OSVERSIONINFO osver = winOsVersion();
#ifdef Q_OS_WINCE
DWORD qt_cever = 0;
qt_cever = osver.dwMajorVersion * 100;
qt_cever += osver.dwMinorVersion * 10;
#endif
switch (osver.dwPlatformId) {
case VER_PLATFORM_WIN32s:
winver = SubsurfaceSysInfo::WV_32s;
break;
case VER_PLATFORM_WIN32_WINDOWS:
// We treat Windows Me (minor 90) the same as Windows 98
if (osver.dwMinorVersion == 90)
winver = SubsurfaceSysInfo::WV_Me;
else if (osver.dwMinorVersion == 10)
winver = SubsurfaceSysInfo::WV_98;
else
winver = SubsurfaceSysInfo::WV_95;
break;
#ifdef Q_OS_WINCE
case VER_PLATFORM_WIN32_CE:
if (qt_cever >= 600)
winver = SubsurfaceSysInfo::WV_CE_6;
if (qt_cever >= 500)
winver = SubsurfaceSysInfo::WV_CE_5;
else if (qt_cever >= 400)
winver = SubsurfaceSysInfo::WV_CENET;
else
winver = SubsurfaceSysInfo::WV_CE;
break;
#endif
default: // VER_PLATFORM_WIN32_NT
if (osver.dwMajorVersion < 5) {
winver = SubsurfaceSysInfo::WV_NT;
} else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 0) {
winver = SubsurfaceSysInfo::WV_2000;
} else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 1) {
winver = SubsurfaceSysInfo::WV_XP;
} else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 2) {
winver = SubsurfaceSysInfo::WV_2003;
} else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 0) {
winver = SubsurfaceSysInfo::WV_VISTA;
} else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 1) {
winver = SubsurfaceSysInfo::WV_WINDOWS7;
} else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 2) {
winver = SubsurfaceSysInfo::WV_WINDOWS8;
} else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 3) {
winver = SubsurfaceSysInfo::WV_WINDOWS8_1;
} else {
qWarning("Qt: Untested Windows version %d.%d detected!",
int(osver.dwMajorVersion), int(osver.dwMinorVersion));
winver = SubsurfaceSysInfo::WV_NT_based;
}
}
#ifdef QT_DEBUG
{
QByteArray override = qgetenv("QT_WINVER_OVERRIDE");
if (override.isEmpty())
return winver;
if (override == "Me")
winver = SubsurfaceSysInfo::WV_Me;
if (override == "95")
winver = SubsurfaceSysInfo::WV_95;
else if (override == "98")
winver = SubsurfaceSysInfo::WV_98;
else if (override == "NT")
winver = SubsurfaceSysInfo::WV_NT;
else if (override == "2000")
winver = SubsurfaceSysInfo::WV_2000;
else if (override == "2003")
winver = SubsurfaceSysInfo::WV_2003;
else if (override == "XP")
winver = SubsurfaceSysInfo::WV_XP;
else if (override == "VISTA")
winver = SubsurfaceSysInfo::WV_VISTA;
else if (override == "WINDOWS7")
winver = SubsurfaceSysInfo::WV_WINDOWS7;
else if (override == "WINDOWS8")
winver = SubsurfaceSysInfo::WV_WINDOWS8;
else if (override == "WINDOWS8_1")
winver = SubsurfaceSysInfo::WV_WINDOWS8_1;
}
#endif
#endif // !Q_OS_WINRT
return winver;
}
static const char *winVer_helper()
{
switch (int(SubsurfaceSysInfo::WindowsVersion)) {
case SubsurfaceSysInfo::WV_NT:
return "NT";
case SubsurfaceSysInfo::WV_2000:
return "2000";
case SubsurfaceSysInfo::WV_XP:
return "XP";
case SubsurfaceSysInfo::WV_2003:
return "2003";
case SubsurfaceSysInfo::WV_VISTA:
return "Vista";
case SubsurfaceSysInfo::WV_WINDOWS7:
return "7";
case SubsurfaceSysInfo::WV_WINDOWS8:
return "8";
case SubsurfaceSysInfo::WV_WINDOWS8_1:
return "8.1";
case SubsurfaceSysInfo::WV_CE:
return "CE";
case SubsurfaceSysInfo::WV_CENET:
return "CENET";
case SubsurfaceSysInfo::WV_CE_5:
return "CE5";
case SubsurfaceSysInfo::WV_CE_6:
return "CE6";
}
// unknown, future version
return 0;
}
const SubsurfaceSysInfo::WinVersion SubsurfaceSysInfo::WindowsVersion = SubsurfaceSysInfo::windowsVersion();
#endif
#if defined(Q_OS_UNIX)
# if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
# define USE_ETC_OS_RELEASE
# endif
struct QUnixOSVersion
{
// from uname(2)
QString sysName;
QString sysNameLower;
QString sysRelease;
# if !defined(Q_OS_ANDROID) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_MAC)
// from /etc/os-release or guessed
QString versionIdentifier; // ${ID}_$VERSION_ID
QString versionText; // $PRETTY_NAME
# endif
};
#ifdef USE_ETC_OS_RELEASE
static QString unquote(const char *begin, const char *end)
{
if (*begin == '"') {
Q_ASSERT(end[-1] == '"');
return QString::fromLatin1(begin + 1, end - begin - 2);
}
return QString::fromLatin1(begin, end - begin);
}
static bool readEtcOsRelease(QUnixOSVersion &v)
{
// we're avoiding QFile here
int fd = QT_OPEN("/etc/os-release", O_RDONLY);
if (fd == -1)
return false;
QT_STATBUF sbuf;
if (QT_FSTAT(fd, &sbuf) == -1) {
QT_CLOSE(fd);
return false;
}
QString partialIdentifier;
QByteArray buffer(sbuf.st_size, Qt::Uninitialized);
buffer.resize(QT_READ(fd, buffer.data(), sbuf.st_size));
QT_CLOSE(fd);
const char *ptr = buffer.constData();
const char *end = buffer.constEnd();
const char *eol;
for ( ; ptr != end; ptr = eol + 1) {
static const char idString[] = "ID=";
static const char prettyNameString[] = "PRETTY_NAME=";
static const char versionIdString[] = "VERSION_ID=";
// find the end of the line after ptr
eol = static_cast<const char *>(memchr(ptr, '\n', end - ptr));
if (!eol)
eol = end - 1;
int cmp = strncmp(ptr, idString, strlen(idString));
if (cmp < 0)
continue;
if (cmp == 0) {
ptr += strlen(idString);
QString id = unquote(ptr, eol);
if (partialIdentifier.isNull())
partialIdentifier = id;
else
v.versionIdentifier = id + QLatin1Char('_') + partialIdentifier;
continue;
}
cmp = strncmp(ptr, prettyNameString, strlen(prettyNameString));
if (cmp < 0)
continue;
if (cmp == 0) {
ptr += strlen(prettyNameString);
v.versionText = unquote(ptr, eol);
continue;
}
cmp = strncmp(ptr, versionIdString, strlen(versionIdString));
if (cmp < 0)
continue;
if (cmp == 0) {
ptr += strlen(versionIdString);
QString id = unquote(ptr, eol);
if (partialIdentifier.isNull())
partialIdentifier = id;
else
v.versionIdentifier = partialIdentifier + QLatin1Char('_') + id;
continue;
}
}
return true;
}
#endif // USE_ETC_OS_RELEASE
static QUnixOSVersion detectUnixVersion()
{
QUnixOSVersion v;
struct utsname u;
if (uname(&u) != -1) {
v.sysName = QString::fromLatin1(u.sysname);
v.sysNameLower = v.sysName.toLower();
v.sysRelease = QString::fromLatin1(u.release);
} else {
v.sysName = QLatin1String("Detection failed");
// leave sysNameLower & sysRelease unset
}
# if !defined(Q_OS_ANDROID) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_MAC)
# ifdef USE_ETC_OS_RELEASE
if (readEtcOsRelease(v))
return v;
# endif
if (!v.sysNameLower.isEmpty()) {
// will produce "qnx_6.5" or "sunos_5.9"
v.versionIdentifier = v.sysNameLower + QLatin1Char('_') + v.sysRelease;
}
# endif
return v;
}
#endif
QString SubsurfaceSysInfo::unknownText()
{
return QStringLiteral("unknown");
}
QString SubsurfaceSysInfo::cpuArchitecture()
{
return QStringLiteral(ARCH_PROCESSOR);
}
QString SubsurfaceSysInfo::fullCpuArchitecture()
{
return QLatin1String(ARCH_FULL);
}
QString SubsurfaceSysInfo::osType()
{
#if defined(Q_OS_WINPHONE)
return QStringLiteral("winphone");
#elif defined(Q_OS_WINRT)
return QStringLiteral("winrt");
#elif defined(Q_OS_WINCE)
return QStringLiteral("wince");
#elif defined(Q_OS_WIN)
return QStringLiteral("windows");
#elif defined(Q_OS_BLACKBERRY)
return QStringLiteral("blackberry");
#elif defined(Q_OS_QNX)
return QStringLiteral("qnx");
#elif defined(Q_OS_ANDROID)
return QStringLiteral("android");
#elif defined(Q_OS_LINUX)
return QStringLiteral("linux");
#elif defined(Q_OS_IOS)
return QStringLiteral("ios");
#elif defined(Q_OS_OSX)
return QStringLiteral("osx");
#elif defined(Q_OS_DARWIN)
return QStringLiteral("darwin");
#elif defined(Q_OS_FREEBSD_KERNEL)
return QStringLiteral("freebsd");
#elif defined(Q_OS_UNIX)
QUnixOSVersion unixOsVersion = detectUnixVersion();
if (!unixOsVersion.sysNameLower.isEmpty())
return unixOsVersion.sysNameLower;
#endif
return unknownText();
}
QString SubsurfaceSysInfo::osKernelVersion()
{
#ifdef Q_OS_WINRT
// TBD
return QString();
#elif defined(Q_OS_WIN)
const OSVERSIONINFO osver = winOsVersion();
return QString::number(int(osver.dwMajorVersion)) + QLatin1Char('.') + QString::number(int(osver.dwMinorVersion))
+ QLatin1Char('.') + QString::number(int(osver.dwBuildNumber));
#else
return detectUnixVersion().sysRelease;
#endif
}
QString SubsurfaceSysInfo::osVersion()
{
#if defined(Q_OS_IOS)
int major = (int(MacintoshVersion) >> 4) & 0xf;
int minor = int(MacintoshVersion) & 0xf;
if (Q_LIKELY(major < 10 && minor < 10)) {
char buf[4] = { char(major + '0'), '.', char(minor + '0'), '\0' };
return QString::fromLatin1(buf, 3);
}
return QString::number(major) + QLatin1Char('.') + QString::number(minor);
#elif defined(Q_OS_OSX)
int minor = int(MacintoshVersion) - 2; // we're not running on Mac OS 9
Q_ASSERT(minor < 100);
char buf[] = "10.0\0";
if (Q_LIKELY(minor < 10)) {
buf[3] += minor;
} else {
buf[3] += minor / 10;
buf[4] = '0' + minor % 10;
}
return QString::fromLatin1(buf);
#elif defined(Q_OS_WIN)
const char *version = winVer_helper();
if (version)
return QString::fromLatin1(version).toLower();
// fall through
// Android and Blackberry should not fall through to the Unix code
#elif defined(Q_OS_ANDROID)
// TBD
#elif defined(Q_OS_BLACKBERRY)
deviceinfo_details_t *deviceInfo;
if (deviceinfo_get_details(&deviceInfo) == BPS_SUCCESS) {
QString bbVersion = QString::fromLatin1(deviceinfo_details_get_device_os_version(deviceInfo));
deviceinfo_free_details(&deviceInfo);
return bbVersion;
}
#elif defined(Q_OS_UNIX)
QUnixOSVersion unixOsVersion = detectUnixVersion();
if (!unixOsVersion.versionIdentifier.isEmpty())
return unixOsVersion.versionIdentifier;
#endif
// fallback
return unknownText();
}
QString SubsurfaceSysInfo::prettyOsName()
{
#if defined(Q_OS_IOS)
return QLatin1String("iOS ") + osVersion();
#elif defined(Q_OS_OSX)
// get the known codenames
const char *basename = 0;
switch (int(MacintoshVersion)) {
case MV_CHEETAH:
case MV_PUMA:
case MV_JAGUAR:
case MV_PANTHER:
case MV_TIGER:
// This version of Qt does not run on those versions of OS X
// so this case label will never be reached
Q_UNREACHABLE();
break;
case MV_LEOPARD:
basename = "Mac OS X Leopard (";
break;
case MV_SNOWLEOPARD:
basename = "Mac OS X Snow Leopard (";
break;
case MV_LION:
basename = "Mac OS X Lion (";
break;
case MV_MOUNTAINLION:
basename = "OS X Mountain Lion (";
break;
case MV_MAVERICKS:
basename = "OS X Mavericks (";
break;
case MV_YOSEMITE:
basename = "OS X Yosemite (";
break;
}
if (basename)
return QLatin1String(basename) + osVersion() + QLatin1Char(')');
// a future version of OS X
return QLatin1String("OS X ") + osVersion();
#elif defined(Q_OS_WINPHONE)
return QLatin1String("Windows Phone ") + QLatin1String(winVer_helper());
#elif defined(Q_OS_WIN)
return QLatin1String("Windows ") + QLatin1String(winVer_helper());
#elif defined(Q_OS_ANDROID)
return QLatin1String("Android ") + osVersion();
#elif defined(Q_OS_BLACKBERRY)
return QLatin1String("BlackBerry ") + osVersion();
#elif defined(Q_OS_UNIX)
QUnixOSVersion unixOsVersion = detectUnixVersion();
if (unixOsVersion.versionText.isEmpty())
return unixOsVersion.sysName;
else
return unixOsVersion.sysName + QLatin1String(" (") + unixOsVersion.versionText + QLatin1Char(')');
#else
return unknownText();
#endif
}