@@ -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 ));
0 commit comments