Fix darwin SDK detection logic (IOT-1862)
authorMats Wichmann <mats@linux.com>
Sat, 4 Mar 2017 15:06:34 +0000 (08:06 -0700)
committerMats Wichmann <mats@linux.com>
Fri, 1 Sep 2017 20:57:21 +0000 (20:57 +0000)
Also a small refactor in the ios sconscript

The logic parses text from querying xcodebuild to find the latest sdk
version supported by the host, but it seems the content of this text has
changed over time, so we have to recognize a couple of tags for the Mac.

The original version had a flaw: the script can be called with
SYS_VERSION set to indicate a minimum version, but then it checks if an
SDK path with that version is installed, which might not be the case.
e.g.: SYS_VERSION=10.10, but the only installed SDK is for 10.12.
Calling with "-mmacosx-version-min=10.10" is fine, but checking if
.../Platforms/MacOSX.platform/Developer/SDKS/MacOSX/x86_64/10.10 exists
as a condition is not.  This has been adjusted by checking if a requested
version exists in the list of detected versions, and bumping the requested
version up to a found one, unless the found ones are actually lowere
than the requested.  It still seems awfully convoluted, but at least
works now on a 10.12 system without breaking the build in use for
IoTivity Jenkins.

Change-Id: I0e19893de52393789d23dcecc8d167d2236d4674
Signed-off-by: Mats Wichmann <mats@linux.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/21475
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
build_common/darwin/SConscript
build_common/ios/SConscript

index 383a208..795cb63 100644 (file)
@@ -1,11 +1,11 @@
 # -*- mode: python; python-indent-offset: 4; indent-tabs-mode: nil -*-
 ##
 # This script sets darwin specific flags (GNU GCC)
+# Note this script is also called from the iOS SConscript, handle both
 #
 ##
 import os
-import platform
-import commands
+import subprocess
 from distutils.version import StrictVersion
 
 Import('env')
@@ -13,47 +13,97 @@ Import('env')
 target_arch = env.get('TARGET_ARCH')
 target_os = env.get('TARGET_OS')
 
-tc_path = commands.getoutput('xcode-select -p')
+# Locate an appropriate SDK to use for the iotivity build.
+# This is a bit fragile, we are parsing text output of a command,
+# output not guaranteed not to change (has already happened)
+# These two commands are used to query the xcode environment:
+tc_path = subprocess.check_output('xcode-select -p', shell=True).rstrip()
+tc_sdks = subprocess.check_output('xcodebuild -showsdks', shell=True)
 
-tc_sdks = commands.getoutput('xcodebuild -showsdks')
-
-# Find the SDK's that are installed
-sdks = []
+# 'xcodebuild -showsdks'  returns information formatted as:
+#
+# SDK category:
+#        target version -sdk sdktag
+#
+# For example:
+# macOS SDKs:
+#        macOS 10.12                     -sdk macosx10.12
+#
+# collect installed sdks for each target we might care about
+mac_sdks = []
+ios_sdks = []
+sim_sdks = []
+collecting = False
 for line in tc_sdks.split('\n'):
-    if (line == ''):
-        bIn = False
-    if (line[:10] == 'OS X SDKs:'):
-        bIn = (target_os == 'darwin')
-    elif (line[:9] == 'iOS SDKs:'):
-        bIn = (target_os == 'ios')
-    elif bIn:
-        sdks.append(line[:14].strip())
+    if collecting:
+        if "-sdk" in line:
+            # drop the trailing "-sdk sdktag
+            sdks.append(line.split('-sdk')[0].strip())
+        else:  # blank line signals end of this list
+            collecting = False
+    elif "SDKs:" in line:  # the start of a new list of sdks
+        collecting = True
+        if line.startswith("macOS SDKs:") or line.startswith("OS X SDKs:"):
+            sdks = mac_sdks
+        elif line.startswith("iOS SDKs:"):
+            sdks = ios_sdks
+        elif line.startswith("iOS Simulator SDKs:"):
+            sdks = sim_sdks
+        else:
+            collecting = False  # not a caregory we care about, so skip
 
-# find the latest sdk
+# now pick the list for the requested target
+if target_os == 'darwin':
+    sdks = mac_sdks
+elif target_os == 'ios':
+    if target_arch in ['i386', 'x86_64']:  # Simulator
+        sdks = sim_sdks
+    else:
+        sdks = ios_sdks
+else:  # no reason for this to happen, but for completeness
+    sdks = None
+
+if not sdks:
+    msg = "\nError: no usable SDK for %s found\n" % target_os
+    msg += "mac sdks found: " + str(mac_sdks)
+    msg += "ios sdks found: " + str(ios_sdks)
+    msg += "simulator sdks found: " + str(sim_sdks)
+    Exit(msg)
+
+# find the latest installed sdk
+# to help compare strings containing version, use distutils.StrictVersion
 maxsdk = '0.0'
