Title: [262653] trunk/Tools
Revision
262653
Author
jbed...@apple.com
Date
2020-06-05 14:15:17 -0700 (Fri, 05 Jun 2020)

Log Message

Generalize configure-xcode-for-ios-development (Part 1)
https://bugs.webkit.org/show_bug.cgi?id=207043

Rubber-stamped by David Kilzer.


Replace configure-xcode-for-ios-development with a Python script that works for iOS,
watchOS and tvOS.

* Scripts/configure-xcode-for-embedded-development: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (262652 => 262653)


--- trunk/Tools/ChangeLog	2020-06-05 20:55:29 UTC (rev 262652)
+++ trunk/Tools/ChangeLog	2020-06-05 21:15:17 UTC (rev 262653)
@@ -1,3 +1,17 @@
+2020-06-05  Jonathan Bedard  <jbed...@apple.com>
+
+        Generalize configure-xcode-for-ios-development (Part 1)
+        https://bugs.webkit.org/show_bug.cgi?id=207043
+
+        Rubber-stamped by David Kilzer.
+
+        Patch by Keith Rollin and Jonathan Bedard.
+
+        Replace configure-xcode-for-ios-development with a Python script that works for iOS,
+        watchOS and tvOS.
+
+        * Scripts/configure-xcode-for-embedded-development: Added.
+
 2020-06-05  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Text manipulation should exclude characters outside of the unicode private use area

Added: trunk/Tools/Scripts/configure-xcode-for-embedded-development (0 => 262653)


