Skip to content

Commit b5f759e

Browse files
committed
dist-apple: options::omit_keys leaves a defaulted Info.plist key out (#649 P1)
1 parent c804312 commit b5f759e

3 files changed

Lines changed: 85 additions & 1 deletion

File tree

dist/apple.cppm

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,17 @@ struct options {
293293
// `NSHighResolutionCapable`) are replaced by the project's value.
294294
std::string info_plist;
295295

296+
// DEFAULTED Info.plist KEYS TO LEAVE OUT (0.12.0). A property list has no
297+
// null, so `info_plist` can replace a default's value but cannot remove the
298+
// key; a project whose other build states neither `NSHighResolutionCapable`
299+
// nor `LSRequiresIPhoneOS` names them here (#649 P1). Only the three keys
300+
// this member defaults are accepted: a key it derives is refused by name, as
301+
// `info_plist` refuses it, and so is any other key, which this member never
302+
// writes. A key named here and also set by `info_plist` is refused, because
303+
// the two statements contradict each other. A key that does not apply to the
304+
// row (`UIDeviceFamily` on macOS) is accepted and changes nothing.
305+
std::vector<std::string> omit_keys;
306+
296307
// AN iOS DEVICE BUNDLE'S PROVISIONING PROFILE (0.11.0), manifest-relative or
297308
// absolute: embedded as `embedded.mobileprovision`, and, when `entitlements`
298309
// is empty, the source of the entitlements the bundle is signed with (the
@@ -948,6 +959,33 @@ inline plan plan_for(options opt = {}) {
948959
if (!read_info_plist_fragment(opt.info_plist, extraEntries, replacedKeys, message))
949960
return refuse(p, "unusable info_plist", message);
950961
}
962+
for (std::size_t i = 0; i < opt.omit_keys.size(); ++i) {
963+
const auto& key = opt.omit_keys[i];
964+
if (std::ranges::find(derived_plist_keys(), key) != derived_plist_keys().end()) {
965+
return refuse(p, "omit_keys names a derived key", std::format(
966+
"mcpp.dist.apple: `options::omit_keys` names {}, which this member derives "
967+
"from its options and the engine; a derived key cannot be omitted.", key));
968+
}
969+
if (std::ranges::find(defaulted_plist_keys(), key) == defaulted_plist_keys().end()) {
970+
return refuse(p, "omit_keys names a key this member does not default", std::format(
971+
"mcpp.dist.apple: `options::omit_keys` names {}, which this member does not "
972+
"write; only UIDeviceFamily, LSRequiresIPhoneOS and NSHighResolutionCapable "
973+
"can be omitted.", key));
974+
}
975+
if (std::find(opt.omit_keys.begin(), opt.omit_keys.begin() + static_cast<std::ptrdiff_t>(i), key)
976+
!= opt.omit_keys.begin() + static_cast<std::ptrdiff_t>(i)) {
977+
return refuse(p, "omit_keys names a key twice", std::format(
978+
"mcpp.dist.apple: `options::omit_keys` names {} twice.", key));
979+
}
980+
if (std::ranges::find(replacedKeys, key) != replacedKeys.end()) {
981+
return refuse(p, "omit_keys and info_plist name one key", std::format(
982+
"mcpp.dist.apple: `options::omit_keys` names {}, and `options::info_plist` ({}) "
983+
"sets it; state one of the two.", key, opt.info_plist));
984+
}
985+
}
986+
// An omitted default is written the way a replaced one is: not at all. The
987+
// replaced list is what `plist_document` consults, so the two share it.
988+
for (auto const& key : opt.omit_keys) replacedKeys.push_back(key);
951989
if (!opt.entitlements.empty() && !is_file(opt.entitlements)) {
952990
return refuse(p, "entitlements not found", std::format(
953991
"mcpp.dist.apple: the entitlements file {} was not found", opt.entitlements));

tests/ios-app-consumer/build.mcpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,19 @@ int main() {
2626
// leave the options as they are, because the script passes every variable
2727
// to every row. The macOS runner's real pack names the first one too.
2828
for (const char* v : {"IOS_APP_CONSUMER_INFO_PLIST", "IOS_APP_CONSUMER_PROFILE",
29-
"IOS_APP_CONSUMER_NO_IDENTITY"})
29+
"IOS_APP_CONSUMER_NO_IDENTITY", "IOS_APP_CONSUMER_OMIT_KEYS"})
3030
mcpp::rerun_if_env_changed(v);
31+
// 0.12.0: `options::omit_keys`, comma-separated in the variable.
32+
if (const char* v = std::getenv("IOS_APP_CONSUMER_OMIT_KEYS"); v && *v) {
33+
std::string list = v;
34+
for (std::size_t b = 0; b <= list.size();) {
35+
const auto e = list.find(',', b);
36+
const auto key = list.substr(b, e == std::string::npos ? std::string::npos : e - b);
37+
if (!key.empty()) opt.omit_keys.push_back(key);
38+
if (e == std::string::npos) break;
39+
b = e + 1;
40+
}
41+
}
3142
if (const char* v = std::getenv("IOS_APP_CONSUMER_INFO_PLIST"); v && *v) opt.info_plist = v;
3243
if (const char* v = std::getenv("IOS_APP_CONSUMER_PROFILE"); v && *v) opt.provisioning_profile = v;
3344
if (const char* v = std::getenv("IOS_APP_CONSUMER_NO_IDENTITY"); v && *v) opt.identity.clear();

tests/ios-app-consumer/check-ios-plan.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ run_row() {
122122
IOS_APP_CONSUMER_INFO_PLIST="${IOS_APP_CONSUMER_INFO_PLIST:-}" \
123123
IOS_APP_CONSUMER_PROFILE="${IOS_APP_CONSUMER_PROFILE:-}" \
124124
IOS_APP_CONSUMER_NO_IDENTITY="${IOS_APP_CONSUMER_NO_IDENTITY:-}" \
125+
IOS_APP_CONSUMER_OMIT_KEYS="${IOS_APP_CONSUMER_OMIT_KEYS:-}" \
125126
"$BIN" > "$log" 2>&1
126127
echo "$out"
127128
}
@@ -250,6 +251,40 @@ grep -q 'mcpp.dist.apple.layout' /tmp/ios-plan-sim-derived.log \
250251
unset IOS_APP_CONSUMER_INFO_PLIST
251252
echo "ok: an entry this member derives is refused, naming the key"
252253

254+
# ── 0.12.0 ─────────────────────────────────────────────────────────────────
255+
echo "== 0.12.0: options::omit_keys leaves a defaulted key out =="
256+
export IOS_APP_CONSUMER_OMIT_KEYS="LSRequiresIPhoneOS"
257+
outdir=$(run_row simomit ios sim /tmp/ios-plan-sim-omit.log)
258+
plist="$outdir/IosAppConsumer-Info.plist"
259+
[ -f "$plist" ] || fail "no Info.plist written with options::omit_keys" /tmp/ios-plan-sim-omit.log
260+
grep -q '<key>LSRequiresIPhoneOS</key>' "$plist" && fail "LSRequiresIPhoneOS was written although omitted" "$plist"
261+
grep -q '<key>UIDeviceFamily</key>' "$plist" || fail "omitting one default removed another" "$plist"
262+
check_terminal_bundle /tmp/ios-plan-sim-omit.log
263+
export IOS_APP_CONSUMER_OMIT_KEYS="NSHighResolutionCapable,UIDeviceFamily"
264+
outdir=$(run_row macosomit macos "" /tmp/ios-plan-macos-omit.log)
265+
plist="$outdir/IosAppConsumer-Info.plist"
266+
grep -q '<key>NSHighResolutionCapable</key>' "$plist" && fail "NSHighResolutionCapable was written on macOS although omitted" "$plist"
267+
grep -q '<key>CFBundleExecutable</key>' "$plist" || fail "the macOS plist lost a derived key" "$plist"
268+
echo "ok: an omitted default is not written, on iOS and on macOS, and the rest of the plist is unchanged"
269+
270+
export IOS_APP_CONSUMER_OMIT_KEYS="CFBundleIdentifier"
271+
run_row simomitderived ios sim /tmp/ios-plan-sim-omit-derived.log > /dev/null
272+
grep -q 'names CFBundleIdentifier, which this member derives' /tmp/ios-plan-sim-omit-derived.log \
273+
|| fail "omitting a derived key was not refused by name" /tmp/ios-plan-sim-omit-derived.log
274+
grep -q 'mcpp.dist.apple.layout' /tmp/ios-plan-sim-omit-derived.log \
275+
&& fail "steps were planned although omit_keys was refused" /tmp/ios-plan-sim-omit-derived.log
276+
export IOS_APP_CONSUMER_OMIT_KEYS="NSCameraUsageDescription"
277+
run_row simomitother ios sim /tmp/ios-plan-sim-omit-other.log > /dev/null
278+
grep -q 'names NSCameraUsageDescription, which this member does not write' /tmp/ios-plan-sim-omit-other.log \
279+
|| fail "omitting a key this member does not default was not refused by name" /tmp/ios-plan-sim-omit-other.log
280+
export IOS_APP_CONSUMER_OMIT_KEYS="UIDeviceFamily"
281+
export IOS_APP_CONSUMER_INFO_PLIST="$PWD/info-plist/usage.plist"
282+
run_row simomitboth ios sim /tmp/ios-plan-sim-omit-both.log > /dev/null
283+
grep -q 'names UIDeviceFamily, and `options::info_plist`' /tmp/ios-plan-sim-omit-both.log \
284+
|| fail "a key both omitted and set by info_plist was not refused" /tmp/ios-plan-sim-omit-both.log
285+
unset IOS_APP_CONSUMER_OMIT_KEYS IOS_APP_CONSUMER_INFO_PLIST
286+
echo "ok: omitting a derived key, a key this member does not write, or a key info_plist sets is refused by name"
287+
253288
# A provisioning profile is a CMS-signed plist; the member reads the plist from
254289
# between its markers, so a plan needs only those bytes framed by binary data.
255290
make_profile() { # make_profile <file> <application-identifier>

0 commit comments

Comments
 (0)