ann-flavor-cocoapods 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -0
- data/lib/ann_flavor_cocoapods/podfile_helper.rb +138 -312
- data/lib/ann_flavor_cocoapods/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0753376f0ddae76a1894acb795e3e1cac3f6a975a79b799e0cccc79c9db2760b
|
|
4
|
+
data.tar.gz: cc9c6297e39a2702dc119975168fc92926eb2db5bdfc9c23ff44eb9076b8b568
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b56c73fc8ced1dbd22e58e2780aded019fc5cf87e76ed6df5fac6ec6ab4650f4391e3707543147095df39b1342fa892a9c6699604cad1ce75daec906b7a37cb
|
|
7
|
+
data.tar.gz: 2c1dcff237aded83a87e68fe1b1d4869bd901d61c5703749307df2dd02ba91105fefdc62363fcc5d7b2dbb095bb09d491d5410b64456eced522e82a2255b9322
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.6
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **Info.plist and entitlements merging moved from a build-time
|
|
7
|
+
`PBXShellScriptBuildPhase` to plain Ruby code that runs during
|
|
8
|
+
CocoaPods' `post_integrate` hook (pod-install time).** 0.4.1-0.4.5
|
|
9
|
+
each narrowed the conditions under which the "Cycle inside Runner"
|
|
10
|
+
failure occurred, but deep research (Apple Developer Forums,
|
|
11
|
+
CocoaPods/CocoaPods#11133, several flutter/flutter issues, and the
|
|
12
|
+
`swift-build` source) confirmed the real cause: *any*
|
|
13
|
+
`PBXShellScriptBuildPhase` with declared `output_paths` feeding
|
|
14
|
+
`INFOPLIST_FILE`/`CODE_SIGN_ENTITLEMENTS` becomes a graph node XCBuild
|
|
15
|
+
must order relative to a shared `ModuleVerifierTaskProducer` gate that
|
|
16
|
+
framework-embed phases (CocoaPods and SPM alike) are also gated
|
|
17
|
+
behind — no `build_phases` position or `input_paths` declaration could
|
|
18
|
+
avoid it. The values these phases merged (flavor name, files already on
|
|
19
|
+
disk) are fully known at `pod install` time, so there is no need for a
|
|
20
|
+
build-time script phase at all — the same pattern CocoaPods' own
|
|
21
|
+
`Pods-Runner-Info.plist` already uses.
|
|
22
|
+
- `[ann-flavor] Merge Info.plist` and `[ann-flavor] Merge Entitlements`
|
|
23
|
+
build phases are no longer injected, at all, for any project (new or
|
|
24
|
+
upgrading from 0.4.1-0.4.5's phases, which are removed).
|
|
25
|
+
- Merged output is now written directly to
|
|
26
|
+
`ios/ann/Info/merged/<flavor>-<buildType>.plist` and
|
|
27
|
+
`ios/ann/Entitlements/merged/<flavor>-<buildType>.entitlements` — one
|
|
28
|
+
file per flavor **and build type** (previously one merged file per
|
|
29
|
+
flavor, always built from the `release`-level cascade regardless of
|
|
30
|
+
build type — see the matching `ann-flavor-flutter` 1.6.0 changelog
|
|
31
|
+
entry for the generator-side half of this fix).
|
|
32
|
+
- `INFOPLIST_FILE`/`CODE_SIGN_ENTITLEMENTS` build settings now point
|
|
33
|
+
directly at these per-build-type merged files under `$(SRCROOT)`,
|
|
34
|
+
instead of a `$(DERIVED_FILE_DIR)/...-merged...` path produced by a
|
|
35
|
+
script phase.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- A stale merged entitlements file is now deleted when a flavor/build-type
|
|
39
|
+
combination that previously had entitlements to sign no longer does
|
|
40
|
+
(previously only the build setting was cleared; the stale file was left
|
|
41
|
+
on disk).
|
|
42
|
+
|
|
43
|
+
## 0.4.5
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
- **0.4.4 stopped the cycle for a *fresh* install, but any project already
|
|
47
|
+
on 0.4.1-0.4.3 kept hitting it after upgrading.** 0.4.4 stopped *setting*
|
|
48
|
+
`input_paths` on `[ann-flavor] Merge Entitlements`/`Merge Info.plist`, but
|
|
49
|
+
never explicitly *cleared* it. `existing = target.build_phases.find { ... }`
|
|
50
|
+
reuses the same Xcodeproj object across runs, and `pod install` never
|
|
51
|
+
touches an attribute it doesn't assign — so a project that had already run
|
|
52
|
+
0.4.1-0.4.3 (which did set `input_paths` on that same, reused object) kept
|
|
53
|
+
the stale value forever, silently reintroducing the exact "Cycle inside
|
|
54
|
+
Runner" failure 0.4.4 was meant to remove. Confirmed live against `ledger`:
|
|
55
|
+
the cycle came back, now involving whichever framework happened to be on
|
|
56
|
+
the other side of the shared `ModuleVerifierTaskProducer` gate (in this
|
|
57
|
+
case an SPM-resolved `AppLovinAdapter.xcframework`, following the
|
|
58
|
+
consumer's separate SPM plugin migration — same underlying mechanism, not
|
|
59
|
+
a new bug). Fixed by explicitly setting `phase.input_paths = []` on both
|
|
60
|
+
phases, so a stale value is genuinely wiped on the next `pod install`, not
|
|
61
|
+
just left unset going forward.
|
|
62
|
+
|
|
3
63
|
## 0.4.4
|
|
4
64
|
|
|
5
65
|
### Fixed
|
|
@@ -2,9 +2,14 @@ require_relative 'spec_loader'
|
|
|
2
2
|
require 'ann_flavor_core'
|
|
3
3
|
require 'xcodeproj'
|
|
4
4
|
require 'stringio'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'fileutils'
|
|
5
7
|
|
|
6
8
|
module AnnFlavorCocoapods
|
|
7
9
|
module PodfileHelper
|
|
10
|
+
# System `plutil` tool -- used to read plist files as JSON (see
|
|
11
|
+
# read_plist below); ships with Xcode/macOS.
|
|
12
|
+
PLUTIL = '/usr/bin/plutil'
|
|
8
13
|
# Call inside your Podfile's post_integrate hook to wire per-flavor Firebase
|
|
9
14
|
# GoogleService-Info.plist into the Xcode build. Must be post_integrate, not
|
|
10
15
|
# post_install (DEF-061, #61): post_install fires before CocoaPods has
|
|
@@ -155,7 +160,6 @@ module AnnFlavorCocoapods
|
|
|
155
160
|
user_project.native_targets.each do |target|
|
|
156
161
|
if target.symbol_type == :application
|
|
157
162
|
find_or_create_icon_catalog(target, user_project)
|
|
158
|
-
target_has_entitlements = false
|
|
159
163
|
|
|
160
164
|
ios.flavors.each_key do |flavor_key|
|
|
161
165
|
flavor = ios.flavors[flavor_key]
|
|
@@ -179,29 +183,21 @@ module AnnFlavorCocoapods
|
|
|
179
183
|
|
|
180
184
|
assert_icon_generated!(user_project, flavor_key, icon_name)
|
|
181
185
|
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
# $FLAVOR_NAME from $CONFIGURATION at build time, so one
|
|
194
|
-
# shared phase correctly serves every flavor, whether or not
|
|
195
|
-
# that specific flavor has its own entitlements: cascade.
|
|
196
|
-
flavor_entitlements_file = File.join(user_project.project_dir.to_s, 'ann', 'Entitlements', "#{flavor_key}.entitlements")
|
|
197
|
-
stock_entitlements_file = File.join(user_project.project_dir.to_s, 'Runner', 'Runner.entitlements')
|
|
198
|
-
has_entitlements = File.exist?(flavor_entitlements_file) || File.exist?(stock_entitlements_file)
|
|
199
|
-
entitlements_path = has_entitlements ? '$(DERIVED_FILE_DIR)/Entitlements-merged.entitlements' : nil
|
|
200
|
-
target_has_entitlements ||= has_entitlements
|
|
186
|
+
# Resolved per build type -- info_plist:/entitlements: are
|
|
187
|
+
# both valid at build_types.<bt> level (REQ-FLVR-00110/
|
|
188
|
+
# REQ-FLVR-00120), so each (flavor, build type) pair needs
|
|
189
|
+
# its own merge, not one shared per flavor. Writes
|
|
190
|
+
# ann/Info/merged/<flavor>-<bt>.plist and (when there's
|
|
191
|
+
# anything to sign with) ann/Entitlements/merged/<flavor>-<bt>
|
|
192
|
+
# .entitlements to disk now, at pod-install time -- see
|
|
193
|
+
# merge_info_plist's doc comment for why this moved off a
|
|
194
|
+
# build-time script phase.
|
|
195
|
+
info_plist_path = merge_info_plist(user_project.project_dir.to_s, flavor_key, bt)
|
|
196
|
+
entitlements_path = merge_entitlements(user_project.project_dir.to_s, flavor_key, bt)
|
|
201
197
|
|
|
202
198
|
configure_build_configuration(
|
|
203
199
|
target, user_project, xcode_config, xcconfig_path,
|
|
204
|
-
icon_name, entitlements_path
|
|
200
|
+
icon_name, entitlements_path, info_plist_path
|
|
205
201
|
)
|
|
206
202
|
end
|
|
207
203
|
|
|
@@ -211,19 +207,6 @@ module AnnFlavorCocoapods
|
|
|
211
207
|
if print_build_info
|
|
212
208
|
inject_build_info_phase(target)
|
|
213
209
|
end
|
|
214
|
-
|
|
215
|
-
inject_merge_info_plist_phase(target)
|
|
216
|
-
|
|
217
|
-
# Unlike Merge Info.plist (unconditional -- every flavor always
|
|
218
|
-
# has an Info.plist), Merge Entitlements is only injected when at
|
|
219
|
-
# least one flavor actually needs it (its own cascade, or a
|
|
220
|
-
# stock Runner.entitlements) -- entitlements are opt-in overall,
|
|
221
|
-
# and a phase with nothing to sign would just be dead weight.
|
|
222
|
-
if target_has_entitlements
|
|
223
|
-
inject_merge_entitlements_phase(target)
|
|
224
|
-
else
|
|
225
|
-
remove_merge_entitlements_phase(target)
|
|
226
|
-
end
|
|
227
210
|
end
|
|
228
211
|
|
|
229
212
|
# Fixes a real pod-install failure: PBXNativeTarget#resolved_build_setting
|
|
@@ -620,7 +603,7 @@ module AnnFlavorCocoapods
|
|
|
620
603
|
CODE_SIGN_ENTITLEMENTS
|
|
621
604
|
].freeze
|
|
622
605
|
|
|
623
|
-
def self.configure_build_configuration(target, user_project, xcode_config, xcconfig_path, icon_name, entitlements_path = nil)
|
|
606
|
+
def self.configure_build_configuration(target, user_project, xcode_config, xcconfig_path, icon_name, entitlements_path = nil, info_plist_path = nil)
|
|
624
607
|
config_list = target.build_configuration_list
|
|
625
608
|
config = config_list[xcode_config]
|
|
626
609
|
|
|
@@ -646,12 +629,16 @@ module AnnFlavorCocoapods
|
|
|
646
629
|
user_project.main_group.new_reference(xcconfig_path)
|
|
647
630
|
config.base_configuration_reference = xcconfig_ref
|
|
648
631
|
|
|
649
|
-
# Points at
|
|
650
|
-
#
|
|
651
|
-
#
|
|
652
|
-
#
|
|
653
|
-
#
|
|
654
|
-
|
|
632
|
+
# Points at merge_info_plist's output (ann/Info/merged/<flavor>.plist),
|
|
633
|
+
# not directly at ann/Info/Info-<flavor>.plist -- that file holds only
|
|
634
|
+
# the flavor's info_plist: cascade delta, not a complete plist;
|
|
635
|
+
# merge_info_plist composes it with Runner/Info.plist (base) at
|
|
636
|
+
# pod-install time and writes the result here. `info_plist_path` is
|
|
637
|
+
# relative to `user_project.project_dir` (== $(SRCROOT) at build time,
|
|
638
|
+
# confirmed via STEP-2.11 real-project verification -- see the comment
|
|
639
|
+
# on xcconfig_path above), so relative-to-$(SRCROOT) is what
|
|
640
|
+
# merge_info_plist/merge_entitlements are expected to return.
|
|
641
|
+
config.build_settings['INFOPLIST_FILE'] = "$(SRCROOT)/#{info_plist_path}"
|
|
655
642
|
config.build_settings['ASSETCATALOG_COMPILER_APPICON_NAME'] = icon_name
|
|
656
643
|
# entitlements: is opt-in (unlike INFOPLIST_FILE/the icon catalog, which
|
|
657
644
|
# every flavor always has) -- only set CODE_SIGN_ENTITLEMENTS when the
|
|
@@ -662,7 +649,7 @@ module AnnFlavorCocoapods
|
|
|
662
649
|
# longer exists on disk (a build-breaking dangling reference, not
|
|
663
650
|
# merely a stale value like most other settings here).
|
|
664
651
|
if entitlements_path
|
|
665
|
-
config.build_settings['CODE_SIGN_ENTITLEMENTS'] = entitlements_path
|
|
652
|
+
config.build_settings['CODE_SIGN_ENTITLEMENTS'] = "$(SRCROOT)/#{entitlements_path}"
|
|
666
653
|
else
|
|
667
654
|
config.build_settings.delete('CODE_SIGN_ENTITLEMENTS')
|
|
668
655
|
end
|
|
@@ -808,138 +795,53 @@ module AnnFlavorCocoapods
|
|
|
808
795
|
# flavor-specific" and gave nothing else (e.g. a CocoaPods plugin like
|
|
809
796
|
# ann_ads) any way to contribute Info.plist keys, since INFOPLIST_FILE
|
|
810
797
|
# pointed at the fully-resolved flavor file and Runner/Info.plist was
|
|
811
|
-
# never read again. This
|
|
812
|
-
#
|
|
798
|
+
# never read again. This composition used to happen as a build-time
|
|
799
|
+
# PBXShellScriptBuildPhase (plan 038/039); plan 040-follow-up moved it
|
|
800
|
+
# here, to pod-install time, because every value it needs (the flavor
|
|
801
|
+
# name, both source files) is already fully known at post_integrate time
|
|
802
|
+
# -- there is no genuine build-time input. A script phase producing the
|
|
803
|
+
# file CODE_SIGN_ENTITLEMENTS/INFOPLIST_FILE point at is what caused a
|
|
804
|
+
# real, unfixable "Cycle inside Runner" failure in Xcode's new build
|
|
805
|
+
# system: any PBXShellScriptBuildPhase with declared output_paths becomes
|
|
806
|
+
# a graph node XCBuild must order relative to a shared per-target
|
|
807
|
+
# "ModuleVerifierTaskProducer" gate that framework-embed phases (Pods,
|
|
808
|
+
# SPM packages alike) are also gated behind -- no position in
|
|
809
|
+
# build_phases avoids it, confirmed live against ledger across several
|
|
810
|
+
# failed attempts (0.4.1-0.4.5). Writing a plain static file at
|
|
811
|
+
# post_integrate time, before Xcode ever opens the project to build,
|
|
812
|
+
# removes the script phase (and its graph node) entirely -- the same
|
|
813
|
+
# pattern CocoaPods' own Pods-Runner-Info.plist already uses.
|
|
813
814
|
#
|
|
814
|
-
#
|
|
815
|
-
#
|
|
816
|
-
#
|
|
817
|
-
# docs/03-planning/features/038-info-plist-build-time-merge.md).
|
|
815
|
+
# Returns the merged file's path (relative to `user_project.project_dir`,
|
|
816
|
+
# suitable for a build setting like `$(SRCROOT)/<path>` -- callers pass
|
|
817
|
+
# the absolute path through as INFOPLIST_FILE, see configure_build_settings).
|
|
818
818
|
#
|
|
819
|
-
#
|
|
820
|
-
#
|
|
821
|
-
#
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
OUT="${DERIVED_FILE_DIR}/Info-merged.plist"
|
|
834
|
-
|
|
835
|
-
if [ ! -f "$BASE" ]; then
|
|
836
|
-
echo "error: [ann-flavor] Runner/Info.plist not found at $BASE"
|
|
837
|
-
exit 1
|
|
838
|
-
fi
|
|
819
|
+
# `build_type` (e.g. "debug"/"release"/a custom build_types key) picks
|
|
820
|
+
# which of ios_generator.dart's per-flavor-per-build-type delta files to
|
|
821
|
+
# read -- annspec.yaml's info_plist: cascade is valid at build_types.<bt>
|
|
822
|
+
# level (REQ-FLVR-00110), so this must resolve per build type, not once
|
|
823
|
+
# per flavor, or a build-type-scoped override is silently dropped.
|
|
824
|
+
def self.merge_info_plist(project_dir, flavor_key, build_type)
|
|
825
|
+
base_path = File.join(project_dir, 'Runner', 'Info.plist')
|
|
826
|
+
flavor_path = File.join(project_dir, 'ann', 'Info', "Info-#{flavor_key}-#{build_type}.plist")
|
|
827
|
+
relative_out_path = File.join('ann', 'Info', 'merged', "#{flavor_key}-#{build_type}.plist")
|
|
828
|
+
out_path = File.join(project_dir, relative_out_path)
|
|
829
|
+
|
|
830
|
+
unless File.exist?(base_path)
|
|
831
|
+
raise "[ann-flavor-cocoapods] Runner/Info.plist not found at #{base_path}"
|
|
832
|
+
end
|
|
839
833
|
|
|
840
|
-
|
|
841
|
-
# Runner/Info.plist already has is correct by default; only the
|
|
842
|
-
# flavor delta's keys need explicit merging in below.
|
|
843
|
-
cp "$BASE" "$OUT"
|
|
834
|
+
merged = read_plist(base_path)
|
|
844
835
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
# Enumerate the flavor delta's top-level keys. PlistBuddy "Print" with
|
|
851
|
-
# no key path dumps the whole dict; grep the top-level "Key = " lines
|
|
852
|
-
# (four leading spaces = depth 1 in PlistBuddy's indented dump format).
|
|
853
|
-
FLAVOR_KEYS=$("$PLISTBUDDY" -c "Print" "$FLAVOR_PLIST" | grep -E '^ [A-Za-z]' | sed 's/ =.*//' | sed 's/^ *//')
|
|
854
|
-
|
|
855
|
-
for KEY in $FLAVOR_KEYS; do
|
|
856
|
-
# OQ-2: warn (non-fatal) only on a genuine collision -- Runner/Info.plist
|
|
857
|
-
# already had this key, with a different value, before the flavor delta
|
|
858
|
-
# is applied. A net-new key (not previously in Runner/Info.plist) is not
|
|
859
|
-
# a collision and stays silent -- that's the expected, common case.
|
|
860
|
-
# Compared via PlistBuddy's human-readable Print output, which is
|
|
861
|
-
# good enough for equality-detection purposes even though it can't
|
|
862
|
-
# reliably tell types apart (see the VALUE_XML extraction below,
|
|
863
|
-
# which is what actually determines the merged value/type).
|
|
864
|
-
BASE_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$OUT" 2>/dev/null)
|
|
865
|
-
BASE_HAD_KEY=$?
|
|
866
|
-
FLAVOR_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$FLAVOR_PLIST")
|
|
867
|
-
|
|
868
|
-
if [ "$BASE_HAD_KEY" -eq 0 ] && [ "$BASE_VALUE" != "$FLAVOR_VALUE" ]; then
|
|
869
|
-
echo "[ann-flavor] Info.plist: '$KEY' overridden by flavor value ($FLAVOR_NAME) -- Runner/Info.plist's value ignored for this build."
|
|
870
|
-
fi
|
|
871
|
-
|
|
872
|
-
# Extract the flavor value's own XML fragment (e.g. <string>x</string>,
|
|
873
|
-
# <true/>, <integer>42</integer>, <array>...</array>) via PlistBuddy's
|
|
874
|
-
# -x (XML) output, scoped to just this key, stripping the <plist
|
|
875
|
-
# version="1.0">...</plist> wrapper that -x always adds. This
|
|
876
|
-
# preserves the flavor delta's real plist type -- Print's
|
|
877
|
-
# human-readable output alone can't distinguish an integer 42 from
|
|
878
|
-
# the string "42", or a bool true from the string "true", so a
|
|
879
|
-
# scalar-vs-composite TYPE guess from Print was unreliable and has
|
|
880
|
-
# been replaced with this approach (verified against a real
|
|
881
|
-
# PlistBuddy: bool/int/real/string/array/dict all round-trip
|
|
882
|
-
# correctly this way, plan 038 STEP-02).
|
|
883
|
-
VALUE_XML=$("$PLISTBUDDY" -x -c "Print :$KEY" "$FLAVOR_PLIST" | sed -n '/<plist version/,/<\\/plist>/p' | sed '1d;$d')
|
|
884
|
-
|
|
885
|
-
"$PLISTBUDDY" -c "Delete :$KEY" "$OUT" >/dev/null 2>&1
|
|
886
|
-
FRAGMENT=$(mktemp /tmp/ann-flavor-plist-fragment.XXXXXX)
|
|
887
|
-
cat > "$FRAGMENT" <<XMLEOF
|
|
888
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
889
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
890
|
-
<plist version="1.0">
|
|
891
|
-
<dict>
|
|
892
|
-
<key>$KEY</key>
|
|
893
|
-
$VALUE_XML
|
|
894
|
-
</dict>
|
|
895
|
-
</plist>
|
|
896
|
-
XMLEOF
|
|
897
|
-
"$PLISTBUDDY" -c "Merge \\"$FRAGMENT\\"" "$OUT"
|
|
898
|
-
rm -f "$FRAGMENT"
|
|
899
|
-
done
|
|
900
|
-
SHELL
|
|
836
|
+
if File.exist?(flavor_path)
|
|
837
|
+
merge_plist_layer!(merged, read_plist(flavor_path), "Info.plist", "#{flavor_key}-#{build_type}")
|
|
838
|
+
else
|
|
839
|
+
puts "ann-flavor-cocoapods: no flavor Info.plist found for '#{flavor_key}' (#{build_type}) at #{flavor_path} -- using Runner/Info.plist unmerged."
|
|
840
|
+
end
|
|
901
841
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
phase = existing || target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
|
|
906
|
-
phase.name = '[ann-flavor] Merge Info.plist'
|
|
907
|
-
phase.shell_path = '/bin/sh'
|
|
908
|
-
phase.shell_script = script
|
|
909
|
-
phase.show_env_vars_in_log = '0'
|
|
910
|
-
# Declares this phase's output so Xcode's build system understands the
|
|
911
|
-
# dependency (same technique already used for the Firebase-copy phase's
|
|
912
|
-
# output_paths) -- suppresses "input file not found" for INFOPLIST_FILE
|
|
913
|
-
# referencing a path nothing else produces until this phase runs.
|
|
914
|
-
phase.output_paths = ['$(DERIVED_FILE_DIR)/Info-merged.plist']
|
|
915
|
-
# NOT declaring input_paths on Check Pods Manifest.lock's output here
|
|
916
|
-
# (tried in v0.4.1) -- see the identical, fuller explanation on
|
|
917
|
-
# inject_merge_entitlements_phase below for why that direction was
|
|
918
|
-
# actually the cause of the real "Cycle inside Runner" failure, not a
|
|
919
|
-
# (partial) fix for it.
|
|
920
|
-
|
|
921
|
-
# Appended at the END of build_phases, not unshifted to the front --
|
|
922
|
-
# see the identical, fuller explanation on inject_merge_entitlements_phase
|
|
923
|
-
# below. This phase's output (INFOPLIST_FILE) is only consumed at
|
|
924
|
-
# packaging/signing time, which is late; it has no reason to race
|
|
925
|
-
# framework/resource-copy phases early in the build.
|
|
926
|
-
#
|
|
927
|
-
# Repositions unconditionally, even when `existing` -- see the identical
|
|
928
|
-
# note on inject_merge_entitlements_phase for why (a pre-v0.4.2 project
|
|
929
|
-
# would otherwise never actually get repositioned on re-`pod install`).
|
|
930
|
-
#
|
|
931
|
-
# Uses << , NOT push -- Xcodeproj::Project::ObjectList (build_phases'
|
|
932
|
-
# real class) overrides <</insert/unshift/delete to also update the
|
|
933
|
-
# object's reference count/registration in the project's global
|
|
934
|
-
# objects table, but does NOT override push. Array#push silently falls
|
|
935
|
-
# through to the plain Array behavior, leaving the phase referenced in
|
|
936
|
-
# buildPhases but never actually registered as a project object --
|
|
937
|
-
# confirmed live: this produced a real, silently-corrupt
|
|
938
|
-
# project.pbxproj (a dangling buildPhases reference to a UUID with no
|
|
939
|
-
# matching object), which then failed at Xcode build time with "Build
|
|
940
|
-
# input file cannot be found" for this phase's declared output.
|
|
941
|
-
target.build_phases.delete(phase) if existing
|
|
942
|
-
target.build_phases << phase
|
|
842
|
+
FileUtils.mkdir_p(File.dirname(out_path))
|
|
843
|
+
Xcodeproj::Plist.write_to_path(merged, out_path)
|
|
844
|
+
relative_out_path
|
|
943
845
|
end
|
|
944
846
|
|
|
945
847
|
# Composes the final .entitlements file Xcode actually signs with, from
|
|
@@ -947,154 +849,78 @@ module AnnFlavorCocoapods
|
|
|
947
849
|
# not exist at all) and the flavor-specific ann/Entitlements/<flavor>
|
|
948
850
|
# .entitlements delta (annspec.yaml's entitlements: cascade, opt-in --
|
|
949
851
|
# may also not exist) -- with the flavor layer winning on any key
|
|
950
|
-
# collision.
|
|
951
|
-
#
|
|
952
|
-
#
|
|
953
|
-
#
|
|
954
|
-
#
|
|
955
|
-
#
|
|
956
|
-
# Runner.
|
|
957
|
-
#
|
|
958
|
-
#
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
983
|
-
<plist version="1.0">
|
|
984
|
-
<dict/>
|
|
985
|
-
</plist>
|
|
986
|
-
PLISTEOF
|
|
987
|
-
fi
|
|
852
|
+
# collision. Mirrors merge_info_plist exactly -- see its comment for why
|
|
853
|
+
# this moved from a build-time script phase to pod-install time. The one
|
|
854
|
+
# real difference: unlike Runner/Info.plist (every Xcode project has
|
|
855
|
+
# one), most projects have no Runner.entitlements at all -- a missing
|
|
856
|
+
# base file here is a normal, non-fatal case (starts from an empty plist
|
|
857
|
+
# dict), not the fatal error merge_info_plist treats a missing
|
|
858
|
+
# Runner/Info.plist as.
|
|
859
|
+
#
|
|
860
|
+
# Returns the merged file's path, or nil when there's nothing to sign
|
|
861
|
+
# with at all (no stock Runner.entitlements AND no flavor delta) --
|
|
862
|
+
# callers use a nil return to decide whether to set CODE_SIGN_ENTITLEMENTS.
|
|
863
|
+
#
|
|
864
|
+
# `build_type` resolves per build type -- see the identical note on
|
|
865
|
+
# merge_info_plist for why (entitlements: is valid at build_types.<bt>
|
|
866
|
+
# level too, REQ-FLVR-00120).
|
|
867
|
+
def self.merge_entitlements(project_dir, flavor_key, build_type)
|
|
868
|
+
base_path = File.join(project_dir, 'Runner', 'Runner.entitlements')
|
|
869
|
+
flavor_path = File.join(project_dir, 'ann', 'Entitlements', "#{flavor_key}-#{build_type}.entitlements")
|
|
870
|
+
relative_out_path = File.join('ann', 'Entitlements', 'merged', "#{flavor_key}-#{build_type}.entitlements")
|
|
871
|
+
out_path = File.join(project_dir, relative_out_path)
|
|
872
|
+
|
|
873
|
+
has_base = File.exist?(base_path)
|
|
874
|
+
has_flavor = File.exist?(flavor_path)
|
|
875
|
+
unless has_base || has_flavor
|
|
876
|
+
# Nothing to sign with -- delete a previously-written merged file
|
|
877
|
+
# rather than leaving it stale on disk (CODE_SIGN_ENTITLEMENTS is
|
|
878
|
+
# correctly cleared by the caller either way, but an orphaned file
|
|
879
|
+
# here is still generated-output drift the same "disposable, always
|
|
880
|
+
# regenerated" convention ios/ann/ uses elsewhere is meant to avoid).
|
|
881
|
+
FileUtils.rm_f(out_path)
|
|
882
|
+
return nil
|
|
883
|
+
end
|
|
988
884
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
885
|
+
merged = has_base ? read_plist(base_path) : {}
|
|
886
|
+
if has_flavor
|
|
887
|
+
merge_plist_layer!(merged, read_plist(flavor_path), "Entitlements", "#{flavor_key}-#{build_type}")
|
|
888
|
+
elsif has_base
|
|
889
|
+
puts "ann-flavor-cocoapods: no flavor delta found for '#{flavor_key}' (#{build_type}) at #{flavor_path} -- using Runner.entitlements unmerged."
|
|
890
|
+
end
|
|
993
891
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
BASE_HAD_KEY=$?
|
|
999
|
-
FLAVOR_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$FLAVOR_FILE")
|
|
1000
|
-
|
|
1001
|
-
if [ "$BASE_HAD_KEY" -eq 0 ] && [ "$BASE_VALUE" != "$FLAVOR_VALUE" ]; then
|
|
1002
|
-
echo "[ann-flavor] Entitlements: '$KEY' overridden by flavor value ($FLAVOR_NAME) -- Runner.entitlements' value ignored for this build."
|
|
1003
|
-
fi
|
|
1004
|
-
|
|
1005
|
-
VALUE_XML=$("$PLISTBUDDY" -x -c "Print :$KEY" "$FLAVOR_FILE" | sed -n '/<plist version/,/<\\/plist>/p' | sed '1d;$d')
|
|
1006
|
-
|
|
1007
|
-
"$PLISTBUDDY" -c "Delete :$KEY" "$OUT" >/dev/null 2>&1
|
|
1008
|
-
FRAGMENT=$(mktemp /tmp/ann-flavor-entitlements-fragment.XXXXXX)
|
|
1009
|
-
cat > "$FRAGMENT" <<XMLEOF
|
|
1010
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
1011
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1012
|
-
<plist version="1.0">
|
|
1013
|
-
<dict>
|
|
1014
|
-
<key>$KEY</key>
|
|
1015
|
-
$VALUE_XML
|
|
1016
|
-
</dict>
|
|
1017
|
-
</plist>
|
|
1018
|
-
XMLEOF
|
|
1019
|
-
"$PLISTBUDDY" -c "Merge \\"$FRAGMENT\\"" "$OUT"
|
|
1020
|
-
rm -f "$FRAGMENT"
|
|
1021
|
-
done
|
|
1022
|
-
SHELL
|
|
892
|
+
FileUtils.mkdir_p(File.dirname(out_path))
|
|
893
|
+
Xcodeproj::Plist.write_to_path(merged, out_path)
|
|
894
|
+
relative_out_path
|
|
895
|
+
end
|
|
1023
896
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
# which Xcode's new build system gates behind a chain of task producers
|
|
1037
|
-
# (ModuleVerifierTaskProducer et al) — declaring Check Pods Manifest
|
|
1038
|
-
# .lock's result file as this phase's OWN input routes this phase
|
|
1039
|
-
# through that SAME gate chain from the other direction, since Check
|
|
1040
|
-
# Pods Manifest.lock's output is itself gated behind
|
|
1041
|
-
# ModuleVerifierTaskProducer too. The Firebase framework-copy phases
|
|
1042
|
-
# are gated behind that identical chain — closing a genuine cycle no
|
|
1043
|
-
# matter where this phase sits in build_phases. Xcode's own error
|
|
1044
|
-
# message ("moving Check Pods Manifest.lock so it runs before the
|
|
1045
|
-
# phase that depends on its outputs") is describing exactly this
|
|
1046
|
-
# self-inflicted dependency, not a real requirement — this phase never
|
|
1047
|
-
# actually needed Check Pods Manifest.lock's result as an input;
|
|
1048
|
-
# output_paths + running after the Pods/framework-copy phases (via
|
|
1049
|
-
# position, below) is sufficient on its own.
|
|
1050
|
-
#
|
|
1051
|
-
# Appended at the END of build_phases, not unshifted to the front.
|
|
1052
|
-
# v0.4.1 tried fixing this phase's real "Cycle inside Runner" Xcode
|
|
1053
|
-
# build failure (confirmed live against ledger, a project with real
|
|
1054
|
-
# CocoaPods frameworks like Firebase) purely by declaring input_paths
|
|
1055
|
-
# above -- that alone was NOT sufficient. The actual cycle: this
|
|
1056
|
-
# phase's output feeds ProcessProductPackaging (entitlements signing,
|
|
1057
|
-
# gated behind a chain of task producers Xcode's new build system
|
|
1058
|
-
# shares with the Firebase framework-copy phases), and unshifting this
|
|
1059
|
-
# phase to the very FRONT of build_phases put it, and its declared
|
|
1060
|
-
# output dependency, on the same task-producer chain the framework
|
|
1061
|
-
# copy phases are gated behind on the other side -- a genuine cycle
|
|
1062
|
-
# through shared gates, not resolvable by declaring one more input.
|
|
1063
|
-
# CODE_SIGN_ENTITLEMENTS/INFOPLIST_FILE are only consumed at
|
|
1064
|
-
# packaging/signing time, which is late in the build -- this phase has
|
|
1065
|
-
# no real reason to run before Sources/Frameworks/Resources/Embed
|
|
1066
|
-
# Frameworks at all. Appending it at the end removes it from that
|
|
1067
|
-
# shared gate entirely.
|
|
1068
|
-
#
|
|
1069
|
-
# Repositions unconditionally, even when `existing` (a project that
|
|
1070
|
-
# already has this phase from a pre-v0.4.2 install, sitting at the
|
|
1071
|
-
# front from the old unshift) -- otherwise re-running `pod install`
|
|
1072
|
-
# after upgrading the gem would never actually move it, and the real
|
|
1073
|
-
# bug would persist for every already-affected consumer.
|
|
1074
|
-
#
|
|
1075
|
-
# Uses << , NOT push -- see the identical, fuller explanation on
|
|
1076
|
-
# inject_merge_info_plist_phase above. Array#push silently bypasses
|
|
1077
|
-
# ObjectList's reference-count/registration bookkeeping, producing a
|
|
1078
|
-
# real, silently-corrupt project.pbxproj (buildPhases referencing a
|
|
1079
|
-
# UUID with no matching object) that fails at Xcode build time with
|
|
1080
|
-
# "Build input file cannot be found" -- confirmed live against ledger.
|
|
1081
|
-
target.build_phases.delete(phase) if existing
|
|
1082
|
-
target.build_phases << phase
|
|
897
|
+
# Reads a plist file as a plain Ruby Hash. Uses `plutil -convert json`
|
|
898
|
+
# (a system tool, ships with Xcode/macOS), NOT Xcodeproj::Plist
|
|
899
|
+
# .read_from_path -- that method's pure-Ruby CFPropertyList fallback path
|
|
900
|
+
# is broken under Ruby 3.4 in this environment (missing stdlib 'kconv',
|
|
901
|
+
# confirmed live) -- same workaround already established in this repo's
|
|
902
|
+
# own spec suite (merge_entitlements_script_spec.rb/
|
|
903
|
+
# merge_info_plist_script_spec.rb).
|
|
904
|
+
def self.read_plist(path)
|
|
905
|
+
json = `#{PLUTIL} -convert json -o - "#{path}"`
|
|
906
|
+
raise "[ann-flavor-cocoapods] plutil failed to read #{path}" unless $?.success?
|
|
907
|
+
|
|
908
|
+
JSON.parse(json)
|
|
1083
909
|
end
|
|
1084
910
|
|
|
1085
|
-
#
|
|
1086
|
-
#
|
|
1087
|
-
#
|
|
1088
|
-
#
|
|
1089
|
-
#
|
|
1090
|
-
#
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
end
|
|
911
|
+
# Merges `layer` into `base` in place, one top-level key at a time --
|
|
912
|
+
# `layer`'s value wins on any key both define, printing a one-line notice
|
|
913
|
+
# so an unintentional override is easy to spot (same collision-warning
|
|
914
|
+
# behavior the build-time script phases had). A net-new key (not
|
|
915
|
+
# previously in `base`) is not a collision and stays silent -- the
|
|
916
|
+
# expected, common case.
|
|
917
|
+
def self.merge_plist_layer!(base, layer, kind, flavor_key)
|
|
918
|
+
layer.each do |key, value|
|
|
919
|
+
if base.key?(key) && base[key] != value
|
|
920
|
+
puts "ann-flavor-cocoapods: [#{kind}] '#{key}' overridden by flavor value (#{flavor_key}) -- base value ignored for this build."
|
|
921
|
+
end
|
|
922
|
+
base[key] = value
|
|
923
|
+
end
|
|
1098
924
|
end
|
|
1099
925
|
end
|
|
1100
926
|
end
|