--- trunk/Tools/Scripts/configure-xcode-for-embedded-development	                        (rev 0)
+++ trunk/Tools/Scripts/configure-xcode-for-embedded-development	2020-06-05 21:15:17 UTC (rev 262653)
@@ -0,0 +1,324 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2014-2020 Apple Inc.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import errno
+import os
+import pathlib
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+from xml.etree import ElementTree as ET
+
+MISSING_HEADERS = [
+    "usr/include/libxslt",
+    "usr/include/mach/mach_types.defs",
+    "usr/include/mach/machine/machine_types.defs",
+    "usr/include/mach/std_types.defs",
+    "usr/include/objc/Protocol.h",
+    "usr/include/objc/objc-class.h",
+    "usr/include/objc/objc-runtime.h",
+    "usr/include/readline/history.h",
+    "usr/include/readline/readline.h",
+]
+
+MISSING_FRAMEWORKS = [
+    ("AVKit.framework", False),
+    ("AudioToolbox.framework", True),
+    ("AudioUnit.framework", False),
+    ("CFNetwork.framework", True),
+    ("CoreImage.framework", False),
+    ("IOKit.framework", True),
+    ("IOSurface.framework", True),
+    ("LocalAuthentication.framework", False),
+    ("MediaAccessibility.framework", False),
+    ("MediaToolbox.framework", False),
+    ("Metal.framework", True),
+    ("OpenGLES.framework", True),
+    ("QuartzCore.framework", True),
+    ("UIKit.framework", True),
+    ("VideoToolbox.framework", False),
+]
+
+XCSPEC_INFO = [
+    {
+        "id":       "com.apple.package-type.mach-o-executable",
+        "source":   "Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX Package Types.xcspec",
+        "dest":     "../PlugIns/IDEiOSSupportCore.ideplugin/Contents/Resources/Embedded-Shared.xcspec"
+    },
+    {
+        "id":       "com.apple.product-type.tool",
+        "source":   "Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX Product Types.xcspec",
+        "dest":     "../PlugIns/IDEiOSSupportCore.ideplugin/Contents/Resources/Embedded-Shared.xcspec"
+    },
+]
+
+AVAILABILITY_FILE = pathlib.Path("usr/local/include/AvailabilityProhibitedInternal.h")
+AVAILABILTY_TEXT = """\
+// Handle __IOS_PROHIBITED and friends.
+#undef __OS_AVAILABILITY
+#define __OS_AVAILABILITY(...)
+
+// Take care of {A,S}PI_AVAILABLE{,_BEGIN,_END}
+#undef __API_AVAILABLE_GET_MACRO
+#define __API_AVAILABLE_GET_MACRO(...) __NULL_AVAILABILITY
+
+// Take care of {A,S}PI_DEPRECATED{,WITH_REPLACEMENT}{,_BEGIN,_END}
+#undef __API_DEPRECATED_MSG_GET_MACRO
+#define __API_DEPRECATED_MSG_GET_MACRO(...) __NULL_AVAILABILITY
+
+// Take care of API_UNAVAILABLE{,_BEGIN,_END}
+#undef __API_UNAVAILABLE_GET_MACRO
+#define __API_UNAVAILABLE_GET_MACRO(...) __NULL_AVAILABILITY
+
+#define __NULL_AVAILABILITY(...)
+"""
+
+SDKS_TO_UPDATE = [
+    "iphoneos",
+    "iphonesimulator",
+    "appletvos",
+    "appletvsimulator",
+    "watchos",
+    "watchsimulator",
+]
+
+PLIST_BUDDY_PATH = pathlib.Path("/usr/libexec/PlistBuddy")
+
+
+def xcode_developer_dir():
+    result = subprocess.run(
+        ["xcode-select", "-p"],
+        capture_output=True, encoding="utf-8", check=True,
+    )
+    return pathlib.Path(result.stdout.strip())
+
+
+def sdk_directory(sdk):
+    result = subprocess.run(
+        ["xcrun", "--sdk", sdk, "--show-sdk-path"],
+        capture_output=True, encoding="utf-8", check=True,
+    )
+    return pathlib.Path(result.stdout.strip())
+
+
+def get_and_check_sdk_directories(source_sdk, dest_sdk):
+    source_sdk_path = sdk_directory(source_sdk)
+    dest_sdk_path = sdk_directory(dest_sdk)
+
+    if not source_sdk_path:
+        raise RuntimeError(f"Could not find SDK: {source_sdk}")
+
+    if not dest_sdk_path:
+        raise RuntimeError(f"Could not find SDK: {dest_sdk}")
+
+    print(source_sdk_path)
+    print(dest_sdk_path)
+    return source_sdk_path, dest_sdk_path
+
+
+def do_copy(source_path, dest_path):
+
+    def ensure_parent_exists(path):
+        os.makedirs(path.parent, exist_ok=True)
+
+    def copy_file(source_path, dest_path):
+        print(f"Copying file")
+        print(f"From: {source_path}")
+        print(f"To:   {dest_path}")
+        if dest_path.exists():
+            dest_path.unlink()
+        ensure_parent_exists(dest_path)
+        shutil.copy2(source_path, dest_path)
+
+    def copy_directory(source_path, dest_path):
+        print(f"Copying directory")
+        print(f"From: {source_path}")
+        print(f"To:   {dest_path}")
+        if dest_path.exists():
+            shutil.rmtree(dest_path)
+        ensure_parent_exists(dest_path)
+        shutil.copytree(source_path, dest_path)
+
+    if source_path.is_file():
+        copy_file(source_path, dest_path)
+        return
+
+    if source_path.is_dir():
+        copy_directory(source_path, dest_path)
+        return
+
+    raise RuntimeError(f"{source_path} does not exist")
+
+
+# .tbd files contain information about how to link a product to a
+# library/framework. This information includes the architecture that the
+# library is built for. If we're copying from an SDK that supports one set of
+# architectures to an SDK that supports another set of architectures, we need
+# to adjust that information in the .tbd file.
+#
+# Note that we don't need to be too particular about this. We only need to
+# link; we don't need to run. This allows us to simply specify the full set of
+# architectures for which WebKit is built, regardless of whether the associated
+# library/framework was actually built for all those architectures.
+
+def patch_tbd_architecture(framework_path):
+    for tbd_path in framework_path.glob("*.tbd"):
+        with open(tbd_path, "r") as f:
+            lines = f.readlines()
+
+        modified_lines = []
+        for line in lines:
+            if re.match(".*archs: +\[.*\]", line) is not None:
+                line = re.sub("\[.*\]", "[ i386, x86_64, arm64, arm64e, arm64_32, armv7k ]", line)
+            modified_lines.append(line)
+
+        with open(tbd_path, "w") as f:
+            f.writelines(modified_lines)
+
+
+# Xcode is driven by .xcspec files. These describe many aspects of the system,
+# including what to build and how to build it. These .xcspec files can be
+# global or they can be platform- or SDK-specific. In the case of building
+# products for the embedded systems, there is some information in some macOS
+# .xcspec files that need to be transferred to the embedded platforms. This
+# function finds that information, extracts it from the macOS files, and copies
+# it to the embedded files.
+
+def update_xcspec_files():
+
+    def read_spec(spec_path):
+        """Read a .xcspec file and return its contents as an XML DOM object."""
+
+        result = subprocess.run(
+            [PLIST_BUDDY_PATH, "-x", "-c", "Print", spec_path],
+            capture_output=True, encoding="utf-8", check=True,
+        )
+        return ET.fromstring(result.stdout.strip())
+
+    def find_spec_entry_index(spec, identifier):
+        """Given an XML DOM object representing an .xcspec file, look for an
+        entry that has a key of "Identifier" and a value equal to the given
+        value. If found, return its index in the array of such entries."""
+
+        for index, spec_entry in enumerate(spec.findall("./array/dict")):
+            spec_entry_iter = iter(spec_entry)
+            for key, value in zip(spec_entry_iter, spec_entry_iter):
+                if key.tag == "key" and key.text == "Identifier" and value.tag == "string" and value.text == identifier:
+                    return index
+        return None
+
+    for spec_info in XCSPEC_INFO:
+        identifier = spec_info["id"]
+        source_spec_path = xcode_developer_dir() / spec_info["source"]
+        dest_spec_path = xcode_developer_dir() / spec_info["dest"]
+
+        for path in [source_spec_path, dest_spec_path]:
+            if not path.exists():
+                raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), path)
+
+        source_spec_entry_index = find_spec_entry_index(read_spec(source_spec_path), identifier)
+        dest_spec_entry_index = find_spec_entry_index(read_spec(dest_spec_path), identifier)
+
+        if source_spec_entry_index is None:
+            raise RuntimeError(f"Unable to find xcspec entry for {identifier} in {source_spec_path}")
+
+        if dest_spec_entry_index is not None:
+            print(f"xcspec entry for {identifier} already exists in {dest_spec_path}, skipping")
+            return
+
+        result = subprocess.run(
+            [PLIST_BUDDY_PATH, "-x", "-c", f"Print {source_spec_entry_index}", source_spec_path],
+            capture_output=True, encoding="utf-8", check=True,
+        )
+        spec_as_xml_snippet = result.stdout.strip()
+
+        with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as temp:
+            temp.write(spec_as_xml_snippet)
+            temp.flush() # It's amazing how important this line is.
+            if not dest_spec_path.exists():
+                print(f"Creating: {dest_spec_path}")
+                subprocess.run([PLIST_BUDDY_PATH, "-c", "clear array", dest_spec_path], capture_output=True, check=True)
+            print(f"Copying:  {identifier}")
+            print(f"From:     {source_spec_path}")
+            print(f"To:       {dest_spec_path}")
+            subprocess.run([PLIST_BUDDY_PATH, "-c", "add 0 dict", dest_spec_path], capture_output=True, check=True)
+            subprocess.run([PLIST_BUDDY_PATH, "-c", f"merge {temp.name} 0", dest_spec_path], capture_output=True, check=True)
+
+
+def copy_missing_headers(source_sdk, dest_sdk):
+    if source_sdk == dest_sdk:
+        return
+    source_sdk_path, dest_sdk_path = get_and_check_sdk_directories(source_sdk, dest_sdk)
+    for missing_header in MISSING_HEADERS:
+        do_copy(source_sdk_path / missing_header, dest_sdk_path / missing_header)
+
+
+def copy_missing_frameworks(source_sdk, dest_sdk):
+    if source_sdk == dest_sdk:
+        return
+    source_sdk_path, dest_sdk_path = get_and_check_sdk_directories(source_sdk, dest_sdk)
+    source_frameworks_path = source_sdk_path / "System" / "Library" / "Frameworks"
+    dest_frameworks_path = dest_sdk_path / "System" / "Library" / "Frameworks"
+    for missing_framework, force in MISSING_FRAMEWORKS:
+        source_framework_path = source_frameworks_path / missing_framework
+        dest_framework_path = dest_frameworks_path / missing_framework
+        if force or not dest_framework_path.exists():
+            do_copy(source_framework_path, dest_framework_path)
+            patch_tbd_architecture(dest_framework_path)
+
+
+# Some functions that WebKit needs to call are marked as "unavailable" on the
+# embedded platforms. Create a stub header that will nullify the effect of the
+# annotations on those functions. Note that there's no guarantee that the
+# resulting product can run -- we just want to build.
+
+def create_availability_header(sdk):
+    sdk_path = sdk_directory(sdk)
+    availability_header_path = sdk_path / AVAILABILITY_FILE
+    print(f"Creating/updating {availability_header_path}")
+    os.makedirs(availability_header_path.parent, exist_ok=True)
+    with open(availability_header_path, "w") as f:
+        f.write(AVAILABILTY_TEXT)
+
+
+def main():
+    if not os.geteuid() == 0 and not os.access(xcode_developer_dir(), os.R_OK | os.W_OK | os.X_OK, effective_ids=True):
+        raise RuntimeError(f"{__file__} must be run as root")
+
+    update_xcspec_files()
+
+    for sdk in SDKS_TO_UPDATE:
+        copy_missing_headers("macosx", sdk)
+        copy_missing_frameworks("iphoneos", sdk)
+        create_availability_header(sdk)
+    return 0
+
+
+if __name__ == "__main__":
+    main()
Property changes on: trunk/Tools/Scripts/configure-xcode-for-embedded-development
___________________________________________________________________

Added: svn:executable

+* \ No newline at end of property
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to