codekingpro/portable-devtools
114k
1# Copyright (c) 2012 Google Inc. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6This module contains classes that help to emulate xcodebuild behavior on top of
7other build systems, such as make and ninja.
8"""
9
10import copy
11import os
12import os.path
13import re
14import shlex
15import subprocess
16import sys
17
18import gyp.common
19from gyp.common import GypError
20
21# Populated lazily by XcodeVersion, for efficiency, and to fix an issue when
22# "xcodebuild" is called too quickly (it has been found to return incorrect
23# version number).
24XCODE_VERSION_CACHE = None
25
26# Populated lazily by GetXcodeArchsDefault, to an |XcodeArchsDefault| instance
27# corresponding to the installed version of Xcode.
28XCODE_ARCHS_DEFAULT_CACHE = None
29
30
31def XcodeArchsVariableMapping(archs, archs_including_64_bit=None):
32 """Constructs a dictionary with expansion for $(ARCHS_STANDARD) variable,
33 and optionally for $(ARCHS_STANDARD_INCLUDING_64_BIT)."""
34 mapping = {"$(ARCHS_STANDARD)": archs}
35 if archs_including_64_bit:
36 mapping["$(ARCHS_STANDARD_INCLUDING_64_BIT)"] = archs_including_64_bit
37 return mapping
38
39
40class XcodeArchsDefault:
41 """A class to resolve ARCHS variable from xcode_settings, resolving Xcode
42 macros and implementing filtering by VALID_ARCHS. The expansion of macros
43 depends on the SDKROOT used ("macosx", "iphoneos", "iphonesimulator") and
44 on the version of Xcode.
45 """
46
47 # Match variable like $(ARCHS_STANDARD).
48 variable_pattern = re.compile(r"\$\([a-zA-Z_][a-zA-Z0-9_]*\)$")
49
50 def __init__(self, default, mac, iphonesimulator, iphoneos):
51 self._default = (default,)
52 self._archs = {"mac": mac, "ios": iphoneos, "iossim": iphonesimulator}
53
54 def _VariableMapping(self, sdkroot):
55 """Returns the dictionary of variable mapping depending on the SDKROOT."""
56 sdkroot = sdkroot.lower()
57 if "iphoneos" in sdkroot:
58 return self._archs["ios"]
59 elif "iphonesimulator" in sdkroot:
60 return self._archs["iossim"]
61 else:
62 return self._archs["mac"]
63
64 def _ExpandArchs(self, archs, sdkroot):
65 """Expands variables references in ARCHS, and remove duplicates."""
66 variable_mapping = self._VariableMapping(sdkroot)
67 expanded_archs = []
68 for arch in archs:
69 if self.variable_pattern.match(arch):
70 variable = arch
71 try:
72 variable_expansion = variable_mapping[variable]
73 for arch in variable_expansion:
74 if arch not in expanded_archs:
75 expanded_archs.append(arch)
76 except KeyError:
77 print('Warning: Ignoring unsupported variable "%s".' % variable)
78 elif arch not in expanded_archs:
79 expanded_archs.append(arch)
80 return expanded_archs
81
82 def ActiveArchs(self, archs, valid_archs, sdkroot):
83 """Expands variables references in ARCHS, and filter by VALID_ARCHS if it
84 is defined (if not set, Xcode accept any value in ARCHS, otherwise, only
85 values present in VALID_ARCHS are kept)."""
86 expanded_archs = self._ExpandArchs(archs or self._default, sdkroot or "")
87 if valid_archs:
88 filtered_archs = []
89 for arch in expanded_archs:
90 if arch in valid_archs:
91 filtered_archs.append(arch)
92 expanded_archs = filtered_archs
93 return expanded_archs
94
95
96def GetXcodeArchsDefault():
97 """Returns the |XcodeArchsDefault| object to use to expand ARCHS for the
98 installed version of Xcode. The default values used by Xcode for ARCHS
99 and the expansion of the variables depends on the version of Xcode used.
100
101 For all version anterior to Xcode 5.0 or posterior to Xcode 5.1 included
102 uses $(ARCHS_STANDARD) if ARCHS is unset, while Xcode 5.0 to 5.0.2 uses
103 $(ARCHS_STANDARD_INCLUDING_64_BIT). This variable was added to Xcode 5.0
104 and deprecated with Xcode 5.1.
105
106 For "macosx" SDKROOT, all version starting with Xcode 5.0 includes 64-bit
107 architecture as part of $(ARCHS_STANDARD) and default to only building it.
108
109 For "iphoneos" and "iphonesimulator" SDKROOT, 64-bit architectures are part
110 of $(ARCHS_STANDARD_INCLUDING_64_BIT) from Xcode 5.0. From Xcode 5.1, they
111 are also part of $(ARCHS_STANDARD).
112
113 All these rules are coded in the construction of the |XcodeArchsDefault|
114 object to use depending on the version of Xcode detected. The object is
115 for performance reason."""
116 global XCODE_ARCHS_DEFAULT_CACHE
117 if XCODE_ARCHS_DEFAULT_CACHE:
118 return XCODE_ARCHS_DEFAULT_CACHE
119 xcode_version, _ = XcodeVersion()
120 if xcode_version < "0500":
121 XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
122 "$(ARCHS_STANDARD)",
123 XcodeArchsVariableMapping(["i386"]),
124 XcodeArchsVariableMapping(["i386"]),
125 XcodeArchsVariableMapping(["armv7"]),
126 )
127 elif xcode_version < "0510":
128 XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
129 "$(ARCHS_STANDARD_INCLUDING_64_BIT)",
130 XcodeArchsVariableMapping(["x86_64"], ["x86_64"]),
131 XcodeArchsVariableMapping(["i386"], ["i386", "x86_64"]),
132 XcodeArchsVariableMapping(
133 ["armv7", "armv7s"], ["armv7", "armv7s", "arm64"]
134 ),
135 )
136 else:
137 XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
138 "$(ARCHS_STANDARD)",
139 XcodeArchsVariableMapping(["x86_64"], ["x86_64"]),
140 XcodeArchsVariableMapping(["i386", "x86_64"], ["i386", "x86_64"]),
141 XcodeArchsVariableMapping(
142 ["armv7", "armv7s", "arm64"], ["armv7", "armv7s", "arm64"]
143 ),
144 )
145 return XCODE_ARCHS_DEFAULT_CACHE
146
147
148class XcodeSettings:
149 """A class that understands the gyp 'xcode_settings' object."""
150
151 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached
152 # at class-level for efficiency.
153 _sdk_path_cache = {}
154 _platform_path_cache = {}
155 _sdk_root_cache = {}
156
157 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so
158 # cached at class-level for efficiency.
159 _plist_cache = {}
160
161 # Populated lazily by GetIOSPostbuilds. Shared by all XcodeSettings, so
162 # cached at class-level for efficiency.
163 _codesigning_key_cache = {}
164
165 def __init__(self, spec):
166 self.spec = spec
167
168 self.isIOS = False
169 self.mac_toolchain_dir = None
170 self.header_map_path = None
171
172 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp.
173 # This means self.xcode_settings[config] always contains all settings
174 # for that config -- the per-target settings as well. Settings that are
175 # the same for all configs are implicitly per-target settings.
176 self.xcode_settings = {}
177 configs = spec["configurations"]
178 for configname, config in configs.items():
179 self.xcode_settings[configname] = config.get("xcode_settings", {})
180 self._ConvertConditionalKeys(configname)
181 if self.xcode_settings[configname].get("IPHONEOS_DEPLOYMENT_TARGET", None):
182 self.isIOS = True
183
184 # This is only non-None temporarily during the execution of some methods.
185 self.configname = None
186
187 # Used by _AdjustLibrary to match .a and .dylib entries in libraries.
188 self.library_re = re.compile(r"^lib([^/]+)\.(a|dylib)$")
189
190 def _ConvertConditionalKeys(self, configname):
191 """Converts or warns on conditional keys. Xcode supports conditional keys,
192 such as CODE_SIGN_IDENTITY[sdk=iphoneos*]. This is a partial implementation
193 with some keys converted while the rest force a warning."""
194 settings = self.xcode_settings[configname]
195 conditional_keys = [key for key in settings if key.endswith("]")]
196 for key in conditional_keys:
197 # If you need more, speak up at http://crbug.com/122592
198 if key.endswith("[sdk=iphoneos*]"):
199 if configname.endswith("iphoneos"):
200 new_key = key.split("[")[0]
201 settings[new_key] = settings[key]
202 else:
203 print(
204 "Warning: Conditional keys not implemented, ignoring:",
205 " ".join(conditional_keys),
206 )
207 del settings[key]
208
209 def _Settings(self):
210 assert self.configname
211 return self.xcode_settings[self.configname]
212
213 def _Test(self, test_key, cond_key, default):
214 return self._Settings().get(test_key, default) == cond_key
215
216 def _Appendf(self, lst, test_key, format_str, default=None):
217 if test_key in self._Settings():
218 lst.append(format_str % str(self._Settings()[test_key]))
219 elif default:
220 lst.append(format_str % str(default))
221
222 def _WarnUnimplemented(self, test_key):
223 if test_key in self._Settings():
224 print('Warning: Ignoring not yet implemented key "%s".' % test_key)
225
226 def IsBinaryOutputFormat(self, configname):
227 default = "binary" if self.isIOS else "xml"
228 format = self.xcode_settings[configname].get("INFOPLIST_OUTPUT_FORMAT", default)
229 return format == "binary"
230
231 def IsIosFramework(self):
232 return self.spec["type"] == "shared_library" and self._IsBundle() and self.isIOS
233
234 def _IsBundle(self):
235 return (
236 int(self.spec.get("mac_bundle", 0)) != 0
237 or self._IsXCTest()
238 or self._IsXCUiTest()
239 )
240
241 def _IsXCTest(self):
242 return int(self.spec.get("mac_xctest_bundle", 0)) != 0
243
244 def _IsXCUiTest(self):
245 return int(self.spec.get("mac_xcuitest_bundle", 0)) != 0
246
247 def _IsIosAppExtension(self):
248 return int(self.spec.get("ios_app_extension", 0)) != 0
249
250 def _IsIosWatchKitExtension(self):
251 return int(self.spec.get("ios_watchkit_extension", 0)) != 0
252
253 def _IsIosWatchApp(self):
254 return int(self.spec.get("ios_watch_app", 0)) != 0
255
256 def GetFrameworkVersion(self):
257 """Returns the framework version of the current target. Only valid for
258 bundles."""
259 assert self._IsBundle()
260 return self.GetPerTargetSetting("FRAMEWORK_VERSION", default="A")
261
262 def GetWrapperExtension(self):
263 """Returns the bundle extension (.app, .framework, .plugin, etc). Only
264 valid for bundles."""
265 assert self._IsBundle()
266 if self.spec["type"] in ("loadable_module", "shared_library"):
267 default_wrapper_extension = {
268 "loadable_module": "bundle",
269 "shared_library": "framework",
270 }[self.spec["type"]]
271 wrapper_extension = self.GetPerTargetSetting(
272 "WRAPPER_EXTENSION", default=default_wrapper_extension
273 )
274 return "." + self.spec.get("product_extension", wrapper_extension)
275 elif self.spec["type"] == "executable":
276 if self._IsIosAppExtension() or self._IsIosWatchKitExtension():
277 return "." + self.spec.get("product_extension", "appex")
278 else:
279 return "." + self.spec.get("product_extension", "app")
280 else:
281 assert False, "Don't know extension for '{}', target '{}'".format(
282 self.spec["type"],
283 self.spec["target_name"],
284 )
285
286 def GetProductName(self):
287 """Returns PRODUCT_NAME."""
288 return self.spec.get("product_name", self.spec["target_name"])
289
290 def GetFullProductName(self):
291 """Returns FULL_PRODUCT_NAME."""
292 if self._IsBundle():
293 return self.GetWrapperName()
294 else:
295 return self._GetStandaloneBinaryPath()
296
297 def GetWrapperName(self):
298 """Returns the directory name of the bundle represented by this target.
299 Only valid for bundles."""
300 assert self._IsBundle()
301 return self.GetProductName() + self.GetWrapperExtension()
302
303 def GetBundleContentsFolderPath(self):
304 """Returns the qualified path to the bundle's contents folder. E.g.
305 Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles."""
306 if self.isIOS:
307 return self.GetWrapperName()
308 assert self._IsBundle()
309 if self.spec["type"] == "shared_library":
310 return os.path.join(
311 self.GetWrapperName(), "Versions", self.GetFrameworkVersion()
312 )
313 else:
314 # loadable_modules have a 'Contents' folder like executables.
315 return os.path.join(self.GetWrapperName(), "Contents")
316
317 def GetBundleResourceFolder(self):
318 """Returns the qualified path to the bundle's resource folder. E.g.
319 Chromium.app/Contents/Resources. Only valid for bundles."""
320 assert self._IsBundle()
321 if self.isIOS:
322 return self.GetBundleContentsFolderPath()
323 return os.path.join(self.GetBundleContentsFolderPath(), "Resources")
324
325 def GetBundleExecutableFolderPath(self):
326 """Returns the qualified path to the bundle's executables folder. E.g.
327 Chromium.app/Contents/MacOS. Only valid for bundles."""
328 assert self._IsBundle()
329 if self.spec["type"] in ("shared_library") or self.isIOS:
330 return self.GetBundleContentsFolderPath()
331 elif self.spec["type"] in ("executable", "loadable_module"):
332 return os.path.join(self.GetBundleContentsFolderPath(), "MacOS")
333
334 def GetBundleJavaFolderPath(self):
335 """Returns the qualified path to the bundle's Java resource folder.
336 E.g. Chromium.app/Contents/Resources/Java. Only valid for bundles."""
337 assert self._IsBundle()
338 return os.path.join(self.GetBundleResourceFolder(), "Java")
339
340 def GetBundleFrameworksFolderPath(self):
341 """Returns the qualified path to the bundle's frameworks folder. E.g,
342 Chromium.app/Contents/Frameworks. Only valid for bundles."""
343 assert self._IsBundle()
344 return os.path.join(self.GetBundleContentsFolderPath(), "Frameworks")
345
346 def GetBundleSharedFrameworksFolderPath(self):
347 """Returns the qualified path to the bundle's frameworks folder. E.g,
348 Chromium.app/Contents/SharedFrameworks. Only valid for bundles."""
349 assert self._IsBundle()
350 return os.path.join(self.GetBundleContentsFolderPath(), "SharedFrameworks")
351
352 def GetBundleSharedSupportFolderPath(self):
353 """Returns the qualified path to the bundle's shared support folder. E.g,
354 Chromium.app/Contents/SharedSupport. Only valid for bundles."""
355 assert self._IsBundle()
356 if self.spec["type"] == "shared_library":
357 return self.GetBundleResourceFolder()
358 else:
359 return os.path.join(self.GetBundleContentsFolderPath(), "SharedSupport")
360
361 def GetBundlePlugInsFolderPath(self):
362 """Returns the qualified path to the bundle's plugins folder. E.g,
363 Chromium.app/Contents/PlugIns. Only valid for bundles."""
364 assert self._IsBundle()
365 return os.path.join(self.GetBundleContentsFolderPath(), "PlugIns")
366
367 def GetBundleXPCServicesFolderPath(self):
368 """Returns the qualified path to the bundle's XPC services folder. E.g,
369 Chromium.app/Contents/XPCServices. Only valid for bundles."""
370 assert self._IsBundle()
371 return os.path.join(self.GetBundleContentsFolderPath(), "XPCServices")
372
373 def GetBundlePlistPath(self):
374 """Returns the qualified path to the bundle's plist file. E.g.
375 Chromium.app/Contents/Info.plist. Only valid for bundles."""
376 assert self._IsBundle()
377 if (
378 self.spec["type"] in ("executable", "loadable_module")
379 or self.IsIosFramework()
380 ):
381 return os.path.join(self.GetBundleContentsFolderPath(), "Info.plist")
382 else:
383 return os.path.join(
384 self.GetBundleContentsFolderPath(), "Resources", "Info.plist"
385 )
386
387 def GetProductType(self):
388 """Returns the PRODUCT_TYPE of this target."""
389 if self._IsIosAppExtension():
390 assert self._IsBundle(), (
391 "ios_app_extension flag requires mac_bundle "
392 "(target %s)" % self.spec["target_name"]
393 )
394 return "com.apple.product-type.app-extension"
395 if self._IsIosWatchKitExtension():
396 assert self._IsBundle(), (
397 "ios_watchkit_extension flag requires "
398 "mac_bundle (target %s)" % self.spec["target_name"]
399 )
400 return "com.apple.product-type.watchkit-extension"
401 if self._IsIosWatchApp():
402 assert self._IsBundle(), (
403 "ios_watch_app flag requires mac_bundle "
404 "(target %s)" % self.spec["target_name"]
405 )
406 return "com.apple.product-type.application.watchapp"
407 if self._IsXCUiTest():
408 assert self._IsBundle(), (
409 "mac_xcuitest_bundle flag requires mac_bundle "
410 "(target %s)" % self.spec["target_name"]
411 )
412 return "com.apple.product-type.bundle.ui-testing"
413 if self._IsBundle():
414 return {
415 "executable": "com.apple.product-type.application",
416 "loadable_module": "com.apple.product-type.bundle",
417 "shared_library": "com.apple.product-type.framework",
418 }[self.spec["type"]]
419 else:
420 return {
421 "executable": "com.apple.product-type.tool",
422 "loadable_module": "com.apple.product-type.library.dynamic",
423 "shared_library": "com.apple.product-type.library.dynamic",
424 "static_library": "com.apple.product-type.library.static",
425 }[self.spec["type"]]
426
427 def GetMachOType(self):
428 """Returns the MACH_O_TYPE of this target."""
429 # Weird, but matches Xcode.
430 if not self._IsBundle() and self.spec["type"] == "executable":
431 return ""
432 return {
433 "executable": "mh_execute",
434 "static_library": "staticlib",
435 "shared_library": "mh_dylib",
436 "loadable_module": "mh_bundle",
437 }[self.spec["type"]]
438
439 def _GetBundleBinaryPath(self):
440 """Returns the name of the bundle binary of by this target.
441 E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles."""
442 assert self._IsBundle()
443 return os.path.join(
444 self.GetBundleExecutableFolderPath(), self.GetExecutableName()
445 )
446
447 def _GetStandaloneExecutableSuffix(self):
448 if "product_extension" in self.spec:
449 return "." + self.spec["product_extension"]
450 return {
451 "executable": "",
452 "static_library": ".a",
453 "shared_library": ".dylib",
454 "loadable_module": ".so",
455 }[self.spec["type"]]
456
457 def _GetStandaloneExecutablePrefix(self):
458 return self.spec.get(
459 "product_prefix",
460 {
461 "executable": "",
462 "static_library": "lib",
463 "shared_library": "lib",
464 # Non-bundled loadable_modules are called foo.so for some reason
465 # (that is, .so and no prefix) with the xcode build -- match that.
466 "loadable_module": "",
467 }[self.spec["type"]],
468 )
469
470 def _GetStandaloneBinaryPath(self):
471 """Returns the name of the non-bundle binary represented by this target.
472 E.g. hello_world. Only valid for non-bundles."""
473 assert not self._IsBundle()
474 assert self.spec["type"] in {
475 "executable",
476 "shared_library",
477 "static_library",
478 "loadable_module",
479 }, "Unexpected type %s" % self.spec["type"]
480 target = self.spec["target_name"]
481 if self.spec["type"] in {"loadable_module", "shared_library", "static_library"}:
482 if target[:3] == "lib":
483 target = target[3:]
484
485 target_prefix = self._GetStandaloneExecutablePrefix()
486 target = self.spec.get("product_name", target)
487 target_ext = self._GetStandaloneExecutableSuffix()
488 return target_prefix + target + target_ext
489
490 def GetExecutableName(self):
491 """Returns the executable name of the bundle represented by this target.
492 E.g. Chromium."""
493 if self._IsBundle():
494 return self.spec.get("product_name", self.spec["target_name"])
495 else:
496 return self._GetStandaloneBinaryPath()
497
498 def GetExecutablePath(self):
499 """Returns the qualified path to the primary executable of the bundle
500 represented by this target. E.g. Chromium.app/Contents/MacOS/Chromium."""
501 if self._IsBundle():
502 return self._GetBundleBinaryPath()
503 else:
504 return self._GetStandaloneBinaryPath()
505
506 def GetActiveArchs(self, configname):
507 """Returns the architectures this target should be built for."""
508 config_settings = self.xcode_settings[configname]
509 xcode_archs_default = GetXcodeArchsDefault()
510 return xcode_archs_default.ActiveArchs(
511 config_settings.get("ARCHS"),
512 config_settings.get("VALID_ARCHS"),
513 config_settings.get("SDKROOT"),
514 )
515
516 def _GetSdkVersionInfoItem(self, sdk, infoitem):
517 # xcodebuild requires Xcode and can't run on Command Line Tools-only
518 # systems from 10.7 onward.
519 # Since the CLT has no SDK paths anyway, returning None is the
520 # most sensible route and should still do the right thing.
521 try:
522 return GetStdoutQuiet(["xcrun", "--sdk", sdk, infoitem])
523 except (GypError, OSError):
524 pass
525
526 def _SdkRoot(self, configname):
527 if configname is None:
528 configname = self.configname
529 return self.GetPerConfigSetting("SDKROOT", configname, default="")
530
531 def _XcodePlatformPath(self, configname=None):
532 sdk_root = self._SdkRoot(configname)
533 if sdk_root not in XcodeSettings._platform_path_cache:
534 platform_path = self._GetSdkVersionInfoItem(
535 sdk_root, "--show-sdk-platform-path"
536 )
537 XcodeSettings._platform_path_cache[sdk_root] = platform_path
538 return XcodeSettings._platform_path_cache[sdk_root]
539
540 def _SdkPath(self, configname=None):
541 sdk_root = self._SdkRoot(configname)
542 if sdk_root.startswith("/"):
543 return sdk_root
544 return self._XcodeSdkPath(sdk_root)
545
546 def _XcodeSdkPath(self, sdk_root):
547 if sdk_root not in XcodeSettings._sdk_path_cache:
548 sdk_path = self._GetSdkVersionInfoItem(sdk_root, "--show-sdk-path")
549 XcodeSettings._sdk_path_cache[sdk_root] = sdk_path
550 if sdk_root:
551 XcodeSettings._sdk_root_cache[sdk_path] = sdk_root
552 return XcodeSettings._sdk_path_cache[sdk_root]
553
554 def _AppendPlatformVersionMinFlags(self, lst):
555 self._Appendf(lst, "MACOSX_DEPLOYMENT_TARGET", "-mmacosx-version-min=%s")
556 if "IPHONEOS_DEPLOYMENT_TARGET" in self._Settings():
557 # TODO: Implement this better?
558 sdk_path_basename = os.path.basename(self._SdkPath())
559 if sdk_path_basename.lower().startswith("iphonesimulator"):
560 self._Appendf(
561 lst, "IPHONEOS_DEPLOYMENT_TARGET", "-mios-simulator-version-min=%s"
562 )
563 else:
564 self._Appendf(
565 lst, "IPHONEOS_DEPLOYMENT_TARGET", "-miphoneos-version-min=%s"
566 )
567
568 def GetCflags(self, configname, arch=None):
569 """Returns flags that need to be added to .c, .cc, .m, and .mm
570 compilations."""
571 # This functions (and the similar ones below) do not offer complete
572 # emulation of all xcode_settings keys. They're implemented on demand.
573
574 self.configname = configname
575 cflags = []
576
577 sdk_root = self._SdkPath()
578 if "SDKROOT" in self._Settings() and sdk_root:
579 cflags.append("-isysroot")
580 cflags.append(sdk_root)
581
582 if self.header_map_path:
583 cflags.append("-I%s" % self.header_map_path)
584
585 if self._Test("CLANG_WARN_CONSTANT_CONVERSION", "YES", default="NO"):
586 cflags.append("-Wconstant-conversion")
587
588 if self._Test("GCC_CHAR_IS_UNSIGNED_CHAR", "YES", default="NO"):
589 cflags.append("-funsigned-char")
590
591 if self._Test("GCC_CW_ASM_SYNTAX", "YES", default="YES"):
592 cflags.append("-fasm-blocks")
593
594 if "GCC_DYNAMIC_NO_PIC" in self._Settings():
595 if self._Settings()["GCC_DYNAMIC_NO_PIC"] == "YES":
596 cflags.append("-mdynamic-no-pic")
597 else:
598 pass
599 # TODO: In this case, it depends on the target. xcode passes
600 # mdynamic-no-pic by default for executable and possibly static lib
601 # according to mento
602
603 if self._Test("GCC_ENABLE_PASCAL_STRINGS", "YES", default="YES"):
604 cflags.append("-mpascal-strings")
605
606 self._Appendf(cflags, "GCC_OPTIMIZATION_LEVEL", "-O%s", default="s")
607
608 if self._Test("GCC_GENERATE_DEBUGGING_SYMBOLS", "YES", default="YES"):
609 dbg_format = self._Settings().get("DEBUG_INFORMATION_FORMAT", "dwarf")
610 if dbg_format == "dwarf":
611 cflags.append("-gdwarf-2")
612 elif dbg_format == "stabs":
613 raise NotImplementedError("stabs debug format is not supported yet.")
614 elif dbg_format == "dwarf-with-dsym":
615 cflags.append("-gdwarf-2")
616 else:
617 raise NotImplementedError("Unknown debug format %s" % dbg_format)
618
619 if self._Settings().get("GCC_STRICT_ALIASING") == "YES":
620 cflags.append("-fstrict-aliasing")
621 elif self._Settings().get("GCC_STRICT_ALIASING") == "NO":
622 cflags.append("-fno-strict-aliasing")
623
624 if self._Test("GCC_SYMBOLS_PRIVATE_EXTERN", "YES", default="NO"):
625 cflags.append("-fvisibility=hidden")
626
627 if self._Test("GCC_TREAT_WARNINGS_AS_ERRORS", "YES", default="NO"):
628 cflags.append("-Werror")
629
630 if self._Test("GCC_WARN_ABOUT_MISSING_NEWLINE", "YES", default="NO"):
631 cflags.append("-Wnewline-eof")
632
633 # In Xcode, this is only activated when GCC_COMPILER_VERSION is clang or
634 # llvm-gcc. It also requires a fairly recent libtool, and
635 # if the system clang isn't used, DYLD_LIBRARY_PATH needs to contain the
636 # path to the libLTO.dylib that matches the used clang.
637 if self._Test("LLVM_LTO", "YES", default="NO"):
638 cflags.append("-flto")
639
640 self._AppendPlatformVersionMinFlags(cflags)
641
642 # TODO:
643 if self._Test("COPY_PHASE_STRIP", "YES", default="NO"):
644 self._WarnUnimplemented("COPY_PHASE_STRIP")
645 self._WarnUnimplemented("GCC_DEBUGGING_SYMBOLS")
646 self._WarnUnimplemented("GCC_ENABLE_OBJC_EXCEPTIONS")
647
648 # TODO: This is exported correctly, but assigning to it is not supported.
649 self._WarnUnimplemented("MACH_O_TYPE")
650 self._WarnUnimplemented("PRODUCT_TYPE")
651
652 # If GYP_CROSSCOMPILE (--cross-compiling), disable architecture-specific
653 # additions and assume these will be provided as required via CC_host,
654 # CXX_host, CC_target and CXX_target.
655 if not gyp.common.CrossCompileRequested():
656 if arch is not None:
657 archs = [arch]
658 else:
659 assert self.configname
660 archs = self.GetActiveArchs(self.configname)
661 if len(archs) != 1:
662 # TODO: Supporting fat binaries will be annoying.
663 self._WarnUnimplemented("ARCHS")
664 archs = ["i386"]
665 cflags.append("-arch")
666 cflags.append(archs[0])
667
668 if archs[0] in ("i386", "x86_64"):
669 if self._Test("GCC_ENABLE_SSE3_EXTENSIONS", "YES", default="NO"):
670 cflags.append("-msse3")
671 if self._Test(
672 "GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS", "YES", default="NO"
673 ):
674 cflags.append("-mssse3") # Note 3rd 's'.
675 if self._Test("GCC_ENABLE_SSE41_EXTENSIONS", "YES", default="NO"):
676 cflags.append("-msse4.1")
677 if self._Test("GCC_ENABLE_SSE42_EXTENSIONS", "YES", default="NO"):
678 cflags.append("-msse4.2")
679
680 cflags += self._Settings().get("WARNING_CFLAGS", [])
681
682 if self._IsXCTest():
683 platform_root = self._XcodePlatformPath(configname)
684 if platform_root:
685 cflags.append("-F" + platform_root + "/Developer/Library/Frameworks/")
686
687 framework_root = sdk_root if sdk_root else ""
688 config = self.spec["configurations"][self.configname]
689 framework_dirs = config.get("mac_framework_dirs", [])
690 for directory in framework_dirs:
691 cflags.append("-F" + directory.replace("$(SDKROOT)", framework_root))
692
693 self.configname = None
694 return cflags
695
696 def GetCflagsC(self, configname):
697 """Returns flags that need to be added to .c, and .m compilations."""
698 self.configname = configname
699 cflags_c = []
700 if self._Settings().get("GCC_C_LANGUAGE_STANDARD", "") == "ansi":
701 cflags_c.append("-ansi")
702 else:
703 self._Appendf(cflags_c, "GCC_C_LANGUAGE_STANDARD", "-std=%s")
704 cflags_c += self._Settings().get("OTHER_CFLAGS", [])
705 self.configname = None
706 return cflags_c
707
708 def GetCflagsCC(self, configname):
709 """Returns flags that need to be added to .cc, and .mm compilations."""
710 self.configname = configname
711 cflags_cc = []
712
713 clang_cxx_language_standard = self._Settings().get(
714 "CLANG_CXX_LANGUAGE_STANDARD"
715 )
716 # Note: Don't make c++0x to c++11 so that c++0x can be used with older
717 # clangs that don't understand c++11 yet (like Xcode 4.2's).
718 if clang_cxx_language_standard:
719 cflags_cc.append("-std=%s" % clang_cxx_language_standard)
720
721 self._Appendf(cflags_cc, "CLANG_CXX_LIBRARY", "-stdlib=%s")
722
723 if self._Test("GCC_ENABLE_CPP_RTTI", "NO", default="YES"):
724 cflags_cc.append("-fno-rtti")
725 if self._Test("GCC_ENABLE_CPP_EXCEPTIONS", "NO", default="YES"):
726 cflags_cc.append("-fno-exceptions")
727 if self._Test("GCC_INLINES_ARE_PRIVATE_EXTERN", "YES", default="NO"):
728 cflags_cc.append("-fvisibility-inlines-hidden")
729 if self._Test("GCC_THREADSAFE_STATICS", "NO", default="YES"):
730 cflags_cc.append("-fno-threadsafe-statics")
731 # Note: This flag is a no-op for clang, it only has an effect for gcc.
732 if self._Test("GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO", "NO", default="YES"):
733 cflags_cc.append("-Wno-invalid-offsetof")
734
735 other_ccflags = []
736
737 for flag in self._Settings().get("OTHER_CPLUSPLUSFLAGS", ["$(inherited)"]):
738 # TODO: More general variable expansion. Missing in many other places too.
739 if flag in ("$inherited", "$(inherited)", "${inherited}"):
740 flag = "$OTHER_CFLAGS"
741 if flag in ("$OTHER_CFLAGS", "$(OTHER_CFLAGS)", "${OTHER_CFLAGS}"):
742 other_ccflags += self._Settings().get("OTHER_CFLAGS", [])
743 else:
744 other_ccflags.append(flag)
745 cflags_cc += other_ccflags
746
747 self.configname = None
748 return cflags_cc
749
750 def _AddObjectiveCGarbageCollectionFlags(self, flags):
751 gc_policy = self._Settings().get("GCC_ENABLE_OBJC_GC", "unsupported")
752 if gc_policy == "supported":
753 flags.append("-fobjc-gc")
754 elif gc_policy == "required":
755 flags.append("-fobjc-gc-only")
756
757 def _AddObjectiveCARCFlags(self, flags):
758 if self._Test("CLANG_ENABLE_OBJC_ARC", "YES", default="NO"):
759 flags.append("-fobjc-arc")
760
761 def _AddObjectiveCMissingPropertySynthesisFlags(self, flags):
762 if self._Test(
763 "CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS", "YES", default="NO"
764 ):
765 flags.append("-Wobjc-missing-property-synthesis")
766
767 def GetCflagsObjC(self, configname):
768 """Returns flags that need to be added to .m compilations."""
769 self.configname = configname
770 cflags_objc = []
771 self._AddObjectiveCGarbageCollectionFlags(cflags_objc)
772 self._AddObjectiveCARCFlags(cflags_objc)
773 self._AddObjectiveCMissingPropertySynthesisFlags(cflags_objc)
774 self.configname = None
775 return cflags_objc
776
777 def GetCflagsObjCC(self, configname):
778 """Returns flags that need to be added to .mm compilations."""
779 self.configname = configname
780 cflags_objcc = []
781 self._AddObjectiveCGarbageCollectionFlags(cflags_objcc)
782 self._AddObjectiveCARCFlags(cflags_objcc)
783 self._AddObjectiveCMissingPropertySynthesisFlags(cflags_objcc)
784 if self._Test("GCC_OBJC_CALL_CXX_CDTORS", "YES", default="NO"):
785 cflags_objcc.append("-fobjc-call-cxx-cdtors")
786 self.configname = None
787 return cflags_objcc
788
789 def GetInstallNameBase(self):
790 """Return DYLIB_INSTALL_NAME_BASE for this target."""
791 # Xcode sets this for shared_libraries, and for nonbundled loadable_modules.
792 if self.spec["type"] != "shared_library" and (
793 self.spec["type"] != "loadable_module" or self._IsBundle()
794 ):
795 return None
796 install_base = self.GetPerTargetSetting(
797 "DYLIB_INSTALL_NAME_BASE",
798 default="/Library/Frameworks" if self._IsBundle() else "/usr/local/lib",
799 )
800 return install_base
801
802 def _StandardizePath(self, path):
803 """Do :standardizepath processing for path."""
804 # I'm not quite sure what :standardizepath does. Just call normpath(),
805 # but don't let @executable_path/../foo collapse to foo.
806 if "/" in path:
807 prefix, rest = "", path
808 if path.startswith("@"):
809 prefix, rest = path.split("/", 1)
810 rest = os.path.normpath(rest) # :standardizepath
811 path = os.path.join(prefix, rest)
812 return path
813
814 def GetInstallName(self):
815 """Return LD_DYLIB_INSTALL_NAME for this target."""
816 # Xcode sets this for shared_libraries, and for nonbundled loadable_modules.
817 if self.spec["type"] != "shared_library" and (
818 self.spec["type"] != "loadable_module" or self._IsBundle()
819 ):
820 return None
821
822 default_install_name = (
823 "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)"
824 )
825 install_name = self.GetPerTargetSetting(
826 "LD_DYLIB_INSTALL_NAME", default=default_install_name
827 )
828
829 # Hardcode support for the variables used in chromium for now, to
830 # unblock people using the make build.
831 if "$" in install_name:
832 assert install_name in (
833 "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/"
834 "$(WRAPPER_NAME)/$(PRODUCT_NAME)",
835 default_install_name,
836 ), (
837 "Variables in LD_DYLIB_INSTALL_NAME are not generally supported "
838 "yet in target '%s' (got '%s')"
839 % (self.spec["target_name"], install_name)
840 )
841
842 install_name = install_name.replace(
843 "$(DYLIB_INSTALL_NAME_BASE:standardizepath)",
844 self._StandardizePath(self.GetInstallNameBase()),
845 )
846 if self._IsBundle():
847 # These are only valid for bundles, hence the |if|.
848 install_name = install_name.replace(
849 "$(WRAPPER_NAME)", self.GetWrapperName()
850 )
851 install_name = install_name.replace(
852 "$(PRODUCT_NAME)", self.GetProductName()
853 )
854 else:
855 assert "$(WRAPPER_NAME)" not in install_name
856 assert "$(PRODUCT_NAME)" not in install_name
857
858 install_name = install_name.replace(
859 "$(EXECUTABLE_PATH)", self.GetExecutablePath()
860 )
861 return install_name
862
863 def _MapLinkerFlagFilename(self, ldflag, gyp_to_build_path):
864 """Checks if ldflag contains a filename and if so remaps it from
865 gyp-directory-relative to build-directory-relative."""
866 # This list is expanded on demand.
867 # They get matched as:
868 # -exported_symbols_list file
869 # -Wl,exported_symbols_list file
870 # -Wl,exported_symbols_list,file
871 LINKER_FILE = r"(\S+)"
872 WORD = r"\S+"
873 linker_flags = [
874 ["-exported_symbols_list", LINKER_FILE], # Needed for NaCl.
875 ["-unexported_symbols_list", LINKER_FILE],
876 ["-reexported_symbols_list", LINKER_FILE],
877 ["-sectcreate", WORD, WORD, LINKER_FILE], # Needed for remoting.
878 ]
879 for flag_pattern in linker_flags:
880 regex = re.compile("(?:-Wl,)?" + "[ ,]".join(flag_pattern))
881 m = regex.match(ldflag)
882 if m:
883 ldflag = (
884 ldflag[: m.start(1)]
885 + gyp_to_build_path(m.group(1))
886 + ldflag[m.end(1) :]
887 )
888 # Required for ffmpeg (no idea why they don't use LIBRARY_SEARCH_PATHS,
889 # TODO(thakis): Update ffmpeg.gyp):
890 if ldflag.startswith("-L"):
891 ldflag = "-L" + gyp_to_build_path(ldflag[len("-L") :])
892 return ldflag
893
894 def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None):
895 """Returns flags that need to be passed to the linker.
896
897 Args:
898 configname: The name of the configuration to get ld flags for.
899 product_dir: The directory where products such static and dynamic
900 libraries are placed. This is added to the library search path.
901 gyp_to_build_path: A function that converts paths relative to the
902 current gyp file to paths relative to the build directory.
903 """
904 self.configname = configname
905 ldflags = []
906
907 # The xcode build is relative to a gyp file's directory, and OTHER_LDFLAGS
908 # can contain entries that depend on this. Explicitly absolutify these.
909 for ldflag in self._Settings().get("OTHER_LDFLAGS", []):
910 ldflags.append(self._MapLinkerFlagFilename(ldflag, gyp_to_build_path))
911
912 if self._Test("DEAD_CODE_STRIPPING", "YES", default="NO"):
913 ldflags.append("-Wl,-dead_strip")
914
915 if self._Test("PREBINDING", "YES", default="NO"):
916 ldflags.append("-Wl,-prebind")
917
918 self._Appendf(
919 ldflags, "DYLIB_COMPATIBILITY_VERSION", "-compatibility_version %s"
920 )
921 self._Appendf(ldflags, "DYLIB_CURRENT_VERSION", "-current_version %s")
922
923 self._AppendPlatformVersionMinFlags(ldflags)
924
925 if "SDKROOT" in self._Settings() and self._SdkPath():
926 ldflags.append("-isysroot")
927 ldflags.append(self._SdkPath())
928
929 for library_path in self._Settings().get("LIBRARY_SEARCH_PATHS", []):
930 ldflags.append("-L" + gyp_to_build_path(library_path))
931
932 if "ORDER_FILE" in self._Settings():
933 ldflags.append("-Wl,-order_file")
934 ldflags.append("-Wl," + gyp_to_build_path(self._Settings()["ORDER_FILE"]))
935
936 if not gyp.common.CrossCompileRequested():
937 if arch is not None:
938 archs = [arch]
939 else:
940 assert self.configname
941 archs = self.GetActiveArchs(self.configname)
942 if len(archs) != 1:
943 # TODO: Supporting fat binaries will be annoying.
944 self._WarnUnimplemented("ARCHS")
945 archs = ["i386"]
946 # Avoid quoting the space between -arch and the arch name
947 ldflags.append("-arch")
948 ldflags.append(archs[0])
949
950 # Xcode adds the product directory by default.
951 # Rewrite -L. to -L./ to work around http://www.openradar.me/25313838
952 ldflags.append("-L" + (product_dir if product_dir != "." else "./"))
953
954 install_name = self.GetInstallName()
955 if install_name and self.spec["type"] != "loadable_module":
956 ldflags.append("-install_name")
957 ldflags.append(install_name.replace(" ", r"\ "))
958
959 for rpath in self._Settings().get("LD_RUNPATH_SEARCH_PATHS", []):
960 ldflags.append("-Wl,-rpath," + rpath)
961
962 sdk_root = self._SdkPath()
963 if not sdk_root:
964 sdk_root = ""
965 config = self.spec["configurations"][self.configname]
966 framework_dirs = config.get("mac_framework_dirs", [])
967 for directory in framework_dirs:
968 ldflags.append("-F" + directory.replace("$(SDKROOT)", sdk_root))
969
970 if self._IsXCTest():
971 platform_root = self._XcodePlatformPath(configname)
972 if sdk_root and platform_root:
973 ldflags.append("-F" + platform_root + "/Developer/Library/Frameworks/")
974 ldflags.append("-framework")
975 ldflags.append("XCTest")
976
977 is_extension = self._IsIosAppExtension() or self._IsIosWatchKitExtension()
978 if sdk_root and is_extension:
979 # Adds the link flags for extensions. These flags are common for all
980 # extensions and provide loader and main function.
981 # These flags reflect the compilation options used by xcode to compile
982 # extensions.
983 xcode_version, _ = XcodeVersion()
984 if xcode_version < "0900":
985 ldflags.append("-lpkstart")
986 ldflags.append(
987 sdk_root
988 + "/System/Library/PrivateFrameworks/PlugInKit.framework/PlugInKit"
989 )
990 else:
991 ldflags.append("-e")
992 ldflags.append("_NSExtensionMain")
993 ldflags.append("-fapplication-extension")
994
995 self._Appendf(ldflags, "CLANG_CXX_LIBRARY", "-stdlib=%s")
996
997 self.configname = None
998 return ldflags
999
1000 def GetLibtoolflags(self, configname):
1001 """Returns flags that need to be passed to the static linker.
1002
1003 Args:
1004 configname: The name of the configuration to get ld flags for.
1005 """
1006 self.configname = configname
1007 libtoolflags = []
1008
1009 for libtoolflag in self._Settings().get("OTHER_LDFLAGS", []):
1010 libtoolflags.append(libtoolflag)
1011 # TODO(thakis): ARCHS?
1012
1013 self.configname = None
1014 return libtoolflags
1015
1016 def GetPerTargetSettings(self):
1017 """Gets a list of all the per-target settings. This will only fetch keys
1018 whose values are the same across all configurations."""
1019 first_pass = True
1020 result = {}
1021 for configname in sorted(self.xcode_settings.keys()):
1022 if first_pass:
1023 result = dict(self.xcode_settings[configname])
1024 first_pass = False
1025 else:
1026 for key, value in self.xcode_settings[configname].items():
1027 if key not in result:
1028 continue
1029 elif result[key] != value:
1030 del result[key]
1031 return result
1032
1033 def GetPerConfigSetting(self, setting, configname, default=None):
1034 if configname in self.xcode_settings:
1035 return self.xcode_settings[configname].get(setting, default)
1036 else:
1037 return self.GetPerTargetSetting(setting, default)
1038
1039 def GetPerTargetSetting(self, setting, default=None):
1040 """Tries to get xcode_settings.setting from spec. Assumes that the setting
1041 has the same value in all configurations and throws otherwise."""
1042 is_first_pass = True
1043 result = None
1044 for configname in sorted(self.xcode_settings.keys()):
1045 if is_first_pass:
1046 result = self.xcode_settings[configname].get(setting, None)
1047 is_first_pass = False
1048 else:
1049 assert result == self.xcode_settings[configname].get(setting, None), (
1050 "Expected per-target setting for '%s', got per-config setting "
1051 "(target %s)" % (setting, self.spec["target_name"])
1052 )
1053 if result is None:
1054 return default
1055 return result
1056
1057 def _GetStripPostbuilds(self, configname, output_binary, quiet):
1058 """Returns a list of shell commands that contain the shell commands
1059 necessary to strip this target's binary. These should be run as postbuilds
1060 before the actual postbuilds run."""
1061 self.configname = configname
1062
1063 result = []
1064 if self._Test("DEPLOYMENT_POSTPROCESSING", "YES", default="NO") and self._Test(
1065 "STRIP_INSTALLED_PRODUCT", "YES", default="NO"
1066 ):
1067 default_strip_style = "debugging"
1068 if (
1069 self.spec["type"] == "loadable_module" or self._IsIosAppExtension()
1070 ) and self._IsBundle():
1071 default_strip_style = "non-global"
1072 elif self.spec["type"] == "executable":
1073 default_strip_style = "all"
1074
1075 strip_style = self._Settings().get("STRIP_STYLE", default_strip_style)
1076 strip_flags = {"all": "", "non-global": "-x", "debugging": "-S"}[
1077 strip_style
1078 ]
1079
1080 explicit_strip_flags = self._Settings().get("STRIPFLAGS", "")
1081 if explicit_strip_flags:
1082 strip_flags += " " + _NormalizeEnvVarReferences(explicit_strip_flags)
1083
1084 if not quiet:
1085 result.append("echo STRIP\\(%s\\)" % self.spec["target_name"])
1086 result.append(f"strip {strip_flags} {output_binary}")
1087
1088 self.configname = None
1089 return result
1090
1091 def _GetDebugInfoPostbuilds(self, configname, output, output_binary, quiet):
1092 """Returns a list of shell commands that contain the shell commands
1093 necessary to massage this target's debug information. These should be run
1094 as postbuilds before the actual postbuilds run."""
1095 self.configname = configname
1096
1097 # For static libraries, no dSYMs are created.
1098 result = []
1099 if (
1100 self._Test("GCC_GENERATE_DEBUGGING_SYMBOLS", "YES", default="YES")
1101 and self._Test(
1102 "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym", default="dwarf"
1103 )
1104 and self.spec["type"] != "static_library"
1105 ):
1106 if not quiet:
1107 result.append("echo DSYMUTIL\\(%s\\)" % self.spec["target_name"])
1108 result.append("dsymutil {} -o {}".format(output_binary, output + ".dSYM"))
1109
1110 self.configname = None
1111 return result
1112
1113 def _GetTargetPostbuilds(self, configname, output, output_binary, quiet=False):
1114 """Returns a list of shell commands that contain the shell commands
1115 to run as postbuilds for this target, before the actual postbuilds."""
1116 # dSYMs need to build before stripping happens.
1117 return self._GetDebugInfoPostbuilds(
1118 configname, output, output_binary, quiet
1119 ) + self._GetStripPostbuilds(configname, output_binary, quiet)
1120
1121 def _GetIOSPostbuilds(self, configname, output_binary):
1122 """Return a shell command to codesign the iOS output binary so it can
1123 be deployed to a device. This should be run as the very last step of the
1124 build."""
1125 if not (
1126 (self.isIOS and (self.spec["type"] == "executable" or self._IsXCTest()))
1127 or self.IsIosFramework()
1128 ):
1129 return []
1130
1131 postbuilds = []
1132 product_name = self.GetFullProductName()
1133 settings = self.xcode_settings[configname]
1134
1135 # Xcode expects XCTests to be copied into the TEST_HOST dir.
1136 if self._IsXCTest():
1137 source = os.path.join("${BUILT_PRODUCTS_DIR}", product_name)
1138 test_host = os.path.dirname(settings.get("TEST_HOST"))
1139 xctest_destination = os.path.join(test_host, "PlugIns", product_name)
1140 postbuilds.extend([f"ditto {source} {xctest_destination}"])
1141
1142 key = self._GetIOSCodeSignIdentityKey(settings)
1143 if not key:
1144 return postbuilds
1145
1146 # Warn for any unimplemented signing xcode keys.
1147 unimpl = ["OTHER_CODE_SIGN_FLAGS"]
1148 unimpl = set(unimpl) & set(self.xcode_settings[configname].keys())
1149 if unimpl:
1150 print(
1151 "Warning: Some codesign keys not implemented, ignoring: %s"
1152 % ", ".join(sorted(unimpl))
1153 )
1154
1155 if self._IsXCTest():
1156 # For device xctests, Xcode copies two extra frameworks into $TEST_HOST.
1157 test_host = os.path.dirname(settings.get("TEST_HOST"))
1158 frameworks_dir = os.path.join(test_host, "Frameworks")
1159 platform_root = self._XcodePlatformPath(configname)
1160 frameworks = [
1161 "Developer/Library/PrivateFrameworks/IDEBundleInjection.framework",
1162 "Developer/Library/Frameworks/XCTest.framework",
1163 ]
1164 for framework in frameworks:
1165 source = os.path.join(platform_root, framework)
1166 destination = os.path.join(frameworks_dir, os.path.basename(framework))
1167 postbuilds.extend([f"ditto {source} {destination}"])
1168
1169 # Then re-sign everything with 'preserve=True'
1170 postbuilds.extend(
1171 [
1172 '%s %s code-sign-bundle "%s" "%s" "%s" "%s" %s'
1173 % (
1174 sys.executable,
1175 os.path.join("${TARGET_BUILD_DIR}", "gyp-mac-tool"),
1176 key,
1177 settings.get("CODE_SIGN_ENTITLEMENTS", ""),
1178 settings.get("PROVISIONING_PROFILE", ""),
1179 destination,
1180 True,
1181 )
1182 ]
1183 )
1184 plugin_dir = os.path.join(test_host, "PlugIns")
1185 targets = [os.path.join(plugin_dir, product_name), test_host]
1186 for target in targets:
1187 postbuilds.extend(
1188 [
1189 '%s %s code-sign-bundle "%s" "%s" "%s" "%s" %s'
1190 % (
1191 sys.executable,
1192 os.path.join("${TARGET_BUILD_DIR}", "gyp-mac-tool"),
1193 key,
1194 settings.get("CODE_SIGN_ENTITLEMENTS", ""),
1195 settings.get("PROVISIONING_PROFILE", ""),
1196 target,
1197 True,
1198 )
1199 ]
1200 )
