diff --git a/project-template-ios/__PROJECT_NAME__/build-debug.xcconfig b/project-template-ios/__PROJECT_NAME__/build-debug.xcconfig index 4526c0226..df536162b 100644 --- a/project-template-ios/__PROJECT_NAME__/build-debug.xcconfig +++ b/project-template-ios/__PROJECT_NAME__/build-debug.xcconfig @@ -1,3 +1,5 @@ // @COCOAPODS_SILENCE_WARNINGS@ // #include "build.xcconfig" #include "../plugins-debug.xcconfig" +// Debug configurations embed every debug framework. +NS_EMBEDDED_DEBUG_FRAMEWORKS = $(NS_DEBUG_FRAMEWORKS) diff --git a/project-template-ios/internal/NativeScriptStart.m b/project-template-ios/internal/NativeScriptStart.m index 563251a7f..2b6f562a4 100644 --- a/project-template-ios/internal/NativeScriptStart.m +++ b/project-template-ios/internal/NativeScriptStart.m @@ -9,11 +9,9 @@ #import #import -#ifdef DEBUG +#include #include -#include #include "macros.h" -#endif extern char startOfMetadataSection __asm("section$start$__DATA$__TNSMetadata"); NativeScript* nativescriptStart; @@ -24,24 +22,27 @@ +(void)setup{ @autoreleasepool { NSString* baseDir = [[NSBundle mainBundle] resourcePath]; - #ifdef DEBUG - int refreshRequestSubscription; - notify_register_dispatch(NOTIFICATION("RefreshRequest"), &refreshRequestSubscription, dispatch_get_main_queue(), ^(int token) { - notify_post(NOTIFICATION("AppRefreshStarted")); - bool success = [nativescriptStart liveSync]; - if (success) { - notify_post(NOTIFICATION("AppRefreshSucceeded")); - } else { - NSLog(@"__onLiveSync call failed"); - notify_post(NOTIFICATION("AppRefreshFailed")); - } - }); + // TKLiveSync is in NS_DEBUG_FRAMEWORKS: only debug configurations embed it, unless + // NS_SHIP_DEBUG_FRAMEWORKS lists it. + void (*initializeLiveSync)(void) = (void (*)(void))dlsym(RTLD_DEFAULT, "TNSInitializeLiveSync"); + if (initializeLiveSync != NULL) { + int refreshRequestSubscription; + notify_register_dispatch(NOTIFICATION("RefreshRequest"), &refreshRequestSubscription, dispatch_get_main_queue(), ^(int token) { + notify_post(NOTIFICATION("AppRefreshStarted")); + bool success = [nativescriptStart liveSync]; + if (success) { + notify_post(NOTIFICATION("AppRefreshSucceeded")); + } else { + NSLog(@"__onLiveSync call failed"); + notify_post(NOTIFICATION("AppRefreshFailed")); + } + }); - TNSInitializeLiveSync(); - if (getenv("TNSBaseDir")) { - baseDir = @(getenv("TNSBaseDir")); + initializeLiveSync(); + if (getenv("TNSBaseDir")) { + baseDir = @(getenv("TNSBaseDir")); + } } - #endif void* metadataPtr = &startOfMetadataSection; diff --git a/project-template-ios/internal/main.m b/project-template-ios/internal/main.m index f9da2556b..e70cbad22 100644 --- a/project-template-ios/internal/main.m +++ b/project-template-ios/internal/main.m @@ -4,11 +4,9 @@ #import #import -#ifdef DEBUG +#include #include -#include #include "macros.h" -#endif #if NS_SWIFTUI_BOOT == 0 // Users can disable Obj-C bootstrapping by defining NS_SWIFTUI_BOOT = 1 in their project's build.xcconfig file @@ -21,24 +19,27 @@ int main(int argc, char *argv[]) { @autoreleasepool { NSString* baseDir = [[NSBundle mainBundle] resourcePath]; -#ifdef DEBUG - int refreshRequestSubscription; - notify_register_dispatch(NOTIFICATION("RefreshRequest"), &refreshRequestSubscription, dispatch_get_main_queue(), ^(int token) { - notify_post(NOTIFICATION("AppRefreshStarted")); - bool success = [nativescript liveSync]; - if (success) { - notify_post(NOTIFICATION("AppRefreshSucceeded")); - } else { - NSLog(@"__onLiveSync call failed"); - notify_post(NOTIFICATION("AppRefreshFailed")); - } - }); + // TKLiveSync is in NS_DEBUG_FRAMEWORKS: only debug configurations embed it, unless + // NS_SHIP_DEBUG_FRAMEWORKS lists it. + void (*initializeLiveSync)(void) = (void (*)(void))dlsym(RTLD_DEFAULT, "TNSInitializeLiveSync"); + if (initializeLiveSync != NULL) { + int refreshRequestSubscription; + notify_register_dispatch(NOTIFICATION("RefreshRequest"), &refreshRequestSubscription, dispatch_get_main_queue(), ^(int token) { + notify_post(NOTIFICATION("AppRefreshStarted")); + bool success = [nativescript liveSync]; + if (success) { + notify_post(NOTIFICATION("AppRefreshSucceeded")); + } else { + NSLog(@"__onLiveSync call failed"); + notify_post(NOTIFICATION("AppRefreshFailed")); + } + }); - TNSInitializeLiveSync(); - if (getenv("TNSBaseDir")) { - baseDir = @(getenv("TNSBaseDir")); + initializeLiveSync(); + if (getenv("TNSBaseDir")) { + baseDir = @(getenv("TNSBaseDir")); + } } -#endif void* metadataPtr = &startOfMetadataSection; diff --git a/project-template-ios/internal/nativescript-build.xcconfig b/project-template-ios/internal/nativescript-build.xcconfig index 6a2167c83..ed2bfbad2 100644 --- a/project-template-ios/internal/nativescript-build.xcconfig +++ b/project-template-ios/internal/nativescript-build.xcconfig @@ -14,6 +14,14 @@ FRAMEWORK_SEARCH_PATHS[sdk=*] = $(inherited) "$(SRCROOT)/internal/" "$(CONFIGURA LD = $SRCROOT/internal/nsld.sh LDPLUSPLUS = $SRCROOT/internal/nsld.sh +// * Frameworks only debug builds use: file names separated by spaces. They are weak-linked, +// * and only configurations based on build-debug.xcconfig embed them. +NS_DEBUG_FRAMEWORKS = TKLiveSync.framework +// * Debug frameworks to embed in every configuration, e.g. in App_Resources/iOS/build.xcconfig: +// * NS_SHIP_DEBUG_FRAMEWORKS = TKLiveSync.framework +EXCLUDED_SOURCE_FILE_NAMES[sdk=*] = $(inherited) $(NS_DEBUG_FRAMEWORKS) +INCLUDED_SOURCE_FILE_NAMES[sdk=*] = $(inherited) $(NS_EMBEDDED_DEBUG_FRAMEWORKS) $(NS_SHIP_DEBUG_FRAMEWORKS) + // * Uncomment this setting to generate TypeScript declarations for the iOS SDK and all linked libraries. // * They will be generated on each build, so you can find them after running "ns build ios" in "YOUR_APP/platforms/ios". // NS_TYPESCRIPT_DECLARATIONS_PATH = $(SRCROOT)/typescript-declarations diff --git a/project-template-ios/internal/nativescript-post-build b/project-template-ios/internal/nativescript-post-build index a9ed8f914..476c99973 100755 --- a/project-template-ios/internal/nativescript-post-build +++ b/project-template-ios/internal/nativescript-post-build @@ -2,5 +2,6 @@ set -e pushd "$SRCROOT/internal" +./remove-stale-debug-frameworks.sh ./strip-dynamic-framework-architectures.sh popd diff --git a/project-template-ios/internal/nativescript-pre-build b/project-template-ios/internal/nativescript-pre-build index 61556db7d..f0b585bec 100755 --- a/project-template-ios/internal/nativescript-pre-build +++ b/project-template-ios/internal/nativescript-pre-build @@ -9,6 +9,15 @@ else mv "$ENV_FILE.tmp" "$ENV_FILE" fi +# Xcode reads these as lists too (EXCLUDED_SOURCE_FILE_NAMES / INCLUDED_SOURCE_FILE_NAMES) +# and only splits on spaces, so a comma would leave the frameworks linked and embedded. +case "$NS_DEBUG_FRAMEWORKS $NS_SHIP_DEBUG_FRAMEWORKS" in + *,*) + echo "error: NS_DEBUG_FRAMEWORKS and NS_SHIP_DEBUG_FRAMEWORKS take framework file names separated by spaces, not commas." >&2 + exit 1 + ;; +esac + # create temporary empty metadata files for XCode LD/LDPLUSPLUS flag analysis. # Never touch existing ones: the link step reads them, so a newer timestamp # would make Xcode relink on every build. @@ -17,10 +26,11 @@ do : [ -e "$CONFIGURATION_BUILD_DIR/metadata-$arch.bin" ] || touch "$CONFIGURATION_BUILD_DIR/metadata-$arch.bin" done -# The metadata generator reads these during linking, which Xcode would skip when -# only they changed. +# The link step reads these (the metadata generator its filters, nsld.sh +# NS_DEBUG_FRAMEWORKS), and Xcode would skip it when only they changed. FILTERS_STAMP="$CONFIGURATION_BUILD_DIR/metadata-filters.stamp" FILTERS_HASH=$(cd "$SRCROOT" && shasum whitelist.mdg blacklist.mdg 2>/dev/null || true) +FILTERS_HASH="$FILTERS_HASH NS_DEBUG_FRAMEWORKS=$NS_DEBUG_FRAMEWORKS" if [ ! -e "$FILTERS_STAMP" ] || [ "$FILTERS_HASH" != "$(cat "$FILTERS_STAMP")" ]; then for arch in $VALID_ARCHS do : diff --git a/project-template-ios/internal/nsld.sh b/project-template-ios/internal/nsld.sh index c61a8c094..23c286fab 100755 --- a/project-template-ios/internal/nsld.sh +++ b/project-template-ios/internal/nsld.sh @@ -117,6 +117,19 @@ if [[ -z "$CLANG_PATH" ]]; then exit 1 fi +# Weak-link NS_DEBUG_FRAMEWORKS (file names): most configurations don't embed them. +DEBUG_FRAMEWORKS=" ${NS_DEBUG_FRAMEWORKS} " +LINK_ARGS=() +while [[ $# -gt 0 ]]; do + if [[ "$1" == "-framework" && $# -gt 1 && "$DEBUG_FRAMEWORKS" == *" $2.framework "* ]]; then + LINK_ARGS+=("-weak_framework" "$2") + shift 2 + continue + fi + LINK_ARGS+=("$1") + shift +done + # For visibility downstream, set NS_LD to the resolved path and invoke. NS_LD="$CLANG_PATH" -"$NS_LD" "$@" +"$NS_LD" "${LINK_ARGS[@]}" diff --git a/project-template-ios/internal/remove-stale-debug-frameworks.sh b/project-template-ios/internal/remove-stale-debug-frameworks.sh new file mode 100755 index 000000000..942dfd8f0 --- /dev/null +++ b/project-template-ios/internal/remove-stale-debug-frameworks.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Xcode embeds only the NS_DEBUG_FRAMEWORKS that INCLUDED_SOURCE_FILE_NAMES lists, but it +# never deletes the copy an earlier build left in the app. + +set -e + +INCLUDED=" ${INCLUDED_SOURCE_FILE_NAMES} " + +for FRAMEWORK in ${NS_DEBUG_FRAMEWORKS}; do + if [[ "$INCLUDED" != *" $FRAMEWORK "* ]]; then + rm -rf "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/${FRAMEWORK}" + fi +done diff --git a/project-template-vision/__PROJECT_NAME__/build-debug.xcconfig b/project-template-vision/__PROJECT_NAME__/build-debug.xcconfig index 4526c0226..df536162b 100644 --- a/project-template-vision/__PROJECT_NAME__/build-debug.xcconfig +++ b/project-template-vision/__PROJECT_NAME__/build-debug.xcconfig @@ -1,3 +1,5 @@ // @COCOAPODS_SILENCE_WARNINGS@ // #include "build.xcconfig" #include "../plugins-debug.xcconfig" +// Debug configurations embed every debug framework. +NS_EMBEDDED_DEBUG_FRAMEWORKS = $(NS_DEBUG_FRAMEWORKS) diff --git a/project-template-vision/internal/NativeScriptStart.m b/project-template-vision/internal/NativeScriptStart.m index 563251a7f..2b6f562a4 100644 --- a/project-template-vision/internal/NativeScriptStart.m +++ b/project-template-vision/internal/NativeScriptStart.m @@ -9,11 +9,9 @@ #import #import -#ifdef DEBUG +#include #include -#include #include "macros.h" -#endif extern char startOfMetadataSection __asm("section$start$__DATA$__TNSMetadata"); NativeScript* nativescriptStart; @@ -24,24 +22,27 @@ +(void)setup{ @autoreleasepool { NSString* baseDir = [[NSBundle mainBundle] resourcePath]; - #ifdef DEBUG - int refreshRequestSubscription; - notify_register_dispatch(NOTIFICATION("RefreshRequest"), &refreshRequestSubscription, dispatch_get_main_queue(), ^(int token) { - notify_post(NOTIFICATION("AppRefreshStarted")); - bool success = [nativescriptStart liveSync]; - if (success) { - notify_post(NOTIFICATION("AppRefreshSucceeded")); - } else { - NSLog(@"__onLiveSync call failed"); - notify_post(NOTIFICATION("AppRefreshFailed")); - } - }); + // TKLiveSync is in NS_DEBUG_FRAMEWORKS: only debug configurations embed it, unless + // NS_SHIP_DEBUG_FRAMEWORKS lists it. + void (*initializeLiveSync)(void) = (void (*)(void))dlsym(RTLD_DEFAULT, "TNSInitializeLiveSync"); + if (initializeLiveSync != NULL) { + int refreshRequestSubscription; + notify_register_dispatch(NOTIFICATION("RefreshRequest"), &refreshRequestSubscription, dispatch_get_main_queue(), ^(int token) { + notify_post(NOTIFICATION("AppRefreshStarted")); + bool success = [nativescriptStart liveSync]; + if (success) { + notify_post(NOTIFICATION("AppRefreshSucceeded")); + } else { + NSLog(@"__onLiveSync call failed"); + notify_post(NOTIFICATION("AppRefreshFailed")); + } + }); - TNSInitializeLiveSync(); - if (getenv("TNSBaseDir")) { - baseDir = @(getenv("TNSBaseDir")); + initializeLiveSync(); + if (getenv("TNSBaseDir")) { + baseDir = @(getenv("TNSBaseDir")); + } } - #endif void* metadataPtr = &startOfMetadataSection; diff --git a/project-template-vision/internal/nativescript-build.xcconfig b/project-template-vision/internal/nativescript-build.xcconfig index c90333496..e1869f622 100644 --- a/project-template-vision/internal/nativescript-build.xcconfig +++ b/project-template-vision/internal/nativescript-build.xcconfig @@ -12,6 +12,14 @@ FRAMEWORK_SEARCH_PATHS[sdk=*] = $(inherited) "$(SRCROOT)/internal/" "$(CONFIGURA LD = $SRCROOT/internal/nsld.sh LDPLUSPLUS = $SRCROOT/internal/nsld.sh +// * Frameworks only debug builds use: file names separated by spaces. They are weak-linked, +// * and only configurations based on build-debug.xcconfig embed them. +NS_DEBUG_FRAMEWORKS = TKLiveSync.framework +// * Debug frameworks to embed in every configuration, e.g. in App_Resources/visionOS/build.xcconfig: +// * NS_SHIP_DEBUG_FRAMEWORKS = TKLiveSync.framework +EXCLUDED_SOURCE_FILE_NAMES[sdk=*] = $(inherited) $(NS_DEBUG_FRAMEWORKS) +INCLUDED_SOURCE_FILE_NAMES[sdk=*] = $(inherited) $(NS_EMBEDDED_DEBUG_FRAMEWORKS) $(NS_SHIP_DEBUG_FRAMEWORKS) + // * Uncomment this setting to generate TypeScript declarations for the iOS SDK and all linked libraries. // * They will be generated on each build, so you can find them after running "ns build ios" in "YOUR_APP/platforms/ios". // NS_TYPESCRIPT_DECLARATIONS_PATH = $(SRCROOT)/typescript-declarations diff --git a/project-template-vision/internal/nativescript-post-build b/project-template-vision/internal/nativescript-post-build index a9ed8f914..476c99973 100755 --- a/project-template-vision/internal/nativescript-post-build +++ b/project-template-vision/internal/nativescript-post-build @@ -2,5 +2,6 @@ set -e pushd "$SRCROOT/internal" +./remove-stale-debug-frameworks.sh ./strip-dynamic-framework-architectures.sh popd diff --git a/project-template-vision/internal/nativescript-pre-build b/project-template-vision/internal/nativescript-pre-build index 61556db7d..f0b585bec 100755 --- a/project-template-vision/internal/nativescript-pre-build +++ b/project-template-vision/internal/nativescript-pre-build @@ -9,6 +9,15 @@ else mv "$ENV_FILE.tmp" "$ENV_FILE" fi +# Xcode reads these as lists too (EXCLUDED_SOURCE_FILE_NAMES / INCLUDED_SOURCE_FILE_NAMES) +# and only splits on spaces, so a comma would leave the frameworks linked and embedded. +case "$NS_DEBUG_FRAMEWORKS $NS_SHIP_DEBUG_FRAMEWORKS" in + *,*) + echo "error: NS_DEBUG_FRAMEWORKS and NS_SHIP_DEBUG_FRAMEWORKS take framework file names separated by spaces, not commas." >&2 + exit 1 + ;; +esac + # create temporary empty metadata files for XCode LD/LDPLUSPLUS flag analysis. # Never touch existing ones: the link step reads them, so a newer timestamp # would make Xcode relink on every build. @@ -17,10 +26,11 @@ do : [ -e "$CONFIGURATION_BUILD_DIR/metadata-$arch.bin" ] || touch "$CONFIGURATION_BUILD_DIR/metadata-$arch.bin" done -# The metadata generator reads these during linking, which Xcode would skip when -# only they changed. +# The link step reads these (the metadata generator its filters, nsld.sh +# NS_DEBUG_FRAMEWORKS), and Xcode would skip it when only they changed. FILTERS_STAMP="$CONFIGURATION_BUILD_DIR/metadata-filters.stamp" FILTERS_HASH=$(cd "$SRCROOT" && shasum whitelist.mdg blacklist.mdg 2>/dev/null || true) +FILTERS_HASH="$FILTERS_HASH NS_DEBUG_FRAMEWORKS=$NS_DEBUG_FRAMEWORKS" if [ ! -e "$FILTERS_STAMP" ] || [ "$FILTERS_HASH" != "$(cat "$FILTERS_STAMP")" ]; then for arch in $VALID_ARCHS do : diff --git a/project-template-vision/internal/nsld.sh b/project-template-vision/internal/nsld.sh index c4773a2da..89757e85a 100755 --- a/project-template-vision/internal/nsld.sh +++ b/project-template-vision/internal/nsld.sh @@ -117,6 +117,19 @@ if [[ -z "$CLANG_PATH" ]]; then exit 1 fi +# Weak-link NS_DEBUG_FRAMEWORKS (file names): most configurations don't embed them. +DEBUG_FRAMEWORKS=" ${NS_DEBUG_FRAMEWORKS} " +LINK_ARGS=() +while [[ $# -gt 0 ]]; do + if [[ "$1" == "-framework" && $# -gt 1 && "$DEBUG_FRAMEWORKS" == *" $2.framework "* ]]; then + LINK_ARGS+=("-weak_framework" "$2") + shift 2 + continue + fi + LINK_ARGS+=("$1") + shift +done + # For visibility downstream, set NS_LD to the resolved path and invoke. NS_LD="$CLANG_PATH" -"$NS_LD" "$@" \ No newline at end of file +"$NS_LD" "${LINK_ARGS[@]}" \ No newline at end of file diff --git a/project-template-vision/internal/remove-stale-debug-frameworks.sh b/project-template-vision/internal/remove-stale-debug-frameworks.sh new file mode 100755 index 000000000..942dfd8f0 --- /dev/null +++ b/project-template-vision/internal/remove-stale-debug-frameworks.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Xcode embeds only the NS_DEBUG_FRAMEWORKS that INCLUDED_SOURCE_FILE_NAMES lists, but it +# never deletes the copy an earlier build left in the app. + +set -e + +INCLUDED=" ${INCLUDED_SOURCE_FILE_NAMES} " + +for FRAMEWORK in ${NS_DEBUG_FRAMEWORKS}; do + if [[ "$INCLUDED" != *" $FRAMEWORK "* ]]; then + rm -rf "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/${FRAMEWORK}" + fi +done