-if len(sdks) > 0:
-    for sdk in sdks:
-        p = sdk.rsplit(' ', 1)[1]
-        if (StrictVersion(p)) > StrictVersion(maxsdk):
-            maxsdk = p
+for sdk in sdks:
+    # want just the version code, which is now last word of the line
+    p = sdk.rsplit(' ', 1)[1]
+    if StrictVersion(p) > StrictVersion(maxsdk):
+        maxsdk = p
 
 # SYS_VERSION build option
 help_vars = Variables()
 help_vars.Add('SYS_VERSION',
-              'MAC OS X SDK version / IOS SDK version',
+              'minimum MAC OS X SDK version / IOS SDK version',
               os.environ.get('SYS_VERSION'))
 help_vars.Update(env)
 Help(help_vars.GenerateHelpText(env))
 
+# Figure out which SDK version to build for:
+# 1. if no version requested, use the largest auto-detected one
+# 2. if version requested and it is too large, bail with error
+# 3. if version requested is not installed, fall back to auto-detected max.
+#    cannot just use as minimum (e.g. -mmacosx-version-min=), since
+#    version also used in building sysroot path, which must exist
 sys_version = env.get('SYS_VERSION')
-
-# if they didn't explictly set it use the auto-detected one
 if sys_version is None:
     sys_version = maxsdk
+elif StrictVersion(sys_version) > StrictVersion(maxsdk):
+    msg = "\nError: target version %s cannot be satisfied\n" % sys_version
+    msg += "max installed SDK version is %s\n" % maxsdk
+    Exit(msg)
+elif sys_version not in sdks:
+    sys_version = maxsdk
+env['SYS_VERSION'] = sys_version  # for the benefit of ../ios/SConscript
 
-env['SYS_VERSION'] = sys_version
-
-# Set release/debug flags
 if env.get('RELEASE'):
     env.AppendUnique(CCFLAGS=['-Os'])
 else:
@@ -63,15 +113,19 @@ else:
 if env.get('SECURED') == '1':
     env.AppendUnique(LIBS=['mbedtls', 'mbedx509', 'mbedcrypto'])
 
+sys_root = tc_path
+tmpl = '/Platforms/%s.platform/Developer/SDKs/%s%s.sdk/'
 if target_os == 'darwin':
-    sys_root = tc_path + '/Platforms/MacOSX.platform/Developer/SDKs/MacOSX' + sys_version + '.sdk/'
+    sys_root += tmpl % ('MacOSX', 'MacOSX', sys_version)
 else:
     if target_arch in ['i386', 'x86_64']:
-        sys_root = tc_path + '/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator' + sys_version + '.sdk/'
+        sys_root += tmpl % ('iPhoneSimulator', 'iPhoneSimulator', sys_version)
     else:
-        sys_root = tc_path + '/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS' + sys_version + '.sdk/'
+        sys_root += tmpl % ('iPhone', 'iPhone', sys_version)
+if not os.path.exists(sys_root):
+    msg = "\nError: SDK directory not found at %s\n" % sys_root
+    Exit(msg)
 
-# Set arch flags
 env.AppendUnique(CCFLAGS=['-arch', target_arch, '-isysroot', sys_root])
 env.AppendUnique(LINKFLAGS=['-arch', target_arch, '-isysroot', sys_root])
 
index 6455f04..2b9b736 100644 (file)
@@ -10,9 +10,7 @@ env.SConscript('../darwin/SConscript')
 sys_version = env.get('SYS_VERSION')
 if env.get('TARGET_ARCH') in ['i386', 'x86_64']:  # Simulator
     flag = '-mios-simulator-version-min=' + sys_version
-    env.AppendUnique(CCFLAGS=[flag])
-    env.AppendUnique(LINKFLAGS=[flag])
 else:
     flag = '-miphoneos-version-min=' + sys_version
-    env.AppendUnique(CCFLAGS=[flag])
-    env.AppendUnique(LINKFLAGS=[flag])
+env.AppendUnique(CCFLAGS=[flag])
+env.AppendUnique(LINKFLAGS=[flag])