10b57cec5SDimitry Andric //===-- os_version_check.c - OS version checking -------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the function __isOSVersionAtLeast, used by
100b57cec5SDimitry Andric // Objective-C's @available
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #ifdef __APPLE__
150b57cec5SDimitry Andric
160b57cec5SDimitry Andric #include <TargetConditionals.h>
170b57cec5SDimitry Andric #include <dispatch/dispatch.h>
180b57cec5SDimitry Andric #include <dlfcn.h>
190b57cec5SDimitry Andric #include <stdint.h>
200b57cec5SDimitry Andric #include <stdio.h>
210b57cec5SDimitry Andric #include <stdlib.h>
220b57cec5SDimitry Andric #include <string.h>
230b57cec5SDimitry Andric
240b57cec5SDimitry Andric // These three variables hold the host's OS version.
250b57cec5SDimitry Andric static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
260b57cec5SDimitry Andric static dispatch_once_t DispatchOnceCounter;
27e8d8bef9SDimitry Andric static dispatch_once_t CompatibilityDispatchOnceCounter;
28e8d8bef9SDimitry Andric
29e8d8bef9SDimitry Andric // _availability_version_check darwin API support.
30e8d8bef9SDimitry Andric typedef uint32_t dyld_platform_t;
31e8d8bef9SDimitry Andric
32e8d8bef9SDimitry Andric typedef struct {
33e8d8bef9SDimitry Andric dyld_platform_t platform;
34e8d8bef9SDimitry Andric uint32_t version;
35e8d8bef9SDimitry Andric } dyld_build_version_t;
36e8d8bef9SDimitry Andric
37e8d8bef9SDimitry Andric typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
38e8d8bef9SDimitry Andric dyld_build_version_t versions[]);
39e8d8bef9SDimitry Andric
40e8d8bef9SDimitry Andric static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
430b57cec5SDimitry Andric // just forward declare everything that we need from it.
440b57cec5SDimitry Andric
450b57cec5SDimitry Andric typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
460b57cec5SDimitry Andric *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric #if __LLP64__
490b57cec5SDimitry Andric typedef unsigned long long CFTypeID;
500b57cec5SDimitry Andric typedef unsigned long long CFOptionFlags;
510b57cec5SDimitry Andric typedef signed long long CFIndex;
520b57cec5SDimitry Andric #else
530b57cec5SDimitry Andric typedef unsigned long CFTypeID;
540b57cec5SDimitry Andric typedef unsigned long CFOptionFlags;
550b57cec5SDimitry Andric typedef signed long CFIndex;
560b57cec5SDimitry Andric #endif
570b57cec5SDimitry Andric
580b57cec5SDimitry Andric typedef unsigned char UInt8;
590b57cec5SDimitry Andric typedef _Bool Boolean;
600b57cec5SDimitry Andric typedef CFIndex CFPropertyListFormat;
610b57cec5SDimitry Andric typedef uint32_t CFStringEncoding;
620b57cec5SDimitry Andric
630b57cec5SDimitry Andric // kCFStringEncodingASCII analog.
640b57cec5SDimitry Andric #define CF_STRING_ENCODING_ASCII 0x0600
650b57cec5SDimitry Andric // kCFStringEncodingUTF8 analog.
660b57cec5SDimitry Andric #define CF_STRING_ENCODING_UTF8 0x08000100
670b57cec5SDimitry Andric #define CF_PROPERTY_LIST_IMMUTABLE 0
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
700b57cec5SDimitry Andric const UInt8 *, CFIndex,
710b57cec5SDimitry Andric CFAllocatorRef);
720b57cec5SDimitry Andric typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
730b57cec5SDimitry Andric CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
740b57cec5SDimitry Andric CFErrorRef *);
750b57cec5SDimitry Andric typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
760b57cec5SDimitry Andric CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
770b57cec5SDimitry Andric typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
780b57cec5SDimitry Andric const char *,
790b57cec5SDimitry Andric CFStringEncoding,
800b57cec5SDimitry Andric CFAllocatorRef);
810b57cec5SDimitry Andric typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
820b57cec5SDimitry Andric const void *);
830b57cec5SDimitry Andric typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
840b57cec5SDimitry Andric typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
850b57cec5SDimitry Andric typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
860b57cec5SDimitry Andric CFStringEncoding);
870b57cec5SDimitry Andric typedef void (*CFReleaseFuncTy)(CFTypeRef);
880b57cec5SDimitry Andric
8906c3fb27SDimitry Andric extern __attribute__((weak_import))
9006c3fb27SDimitry Andric bool _availability_version_check(uint32_t count,
9106c3fb27SDimitry Andric dyld_build_version_t versions[]);
9206c3fb27SDimitry Andric
_initializeAvailabilityCheck(bool LoadPlist)93e8d8bef9SDimitry Andric static void _initializeAvailabilityCheck(bool LoadPlist) {
94e8d8bef9SDimitry Andric if (AvailabilityVersionCheck && !LoadPlist) {
95e8d8bef9SDimitry Andric // New API is supported and we're not being asked to load the plist,
96e8d8bef9SDimitry Andric // exit early!
97e8d8bef9SDimitry Andric return;
98e8d8bef9SDimitry Andric }
99e8d8bef9SDimitry Andric
100e8d8bef9SDimitry Andric // Use the new API if it's is available.
10106c3fb27SDimitry Andric if (_availability_version_check)
10206c3fb27SDimitry Andric AvailabilityVersionCheck = &_availability_version_check;
103e8d8bef9SDimitry Andric
104e8d8bef9SDimitry Andric if (AvailabilityVersionCheck && !LoadPlist) {
105e8d8bef9SDimitry Andric // New API is supported and we're not being asked to load the plist,
106e8d8bef9SDimitry Andric // exit early!
107e8d8bef9SDimitry Andric return;
108e8d8bef9SDimitry Andric }
109e8d8bef9SDimitry Andric // Still load the PLIST to ensure that the existing calls to
110e8d8bef9SDimitry Andric // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
111e8d8bef9SDimitry Andric
1120b57cec5SDimitry Andric // Load CoreFoundation dynamically
1130b57cec5SDimitry Andric const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
1140b57cec5SDimitry Andric if (!NullAllocator)
1150b57cec5SDimitry Andric return;
1160b57cec5SDimitry Andric const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
1170b57cec5SDimitry Andric CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
1180b57cec5SDimitry Andric (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
1190b57cec5SDimitry Andric "CFDataCreateWithBytesNoCopy");
1200b57cec5SDimitry Andric if (!CFDataCreateWithBytesNoCopyFunc)
1210b57cec5SDimitry Andric return;
1220b57cec5SDimitry Andric CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
1230b57cec5SDimitry Andric (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,
1240b57cec5SDimitry Andric "CFPropertyListCreateWithData");
1250b57cec5SDimitry Andric // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
1260b57cec5SDimitry Andric // will be NULL on earlier OS versions.
1270b57cec5SDimitry Andric #pragma clang diagnostic push
1280b57cec5SDimitry Andric #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1290b57cec5SDimitry Andric CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
1300b57cec5SDimitry Andric (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
1310b57cec5SDimitry Andric RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
1320b57cec5SDimitry Andric #pragma clang diagnostic pop
1330b57cec5SDimitry Andric // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
1340b57cec5SDimitry Andric // might be NULL in future OS versions.
1350b57cec5SDimitry Andric if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
1360b57cec5SDimitry Andric return;
1370b57cec5SDimitry Andric CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
1380b57cec5SDimitry Andric (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
1390b57cec5SDimitry Andric RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
1400b57cec5SDimitry Andric if (!CFStringCreateWithCStringNoCopyFunc)
1410b57cec5SDimitry Andric return;
1420b57cec5SDimitry Andric CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
1430b57cec5SDimitry Andric (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
1440b57cec5SDimitry Andric if (!CFDictionaryGetValueFunc)
1450b57cec5SDimitry Andric return;
1460b57cec5SDimitry Andric CFGetTypeIDFuncTy CFGetTypeIDFunc =
1470b57cec5SDimitry Andric (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
1480b57cec5SDimitry Andric if (!CFGetTypeIDFunc)
1490b57cec5SDimitry Andric return;
1500b57cec5SDimitry Andric CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
1510b57cec5SDimitry Andric (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
1520b57cec5SDimitry Andric if (!CFStringGetTypeIDFunc)
1530b57cec5SDimitry Andric return;
1540b57cec5SDimitry Andric CFStringGetCStringFuncTy CFStringGetCStringFunc =
1550b57cec5SDimitry Andric (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
1560b57cec5SDimitry Andric if (!CFStringGetCStringFunc)
1570b57cec5SDimitry Andric return;
1580b57cec5SDimitry Andric CFReleaseFuncTy CFReleaseFunc =
1590b57cec5SDimitry Andric (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
1600b57cec5SDimitry Andric if (!CFReleaseFunc)
1610b57cec5SDimitry Andric return;
1620b57cec5SDimitry Andric
1630b57cec5SDimitry Andric char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
1640b57cec5SDimitry Andric
1650b57cec5SDimitry Andric #if TARGET_OS_SIMULATOR
1660b57cec5SDimitry Andric char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
1670b57cec5SDimitry Andric if (!PListPathPrefix)
1680b57cec5SDimitry Andric return;
1690b57cec5SDimitry Andric char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
1700b57cec5SDimitry Andric strcpy(FullPath, PListPathPrefix);
1710b57cec5SDimitry Andric strcat(FullPath, PListPath);
1720b57cec5SDimitry Andric PListPath = FullPath;
1730b57cec5SDimitry Andric #endif
1740b57cec5SDimitry Andric FILE *PropertyList = fopen(PListPath, "r");
1750b57cec5SDimitry Andric if (!PropertyList)
1760b57cec5SDimitry Andric return;
1770b57cec5SDimitry Andric
1780b57cec5SDimitry Andric // Dynamically allocated stuff.
1790b57cec5SDimitry Andric CFDictionaryRef PListRef = NULL;
1800b57cec5SDimitry Andric CFDataRef FileContentsRef = NULL;
1810b57cec5SDimitry Andric UInt8 *PListBuf = NULL;
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric fseek(PropertyList, 0, SEEK_END);
1840b57cec5SDimitry Andric long PListFileSize = ftell(PropertyList);
1850b57cec5SDimitry Andric if (PListFileSize < 0)
1860b57cec5SDimitry Andric goto Fail;
1870b57cec5SDimitry Andric rewind(PropertyList);
1880b57cec5SDimitry Andric
1890b57cec5SDimitry Andric PListBuf = malloc((size_t)PListFileSize);
1900b57cec5SDimitry Andric if (!PListBuf)
1910b57cec5SDimitry Andric goto Fail;
1920b57cec5SDimitry Andric
1930b57cec5SDimitry Andric size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
1940b57cec5SDimitry Andric if (NumRead != (size_t)PListFileSize)
1950b57cec5SDimitry Andric goto Fail;
1960b57cec5SDimitry Andric
1970b57cec5SDimitry Andric // Get the file buffer into CF's format. We pass in a null allocator here *
1980b57cec5SDimitry Andric // because we free PListBuf ourselves
1990b57cec5SDimitry Andric FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
2000b57cec5SDimitry Andric NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
2010b57cec5SDimitry Andric if (!FileContentsRef)
2020b57cec5SDimitry Andric goto Fail;
2030b57cec5SDimitry Andric
2040b57cec5SDimitry Andric if (CFPropertyListCreateWithDataFunc)
2050b57cec5SDimitry Andric PListRef = (*CFPropertyListCreateWithDataFunc)(
2060b57cec5SDimitry Andric NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
2070b57cec5SDimitry Andric else
2080b57cec5SDimitry Andric PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
2090b57cec5SDimitry Andric NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
2100b57cec5SDimitry Andric if (!PListRef)
2110b57cec5SDimitry Andric goto Fail;
2120b57cec5SDimitry Andric
2130b57cec5SDimitry Andric CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
2140b57cec5SDimitry Andric NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
2150b57cec5SDimitry Andric if (!ProductVersion)
2160b57cec5SDimitry Andric goto Fail;
2170b57cec5SDimitry Andric CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
2180b57cec5SDimitry Andric (*CFReleaseFunc)(ProductVersion);
2190b57cec5SDimitry Andric if (!OpaqueValue ||
2200b57cec5SDimitry Andric (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
2210b57cec5SDimitry Andric goto Fail;
2220b57cec5SDimitry Andric
2230b57cec5SDimitry Andric char VersionStr[32];
2240b57cec5SDimitry Andric if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
2250b57cec5SDimitry Andric sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
2260b57cec5SDimitry Andric goto Fail;
2270b57cec5SDimitry Andric sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
2280b57cec5SDimitry Andric
2290b57cec5SDimitry Andric Fail:
2300b57cec5SDimitry Andric if (PListRef)
2310b57cec5SDimitry Andric (*CFReleaseFunc)(PListRef);
2320b57cec5SDimitry Andric if (FileContentsRef)
2330b57cec5SDimitry Andric (*CFReleaseFunc)(FileContentsRef);
2340b57cec5SDimitry Andric free(PListBuf);
2350b57cec5SDimitry Andric fclose(PropertyList);
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric
238e8d8bef9SDimitry Andric // Find and parse the SystemVersion.plist file.
compatibilityInitializeAvailabilityCheck(void * Unused)239e8d8bef9SDimitry Andric static void compatibilityInitializeAvailabilityCheck(void *Unused) {
240e8d8bef9SDimitry Andric (void)Unused;
241e8d8bef9SDimitry Andric _initializeAvailabilityCheck(/*LoadPlist=*/true);
242e8d8bef9SDimitry Andric }
243e8d8bef9SDimitry Andric
initializeAvailabilityCheck(void * Unused)244e8d8bef9SDimitry Andric static void initializeAvailabilityCheck(void *Unused) {
245e8d8bef9SDimitry Andric (void)Unused;
246e8d8bef9SDimitry Andric _initializeAvailabilityCheck(/*LoadPlist=*/false);
247e8d8bef9SDimitry Andric }
248e8d8bef9SDimitry Andric
249e8d8bef9SDimitry Andric // This old API entry point is no longer used by Clang for Darwin. We still need
250e8d8bef9SDimitry Andric // to keep it around to ensure that object files that reference it are still
251e8d8bef9SDimitry Andric // usable when linked with new compiler-rt.
__isOSVersionAtLeast(int32_t Major,int32_t Minor,int32_t Subminor)2520b57cec5SDimitry Andric int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
2530b57cec5SDimitry Andric // Populate the global version variables, if they haven't already.
254e8d8bef9SDimitry Andric dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL,
255e8d8bef9SDimitry Andric compatibilityInitializeAvailabilityCheck);
2560b57cec5SDimitry Andric
2570b57cec5SDimitry Andric if (Major < GlobalMajor)
2580b57cec5SDimitry Andric return 1;
2590b57cec5SDimitry Andric if (Major > GlobalMajor)
2600b57cec5SDimitry Andric return 0;
2610b57cec5SDimitry Andric if (Minor < GlobalMinor)
2620b57cec5SDimitry Andric return 1;
2630b57cec5SDimitry Andric if (Minor > GlobalMinor)
2640b57cec5SDimitry Andric return 0;
2650b57cec5SDimitry Andric return Subminor <= GlobalSubminor;
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric
ConstructVersion(uint32_t Major,uint32_t Minor,uint32_t Subminor)268e8d8bef9SDimitry Andric static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
269e8d8bef9SDimitry Andric uint32_t Subminor) {
270e8d8bef9SDimitry Andric return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
271e8d8bef9SDimitry Andric }
272e8d8bef9SDimitry Andric
__isPlatformVersionAtLeast(uint32_t Platform,uint32_t Major,uint32_t Minor,uint32_t Subminor)273e8d8bef9SDimitry Andric int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
274e8d8bef9SDimitry Andric uint32_t Minor, uint32_t Subminor) {
275e8d8bef9SDimitry Andric dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
276e8d8bef9SDimitry Andric
277e8d8bef9SDimitry Andric if (!AvailabilityVersionCheck) {
278e8d8bef9SDimitry Andric return __isOSVersionAtLeast(Major, Minor, Subminor);
279e8d8bef9SDimitry Andric }
280e8d8bef9SDimitry Andric dyld_build_version_t Versions[] = {
281e8d8bef9SDimitry Andric {Platform, ConstructVersion(Major, Minor, Subminor)}};
282e8d8bef9SDimitry Andric return AvailabilityVersionCheck(1, Versions);
283e8d8bef9SDimitry Andric }
284e8d8bef9SDimitry Andric
285e8d8bef9SDimitry Andric #elif __ANDROID__
286e8d8bef9SDimitry Andric
287e8d8bef9SDimitry Andric #include <pthread.h>
288e8d8bef9SDimitry Andric #include <stdlib.h>
289e8d8bef9SDimitry Andric #include <string.h>
290e8d8bef9SDimitry Andric #include <sys/system_properties.h>
291e8d8bef9SDimitry Andric
292e8d8bef9SDimitry Andric static int SdkVersion;
293e8d8bef9SDimitry Andric static int IsPreRelease;
294e8d8bef9SDimitry Andric
readSystemProperties(void)295e8d8bef9SDimitry Andric static void readSystemProperties(void) {
296e8d8bef9SDimitry Andric char buf[PROP_VALUE_MAX];
297e8d8bef9SDimitry Andric
298e8d8bef9SDimitry Andric if (__system_property_get("ro.build.version.sdk", buf) == 0) {
299e8d8bef9SDimitry Andric // When the system property doesn't exist, defaults to future API level.
300e8d8bef9SDimitry Andric SdkVersion = __ANDROID_API_FUTURE__;
301e8d8bef9SDimitry Andric } else {
302e8d8bef9SDimitry Andric SdkVersion = atoi(buf);
303e8d8bef9SDimitry Andric }
304e8d8bef9SDimitry Andric
305e8d8bef9SDimitry Andric if (__system_property_get("ro.build.version.codename", buf) == 0) {
306e8d8bef9SDimitry Andric IsPreRelease = 1;
307e8d8bef9SDimitry Andric } else {
308e8d8bef9SDimitry Andric IsPreRelease = strcmp(buf, "REL") != 0;
309e8d8bef9SDimitry Andric }
310e8d8bef9SDimitry Andric return;
311e8d8bef9SDimitry Andric }
312e8d8bef9SDimitry Andric
__isOSVersionAtLeast(int32_t Major,int32_t Minor,int32_t Subminor)313e8d8bef9SDimitry Andric int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
31481ad6265SDimitry Andric (void) Minor;
31581ad6265SDimitry Andric (void) Subminor;
316e8d8bef9SDimitry Andric static pthread_once_t once = PTHREAD_ONCE_INIT;
317e8d8bef9SDimitry Andric pthread_once(&once, readSystemProperties);
318e8d8bef9SDimitry Andric
319*0fca6ea1SDimitry Andric // Allow all on pre-release. Note that we still rely on compile-time checks.
320*0fca6ea1SDimitry Andric return SdkVersion >= Major || IsPreRelease;
321e8d8bef9SDimitry Andric }
322e8d8bef9SDimitry Andric
3230b57cec5SDimitry Andric #else
3240b57cec5SDimitry Andric
3250b57cec5SDimitry Andric // Silence an empty translation unit warning.
3260b57cec5SDimitry Andric typedef int unused;
3270b57cec5SDimitry Andric
3280b57cec5SDimitry Andric #endif
329