Xcode/XcodeVersioner.py (108 lines of code) (raw):
#!/usr/bin/python
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Get all Version information from Xcode."""
from collections import namedtuple
from autopkglib import Processor, ProcessorError
try:
import objc
except ImportError:
pass
__all__ = ["XcodeVersioner"]
class XcodeVersioner(Processor):
"""Break down a version number into its separate components."""
description = __doc__
input_variables = {
"version": {
"required": True,
"description": (
"CFBundleShortVersionString from an Xcode Info.plist. Produced by "
"PlistReader."
),
},
"app_path": {
"required": True,
"description": (
"Path to Xcode app to look up version information from the bundle."
),
},
}
output_variables = {
"major_version": {"description": "Major version of Xcode - i.e. Xcode 7, 8."},
"minor_version": {
"description": "Minor version of Xcode - i.e. Xcode X.1, X.2."
},
"patch_version": {
"description": (
"Patch version of Xcode - i.e. Xcode X.Y.0, X.Y.1. "
"Patch version will be normalized to 0 if missing (i.e. 8.3 "
"becomes 8.3.0)."
)
},
"is_beta": {
"description": ("Boolean that is true if this Xcode is a beta version.")
},
"beta_version": {"description": ("The beta number - 1, 2, 3, etc.")},
"build_version": {"description": ("Build version of Xcode - e.g. 11B500")},
}
__doc__ = description
def _load_objc_framework(self, f_name, f_path, class_whitelist):
loaded = {}
framework_bundle = objc.loadBundle( # NOQA
f_name, bundle_path=f_path, module_globals=loaded
)
desired = {}
for x in class_whitelist:
if x in loaded:
desired[x] = loaded[x]
return namedtuple("AttributedFramework", desired.keys())(**desired)
def xcode_info(self, app_path):
DVTFoundation_path = (
"%s/Contents/SharedFrameworks/" + "DVTFoundation.framework"
) % app_path
desired_classes = ["DVTToolsInfo"]
DVTFoundation = self._load_objc_framework(
"DVTFoundation", DVTFoundation_path, desired_classes
)
x_info = DVTFoundation.DVTToolsInfo.toolsInfo()
x_v = x_info.toolsVersion()
x_b = x_info.toolsBuildVersion()
app_info = []
app_info.append(["major_version", str(x_v.versionMajorComponent())])
app_info.append(["minor_version", str(x_v.versionMinorComponent())])
app_info.append(["patch_version", str(x_v.versionUpdateComponent())])
app_info.append(["build_version", x_b.name()])
is_beta = bool(x_info.isBeta())
app_info.append(["is_beta", is_beta])
if is_beta:
app_info.append(["beta_version", str(x_info.toolsBetaVersion())])
else:
app_info.append(["beta_version", "0"])
return app_info
def main(self):
"""Main."""
main_version_string = self.env["version"]
split_string = main_version_string.split(".")
if len(split_string) < 2:
raise ProcessorError(
"Version string should be in format X.Y, unless Apple broke "
"literally everything again."
)
self.env["major_version"] = str(split_string[0])
self.output("Major version: %s" % self.env["major_version"])
self.env["minor_version"] = str(split_string[1])
self.output("Minor version: %s" % self.env["minor_version"])
try:
self.env["patch_version"] = split_string[2]
except IndexError:
self.output("Normalizing patch to 0")
self.env["patch_version"] = str("0")
self.env["is_beta"] = False
xcode_info_results = self.xcode_info(self.env["app_path"])
xcode_data = {}
for info_pair in xcode_info_results:
xcode_data[info_pair[0]] = info_pair[1]
if xcode_data["is_beta"]:
self.output("Beta version: %s" % xcode_data["beta_version"])
self.env["is_beta"] = xcode_data["is_beta"]
self.env["beta_version"] = xcode_data["beta_version"]
self.output("Patch version: %s" % self.env["patch_version"])
self.env["build_version"] = xcode_data["build_version"]
self.output("Build version: %s" % self.env["build_version"])
if __name__ == "__main__":
PROCESSOR = XcodeVersioner()
PROCESSOR.execute_shell()