Rewrite generate-xcfilelists in Python
[WebKit-https.git] / Tools / Scripts / webkitpy / xcode / sdk.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Copyright (C) 2019 Apple Inc.  All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # 1. Redistributions of source code must retain the above copyright
11 #    notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
17 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29 # Class for representing SDK information, in the form of Platform, Version, and
30 # whether or not it's an Internal SDK. This class also contains a factory
31 # method for producing an SDK instance from a "specification" string such as
32 # "macOS15.3Internal", like you would get from 'xcodebuild -showsdks".
33
34
35 import re
36
37 from webkitpy.common.system.executive import Executive
38
39
40 test_output = None
41
42
43 class SDK(object):
44     __slots__ = ["xcode_specification", "platform", "version", "internal"]
45
46     def __init__(self, xcode_specification, platform, version, internal):
47         super(SDK, self).__init__()
48         self.xcode_specification = xcode_specification
49         self.platform = platform
50         self.version = version
51         self.internal = internal
52
53     def __repr__(self):
54         return "SDK({}, {}, {}, {})".format(
55                 self.xcode_specification,
56                 self.platform,
57                 self.version,
58                 self.internal)
59
60     def as_xcode_specification(self):
61         return "{}.internal".format(self.platform) if self.internal else self.platform
62
63     @classmethod
64     def get_preferred_sdk_for_platform(cls, platform, executive=None):
65
66         preferred_sdk = cls._parse_sdk(platform)
67
68         executive = executive or Executive()
69         stdout = executive.run_command(["xcodebuild", "-showsdks"])
70
71         for line in stdout.splitlines():
72             m = re.match(r".*-sdk (.*)", line)
73             if m:
74                 this_sdk = cls._parse_sdk(m.group(1))
75
76                 if preferred_sdk.platform != this_sdk.platform:
77                     continue
78
79                 # Both have version information, so compare versions. If the
80                 # versions are equal, prefer the Internal sdk. Otherwise, keep
81                 # the one with the higher version.
82
83                 if preferred_sdk.version and this_sdk.version:
84                     if float(preferred_sdk.version) == float(this_sdk.version):
85                         if this_sdk.internal:
86                             preferred_sdk = this_sdk
87                     elif float(preferred_sdk.version) < float(this_sdk.version):
88                         preferred_sdk = this_sdk
89
90                 # The preferred sdk does not have a version but the prospective
91                 # one does, so keep the prospective one.
92
93                 elif not preferred_sdk.version and this_sdk.version:
94                     preferred_sdk = this_sdk
95
96                 # The preferred sdk has a version but the prospective one does
97                 # not, so keep the preferred one.
98
99                 elif preferred_sdk.version and not this_sdk.version:
100                     pass
101
102                 # Neither has version information; prefer the current one if
103                 # it's internal.
104
105                 elif not preferred_sdk.version and not this_sdk.version:
106                     if this_sdk.internal:
107                         preferred_sdk = this_sdk
108
109         return preferred_sdk
110
111     @classmethod
112     def _parse_sdk(cls, xcode_specification):
113         # letters-and-dots, followed by optional numbers-and-dots, followed by
114         # an optional "internal"
115         m = re.match(r"([a-zA-Z.]+[a-zA-Z])([0-9.]+[0-9])?\.?(internal)?", xcode_specification)
116         if not m:
117             return SDK(xcode_specification, None, None, None)
118
119         platform = m.group(1)
120         version = m.group(2)
121         internal = m.group(3)
122
123         return SDK(xcode_specification, platform, version, internal)