Skip to content

Commit be3abb6

Browse files
committed
APPLE: Make codesigning improvements
1 parent aa9efa2 commit be3abb6

2 files changed

Lines changed: 161 additions & 33 deletions

File tree

build_scripts/apple_utils.py

Lines changed: 154 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
import sys
1616
import locale
1717
import os
18+
import re
1819
import platform
1920
import shlex
2021
import subprocess
21-
from typing import Optional, List
22+
from typing import Optional, List, Dict
2223

2324
TARGET_NATIVE = "native"
2425
TARGET_X86 = "x86_64"
@@ -115,10 +116,8 @@ def GetTargetArchPair(context):
115116
def SupportsMacOSUniversalBinaries():
116117
if not MacOS():
117118
return False
118-
XcodeOutput = GetCommandOutput(["/usr/bin/xcodebuild", "-version"])
119-
XcodeFind = XcodeOutput.rfind('Xcode ', 0, len(XcodeOutput))
120-
XcodeVersion = XcodeOutput[XcodeFind:].split(' ')[1]
121-
return (XcodeVersion > '11.0')
119+
XcodeVersion = GetXcodeVersion()[0]
120+
return (XcodeVersion > 11)
122121

123122
def GetSDKRoot(context) -> Optional[str]:
124123
sdk = "macosx"
@@ -163,36 +162,162 @@ def ExtractFilesRecursive(path, cond):
163162
files.append(os.path.join(r, file))
164163
return files
165164

166-
def CodesignFiles(files):
167-
SDKVersion = subprocess.check_output(
168-
['xcodebuild', '-version']).strip()[6:10]
169-
codeSignIDs = subprocess.check_output(
170-
['security', 'find-identity', '-vp', 'codesigning'])
171-
172-
codeSignID = "-"
173-
if os.environ.get('CODE_SIGN_ID'):
174-
codeSignID = os.environ.get('CODE_SIGN_ID')
175-
elif float(SDKVersion) >= 11.0 and \
176-
codeSignIDs.find(b'Apple Development') != -1:
177-
codeSignID = "Apple Development"
178-
elif codeSignIDs.find(b'Mac Developer') != -1:
179-
codeSignID = "Mac Developer"
180-
181-
for f in files:
182-
subprocess.call(['codesign', '-f', '-s', '{codesignid}'
183-
.format(codesignid=codeSignID), f],
184-
stdout=devout, stderr=devout)
185-
186-
def Codesign(install_path, verbose_output=False):
165+
def _GetCodeSignStringFromTerminal():
166+
"""Return the output from the string codesigning variables"""
167+
codeSignIDs = GetCommandOutput(['security', 'find-identity', '-vp', 'codesigning'])
168+
return codeSignIDs
169+
170+
171+
def GetXcodeVersion():
172+
output = GetCommandOutput(['xcodebuild', '-version']).split()
173+
version = float(output[1])
174+
build = output[-1]
175+
176+
return version, build
177+
178+
179+
def GetCodeSigningIdentifiers() -> Dict[str, str]:
180+
"""Returns a dictionary of codesigning identifiers and their hashes"""
181+
XcodeVersion = GetXcodeVersion()[0]
182+
codeSignIDs = _GetCodeSignStringFromTerminal()
183+
184+
if not codeSignIDs:
185+
return {"-": None}
186+
187+
identifiers = {}
188+
for codeSignID in codeSignIDs.splitlines():
189+
if "CSSMERR_TP_CERT_REVOKED" in codeSignID:
190+
continue
191+
if ")" not in codeSignID:
192+
continue
193+
if (XcodeVersion >= 11 and "Apple Development" in codeSignID) or "Mac Developer" in codeSignID:
194+
identifier = codeSignID.split()[1]
195+
identifier_hash = re.search(r'\(.*?\)', codeSignID)
196+
if identifier_hash:
197+
identifier_hash = identifier_hash[0][1:-1]
198+
else:
199+
identifier_hash = None
200+
201+
identifiers[identifier] = identifier_hash
202+
203+
if not identifiers:
204+
raise RuntimeError("Could not find a valid codesigning ID. Try re-logging into your Xcode developer account.")
205+
206+
return identifiers
207+
208+
209+
def GetCodeSignID() -> str:
210+
"""Return the first code signing identifier"""
211+
identifiers = GetCodeSigningIdentifiers()
212+
env_signing_id = os.environ.get('CODE_SIGN_ID')
213+
if env_signing_id:
214+
if env_signing_id in identifiers:
215+
return env_signing_id
216+
raise RuntimeError(
217+
f"Could not find environment specified identifier {env_signing_id} in registered code signing identifiers")
218+
219+
return list(GetCodeSigningIdentifiers().keys())[0]
220+
221+
222+
def GetDevelopmentTeamID(identifier=None):
223+
if os.environ.get("DEVELOPMENT_TEAM"):
224+
return os.environ.get("DEVELOPMENT_TEAM")
225+
226+
if not identifier:
227+
identifier = GetCodeSignID()
228+
229+
identifier_hash = GetCodeSigningIdentifiers().get(identifier)
230+
if not identifier_hash:
231+
raise RuntimeError("Could not get identifiers hash")
232+
233+
certs = subprocess.check_output(["security", "find-certificate", "-c", identifier_hash, "-p"])
234+
subject = GetCommandOutput(["openssl", "x509", "-subject"], input=certs)
235+
subject = subject.splitlines()[0]
236+
match = re.search("OU\s*=\s*(?P<team>([A-Za-z0-9_])+)", subject)
237+
if not match:
238+
raise RuntimeError("Could not parse the output certificate to find the team ID")
239+
240+
groups = match.groupdict()
241+
team = groups.get("team")
242+
243+
if not team:
244+
raise RuntimeError("Could not extract team id from certificate")
245+
246+
return team
247+
248+
249+
def CodesignPath(path, identifier, team_identifier, force=False) -> bool:
250+
resign = force
251+
if not force:
252+
codesigning_info = GetCommandOutput(["codesign", "-vd", path])
253+
if not codesigning_info:
254+
resign = True
255+
else:
256+
for line in codesigning_info.splitlines():
257+
if line.startswith("TeamIdentifier="):
258+
current_team_identifier = line.split("=")[-1]
259+
if not team_identifier and "not set" in team_identifier:
260+
break
261+
elif team_identifier == current_team_identifier:
262+
break
263+
else:
264+
resign = True
265+
266+
if not resign:
267+
return False
268+
269+
# Frameworks need to be signed with different parameters than loose binaries
270+
if path.endswith(".framework"):
271+
subprocess.check_output(
272+
["codesign", "--force", "--sign", identifier, "--generate-entitlement-der", "--verbose", path])
273+
else:
274+
subprocess.check_call(["codesign", "--force", "--sign", identifier, path], stdout=devout, stderr=devout)
275+
return True
276+
277+
278+
def Codesign(install_path, identifier=None, force=False, verbose_output=False) -> bool:
187279
if not MacOS():
188280
return False
281+
282+
codeSignID = identifier or GetCodeSignID()
283+
189284
if verbose_output:
190285
global devout
191286
devout = sys.stdout
287+
print(f"Code-signing files in {install_path} with {identifier}", file=devout)
192288

