Give an app extension its build settings, so UIKit links - #5578
Give an app extension its build settings, so UIKit links#5578shai-almog wants to merge 23 commits into
Conversation
Mirrors the cloud builder fix. The extension target's settings block is parsed out of a pbxproj-shaped string, and the parser sliced the last character of the value INSTEAD of dropping it, so every "KEY = YES;" line produced ";". Xcode reads that as off: CLANG_ENABLE_MODULES never took, clang compiled the extension without -fmodules, and without modules there is no autolinking. The target's frameworks phase carries Foundation alone, so an extension importing UIKit reached ld with nothing to resolve _OBJC_CLASS_$_UIView against. CLANG_ENABLE_OBJC_ARC was off for the same reason, so the extension built as MRC and leaked. Dropping the semicolon exposes the second half, which never ran before: a quoted value like "gnu++14" is re-emitted inside a Ruby string literal in the project fixup script, where the kept quotes are a syntax error. So unwrap those too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Compared 151 screenshots: 151 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 148 screenshots: 148 matched. Benchmark Results
Detailed Performance Metrics
|
|
Compared 181 screenshots: 181 matched. |
|
Compared 144 screenshots: 144 matched. |
|
Compared 149 screenshots: 149 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
|
Compared 217 screenshots: 217 matched. |
|
Compared 143 screenshots: 143 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Mirrors the cloud builder fix. Past the frameworks problem the archive dies in the app's own target with "Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier -- Embedded Binary Bundle Identifier: (null)". Not prefixed wrongly: absent. A modern Xcode target keeps CFBundleIdentifier and the version strings in build settings and generates them into the plist, so an extension folder exported from such a project ships an Info.plist without those keys, and builtin-infoPlistUtility only expands $(...) references that are already there -- it does not add the key. Every extension the builder generates itself writes CFBundleIdentifier = $(PRODUCT_BUNDLE_IDENTIFIER) into its plist; the generic .ios.appext path trusted whatever the archive carried. It now adds the same reference when the key is missing and aligns CFBundleShortVersionString / CFBundleVersion with the app, which Apple requires of an embedded extension. A correct value, and one written as a $(...) reference, are left alone; anything changed is logged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee7eb951e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder fix. The archive was unpacked into dist/ and deleted from the resources directory at that point -- after the translator has already copied that directory into <main>-src, where every file becomes an app resource. An unbuilt second copy of the extension therefore rode along inside the .app beside the .appex it was unpacked into, adding its weight to the IPA and putting the extension's sources in the bundle. Unpacking cannot move earlier: it wires Xcode targets, and the project does not exist yet. The move out of resDir can, and that is all that was needed -- every other archive kind consumed out of resDir (.lproj.zip, .placeindist.zip, .framework.zip) deletes itself in the same early pass for exactly this reason. The archives are now staged into tmp/appext before the resources are walked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 687ec047e0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder fix. The stamper took buildVersion and the ios.bundleVersion hint, but the app's own Info.plist does not: ios.plistInject wins for CFBundleShortVersionString and CFBundleVersion, and the injection only falls back to buildVersion when it says nothing. An app that injects its version ships that value, while the extension was stamped with the raw one -- so an extension whose version already MATCHED its app could be rewritten into one that does not, which is the embedded-bundle validation failure the stamping exists to prevent. The Matter extension already resolved both keys correctly, inline. That resolution is now two named helpers shared by both callers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix for two review catches, both cases where
the stamping edits the wrong thing.
INFOPLIST_FILE: an archive's buildSettings.properties may point the
target at a plist other than <folder>/Info.plist, and those properties
are folded into the target's build settings further down, so Xcode
processes that file into the .appex. Stamping the default left the plist
that actually ships without the identifier and versions. The effective
path is now resolved first (relative to the project directory, with
$(SRCROOT) and $(PROJECT_DIR) understood); a reference this build cannot
resolve returns null and the build says so rather than editing a file
nothing reads.
The value scan: indexOf("<string>") from the key found the next string
ANYWHERE after it, so a CFBundleVersion given <integer>7</integer>, or
the valid empty form <string/>, sent the rewrite into an unrelated later
value -- CFBundleName, or a field inside the NSExtension dict, stamped
with a version number. This is the trap injectedPlistString's comment
records, so the fix is its machinery: the key's own value element, with
whitespace, comments and CDATA skipped. A non-string value is left
alone; <string/> is this key's own empty value and gets filled.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. The key lookup was a whole-file search, so a key of the same name inside a nested dictionary -- NSExtensionAttributes, CFBundleURLTypes and CFBundleDocumentTypes all carry dictionaries of their own -- answered first when it came earlier in the file. That reads as "the bundle already has an identifier" while the real key is still missing, or sends the version rewrite into an unrelated nested value. The three keys are now looked up among the DIRECT children of the root dict: a small walker that steps over each value whole (depth counted on the element's own name) and skips comments and CDATA rather than reading a < inside either as a tag. And CFBundleIdentifier is filled when it is present but empty. It is set with overwrite off, because an explicit identifier is the extension's own business -- but <string/> is not an explicit identifier, it is no identifier, and it fails the embedded-binary check exactly like a missing one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. Stripping a leading $(SRCROOT)/ handled
the prefix and nothing else, so INFOPLIST_FILE =
$(SRCROOT)/$(TARGET_NAME)/Info.plist -- how an Xcode project writes the
plist that sits in the extension's own folder, i.e. the common case --
was left holding $(TARGET_NAME) and refused as unresolvable, and the
stamper skipped a plist it could have found.
Every setting in that path is known here: SRCROOT and PROJECT_DIR are the
directory the extension folders are extracted into, and TARGET_NAME is
the folder's name, because that is the name the target is created with.
PRODUCT_NAME follows TARGET_NAME unless the archive overrode it with a
literal. Both spellings are substituted, $(NAME) and ${NAME}. A path
still holding a $ afterwards is refused, because a half-resolved path
names some file and editing whichever one it lands on is worse than
saying so. The properties reader is now shared with the other callers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da6420bb2b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder fix, and the exemption was wrong in both directions. A version written as $(MARKETING_VERSION) was left standing on the grounds that a build-setting reference is the project saying it knows what it is doing. It is not: the archive's buildSettings.properties are copied into this target's build configurations further down, so the reference resolves to whatever they say -- a stale 1.0 under an app at 5.4. And a reference to a setting they do NOT define resolves to nothing at all, because the generated target carries no version settings of its own. A reference is now resolved against those same properties and judged by the result: one that already lands on the app's version is left alone, anything else is replaced with the literal, and the log says what it resolved to. The identifier is untouched by this -- it is written with overwrite off, so an explicit $(PRODUCT_BUNDLE_IDENTIFIER) still stands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. Emptiness was tested on the raw text between the tags, so <string><!-- note --></string> and <string><![CDATA[]]></string> counted as values that are already there: a nonzero run of characters and an empty value. The identifier was then preserved and the extension shipped without one. The content is now resolved with plistStringContent first -- CDATA read, comments stripped, entities decoded -- and emptiness, equality and the build-setting resolution all run on that, so a version written <![CDATA[5.4]]> under an app at 5.4 is recognised as already right and left as the archive wrote it. Padding is not accepted as right: a plist parser keeps the spaces in <string> 5.4 </string>, so Apple compares " 5.4 " with the app's "5.4" and rejects the pair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. A setting's value may name another setting and Xcode keeps expanding until none is left; one traversal of the map does that only when the iteration order happens to be the dependency order, and Properties hands them over in hash order. With VERSION_SUFFIX = 1 and MARKETING_VERSION = 5.4$(VERSION_SUFFIX), visiting MARKETING_VERSION first left $(VERSION_SUFFIX) behind, the strip deleted it as though nothing defined it, and a version the device resolves to 5.41 read as the app's own 5.4. Now it expands until nothing changes or nothing is left to expand, capped so a cycle settles instead of spinning; what survives is treated as resolving to nothing, which is what Xcode does with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. INFOPLIST_FILE arrives inside the developer's .ios.appext and the stamper WRITES to whatever it names, so an absolute path or a ../../ traversal had the builder rewriting a file outside the project. The resolved path is now compared, canonically, against the project directory, and anything landing outside is refused with a log line rather than edited. Canonical because an archive can carry symlinks: a path that sits inside the project can still point out of it. The default <folder>/Info.plist goes through the same check, since a zip may plant a symlink at that very name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db507a7946
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder comment. Two review rounds proposed the same fix in different spellings -- that <string /> is not recognised, that <string> </string> survives -- and both were already handled, for a reason that sat one line away and was not written down: plistStringContent trims on both of its paths, so whitespace, a comment, an empty CDATA section and any mix arrive as "", and the self-closing test above catches both <string/> and <string /> because XML puts the slash against the '>' whatever precedes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n in Mirrors the cloud builder fix for two review catches. A qualified setting names a plist too. Xcode honours INFOPLIST_FILE[sdk=iphoneos*], the archive's properties are copied into the target verbatim, and a qualified value beats the base one for the builds it matches, so the device build shipped the one plist the stamper had not touched. All of them are stamped now; which applies depends on the sdk, configuration and arch, and stamping is idempotent. Only the ESCAPED spelling gets this far -- an unescaped one splits on the = inside the brackets and leaves a key Xcode does not recognise -- so the filter requires the closing bracket. And the plist is read as its own bytes declare -- byte order mark first, then the encoding in its XML declaration -- and written back the same way. Reading with the platform default charset left a UTF-16 plist as noise that would not parse, and turned a Latin-1 one's accented characters into replacements that would then have been written back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. A build that succeeds and an archive that
exports can still be rejected on upload:
Invalid bundle structure. The ".appex/WalletNonUIExtension" binary
file is not permitted. Your app cannot contain standalone executables
or libraries, other than a valid CFBundleExecutable of supported
bundles.
The .appex never claimed its own binary: its plist has no
CFBundleExecutable, so validation reads the executable inside it as a
loose program rather than the bundle's own. Every extension this builder
generates itself writes CFBundleExecutable, CFBundlePackageType,
CFBundleName, CFBundleInfoDictionaryVersion and CFBundleDevelopmentRegion
into its plist; a brought-in archive that leaves those to
GENERATE_INFOPLIST_FILE arrives without them, so the generic path now
fills the same set when they are missing and keeps whatever the extension
declares itself.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. CFBundleDevelopmentRegion was filled with a literal "en", so an extension whose development language is not English advertised the wrong fallback localization -- and it need not have, because the archive's buildSettings.properties are copied into this target, so $(DEVELOPMENT_LANGUAGE) lands on whatever that extension set. It is also how the builder writes this key for the extensions it generates itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix. Every generic .ios.appext target was created with '10.0', hard-coded. Xcode writes that into the .appex as MinimumOSVersion, so an extension calling iOS 14 APIs shipped claiming iOS 10 and App Store validation rejected the upload -- "Please ensure the MinimumOSVersion value of your extension is 14 or later" -- after a build that succeeded. 10.0 is also below the floor the current SDK builds against at all. The target now takes, in order: what the archive's buildSettings.properties says; 14.0 when its entitlements ask for payment-pass-provisioning, since PKIssuerProvisioningExtensionHandler is an iOS 14 API; otherwise the app's own deployment target, never below 12.0. A bare major from the app's hint is normalised to major.minor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a62805064c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder fix. The declared deployment target won unconditionally, so an archive exported from an old project carrying IPHONEOS_DEPLOYMENT_TARGET = 10.0 reproduced the rejection the change exists to prevent. The floor is now a floor -- 14.0 for a payment-pass extension, 12.0 otherwise -- and the declared value wins above it. SWIFT_VERSION: the project's Swift settings are applied to the app target alone, so a brought-in extension holding .swift reached the compiler with none and died on "SWIFT_VERSION '' is unsupported", after its sources had been added to the target. Set from ios.swiftVersion (5.0 by default) with ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES when there is Swift to compile. TARGETED_DEVICE_FAMILY and SKIP_INSTALL: every generated extension sets both and the generic path set neither. And extraction now refuses an archive whose symlinks leave the extension folder: unzip creates links happily, and everything under that folder is handed to Xcode, copied into the bundle and swept into the sources tarball. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the cloud builder fix, found by running the code against a real customer archive. An extension folder exported from Xcode often carries two entitlements files -- <Name>.entitlements beside <Name>Release. entitlements -- and the scan kept overwriting its pick as it walked, so the file the target is SIGNED with was whichever listFiles() returned last. The pick is now <ExtensionName>.entitlements when present, else the first by name, and a folder carrying more than one says so in the log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79d1cc668e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder fixes for the review on this PR. Insertion point: endOfElement returns the index just PAST the closing '>', which in a compact plist ending "</dict></plist>" is the '<' of </plist>, so the inclusive lastIndexOf picked that one and the keys went between the two closing tags, outside the dict they belong to. Padding inside markup: <string><![CDATA[ 5.4 ]]></string> parses as " 5.4 " and was judged on trimmed text, so it counted as matching an app at "5.4". Emptiness still uses the trimmed value; equality and the reference resolution use an exact decode, which is what a plist parser sees. Same for a setting value's own trailing space, which Xcode expands verbatim. A stale literal identifier: an archive reused under a renamed package carries an identifier that is not under the host's bundle id, and PRODUCT_BUNDLE_IDENTIFIER cannot save it because the literal is what ships. A literal that could be this app's extension is kept; one that could not is replaced with the reference. The entitlements the floor is read from: buildSettings.properties may point CODE_SIGN_ENTITLEMENTS at another file, and that is the one Xcode signs against, so that is the one whose payment-pass-provisioning decides whether the floor is 14.0. BOM-less UTF-16: legal, and its declaration is NUL-interleaved, so the ISO-8859-1 probe fell through to UTF-8 and the plist went unstamped. The first characters of an XML document are "<?", unmistakable either way round. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdeffa8faf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Mirrors the cloud builder fixes. An identifier reference is only as good as what it lands on: the archive may override PRODUCT_BUNDLE_IDENTIFIER with the identifier from the project the extension was exported from, and that override is written onto this target, so the ordinary CFBundleIdentifier=$(PRODUCT_BUNDLE_IDENTIFIER) can still resolve outside the app. The value is resolved against the settings the target will carry before it is preserved. Paths resolve from the settings map, not the properties file: that file is loaded into the map and deleted before the entitlements lookup runs, so a CODE_SIGN_ENTITLEMENTS holding $(PRODUCT_NAME) resolved against an override that was no longer readable and named a different file -- and the deployment floor was read from entitlements the target is not signed with. And a key's own non-string value is replaced rather than left: every key this stamper manages must be a string, so <integer>7</integer> for CFBundleVersion is not a version to preserve. Wandering off to a later key's <string> remains prevented by the anchored lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff50725d56
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…rser Mirrors the cloud builder fixes for two review catches, both where "we cannot tell" was being read as "it is fine". CFBundleIdentifier = $(EXTENSION_BUNDLE_ID) with nothing defining that setting does not fall back to the target's identifier: Xcode expands it to the empty string and the .appex ships without one. The reference that IS safe, $(PRODUCT_BUNDLE_IDENTIFIER), resolves through the settings because the caller puts the target's own identifier there. And the deployment floor was decided by searching the entitlements file's bytes. Entitlements may be UTF-16, where that finds nothing and a Wallet extension keeps the 12.0 floor Apple rejects it for; and the same text in a comment or an unrelated value is not a granted entitlement -- taking it for one drops the extension off every iOS 12 and 13 device. It is read as a property list now, through the encoding-aware reader, and the key has to be a top-level <true/>. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every fix in this PR was mirrored from the cloud builder, and every test stayed behind in it. That is exactly how two copies of the same logic drift: this one compiles, nothing checks what it does, and the next divergence is found by a customer. The six suites are ported to com.codename1.builders, where 41 other JUnit 4 tests already run under the vintage engine and AppExtensionResourcesTest already covers the file grouping: the settings parser, the identity stamping (root-dict anchoring, empty forms, nested keys, compact plists, references judged by what they resolve to, non-string values), plist path resolution and confinement, encodings and qualified INFOPLIST_FILE settings, the deployment floor including the entitlement parse, and the archive staging and symlink refusal. 70 tests, run against this module's own IPhoneBuilder rather than the daemon's -- which is the point. They pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de24da07cd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…tlements file Two review catches, mirrored from the cloud builder. INFOPLIST_FILE = $(PLIST_DIR)/Info.plist, with PLIST_DIR defined two lines above it in the same buildSettings.properties, is a path Xcode resolves: both settings are copied onto the target. Expanding only SRCROOT, PROJECT_DIR, TARGET_NAME and PRODUCT_NAME called it unresolvable and left the plist Xcode actually builds unstamped. Every declared setting now expands, to a fixed point, before those four. And an entitlements file may be binary -- Xcode writes those as readily as XML and they sign identically, but the XML walk cannot read one, so an issuer-provisioning extension was handed the 12.0 floor Apple rejects it for. Binary files go through plutil where there is one, and fall back to a byte search where there is not: the old behaviour, kept only for the file kind that has no comments to be fooled by. XML still goes through the parser. The two affected suites are re-ported with the new cases; 74 tests run against this module's own IPhoneBuilder, all green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 894b2d2c0a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| String declaredId = appExtensionBuildSetting(appExtension, "PRODUCT_BUNDLE_IDENTIFIER"); | ||
| settings.put("PRODUCT_BUNDLE_IDENTIFIER", declaredId != null ? declaredId | ||
| : request.getPackageName() + "." + appExtension.getName()); |
There was a problem hiding this comment.
Resolve qualified bundle-identifier overrides
When buildSettings.properties contains a device-qualified override such as PRODUCT_BUNDLE_IDENTIFIER[sdk\=iphoneos*]=com.old.Ext, this lookup sees only the unqualified value and adds the host-derived default, so identifierBelongsToApp() preserves the usual $(PRODUCT_BUNDLE_IDENTIFIER) reference. The later Ruby loop emits both settings, and the qualified value wins for the device archive, leaving the extension outside the containing app's identifier prefix. Fresh evidence in this revision is that qualified settings are explicitly supported for INFOPLIST_FILE and copied verbatim, but the identifier check still consults only the base key.
Useful? React with 👍 / 👎.
| String extDeploymentTarget = appExtensionDeploymentTarget( | ||
| buildSettingsMap.get("IPHONEOS_DEPLOYMENT_TARGET"), | ||
| signedEntitlements, | ||
| request.getArg("ios.deployment_target", null)); | ||
| buildSettingsMap.put("IPHONEOS_DEPLOYMENT_TARGET", extDeploymentTarget); |
There was a problem hiding this comment.
Clamp qualified deployment-target settings
When an archive supplies IPHONEOS_DEPLOYMENT_TARGET[sdk\=iphoneos*]=10.0, this calculation clamps only the unqualified setting to 12.0 or 14.0 while leaving the qualified entry in buildSettingsMap. The Ruby loop subsequently writes both entries to the target, so the device build selects 10.0 and bypasses the newly calculated floor, which can again fail SDK or Wallet-extension validation. Fresh evidence in this revision is the new explicit handling of qualified plist settings, confirming that conditional settings are supported by these imported archives even though the deployment-floor logic ignores them.
Useful? React with 👍 / 👎.
Mirrors codenameone/BuildDaemon#191 in the open-source builder, which is what a
local iOS build uses.
An iOS archive with a
.ios.appextapp extension fails at link time:The extension's
Ldline carries-framework Foundationand nothing else, whilethe main app's compile line has
-fmodulesand the extension's does not. Nomodules means no clang autolinking, so
#import <UIKit/UIKit.h>never emits-framework UIKit, and the generated target's frameworks phase has only what thebuilder attaches by hand.
The settings that should have turned modules on are parsed out of a
pbxproj-shaped block, and the parser sliced the last character of the value
INSTEAD of dropping it:
So every
KEY = YES;line produced";", which Xcode reads as off.CLANG_ENABLE_OBJC_ARCwent the same way, so the extension has been building asMRC and leaking.
Dropping the semicolon exposes the second half, which never ran before. A quoted
value like
"gnu++14"is re-emitted inside a Ruby string literal in the projectfixup script, where the kept quotes are a syntax error. So unwrap those too.
Note this flips ARC on for
.ios.appexttargets that have silently been MRC.An extension written with explicit
retain/releasewill now fail to compile andneeds
CLANG_ENABLE_OBJC_ARC = NOin itsbuildSettings.properties(that file isread with
Properties.loadand was never affected by the bug). Swift extensionsare unaffected.
mvn -pl codenameone-maven-plugin compileon JDK 8 is green.🤖 Generated with Claude Code