Skip to content

Commit 0405cb5

Browse files
authored
Merge pull request #29315 from Rostepher/xcrun-module
[Build System: build-script] Adds a new xcrun module to build_swift.
2 parents 2d1930f + a6dab52 commit 0405cb5

File tree

12 files changed

+599
-112
lines changed

12 files changed

+599
-112
lines changed

utils/build-parser-lib

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,10 @@ import platform
3131
import sys
3232

3333
from build_swift.build_swift import argparse, defaults
34-
from swift_build_support.swift_build_support import (
35-
shell,
36-
xcrun,
37-
)
34+
from build_swift.build_swift.wrappers import xcrun
35+
36+
from swift_build_support.swift_build_support import shell
3837
from swift_build_support.swift_build_support.SwiftBuildSupport import (
39-
HOME,
4038
SWIFT_BUILD_ROOT,
4139
SWIFT_SOURCE_ROOT,
4240
)

utils/build_swift/build_swift/shell.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,12 @@ def check_output(command, **kwargs):
337337
Output is returned as a unicode string.
338338
"""
339339

340+
if six.PY3:
341+
kwargs['encoding'] = 'utf-8'
342+
340343
output = subprocess.check_output(command, **kwargs)
341344

342-
if _PY_VERSION >= (3, 0):
345+
if six.PY3:
343346
return output
344347

345348
# Return unicode string rather than bytes in Python 2.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# This source file is part of the Swift.org open source project
2+
#
3+
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
4+
# Licensed under Apache License v2.0 with Runtime Library Exception
5+
#
6+
# See https://swift.org/LICENSE.txt for license information
7+
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
9+
10+
from __future__ import absolute_import, unicode_literals
11+
12+
from . import xcrun as _xcrun
13+
14+
15+
__all__ = [
16+
'xcrun',
17+
]
18+
19+
20+
xcrun = _xcrun.XcrunWrapper()
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# This source file is part of the Swift.org open source project
2+
#
3+
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
4+
# Licensed under Apache License v2.0 with Runtime Library Exception
5+
#
6+
# See https://swift.org/LICENSE.txt for license information
7+
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
9+
10+
"""
11+
Wrapper module around the 'xcrun' command-line utility.
12+
"""
13+
14+
15+
from __future__ import absolute_import, unicode_literals
16+
17+
import functools
18+
import re
19+
import shlex
20+
21+
import six
22+
23+
from .. import shell
24+
from ..versions import Version
25+
26+
27+
__all__ = [
28+
'XcrunWrapper',
29+
]
30+
31+
32+
# -----------------------------------------------------------------------------
33+
# Constants
34+
35+
_VERSION_PATTERN = re.compile(r'^xcrun version (?P<version>[0-9.]+)\.$')
36+
37+
38+
# -----------------------------------------------------------------------------
39+
# Helpers
40+
41+
def _catch_return_none(exceptions):
42+
"""Decorator used to catch exceptions and return None.
43+
"""
44+
45+
def decorator(func):
46+
@functools.wraps(func)
47+
def wrapper(*args, **kwargs):
48+
try:
49+
return func(*args, **kwargs)
50+
except exceptions:
51+
return None
52+
53+
return wrapper
54+
return decorator
55+
56+
57+
def _prepend_sdk_and_toolchain(func):
58+
"""Method decorator used to prepend the sdk and toolchain arguments to the
59+
final command passed to xcrun.
60+
"""
61+
62+
@functools.wraps(func)
63+
def wrapper(self, args, sdk=None, toolchain=None, **kwargs):
64+
if isinstance(args, six.string_types):
65+
args = shlex.split(args)
66+
if toolchain:
67+
args = ['--toolchain', toolchain] + args
68+
if sdk:
69+
args = ['--sdk', sdk] + args
70+
71+
return func(self, args, **kwargs)
72+
return wrapper
73+
74+
75+
# -----------------------------------------------------------------------------
76+
77+
class XcrunWrapper(shell.ExecutableWrapper):
78+
"""Wrapper class around the 'xcrun' command-line utility.
79+
"""
80+
81+
EXECUTABLE = shell.which('xcrun') or 'xcrun'
82+
83+
@_prepend_sdk_and_toolchain
84+
def Popen(self, args, **kwargs):
85+
return super(XcrunWrapper, self).Popen(args, **kwargs)
86+
87+
@_prepend_sdk_and_toolchain
88+
def call(self, args, **kwargs):
89+
return super(XcrunWrapper, self).call(args, **kwargs)
90+
91+
@_prepend_sdk_and_toolchain
92+
def check_call(self, args, **kwargs):
93+
return super(XcrunWrapper, self).check_call(args, **kwargs)
94+
95+
@_prepend_sdk_and_toolchain
96+
def check_output(self, args, **kwargs):
97+
return super(XcrunWrapper, self).check_output(args, **kwargs)
98+
99+
# -------------------------------------------------------------------------
100+
101+
@property
102+
@_catch_return_none(shell.CalledProcessError)
103+
def version(self):
104+
"""Returns the xcrun version.
105+
"""
106+
107+
output = self.check_output('--version')
108+
matches = _VERSION_PATTERN.match(output.rstrip())
109+
if matches:
110+
return Version(matches.group('version'))
111+
112+
return None
113+
114+
# -------------------------------------------------------------------------
115+
# Subcommands
116+
117+
@_catch_return_none(shell.CalledProcessError)
118+
def find(self, tool, sdk=None, toolchain=None):
119+
"""Finds and returns the path to tool using xcrun. Returns None if the
120+
tool cannot be found.
121+
"""
122+
123+
return self.check_output(
124+
['--find', tool],
125+
sdk=sdk,
126+
toolchain=toolchain,
127+
stderr=shell.DEVNULL).rstrip()
128+
129+
def kill_cache(self):
130+
"""Kills the xcrun cache. Returns the status code from the command.
131+
"""
132+
133+
return self.call('--kill-cache')
134+
135+
@_catch_return_none(shell.CalledProcessError)
136+
def sdk_path(self, sdk=None, toolchain=None):
137+
"""Returns the SDK path. Returns None if the SDK cannot be found.
138+
"""
139+
140+
return self.check_output(
141+
'--show-sdk-path',
142+
sdk=sdk,
143+
toolchain=toolchain,
144+
stderr=shell.DEVNULL).rstrip()
145+
146+
@_catch_return_none(shell.CalledProcessError)
147+
def sdk_version(self, sdk=None, toolchain=None):
148+
"""Returns the SDK version. Returns None if the SDK cannot be found.
149+
"""
150+
151+
output = self.check_output(
152+
'--show-sdk-version',
153+
sdk=sdk,
154+
toolchain=toolchain,
155+
stderr=shell.DEVNULL)
156+
157+
return Version(output.rstrip())
158+
159+
@_catch_return_none(shell.CalledProcessError)
160+
def sdk_build_version(self, sdk=None, toolchain=None):
161+
"""Returns the SDK build version. Returns None if the SDK cannot be
162+
found.
163+
"""
164+
165+
output = self.check_output(
166+
'--show-sdk-build-version',
167+
sdk=sdk,
168+
toolchain=toolchain,
169+
stderr=shell.DEVNULL)
170+
171+
return Version(output.rstrip())
172+
173+
@_catch_return_none(shell.CalledProcessError)
174+
def sdk_platform_path(self, sdk=None, toolchain=None):
175+
"""Returns the SDK platform path. Returns None if the SDK cannot be
176+
found.
177+
"""
178+
179+
return self.check_output(
180+
'--show-sdk-platform-path',
181+
sdk=sdk,
182+
toolchain=toolchain,
183+
stderr=shell.DEVNULL).rstrip()
184+
185+
@_catch_return_none(shell.CalledProcessError)
186+
def sdk_platform_version(self, sdk=None, toolchain=None):
187+
"""Returns the SDK platform version. Returns None if the SDK cannot be
188+
found.
189+
"""
190+
191+
output = self.check_output(
192+
'--show-sdk-platform-version',
193+
sdk=sdk,
194+
toolchain=toolchain,
195+
stderr=shell.DEVNULL)
196+
197+
return Version(output.rstrip())

utils/build_swift/tests/build_swift/test_shell.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
# Python 3.4
2626
from pathlib import Path
2727
except ImportError:
28-
Path = None
28+
pass
2929

3030
try:
3131
# Python 3.3
@@ -368,17 +368,20 @@ def test_check_call(self, mock_check_call):
368368
@patch('subprocess.check_output')
369369
def test_check_output(self, mock_check_output):
370370
# Before Python 3 the subprocess.check_output function returned bytes.
371-
if six.PY2:
372-
mock_check_output.return_value = b''
373-
else:
371+
if six.PY3:
374372
mock_check_output.return_value = ''
373+
else:
374+
mock_check_output.return_value = b''
375375

376376
output = shell.check_output('ls')
377377

378378
# We always expect str (utf-8) output
379379
self.assertIsInstance(output, six.text_type)
380380

381-
mock_check_output.assert_called_with('ls')
381+
if six.PY3:
382+
mock_check_output.assert_called_with('ls', encoding='utf-8')
383+
else:
384+
mock_check_output.assert_called_with('ls')
382385

383386

384387
class TestShellUtilities(unittest.TestCase):
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This source file is part of the Swift.org open source project
2+
#
3+
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
4+
# Licensed under Apache License v2.0 with Runtime Library Exception
5+
#
6+
# See https://swift.org/LICENSE.txt for license information
7+
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

0 commit comments

Comments
 (0)