Path: blob/main/Scripts/sds_codegen/sds_parse_swift_bridging.py
1 views
#!/usr/bin/env python312import os3import sys4import subprocess5import datetime6import argparse7import re8import json9import sds_common10from sds_common import fail11import tempfile12import shutil131415# We need to generate fake -Swift.h bridging headers that declare the Swift16# types that our Objective-C files might use. This script does that.171819def ows_getoutput(cmd):20proc = subprocess.Popen(21cmd,22stdout=subprocess.PIPE,23stderr=subprocess.PIPE,24)25stdout, stderr = proc.communicate()2627return proc.returncode, stdout, stderr282930class Namespace:31def __init__(self):32self.swift_protocol_names = []33self.swift_class_names = []343536def parse_swift_ast(file_path, namespace, ast):37json_data = json.loads(ast)3839json_maps = json_data.get("key.substructure")40if json_maps is None:41return4243for json_map in json_maps:44kind = json_map.get("key.kind")45if kind is None:46continue47elif kind == "source.lang.swift.decl.protocol":48# "key.kind" : "source.lang.swift.decl.protocol",49# "key.length" : 1067,50# "key.name" : "TypingIndicators",51# "key.namelength" : 16,52# "key.nameoffset" : 135,53# "key.offset" : 126,54# "key.runtime_name" : "OWSTypingIndicators",55name = json_map.get("key.runtime_name")56if name is None or len(name) < 1 or name.startswith("_"):57name = json_map.get("key.name")58if name is None or len(name) < 1:59fail("protocol is missing name.")60continue61if name.startswith("_"):62continue63namespace.swift_protocol_names.append(name)64elif kind == "source.lang.swift.decl.class":65# "key.kind" : "source.lang.swift.decl.class",66# "key.length" : 15057,67# "key.name" : "TypingIndicatorsImpl",68# "key.namelength" : 20,69# "key.nameoffset" : 1251,70# "key.offset" : 1245,71# "key.runtime_name" : "OWSTypingIndicatorsImpl",72name = json_map.get("key.runtime_name")73if name is None or len(name) < 1 or name.startswith("_"):74name = json_map.get("key.name")75if name is None or len(name) < 1:76fail("class is missing name.")77continue78if name.startswith("_"):79continue80namespace.swift_class_names.append(name)818283def process_file(file_path, namespace):84filename = os.path.basename(file_path)85if not filename.endswith(".swift"):86return87if filename == "EmojiWithSkinTones+String.swift":88return8990command = ["sourcekitten", "structure", "--file", file_path]91# for part in command:92# print '\t', part93# command = ' '.join(command).strip()94# print 'command', command95# output = commands.getoutput(command)9697# command = ' '.join(command).strip()98# print 'command', command99exit_code, output, error_output = ows_getoutput(command)100if exit_code != 0:101print("exit_code:", exit_code)102fail("Are you missing sourcekitten? Install with homebrew?")103if len(error_output.strip()) > 0:104print("error_output:", error_output)105# print 'output:', len(output)106107# exit(1)108109output = output.strip()110# print 'output', output111112parse_swift_ast(file_path, namespace, output)113114115def generate_swift_bridging_header(namespace, swift_bridging_path):116117output = []118119for name in namespace.swift_protocol_names:120output.append(121"""122@protocol %s123@end124"""125% (name,)126)127128for name in namespace.swift_class_names:129output.append(130"""131@interface %s : NSObject132@end133"""134% (name,)135)136137output = "\n".join(output).strip()138if len(output) < 1:139return140141header = """//142// Copyright 2022 Signal Messenger, LLC143// SPDX-License-Identifier: AGPL-3.0-only144//145146#import <Foundation/Foundation.h>147148// NOTE: This file is generated by %s.149// Do not manually edit it, instead run `sds_codegen.sh`.150151""" % (152sds_common.pretty_module_path(__file__),153)154output = (header + output).strip()155156# print 'output:', output[:500]157158output = sds_common.clean_up_generated_swift(output)159160# print 'output:', output[:500]161162# print 'output', output163164parent_dir_path = os.path.dirname(swift_bridging_path)165# print 'parent_dir_path', parent_dir_path166if not os.path.exists(parent_dir_path):167os.makedirs(parent_dir_path)168169print("Writing:", swift_bridging_path)170with open(swift_bridging_path, "wt") as f:171f.write(output)172173174# ---175176177def process_dir(src_dir_path, dir_name, dst_dir_path):178namespace = Namespace()179180dir_path = os.path.abspath(os.path.join(src_dir_path, dir_name))181182file_paths = []183for rootdir, dirnames, filenames in os.walk(dir_path):184for filename in filenames:185file_path = os.path.abspath(os.path.join(rootdir, filename))186file_paths.append(file_path)187188print(f"Found {len(file_paths)} files in {dir_path}")189for idx, file_path in enumerate(file_paths):190process_file(file_path, namespace)191if idx % 100 == 99:192print(f"... {idx+1} / {len(file_paths)}")193194bridging_header_path = os.path.abspath(195os.path.join(dst_dir_path, dir_name, dir_name + "-Swift.h")196)197generate_swift_bridging_header(namespace, bridging_header_path)198199200# ---201202if __name__ == "__main__":203204parser = argparse.ArgumentParser(description="Parse Objective-C AST.")205parser.add_argument(206"--src-path", required=True, help="used to specify a path to process."207)208parser.add_argument(209"--swift-bridging-path",210required=True,211help="used to specify a path to process.",212)213args = parser.parse_args()214215src_dir_path = os.path.abspath(args.src_path)216swift_bridging_path = os.path.abspath(args.swift_bridging_path)217218if os.path.exists(swift_bridging_path):219shutil.rmtree(swift_bridging_path)220221process_dir(src_dir_path, "SignalServiceKit", swift_bridging_path)222223224