180 lines
6.6 KiB
Bash
180 lines
6.6 KiB
Bash
|
#!/bin/bash
|
||
|
|
||
|
# Updates nsobject_methods.h by generating a list of all of the properties and
|
||
|
# methods on NSObject that Protobufs should not overload from iOS and macOS combined.
|
||
|
#
|
||
|
# The rules:
|
||
|
# - No property should ever be overloaded.
|
||
|
# - Do not overload any methods that have 0 args such as "autorelease".
|
||
|
# - Do not overload any methods that start with "set[A-Z]" and have 1 arg such as
|
||
|
# "setValuesForKeysWithDictionary:". Note that these will end up in the list as just
|
||
|
# the "proto field" name, so "setValuesForKeysWithDictionary:" will become
|
||
|
# "valuesForKeysWithDictionary".
|
||
|
|
||
|
set -eu
|
||
|
|
||
|
trim() {
|
||
|
local var="$*"
|
||
|
# remove leading whitespace characters
|
||
|
var="${var#"${var%%[![:space:]]*}"}"
|
||
|
# remove trailing whitespace characters
|
||
|
var="${var%"${var##*[![:space:]]}"}"
|
||
|
echo -n "$var"
|
||
|
}
|
||
|
|
||
|
objc_code=$(cat <<'END_CODE'
|
||
|
#import <Foundation/Foundation.h>
|
||
|
#import <objc/runtime.h>
|
||
|
|
||
|
int main(int argc, const char * argv[]) {
|
||
|
@autoreleasepool {
|
||
|
Class cls = [NSObject class];
|
||
|
|
||
|
// List out the protocols on NSObject just so we are aware if they change.
|
||
|
unsigned int protocolCount;
|
||
|
__unsafe_unretained Protocol **protocols =
|
||
|
class_copyProtocolList(cls, &protocolCount);
|
||
|
for (unsigned int i = 0; i < protocolCount; i++) {
|
||
|
printf("// Protocol: %s\n", protocol_getName(protocols[i]));
|
||
|
}
|
||
|
free(protocols);
|
||
|
|
||
|
// Grab all the properties.
|
||
|
unsigned int propCount;
|
||
|
objc_property_t *props = class_copyPropertyList(cls, &propCount);
|
||
|
NSMutableSet *reservedNames = [[NSMutableSet alloc] init];
|
||
|
for (unsigned int i = 0; i < propCount; ++i) {
|
||
|
NSString *propertyName = [NSString stringWithUTF8String:property_getName(props[i])];
|
||
|
[reservedNames addObject:propertyName];
|
||
|
}
|
||
|
free(props);
|
||
|
|
||
|
// Note that methods have 2 defaults args (_cmd and SEL) so a method "0 arg method"
|
||
|
// actually has 2.
|
||
|
unsigned int methodCount;
|
||
|
Method *methods = class_copyMethodList(cls, &methodCount);
|
||
|
for (unsigned int i = 0; i < methodCount; ++i) {
|
||
|
int argCount = method_getNumberOfArguments(methods[i]);
|
||
|
NSString *methodName =
|
||
|
[NSString stringWithUTF8String:sel_getName(method_getName(methods[i]))];
|
||
|
if (argCount == 2) {
|
||
|
[reservedNames addObject:methodName];
|
||
|
}
|
||
|
if (argCount == 3 && [methodName hasPrefix:@"set"] && methodName.length > 4) {
|
||
|
NSString *firstLetter = [methodName substringWithRange:NSMakeRange(3,1)];
|
||
|
NSString *lowerFirstLetter = [firstLetter lowercaseString];
|
||
|
if ([lowerFirstLetter isEqual:firstLetter]) {
|
||
|
// Make sure the next letter is a capital letter so we do not take things like
|
||
|
// settingSomething:
|
||
|
continue;
|
||
|
}
|
||
|
// -5 because 3 for set, 1 for the firstLetter and 1 for the colon on the end.
|
||
|
NSString *restOfString =
|
||
|
[methodName substringWithRange:NSMakeRange(4, methodName.length - 5)];
|
||
|
methodName = [lowerFirstLetter stringByAppendingString:restOfString];
|
||
|
[reservedNames addObject:methodName];
|
||
|
}
|
||
|
}
|
||
|
free(methods);
|
||
|
|
||
|
SEL sortSelector = @selector(caseInsensitiveCompare:);
|
||
|
NSArray *array = [reservedNames.allObjects sortedArrayUsingSelector:sortSelector];
|
||
|
for (NSString *item in array) {
|
||
|
// Some items with _ in them get returned in quotes, so do not add more.
|
||
|
if ([item hasPrefix:@"\""]) {
|
||
|
printf(" %s,\n", item.UTF8String);
|
||
|
} else {
|
||
|
printf(" \"%s\",\n", item.UTF8String);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
END_CODE
|
||
|
)
|
||
|
|
||
|
file_header=$(cat <<'END_HEADER'
|
||
|
// NSObject methods
|
||
|
// Autogenerated by method_dump.sh. Do not edit by hand.
|
||
|
// Date: %DATE%
|
||
|
// macOS: %MACOS%
|
||
|
// iOS: %IOS%
|
||
|
|
||
|
const char* const kNSObjectMethodsList[] = {
|
||
|
END_HEADER
|
||
|
)
|
||
|
|
||
|
file_footer=$(cat <<'END_FOOTER'
|
||
|
};
|
||
|
END_FOOTER
|
||
|
)
|
||
|
|
||
|
# Check to make sure we are updating the correct file.
|
||
|
if [[ ! -e "nsobject_methods.h" ]]; then
|
||
|
echo "error: Must be run in the src/google/protobuf/compiler/objectivec directory"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
temp_dir=$(mktemp -d)
|
||
|
|
||
|
echo "$objc_code" >> "$temp_dir"/method_dump.m
|
||
|
|
||
|
# Compile up iphonesimulator and macos version of cmd line app.
|
||
|
iphone_simulator_sdk=$(xcrun --sdk iphonesimulator --show-sdk-path)
|
||
|
clang -isysroot "$iphone_simulator_sdk" -o "$temp_dir"/method_dump_ios \
|
||
|
-framework Foundation -framework UIKit "$temp_dir"/method_dump.m
|
||
|
macos_sdk=$(xcrun --sdk macosx --show-sdk-path)
|
||
|
clang -isysroot "$macos_sdk" -o "$temp_dir"/method_dump_macos -framework Foundation \
|
||
|
-framework Cocoa "$temp_dir"/method_dump.m
|
||
|
|
||
|
# Create a device of the latest phone and iphonesimulator SDK and run our iOS cmd line.
|
||
|
device_type=$(xcrun simctl list devicetypes | grep \.iPhone- | tail -1 | sed 's/.*(\(.*\))/\1/')
|
||
|
# runtimes come with a space at the end (for Xcode 10) so let's trim all of our input to
|
||
|
# be safe.
|
||
|
device_type=$(trim "$device_type")
|
||
|
runtime=$(xcrun simctl list runtimes | grep \.iOS- | tail -1 | \
|
||
|
sed 's/.*\(com\.apple.\CoreSimulator\.SimRuntime\.iOS.*\)/\1/')
|
||
|
runtime=$(trim "$runtime")
|
||
|
uuid=$(uuidgen)
|
||
|
device_name="method_dump_device_$uuid"
|
||
|
device=$(xcrun simctl create "$device_name" "$device_type" "$runtime")
|
||
|
xcrun simctl spawn "$device" "$temp_dir"/method_dump_ios > "$temp_dir"/methods_unsorted_ios.txt
|
||
|
xcrun simctl delete "$device"
|
||
|
|
||
|
# Run the Mac version
|
||
|
"$temp_dir"/method_dump_macos >> "$temp_dir"/methods_unsorted_macos.txt
|
||
|
|
||
|
# Generate sorted output
|
||
|
echo "$file_header" | sed -e "s|%DATE%|$(date)|" -e "s|%MACOS%|$(basename $macos_sdk)|" \
|
||
|
-e "s|%IOS%|$(basename $iphone_simulator_sdk)|" > "$temp_dir"/methods_sorted.txt
|
||
|
sort -u "$temp_dir"/methods_unsorted_ios.txt \
|
||
|
"$temp_dir"/methods_unsorted_macos.txt >> "$temp_dir"/methods_sorted.txt
|
||
|
echo $"$file_footer" >> "$temp_dir"/methods_sorted.txt
|
||
|
|
||
|
# Check for differences. Turn off error checking because we expect diff to fail when
|
||
|
# there are no differences.
|
||
|
set +e
|
||
|
diff_out=$(diff -I "^//.*$" "$temp_dir"/methods_sorted.txt nsobject_methods.h)
|
||
|
removed_methods=$(echo "$diff_out" | grep '^>.*$')
|
||
|
set -e
|
||
|
if [[ -n "$removed_methods" ]]; then
|
||
|
echo "error: Methods removed from NSObject"
|
||
|
echo "It appears that some methods may have been removed from NSObject."
|
||
|
echo "This could mean that there may be some backwards compatibility issues."
|
||
|
echo "You could potentially build apps that may not work on earlier systems than:"
|
||
|
echo "$iphone_simulator_sdk"
|
||
|
echo "$macos_sdk"
|
||
|
echo "If they declare protobuf types that use any of the following as names:"
|
||
|
echo "$removed_methods"
|
||
|
echo ""
|
||
|
echo "New Version: $temp_dir/methods_sorted.txt"
|
||
|
echo "Old Version: nsobject_methods.h"
|
||
|
exit 1
|
||
|
fi
|
||
|
if [[ -n "$diff_out" ]]; then
|
||
|
echo "Added Methods:"
|
||
|
echo "$(echo "$diff_out" | grep '^<.*$' | sed -e 's/^< "\(.*\)",$/ \1/')"
|
||
|
fi;
|
||
|
cp "$temp_dir"/methods_sorted.txt nsobject_methods.h
|
||
|
rm -rf "$temp_dir"
|