3535import platform
3636import shlex
3737import subprocess
38+ import re
39+ from typing import Optional , List
3840
3941TARGET_NATIVE = "native"
4042TARGET_X86 = "x86_64"
4143TARGET_ARM64 = "arm64"
4244TARGET_UNIVERSAL = "universal"
45+ TARGET_IOS = "ios"
46+
47+ EMBEDDED_PLATFORMS = [TARGET_IOS ]
4348
4449def GetBuildTargets ():
4550 return [TARGET_NATIVE ,
4651 TARGET_X86 ,
4752 TARGET_ARM64 ,
48- TARGET_UNIVERSAL ]
53+ TARGET_UNIVERSAL ,
54+ TARGET_IOS ]
4955
5056def GetBuildTargetDefault ():
51- return TARGET_NATIVE ;
57+ return TARGET_NATIVE
5258
5359def MacOS ():
5460 return platform .system () == "Darwin"
5561
5662def GetLocale ():
5763 return sys .stdout .encoding or locale .getdefaultlocale ()[1 ] or "UTF-8"
5864
59- def GetCommandOutput (command ):
60- """Executes the specified command and returns output or None."""
65+ def GetCommandOutput (command , ** kwargs ):
6166 try :
62- return subprocess .check_output (
63- shlex .split (command ),
64- stderr = subprocess .STDOUT ).decode (GetLocale (), 'replace' ).strip ()
65- except subprocess .CalledProcessError :
66- pass
67- return None
67+ return subprocess .check_output (command , stderr = subprocess .STDOUT , ** kwargs ).decode (GetLocale (), 'replace' ).strip ()
68+ except :
69+ return None
6870
6971def GetTargetArmArch ():
7072 # Allows the arm architecture string to be overridden by
7173 # setting MACOS_ARM_ARCHITECTURE
7274 return os .environ .get ('MACOS_ARM_ARCHITECTURE' ) or TARGET_ARM64
7375
7476def GetHostArch ():
75- macArch = GetCommandOutput (' arch' ). strip ( )
77+ macArch = GetCommandOutput ([ " arch" ] )
7678 if macArch == "i386" or macArch == TARGET_X86 :
7779 macArch = TARGET_X86
7880 else :
@@ -85,7 +87,7 @@ def GetTargetArch(context):
8587 else :
8688 if context .targetX86 :
8789 macTargets = TARGET_X86
88- if context .targetARM64 :
90+ if context .targetARM64 or context . targetIos :
8991 macTargets = GetTargetArmArch ()
9092 if context .targetUniversal :
9193 macTargets = TARGET_X86 + ";" + GetTargetArmArch ()
@@ -106,6 +108,8 @@ def GetTargetArchPair(context):
106108 primaryArch = TARGET_X86
107109 if context .targetARM64 :
108110 primaryArch = GetTargetArmArch ()
111+ if context .targetIos :
112+ primaryArch = TARGET_IOS
109113 if context .targetUniversal :
110114 primaryArch = GetHostArch ()
111115 if (primaryArch == TARGET_X86 ):
@@ -118,18 +122,52 @@ def GetTargetArchPair(context):
118122def SupportsMacOSUniversalBinaries ():
119123 if not MacOS ():
120124 return False
121- XcodeOutput = GetCommandOutput (' /usr/bin/xcodebuild -version' )
125+ XcodeOutput = GetCommandOutput ([ " /usr/bin/xcodebuild" , " -version" ] )
122126 XcodeFind = XcodeOutput .rfind ('Xcode ' , 0 , len (XcodeOutput ))
123127 XcodeVersion = XcodeOutput [XcodeFind :].split (' ' )[1 ]
124128 return (XcodeVersion > '11.0' )
125129
130+
131+ def GetSDKVersion (sdk : str ) -> Optional [str ]:
132+ """Gets the SDK version from the currently active Xcode"""
133+ sdks = GetCommandOutput (["xcodebuild" , "-showsdks" ]).split (os .linesep )
134+ for line in sdks :
135+ line = line .strip ()
136+ if sdk not in line or "-sdk" not in line :
137+ continue
138+
139+ line = line .split ("-sdk " )[- 1 ]
140+ if not line .startswith (sdk ):
141+ continue
142+
143+ tokens = [t for t in line .replace (sdk , "" ).split ("." ) if t .isnumeric ()]
144+ if not tokens :
145+ continue
146+ return "." .join (tokens )
147+ return None
148+
149+ def GetSDKRoot (context ) -> Optional [str ]:
150+ sdk = "macosx"
151+ if context .buildTarget == TARGET_IOS :
152+ sdk = "iphoneos"
153+
154+ for arg in (context .cmakeBuildArgs or '' ).split ():
155+ if "CMAKE_OSX_SYSROOT" in arg :
156+ override = arg .split ('=' )[1 ].strip ('"' ).strip ()
157+ if override :
158+ sdk = override
159+ return GetCommandOutput (["xcrun" , "--sdk" , sdk , "--show-sdk-path" ])
160+
161+
126162def SetTarget (context , targetName ):
127163 context .targetNative = (targetName == TARGET_NATIVE )
128164 context .targetX86 = (targetName == TARGET_X86 )
129165 context .targetARM64 = (targetName == GetTargetArmArch ())
130166 context .targetUniversal = (targetName == TARGET_UNIVERSAL )
167+ context .targetIos = (targetName == TARGET_IOS )
168+ context .iosVersion = GetSDKVersion ("iphoneos" ) if context .targetIos else None
131169 if context .targetUniversal and not SupportsMacOSUniversalBinaries ():
132- self .targetUniversal = False
170+ context .targetUniversal = False
133171 raise ValueError (
134172 "Universal binaries only supported in macOS 11.0 and later." )
135173
@@ -150,26 +188,63 @@ def ExtractFilesRecursive(path, cond):
150188 files .append (os .path .join (r , file ))
151189 return files
152190
153- def CodesignFiles (files ):
154- SDKVersion = subprocess .check_output (
155- ['xcodebuild' , '-version' ]).strip ()[6 :10 ]
156- codeSignIDs = subprocess .check_output (
157- ['security' , 'find-identity' , '-vp' , 'codesigning' ])
191+ def _GetCodeSignStringFromTerminal ():
192+ codeSignIDs = GetCommandOutput (['security' , 'find-identity' , '-vp' , 'codesigning' ])
193+ return codeSignIDs
158194
159- codeSignID = "-"
195+ def GetCodeSignID ():
160196 if os .environ .get ('CODE_SIGN_ID' ):
161- codeSignID = os .environ .get ('CODE_SIGN_ID' )
162- elif float (SDKVersion ) >= 11.0 and \
163- codeSignIDs .find (b'Apple Development' ) != - 1 :
164- codeSignID = "Apple Development"
165- elif codeSignIDs .find (b'Mac Developer' ) != - 1 :
166- codeSignID = "Mac Developer"
197+ return os .environ .get ('CODE_SIGN_ID' )
198+
199+ codeSignIDs = _GetCodeSignStringFromTerminal ()
200+ if not codeSignIDs :
201+ return "-"
202+ for codeSignID in codeSignIDs .splitlines ():
203+ if "CSSMERR_TP_CERT_REVOKED" in codeSignID :
204+ continue
205+ if ")" not in codeSignID :
206+ continue
207+ codeSignID = codeSignID .split ()[1 ]
208+ break
209+ else :
210+ raise RuntimeError ("Could not find a valid codesigning ID" )
211+
212+ return codeSignID or "-"
213+
214+ def GetCodeSignIDHash ():
215+ codeSignIDs = _GetCodeSignStringFromTerminal ()
216+ try :
217+ return re .findall (r'\(.*?\)' , codeSignIDs )[0 ][1 :- 1 ]
218+ except :
219+ raise Exception ("Unable to parse codesign ID hash" )
220+
221+ def GetDevelopmentTeamID ():
222+ if os .environ .get ("DEVELOPMENT_TEAM" ):
223+ return os .environ .get ("DEVELOPMENT_TEAM" )
224+ codesignID = GetCodeSignIDHash ()
225+
226+ certs = subprocess .check_output (["security" , "find-certificate" , "-c" , codesignID , "-p" ])
227+ subject = GetCommandOutput (["openssl" , "x509" , "-subject" ], input = certs )
228+ subject = subject .splitlines ()[0 ]
229+
230+ # Extract the Organizational Unit (OU field) from the cert
231+ try :
232+ team = [elm for elm in subject .split (
233+ '/' ) if elm .startswith ('OU' )][0 ].split ('=' )[1 ]
234+ if team is not None and team != "" :
235+ return team
236+ except Exception as ex :
237+ raise Exception ("No development team found with exception " + ex )
238+
239+ def CodesignFiles (files ):
240+ codeSignID = GetCodeSignID ()
167241
168242 for f in files :
169243 subprocess .call (['codesign' , '-f' , '-s' , '{codesignid}'
170- .format (codesignid = codeSignID ), f ],
244+ .format (codesignid = codeSignID ), f ],
171245 stdout = devout , stderr = devout )
172246
247+
173248def Codesign (install_path , verbose_output = False ):
174249 if not MacOS ():
175250 return False
@@ -217,3 +292,12 @@ def CreateUniversalBinaries(context, libNames, x86Dir, armDir):
217292 instDir = context .instDir , libName = targetName ),
218293 outputName )
219294 return lipoCommands
295+
296+ def ConfigureCMakeExtraArgs (context , args :List [str ]) -> List [str ]:
297+ system_name = None
298+ if context .buildTarget == TARGET_IOS :
299+ system_name = "iOS"
300+
301+ if system_name :
302+ args .append (f"-DCMAKE_SYSTEM_NAME={ system_name } " )
303+ return args
0 commit comments