193-
files = ExtractFilesRecursive(install_path,
194-
(lambda file: '.so' in file or '.dylib' in file))
195-
CodesignFiles(files)
289+
try:
290+
team_identifier = GetDevelopmentTeamID(codeSignID)
291+
except:
292+
team_identifier = None
293+
294+
for root, dirs, files in os.walk(install_path, topdown=True):
295+
for f in files:
296+
297+
_, ext = os.path.splitext(f)
298+
if ext in (".dylib", ".so"):
299+
path = os.path.join(root, f)
300+
result = CodesignPath(path, identifier, team_identifier=team_identifier, force=force)
301+
if verbose_output:
302+
if result:
303+
print(f"Code-signed binary: {path}")
304+
else:
305+
print(f"Did not code-sign binary: {path}")
306+
307+
# Bit annoying to have to do this twice, but seems the fastest way to skip traversing frameworks
308+
frameworks = [d for d in dirs if d.endswith(".framework")]
309+
dirs[:] = [d for d in dirs if not d.endswith(".framework")]
310+
311+
for framework in frameworks:
312+
path = os.path.join(root, framework)
313+
result = CodesignPath(path, identifier, team_identifier=team_identifier, force=force)
314+
if verbose_output:
315+
if result:
316+
print(f"Code-signed framework: {path}")
317+
else:
318+
print(f"Did not code-sign framework: {path}")
319+
320+
return True
196321

197322
def CreateUniversalBinaries(context, libNames, x86Dir, armDir):
198323
if not MacOS():

build_scripts/build_usd.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,6 +2002,7 @@ def InstallUSD(context, force, buildArgs):
20022002
default=codesignDefault, action="store_true",
20032003
help=("Enable code signing for macOS builds "
20042004
"(defaults to enabled on Apple Silicon)"))
2005+
group.add_argument("--codesign-id", dest="macos_codesign_id", type=str)
20052006

20062007
if Linux():
20072008
group.add_argument("--use-cxx11-abi", type=int, choices=[0, 1],
@@ -2310,9 +2311,9 @@ def __init__(self, args):
23102311
self.buildTarget = args.build_target
23112312
apple_utils.SetTarget(self, self.buildTarget)
23122313

2313-
self.macOSCodesign = \
2314-
(args.macos_codesign if hasattr(args, "macos_codesign")
2315-
else False)
2314+
self.macOSCodesign = False
2315+
if args.macos_codesign:
2316+
self.macOSCodesign = args.macos_codesign_id or apple_utils.GetCodeSignID()
23162317
if apple_utils.IsHostArm() and args.ignore_homebrew:
23172318
self.ignorePaths.append("/opt/homebrew")
23182319
else:
@@ -2843,7 +2844,9 @@ def FormatBuildArguments(buildArgs):
28432844

28442845
if MacOS():
28452846
if context.macOSCodesign:
2846-
apple_utils.Codesign(context.usdInstDir, verbosity > 1)
2847+
apple_utils.Codesign(context.usdInstDir,
2848+
identifier=context.macOSCodesign,
2849+
verbose_output=verbosity > 1)
28472850

28482851
additionalInstructions = any([context.buildPython, context.buildTools, context.buildPrman])
28492852
if additionalInstructions:

0 commit comments

Comments
 (0)