From 315e4e7dc45bd4cd86ef33265354f5fa983c7ab4 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Mon, 5 Oct 2026 13:50:51 -0300 Subject: [PATCH 1/4] build(ios): stop copying unused default metadata into NativeScript.framework The framework already embeds metadata-arm64.bin as its __DATA,__TNSMetadata section, which is the only copy the runtime reads. The loose 8.8 MB resource copy is never read, and the opt-in "Add default metadata" phase copied from a NativeScript/metadata/ directory that does not exist, gated on an INCLUDE_DEFAULT_METADATA setting nothing sets. --- build_nativescript.sh | 3 --- v8ios.xcodeproj/project.pbxproj | 21 --------------------- 2 files changed, 24 deletions(-) diff --git a/build_nativescript.sh b/build_nativescript.sh index 0311fcb8f..abd094a34 100755 --- a/build_nativescript.sh +++ b/build_nativescript.sh @@ -75,7 +75,6 @@ xcodebuild archive -project v8ios.xcodeproj \ EXCLUDED_ARCHS="x86_64" \ SKIP_INSTALL=NO \ BUILD_LIBRARIES_FOR_DISTRIBUTION=YES \ - INCLUDE_DEFAULT_METADATA=$INCLUDE_DEFAULT_METADATA\ -archivePath $DIST/intermediates/NativeScript.maccatalyst.xcarchive fi @@ -90,7 +89,6 @@ xcodebuild archive -project v8ios.xcodeproj \ DEVELOPMENT_TEAM=$DEV_TEAM \ SKIP_INSTALL=NO \ BUILD_LIBRARIES_FOR_DISTRIBUTION=YES \ - INCLUDE_DEFAULT_METADATA=$INCLUDE_DEFAULT_METADATA\ -archivePath $DIST/intermediates/NativeScript.iphonesimulator.xcarchive fi @@ -105,7 +103,6 @@ xcodebuild archive -project v8ios.xcodeproj \ DEVELOPMENT_TEAM=$DEV_TEAM \ SKIP_INSTALL=NO \ BUILD_LIBRARIES_FOR_DISTRIBUTION=YES \ - INCLUDE_DEFAULT_METADATA=$INCLUDE_DEFAULT_METADATA\ -archivePath $DIST/intermediates/NativeScript.iphoneos.xcarchive fi diff --git a/v8ios.xcodeproj/project.pbxproj b/v8ios.xcodeproj/project.pbxproj index 7c53cc0fb..f2288be8b 100644 --- a/v8ios.xcodeproj/project.pbxproj +++ b/v8ios.xcodeproj/project.pbxproj @@ -19,7 +19,6 @@ 2B5088A72BBEC1BC00F6EB68 /* TNSWidgets.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C20525A72577D86600C12A5C /* TNSWidgets.xcframework */; }; 2B7EA6AF2353477000E5184E /* NativeScriptException.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B7EA6AD2353476F00E5184E /* NativeScriptException.mm */; }; 2B7EA6B02353477000E5184E /* NativeScriptException.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B7EA6AE2353477000E5184E /* NativeScriptException.h */; }; - 2BFE22062AC1C93100307752 /* metadata-arm64.bin in Resources */ = {isa = PBXBuildFile; fileRef = 2BFE22052AC1C93100307752 /* metadata-arm64.bin */; }; 3C1850542A6DCB2D002ACC81 /* Timers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1850522A6DCB2D002ACC81 /* Timers.cpp */; }; 3C1850552A6DCB2D002ACC81 /* Timers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3C1850532A6DCB2D002ACC81 /* Timers.hpp */; }; 3CAE10112F900001002ACC81 /* TextEncoding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CAE10012F900001002ACC81 /* TextEncoding.cpp */; }; @@ -1844,7 +1843,6 @@ C2DDEB2F229EAB3B00345BFE /* Frameworks */, C2DDEB30229EAB3B00345BFE /* Resources */, 65A6D684291F63A30074DF31 /* ShellScript */, - 2BFE21F02AC1B28000307752 /* Add default metadata */, ); buildRules = ( ); @@ -1969,7 +1967,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2BFE22062AC1C93100307752 /* metadata-arm64.bin in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1996,24 +1993,6 @@ shellPath = /bin/sh; shellScript = "set -e\ncd \"${SRCROOT}\"\n[ -f .xcode.env ] && . ./.xcode.env\n[ -f .xcode.env.local ] && . ./.xcode.env.local\nif [ -z \"${NODE_BINARY:-}\" ] || [ ! -x \"${NODE_BINARY:-}\" ]; then\n echo \"error: no usable node for tools/js2c.mjs (NODE_BINARY='${NODE_BINARY:-}'). Run 'npm run setup-xcode-env' in the runtime repo to record your node in .xcode.env.local\" >&2\n exit 1\nfi\n\"$NODE_BINARY\" tools/js2c.mjs --out-dir NativeScript/runtime/generated --filelist tools/js2c-inputs.xcfilelist NativeScript/runtime/js/*.js\n"; }; - 2BFE21F02AC1B28000307752 /* Add default metadata */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Add default metadata"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"Checking flags to determine whether to copy default metadata\"\nif [ \"$INCLUDE_DEFAULT_METADATA\" = true ] ; then\necho \"Copying ${SRCROOT}/${PRODUCT_NAME}/metadata/metadata-arm64.bin directory to ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework\"\ncp ${SRCROOT}/${PRODUCT_NAME}/metadata/metadata-arm64.bin ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/metadata-arm64.bin\necho \"metadata-arm64.bin has been copied.\"\necho \"Copying ${SRCROOT}/${PRODUCT_NAME}/metadata/metadata-x86_64.bin directory to ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework\"\ncp ${SRCROOT}/${PRODUCT_NAME}/metadata/metadata-x86_64.bin ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/metadata-x86_64.bin\necho \"metadata-x86_64.bin has been copied.\"\nelse\necho \"Skipped copying metadata binaries.\"\nfi\n\n"; - }; 65A6D684291F63A30074DF31 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; From 170f3c80100e3cb8b28b2773a801f2f49b38448e Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Mon, 5 Oct 2026 13:58:31 -0300 Subject: [PATCH 2/4] feat(runtime): resolve metadata from the host executable when no pointer is given When Config.MetadataPtr is nil, look for a non-empty __DATA,__TNSMetadata section in the host executable (or its .debug.dylib in Xcode debug-dylib builds) before falling back to the metadata embedded in NativeScript.framework. Hosts set up by 'ns embed ios' link their own metadata this way but never pass the pointer, so they were silently running on the framework's 2024 snapshot. --- NativeScript/NativeScript.mm | 48 ++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/NativeScript/NativeScript.mm b/NativeScript/NativeScript.mm index c35fe1825..3dbef016a 100644 --- a/NativeScript/NativeScript.mm +++ b/NativeScript/NativeScript.mm @@ -1,5 +1,7 @@ #include "NativeScript.h" #include +#include +#include #include "inspector/JsV8InspectorClient.h" #include "runtime/Console.h" #include "runtime/Helpers.h" @@ -28,6 +30,46 @@ @implementation NativeScript extern char defaultStartOfMetadataSection __asm("section$start$__DATA$__TNSMetadata"); +static void* HostExecutableMetadata() { + // Debug builds with ENABLE_DEBUG_DYLIB link the app's code and linker flags + // into .debug.dylib and leave the executable a stub. + NSString* debugDylibName = [[[[NSBundle mainBundle] executablePath] lastPathComponent] + stringByAppendingString:@".debug.dylib"]; + for (uint32_t i = 0, count = _dyld_image_count(); i < count; i++) { + const struct mach_header* header = _dyld_get_image_header(i); + if (header == nullptr) { + continue; + } + if (header->filetype != MH_EXECUTE) { + const char* path = _dyld_get_image_name(i); + const char* name = path != nullptr ? strrchr(path, '/') : nullptr; + if (debugDylibName == nil || name == nullptr || + strcmp(name + 1, debugDylibName.UTF8String) != 0) { + continue; + } + } + unsigned long size = 0; + uint8_t* data = getsectiondata(reinterpret_cast(header), "__DATA", + "__TNSMetadata", &size); + // The app template creates an empty metadata file before the generator + // runs, so an empty section means the host carries no metadata. + if (data != nullptr && size > 0) { + return data; + } + } + return nullptr; +} + +static void* ResolveMetadataPtr(Config* config) { + if (config.MetadataPtr != nil) { + return config.MetadataPtr; + } + if (void* hostMetadata = HostExecutableMetadata()) { + return hostMetadata; + } + return &defaultStartOfMetadataSection; +} + - (void)runScriptString:(NSString*)script runLoop:(BOOL)runLoop { std::string cppString = std::string([script UTF8String]); runtime_->RunScript(cppString); @@ -165,11 +207,7 @@ - (instancetype)initializeWithConfig:(Config*)config { RuntimeConfig.ApplicationPath = [[config.BaseDir stringByAppendingPathComponent:@"app"] UTF8String]; } - if (config.MetadataPtr != nil) { - RuntimeConfig.MetadataPtr = [config MetadataPtr]; - } else { - RuntimeConfig.MetadataPtr = &defaultStartOfMetadataSection; - } + RuntimeConfig.MetadataPtr = ResolveMetadataPtr(config); RuntimeConfig.IsDebug = [config IsDebug]; RuntimeConfig.LogToSystemConsole = [config LogToSystemConsole]; From 29837f5a801a9acf997a0c00b9d0cd47c8e45f2e Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Mon, 5 Oct 2026 14:07:17 -0300 Subject: [PATCH 3/4] feat(ios): move default metadata into a NativeScriptDefaultMetadata framework NativeScript.framework no longer embeds the 8.8 MB default metadata snapshot. It now lives in a separate NativeScriptDefaultMetadata framework (iOS, simulator and Mac Catalyst) that only the NativeScriptSDK SwiftPM product links, so apps built by the NativeScript CLI, which use the NativeScript product and link their own metadata, stop shipping it. When neither Config.MetadataPtr nor a host executable section is available, the runtime loads the accessor exported by NativeScriptDefaultMetadata.framework (already loaded, or found next to NativeScript.framework), and otherwise fails to start with an actionable message instead of running on metadata the host never asked for. build_default_metadata.sh builds the xcframework, build_spm_artifacts.sh packages it with an NS_CHECKSUM_DEFAULTMETADATA_IOS checksum, and generate-spm-manifest.mjs declares it as a binary target of the NativeScriptSDK product. visionOS has no default metadata build. --- NativeScript/NativeScript.mm | 36 ++++- NativeScriptDefaultMetadata/Info.plist | 22 +++ .../NativeScriptDefaultMetadata.c | 8 + .../metadata-arm64.bin | Bin README.md | 14 ++ build_all_ios.sh | 1 + build_default_metadata.sh | 111 ++++++++++++++ build_spm_artifacts.sh | 10 ++ scripts/generate-spm-manifest.mjs | 19 ++- spm-templates/local-spm-ios/Package.swift | 6 +- v8ios.xcodeproj/project.pbxproj | 141 ++++++++++++++++-- .../NativeScriptDefaultMetadata.xcscheme | 67 +++++++++ 12 files changed, 417 insertions(+), 18 deletions(-) create mode 100644 NativeScriptDefaultMetadata/Info.plist create mode 100644 NativeScriptDefaultMetadata/NativeScriptDefaultMetadata.c rename {NativeScript => NativeScriptDefaultMetadata}/metadata-arm64.bin (100%) create mode 100755 build_default_metadata.sh create mode 100644 v8ios.xcodeproj/xcshareddata/xcschemes/NativeScriptDefaultMetadata.xcscheme diff --git a/NativeScript/NativeScript.mm b/NativeScript/NativeScript.mm index 3dbef016a..54f4b14a5 100644 --- a/NativeScript/NativeScript.mm +++ b/NativeScript/NativeScript.mm @@ -1,5 +1,6 @@ #include "NativeScript.h" #include +#include #include #include #include "inspector/JsV8InspectorClient.h" @@ -28,8 +29,6 @@ @implementation Config @implementation NativeScript -extern char defaultStartOfMetadataSection __asm("section$start$__DATA$__TNSMetadata"); - static void* HostExecutableMetadata() { // Debug builds with ENABLE_DEBUG_DYLIB link the app's code and linker flags // into .debug.dylib and leave the executable a stub. @@ -60,6 +59,29 @@ @implementation NativeScript return nullptr; } +// NativeScriptDefaultMetadata.framework ships only with the NativeScriptSDK +// package product, embedded next to NativeScript.framework. +static void* DefaultFrameworkMetadata() { + const char* accessorName = "NativeScriptDefaultMetadata"; + void* accessor = dlsym(RTLD_DEFAULT, accessorName); + if (accessor == nullptr) { + NSString* frameworksDir = [[[NSBundle bundleForClass:[NativeScript class]] bundlePath] + stringByDeletingLastPathComponent]; + NSBundle* bundle = + [NSBundle bundleWithPath:[frameworksDir stringByAppendingPathComponent: + @"NativeScriptDefaultMetadata.framework"]]; + // executablePath, unlike a hand-built path, follows Mac Catalyst's versioned layout. + NSString* executable = [bundle executablePath]; + void* handle = + executable != nil ? dlopen(executable.fileSystemRepresentation, RTLD_NOW) : nullptr; + accessor = handle != nullptr ? dlsym(handle, accessorName) : nullptr; + } + if (accessor == nullptr) { + return nullptr; + } + return const_cast(reinterpret_cast(accessor)()); +} + static void* ResolveMetadataPtr(Config* config) { if (config.MetadataPtr != nil) { return config.MetadataPtr; @@ -67,7 +89,15 @@ @implementation NativeScript if (void* hostMetadata = HostExecutableMetadata()) { return hostMetadata; } - return &defaultStartOfMetadataSection; + if (void* defaultMetadata = DefaultFrameworkMetadata()) { + return defaultMetadata; + } + std::string fatal = + "no NativeScript metadata found. Pass Config.MetadataPtr, link a __DATA,__TNSMetadata " + "section into the app executable (-sectcreate __DATA __TNSMetadata ), or " + "depend on the NativeScriptSDK package product, which bundles default metadata."; + Log(@"Fatal: %s", fatal.c_str()); + throw tns::NativeScriptException("Fatal: " + fatal); } - (void)runScriptString:(NSString*)script runLoop:(BOOL)runLoop { diff --git a/NativeScriptDefaultMetadata/Info.plist b/NativeScriptDefaultMetadata/Info.plist new file mode 100644 index 000000000..e1fe4cfb7 --- /dev/null +++ b/NativeScriptDefaultMetadata/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/NativeScriptDefaultMetadata/NativeScriptDefaultMetadata.c b/NativeScriptDefaultMetadata/NativeScriptDefaultMetadata.c new file mode 100644 index 000000000..f31cbd379 --- /dev/null +++ b/NativeScriptDefaultMetadata/NativeScriptDefaultMetadata.c @@ -0,0 +1,8 @@ +// Linked with -sectcreate __DATA __TNSMetadata metadata-arm64.bin. +extern char startOfMetadataSection __asm("section$start$__DATA$__TNSMetadata"); + +// Looked up by name with dlsym from NativeScript.framework; keep the symbol +// name and signature stable. +__attribute__((visibility("default"), used)) const void* NativeScriptDefaultMetadata(void) { + return &startOfMetadataSection; +} diff --git a/NativeScript/metadata-arm64.bin b/NativeScriptDefaultMetadata/metadata-arm64.bin similarity index 100% rename from NativeScript/metadata-arm64.bin rename to NativeScriptDefaultMetadata/metadata-arm64.bin diff --git a/README.md b/README.md index 6cec34f82..86ebe0526 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,20 @@ package can be built in two modes (`--spm-mode`, see `./build_npm_ios.sh --help` so a remote-mode package only resolves for versions that shipped through `.github/workflows/npm_release.yml` — don't use this mode for local builds. +ios-spm exposes two iOS products: + +- **`NativeScript`** — the runtime and TKLiveSync. {N} CLI apps use this product and link + the metadata generated for them into their own executable. +- **`NativeScriptSDK`** — additionally links `NativeScriptDefaultMetadata`, a separate + framework carrying a default snapshot of iOS SDK metadata, for apps that embed NativeScript + without generating metadata of their own. Built by `./build_default_metadata.sh` (iOS + family only; there is no visionOS build). + +At startup the runtime uses `Config.MetadataPtr` if set, otherwise a `__DATA,__TNSMetadata` +section linked into the host executable, otherwise `NativeScriptDefaultMetadata.framework` if +it is embedded next to `NativeScript.framework`, and fails to start if none is found. The +embedded local package only serves CLI apps, so it does not include the default metadata. + # Local Development & Linking To use a locally built runtime in an app, build the default (embedded) package and point diff --git a/build_all_ios.sh b/build_all_ios.sh index 07a0fd2ab..9aa0bc646 100755 --- a/build_all_ios.sh +++ b/build_all_ios.sh @@ -16,6 +16,7 @@ rm -rf ./dist ./build_metadata_generator.sh ./build_nativescript.sh --no-vision ./build_tklivesync.sh --no-vision +./build_default_metadata.sh ./prepare_dSYMs.sh ./build_spm_artifacts.sh ios ./build_npm_ios.sh "$@" diff --git a/build_default_metadata.sh b/build_default_metadata.sh new file mode 100755 index 000000000..78d27cf78 --- /dev/null +++ b/build_default_metadata.sh @@ -0,0 +1,111 @@ +#!/bin/bash +set -e +source "$(dirname "$0")/build_utils.sh" + +function to_bool() { + local arg="$1" + case "$(echo "$arg" | tr '[:upper:]' '[:lower:]')" in + [0-9]+) + if [ $arg -eq 0 ]; then + echo false + else + echo true + fi + ;; + n|no|f|false) echo false ;; + y|yes|t|true) echo true ;; + * ) + if [ -n "$arg" ]; then + echo "warning: invalid boolean argument ('$arg'). Expected true or false" >&2 + fi + echo false + ;; + esac; +} + +BUILD_CATALYST=$(to_bool ${BUILD_CATALYST:=true}) +BUILD_IPHONE=$(to_bool ${BUILD_IPHONE:=true}) +BUILD_SIMULATOR=$(to_bool ${BUILD_SIMULATOR:=true}) + +for arg in $@; do + case $arg in + --catalyst|--maccatalyst) BUILD_CATALYST=true ;; + --no-catalyst|--no-maccatalyst) BUILD_CATALYST=false ;; + --sim|--simulator) BUILD_SIMULATOR=true ;; + --no-sim|--no-simulator) BUILD_SIMULATOR=false ;; + --iphone|--device) BUILD_IPHONE=true ;; + --no-iphone|--no-device) BUILD_IPHONE=false ;; + *) ;; + esac +done + +DIST=$(PWD)/dist +mkdir -p $DIST + +mkdir -p $DIST/intermediates + +checkpoint "Cleanup NativeScriptDefaultMetadata" +xcodebuild -project v8ios.xcodeproj \ + -target NativeScriptDefaultMetadata \ + -configuration Release clean \ + -quiet + +if $BUILD_SIMULATOR; then +checkpoint "Building NativeScriptDefaultMetadata for iphone simulators (multi-arch)" +xcodebuild archive -project v8ios.xcodeproj \ + -scheme NativeScriptDefaultMetadata \ + -configuration Release \ + -destination "generic/platform=iOS Simulator" \ + -quiet \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + -archivePath $DIST/intermediates/NativeScriptDefaultMetadata.iphonesimulator.xcarchive +fi + +if $BUILD_IPHONE; then +checkpoint "Building NativeScriptDefaultMetadata for ARM64 device" +xcodebuild archive -project v8ios.xcodeproj \ + -scheme NativeScriptDefaultMetadata \ + -configuration Release \ + -destination "generic/platform=iOS" \ + -quiet \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + -archivePath $DIST/intermediates/NativeScriptDefaultMetadata.iphoneos.xcarchive +fi + +if $BUILD_CATALYST; then +checkpoint "Building NativeScriptDefaultMetadata for Mac Catalyst" +xcodebuild archive -project v8ios.xcodeproj \ + -scheme NativeScriptDefaultMetadata \ + -configuration Release \ + -destination "generic/platform=macOS,variant=Mac Catalyst" \ + -quiet \ + EXCLUDED_ARCHS="x86_64" \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + -archivePath $DIST/intermediates/NativeScriptDefaultMetadata.maccatalyst.xcarchive +fi + +XCFRAMEWORKS=() +if $BUILD_CATALYST; then + XCFRAMEWORKS+=( -framework "$DIST/intermediates/NativeScriptDefaultMetadata.maccatalyst.xcarchive/Products/Library/Frameworks/NativeScriptDefaultMetadata.framework" \ + -debug-symbols "$DIST/intermediates/NativeScriptDefaultMetadata.maccatalyst.xcarchive/dSYMs/NativeScriptDefaultMetadata.framework.dSYM" ) +fi + +if $BUILD_SIMULATOR; then + XCFRAMEWORKS+=( -framework "$DIST/intermediates/NativeScriptDefaultMetadata.iphonesimulator.xcarchive/Products/Library/Frameworks/NativeScriptDefaultMetadata.framework" \ + -debug-symbols "$DIST/intermediates/NativeScriptDefaultMetadata.iphonesimulator.xcarchive/dSYMs/NativeScriptDefaultMetadata.framework.dSYM" ) +fi + +if $BUILD_IPHONE; then + XCFRAMEWORKS+=( -framework "$DIST/intermediates/NativeScriptDefaultMetadata.iphoneos.xcarchive/Products/Library/Frameworks/NativeScriptDefaultMetadata.framework" \ + -debug-symbols "$DIST/intermediates/NativeScriptDefaultMetadata.iphoneos.xcarchive/dSYMs/NativeScriptDefaultMetadata.framework.dSYM" ) +fi + +checkpoint "Creating NativeScriptDefaultMetadata.xcframework" +OUTPUT_DIR="$DIST/NativeScriptDefaultMetadata.xcframework" +rm -rf $OUTPUT_DIR +xcodebuild -create-xcframework ${XCFRAMEWORKS[@]} -output "$OUTPUT_DIR" + +rm -rf "$DIST/intermediates" diff --git a/build_spm_artifacts.sh b/build_spm_artifacts.sh index a9b35c3d5..28691544b 100755 --- a/build_spm_artifacts.sh +++ b/build_spm_artifacts.sh @@ -69,12 +69,22 @@ zip_xcframework "TKLiveSync.xcframework" "$TK_ZIP" NS_SUM="$(compute_checksum "$OUT/$NS_ZIP")" TK_SUM="$(compute_checksum "$OUT/$TK_ZIP")" +# Default metadata ships for the iOS family only. +if [ "$TARGET" = "ios" ]; then + DM_ZIP="NativeScriptDefaultMetadata.xcframework.zip" + zip_xcframework "NativeScriptDefaultMetadata.xcframework" "$DM_ZIP" + DM_SUM="$(compute_checksum "$OUT/$DM_ZIP")" +fi + # Per-target filename so the iOS and visionOS env files don't collide when the # release/stamp jobs merge both platforms' artifacts into one directory. CHECKSUMS="$OUT/checksums-$TARGET.env" { echo "${NS_KEY}=${NS_SUM}" echo "${TK_KEY}=${TK_SUM}" + if [ -n "${DM_SUM:-}" ]; then + echo "NS_CHECKSUM_DEFAULTMETADATA_IOS=${DM_SUM}" + fi } > "$CHECKSUMS" checkpoint "SwiftPM artifacts ready in $OUT:" diff --git a/scripts/generate-spm-manifest.mjs b/scripts/generate-spm-manifest.mjs index 8619c4bd4..f84ac46dc 100644 --- a/scripts/generate-spm-manifest.mjs +++ b/scripts/generate-spm-manifest.mjs @@ -89,7 +89,11 @@ if (!VERSION_RE.test(opts.version)) { process.exit(1); } -const IOS_KEYS = ["NS_CHECKSUM_NATIVESCRIPT_IOS", "NS_CHECKSUM_TKLIVESYNC_IOS"]; +const IOS_KEYS = [ + "NS_CHECKSUM_NATIVESCRIPT_IOS", + "NS_CHECKSUM_TKLIVESYNC_IOS", + "NS_CHECKSUM_DEFAULTMETADATA_IOS", +]; const VISION_KEYS = [ "NS_CHECKSUM_NATIVESCRIPT_VISIONOS", "NS_CHECKSUM_TKLIVESYNC_VISIONOS", @@ -209,10 +213,12 @@ let package = Package( .macCatalyst(.v13),${visionPlatform} ], products: [ - // iOS family (iphoneos + iphonesimulator + Mac Catalyst) + // iOS family (iphoneos + iphonesimulator + Mac Catalyst). Apps built by + // the NativeScript CLI link their own metadata and use this product. .library(name: "NativeScript", targets: ["NativeScript", "TKLiveSync"]), - // Backwards-compatible alias for the historical product name. - .library(name: "NativeScriptSDK", targets: ["NativeScript", "TKLiveSync"]),${visionProduct} + // For embedding NativeScript into an existing app: also bundles default + // iOS SDK metadata, used when the host provides none of its own. + .library(name: "NativeScriptSDK", targets: ["NativeScript", "TKLiveSync", "NativeScriptDefaultMetadata"]),${visionProduct} ], dependencies: [], targets: [ @@ -225,6 +231,11 @@ let package = Package( name: "TKLiveSync", url: "\\(releaseBase)/TKLiveSync.xcframework.zip", checksum: "${checksums.NS_CHECKSUM_TKLIVESYNC_IOS}" + ), + .binaryTarget( + name: "NativeScriptDefaultMetadata", + url: "\\(releaseBase)/NativeScriptDefaultMetadata.xcframework.zip", + checksum: "${checksums.NS_CHECKSUM_DEFAULTMETADATA_IOS}" ),${visionTargets} ] ) diff --git a/spm-templates/local-spm-ios/Package.swift b/spm-templates/local-spm-ios/Package.swift index c73bd8bf0..1c498fa07 100644 --- a/spm-templates/local-spm-ios/Package.swift +++ b/spm-templates/local-spm-ios/Package.swift @@ -1,10 +1,12 @@ // swift-tools-version: 5.10 // Local SwiftPM package embedded in @nativescript/ios packages built with // NS_SPM_MODE=embedded (the default outside the release pipeline). Same -// product shape as the released github.com/NativeScript/ios-spm manifest, but +// product names as the released github.com/NativeScript/ios-spm manifest, but // the binary targets point at the xcframework zips packed next to this // manifest (framework/internal/local-spm), so the npm package is fully -// self-contained and portable. +// self-contained and portable. Only CLI apps consume this package, and they +// link their own metadata, so unlike ios-spm's NativeScriptSDK product nothing +// here bundles NativeScriptDefaultMetadata. // // The frameworks are zipped because npm strips symlinks (the Mac Catalyst // slices contain them); SwiftPM extracts local zip binary targets itself. diff --git a/v8ios.xcodeproj/project.pbxproj b/v8ios.xcodeproj/project.pbxproj index f2288be8b..8d4bac3ae 100644 --- a/v8ios.xcodeproj/project.pbxproj +++ b/v8ios.xcodeproj/project.pbxproj @@ -340,6 +340,7 @@ F6191AB229C0FCE8003F588F /* InspectorServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6191AAA29C0FCE7003F588F /* InspectorServer.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; F6191AB629C0FF87003F588F /* utils.h in Headers */ = {isa = PBXBuildFile; fileRef = F6191AB429C0FF86003F588F /* utils.h */; }; F6191AB729C0FF87003F588F /* utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6191AB529C0FF86003F588F /* utils.mm */; }; + D3FA1E7A0000000000000004 /* NativeScriptDefaultMetadata.c in Sources */ = {isa = PBXBuildFile; fileRef = D3FA1E7A0000000000000002 /* NativeScriptDefaultMetadata.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -871,6 +872,9 @@ F6191AAA29C0FCE7003F588F /* InspectorServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InspectorServer.mm; sourceTree = ""; }; F6191AB429C0FF86003F588F /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = ""; }; F6191AB529C0FF86003F588F /* utils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = utils.mm; sourceTree = ""; }; + D3FA1E7A0000000000000001 /* NativeScriptDefaultMetadata.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NativeScriptDefaultMetadata.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D3FA1E7A0000000000000002 /* NativeScriptDefaultMetadata.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = NativeScriptDefaultMetadata.c; sourceTree = ""; }; + D3FA1E7A0000000000000003 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -927,6 +931,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D3FA1E7A0000000000000008 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -1102,6 +1113,7 @@ C27E5D8C22F31CCA00498ED0 /* AppWithModules */, C29374E3229FC0F60075CB16 /* TestFixtures */, C2A5DFE62328C4B900A91A76 /* TKLiveSync */, + D3FA1E7A0000000000000005 /* NativeScriptDefaultMetadata */, C27E278122202664009C10A3 /* Products */, C2FF3016225203FD00933782 /* Frameworks */, ); @@ -1117,6 +1129,7 @@ C27E5D8B22F31CC900498ED0 /* AppWithModules.app */, C23992C7236C2D6E00D2F720 /* TestRunnerTests.xctest */, C2253782241A41C500192740 /* TKLiveSync.framework */, + D3FA1E7A0000000000000001 /* NativeScriptDefaultMetadata.framework */, ); name = Products; sourceTree = ""; @@ -1251,6 +1264,16 @@ path = TKLiveSync; sourceTree = ""; }; + D3FA1E7A0000000000000005 /* NativeScriptDefaultMetadata */ = { + isa = PBXGroup; + children = ( + D3FA1E7A0000000000000002 /* NativeScriptDefaultMetadata.c */, + 2BFE22052AC1C93100307752 /* metadata-arm64.bin */, + D3FA1E7A0000000000000003 /* Info.plist */, + ); + path = NativeScriptDefaultMetadata; + sourceTree = ""; + }; C2A5DFF12328CA6F00A91A76 /* libzip */ = { isa = PBXGroup; children = ( @@ -1409,7 +1432,6 @@ isa = PBXGroup; children = ( 3C48F68C2F57905500C14231 /* third_party */, - 2BFE22052AC1C93100307752 /* metadata-arm64.bin */, 2BFE21F12AC1B79F00307752 /* embedding */, F1F30E862B58FE28006A62C0 /* ada */, F6191AA529C0FCE7003F588F /* inspector */, @@ -1871,6 +1893,22 @@ productReference = C2F4CBB322C60BFD0036B56F /* metadata-generator */; productType = "com.apple.product-type.tool"; }; + D3FA1E7A0000000000000006 /* NativeScriptDefaultMetadata */ = { + isa = PBXNativeTarget; + buildConfigurationList = D3FA1E7A0000000000000009 /* Build configuration list for PBXNativeTarget "NativeScriptDefaultMetadata" */; + buildPhases = ( + D3FA1E7A0000000000000007 /* Sources */, + D3FA1E7A0000000000000008 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NativeScriptDefaultMetadata; + productName = NativeScriptDefaultMetadata; + productReference = D3FA1E7A0000000000000001 /* NativeScriptDefaultMetadata.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1903,6 +1941,9 @@ C2F4CBB222C60BFD0036B56F = { CreatedOnToolsVersion = 10.2.1; }; + D3FA1E7A0000000000000006 = { + CreatedOnToolsVersion = 26.3; + }; }; }; buildConfigurationList = C27E277B22202663009C10A3 /* Build configuration list for PBXProject "v8ios" */; @@ -1925,6 +1966,7 @@ C2F4CBB222C60BFD0036B56F /* metadata-generator */, C23992C6236C2D6E00D2F720 /* TestRunnerTests */, C2253781241A41C500192740 /* TKLiveSync */, + D3FA1E7A0000000000000006 /* NativeScriptDefaultMetadata */, ); }; /* End PBXProject section */ @@ -2348,6 +2390,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D3FA1E7A0000000000000007 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D3FA1E7A0000000000000004 /* NativeScriptDefaultMetadata.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -2960,10 +3010,6 @@ "-lcrdtp_platform", "-linspector", "-linspector_string_conversions", - "-sectcreate", - __DATA, - __TNSMetadata, - "\"$(SRCROOT)/NativeScript/metadata-arm64.bin\"", ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.NativeScript; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -3058,10 +3104,6 @@ "-lcrdtp_platform", "-linspector", "-linspector_string_conversions", - "-sectcreate", - __DATA, - __TNSMetadata, - "\"$(SRCROOT)/NativeScript/metadata-arm64.bin\"", ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.NativeScript; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -3115,6 +3157,78 @@ }; name = Release; }; + D3FA1E7A000000000000000A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = NativeScriptDefaultMetadata/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-sectcreate", + __DATA, + __TNSMetadata, + "\"$(SRCROOT)/NativeScriptDefaultMetadata/metadata-arm64.bin\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.NativeScriptDefaultMetadata; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D3FA1E7A000000000000000B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = NativeScriptDefaultMetadata/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-sectcreate", + __DATA, + __TNSMetadata, + "\"$(SRCROOT)/NativeScriptDefaultMetadata/metadata-arm64.bin\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.NativeScriptDefaultMetadata; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -3190,6 +3304,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D3FA1E7A0000000000000009 /* Build configuration list for PBXNativeTarget "NativeScriptDefaultMetadata" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D3FA1E7A000000000000000A /* Debug */, + D3FA1E7A000000000000000B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = C27E277822202663009C10A3 /* Project object */; diff --git a/v8ios.xcodeproj/xcshareddata/xcschemes/NativeScriptDefaultMetadata.xcscheme b/v8ios.xcodeproj/xcshareddata/xcschemes/NativeScriptDefaultMetadata.xcscheme new file mode 100644 index 000000000..9fe6b5df3 --- /dev/null +++ b/v8ios.xcodeproj/xcshareddata/xcschemes/NativeScriptDefaultMetadata.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + From 138e50244d23532b20a5348acab80819d46c6721 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Mon, 5 Oct 2026 15:18:45 -0300 Subject: [PATCH 4/4] fix(build): accept numeric boolean flags and use $PWD in the framework build scripts to_bool matched numbers with the case pattern [0-9]+, which is a glob for one digit followed by a literal plus, so BUILD_CATALYST=1 and friends were treated as invalid and turned the slice off. DIST was computed with $(PWD), which only resolves to /bin/pwd on a case-insensitive filesystem. --- build_default_metadata.sh | 19 ++++++++++--------- build_nativescript.sh | 19 ++++++++++--------- build_tklivesync.sh | 19 ++++++++++--------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/build_default_metadata.sh b/build_default_metadata.sh index 78d27cf78..20a19c6a0 100755 --- a/build_default_metadata.sh +++ b/build_default_metadata.sh @@ -5,21 +5,22 @@ source "$(dirname "$0")/build_utils.sh" function to_bool() { local arg="$1" case "$(echo "$arg" | tr '[:upper:]' '[:lower:]')" in - [0-9]+) - if [ $arg -eq 0 ]; then - echo false - else - echo true - fi - ;; n|no|f|false) echo false ;; y|yes|t|true) echo true ;; - * ) + # case patterns are globs, not regexes: whatever gets past this arm is all digits + ''|*[!0-9]*) if [ -n "$arg" ]; then echo "warning: invalid boolean argument ('$arg'). Expected true or false" >&2 fi echo false ;; + * ) + if [ "$arg" -eq 0 ]; then + echo false + else + echo true + fi + ;; esac; } @@ -39,7 +40,7 @@ for arg in $@; do esac done -DIST=$(PWD)/dist +DIST="$PWD/dist" mkdir -p $DIST mkdir -p $DIST/intermediates diff --git a/build_nativescript.sh b/build_nativescript.sh index abd094a34..7c0dd2da5 100755 --- a/build_nativescript.sh +++ b/build_nativescript.sh @@ -9,21 +9,22 @@ source "$(dirname "$0")/build_utils.sh" function to_bool() { local arg="$1" case "$(echo "$arg" | tr '[:upper:]' '[:lower:]')" in - [0-9]+) - if [ $arg -eq 0 ]; then - echo false - else - echo true - fi - ;; n|no|f|false) echo false ;; y|yes|t|true) echo true ;; - * ) + # case patterns are globs, not regexes: whatever gets past this arm is all digits + ''|*[!0-9]*) if [ -n "$arg" ]; then echo "warning: invalid boolean argument ('$arg'). Expected true or false" >&2 fi echo false ;; + * ) + if [ "$arg" -eq 0 ]; then + echo false + else + echo true + fi + ;; esac; } @@ -54,7 +55,7 @@ if ! $VERBOSE; then fi DEV_TEAM=${DEVELOPMENT_TEAM:-} -DIST=$(PWD)/dist +DIST="$PWD/dist" mkdir -p $DIST mkdir -p $DIST/intermediates diff --git a/build_tklivesync.sh b/build_tklivesync.sh index e24a0e3b1..c5f72f4c0 100755 --- a/build_tklivesync.sh +++ b/build_tklivesync.sh @@ -5,21 +5,22 @@ source "$(dirname "$0")/build_utils.sh" function to_bool() { local arg="$1" case "$(echo "$arg" | tr '[:upper:]' '[:lower:]')" in - [0-9]+) - if [ $arg -eq 0 ]; then - echo false - else - echo true - fi - ;; n|no|f|false) echo false ;; y|yes|t|true) echo true ;; - * ) + # case patterns are globs, not regexes: whatever gets past this arm is all digits + ''|*[!0-9]*) if [ -n "$arg" ]; then echo "warning: invalid boolean argument ('$arg'). Expected true or false" >&2 fi echo false ;; + * ) + if [ "$arg" -eq 0 ]; then + echo false + else + echo true + fi + ;; esac; } @@ -44,7 +45,7 @@ for arg in $@; do esac done -DIST=$(PWD)/dist +DIST="$PWD/dist" mkdir -p $DIST mkdir -p $DIST/intermediates