Files
Nearfield_Friends/scripts/generate_xcodeproj.py

638 lines
28 KiB
Python

#!/usr/bin/env python3
"""
Generate Proximity.xcodeproj from the source tree.
Produces a valid Xcode project (objectVersion 77 / Xcode 16) with:
- Proximity app target (iOS, SwiftUI)
- ProximityUITests UI testing target
- A shared scheme so `xcodebuild test` works out of the box
Run from the repo root:
python3 scripts/generate_xcodeproj.py
"""
import os
import uuid
import json
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
APP_DIR = os.path.join(ROOT, "Proximity")
UITEST_DIR = os.path.join(ROOT, "ProximityUITests")
OUT_DIR = os.path.join(ROOT, "Proximity.xcodeproj")
# ---------------------------------------------------------------------------
# Stable-ish ID generation (deterministic per path so re-runs are stable)
# ---------------------------------------------------------------------------
def stable_id(seed: str) -> str:
# 24 hex chars, deterministic from the seed string.
h = uuid.uuid5(uuid.NAMESPACE_URL, seed).hex
return h[:24].upper()
# ---------------------------------------------------------------------------
# Collect source files
# ---------------------------------------------------------------------------
def swift_files(directory: str) -> list[str]:
files = []
for dirpath, _, filenames in os.walk(directory):
for f in sorted(filenames):
if f.endswith(".swift"):
full = os.path.join(dirpath, f)
rel = os.path.relpath(full, ROOT)
files.append(rel)
return files
app_sources = swift_files(APP_DIR)
uitest_sources = swift_files(UITEST_DIR)
# ---------------------------------------------------------------------------
# Build the pbxproj
# ---------------------------------------------------------------------------
def build():
lines = []
add = lines.append
# --- Object IDs ---
proj_id = stable_id("project")
app_target_id = stable_id("target:Proximity")
uitest_target_id = stable_id("target:ProximityUITests")
app_config_list_id = stable_id("configlist:Proximity")
uitest_config_list_id = stable_id("configlist:ProximityUITests")
proj_config_list_id = stable_id("configlist:project")
app_debug_id = stable_id("config:Proximity:Debug")
app_release_id = stable_id("config:Proximity:Release")
uitest_debug_id = stable_id("config:ProximityUITests:Debug")
uitest_release_id = stable_id("config:ProximityUITests:Release")
proj_debug_id = stable_id("config:project:Debug")
proj_release_id = stable_id("config:project:Release")
sources_build_phase_id = stable_id("phase:sources:Proximity")
frameworks_build_phase_id = stable_id("phase:frameworks:Proximity")
resources_build_phase_id = stable_id("phase:resources:Proximity")
uitest_sources_phase_id = stable_id("phase:sources:ProximityUITests")
uitest_frameworks_phase_id = stable_id("phase:frameworks:ProximityUITests")
uitest_resources_phase_id = stable_id("phase:resources:ProximityUITests")
app_product_id = stable_id("product:Proximity.app")
uitest_product_id = stable_id("product:ProximityUITests.xctest")
app_group_id = stable_id("group:Proximity")
uitest_group_id = stable_id("group:ProximityUITests")
main_group_id = stable_id("group:main")
products_group_id = stable_id("group:products")
app_target_dep_id = stable_id("dep:ProximityUITests->Proximity")
container_proxy_id = stable_id("proxy:ProximityUITests->Proximity")
# Build file IDs per source file (deterministic).
app_build_files = {}
for src in app_sources:
app_build_files[src] = stable_id("buildfile:" + src)
uitest_build_files = {}
for src in uitest_sources:
uitest_build_files[src] = stable_id("buildfile:" + src)
# File reference IDs per source file.
app_file_refs = {}
for src in app_sources:
app_file_refs[src] = stable_id("fileref:" + src)
uitest_file_refs = {}
for src in uitest_sources:
uitest_file_refs[src] = stable_id("fileref:" + src)
add("// !$*UTF8*$!")
add("{")
add("\tarchiveVersion = 1;")
add("\tclasses = {")
add("\t};")
add("\tobjectVersion = 77;")
add("\tobjects = {")
# ---------------- PBXBuildFile ----------------
add("\t\t/* Begin PBXBuildFile section */")
for src in app_sources:
add(f'\t\t{app_build_files[src]} /* {os.path.basename(src)} in Sources */ = {{isa = PBXBuildFile; fileRef = {app_file_refs[src]} /* {os.path.basename(src)} */; }};')
for src in uitest_sources:
add(f'\t\t{uitest_build_files[src]} /* {os.path.basename(src)} in Sources */ = {{isa = PBXBuildFile; fileRef = {uitest_file_refs[src]} /* {os.path.basename(src)} */; }};')
add("\t\t/* End PBXBuildFile section */")
# ---------------- PBXContainerItemProxy ----------------
add("\t\t/* Begin PBXContainerItemProxy section */")
add(f'\t\t{container_proxy_id} /* PBXContainerItemProxy */ = {{')
add("\t\t\tisa = PBXContainerItemProxy;")
add(f"\t\t\tcontainerPortal = {proj_id} /* Project object */;")
add("\t\t\tproxyType = 1;")
add(f"\t\t\tremoteGlobalIDString = {app_target_id};")
add(f"\t\t\tremoteInfo = Proximity;")
add("\t\t};")
add("\t\t/* End PBXContainerItemProxy section */")
# ---------------- PBXFileReference (products) ----------------
add("\t\t/* Begin PBXFileReference section */")
add(f'\t\t{app_product_id} /* Proximity.app */ = {{isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Proximity.app; sourceTree = BUILT_PRODUCTS_DIR; }};')
add(f'\t\t{uitest_product_id} /* ProximityUITests.xctest */ = {{isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ProximityUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }};')
# Source file refs
for src in app_sources:
add(f'\t\t{app_file_refs[src]} /* {os.path.basename(src)} */ = {{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = {os.path.basename(src)}; sourceTree = "<group>"; }};')
for src in uitest_sources:
add(f'\t\t{uitest_file_refs[src]} /* {os.path.basename(src)} */ = {{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = {os.path.basename(src)}; sourceTree = "<group>"; }};')
add("\t\t/* End PBXFileReference section */")
# ---------------- PBXFrameworksBuildPhase ----------------
add("\t\t/* Begin PBXFrameworksBuildPhase section */")
add(f'\t\t{frameworks_build_phase_id} /* Frameworks */ = {{')
add("\t\t\tisa = PBXFrameworksBuildPhase;")
add("\t\t\tbuildActionMask = 2147483647;")
add("\t\t\tfiles = (")
add("\t\t\t);")
add("\t\t\trunOnlyForDeploymentPostprocessing = 0;")
add("\t\t};")
add(f'\t\t{uitest_frameworks_phase_id} /* Frameworks */ = {{')
add("\t\t\tisa = PBXFrameworksBuildPhase;")
add("\t\t\tbuildActionMask = 2147483647;")
add("\t\t\tfiles = (")
add("\t\t\t);")
add("\t\t\trunOnlyForDeploymentPostprocessing = 0;")
add("\t\t};")
add("\t\t/* End PBXFrameworksBuildPhase section */")
# ---------------- PBXGroup ----------------
add("\t\t/* Begin PBXGroup section */")
add(f'\t\t{main_group_id} = {{')
add("\t\t\tisa = PBXGroup;")
add("\t\t\tchildren = (")
add(f"\t\t\t\t{app_group_id} /* Proximity */,")
add(f"\t\t\t\t{uitest_group_id} /* ProximityUITests */,")
add(f"\t\t\t\t{products_group_id} /* Products */,")
add("\t\t\t);")
add("\t\t\tsourceTree = \"<group>\";")
add("\t\t};")
add(f'\t\t{app_group_id} /* Proximity */ = {{')
add("\t\t\tisa = PBXGroup;")
add("\t\t\tchildren = (")
for src in app_sources:
add(f"\t\t\t\t{app_file_refs[src]} /* {os.path.basename(src)} */,")
add("\t\t\t);")
add("\t\t\tpath = Proximity;")
add("\t\t\tsourceTree = \"<group>\";")
add("\t\t};")
add(f'\t\t{uitest_group_id} /* ProximityUITests */ = {{')
add("\t\t\tisa = PBXGroup;")
add("\t\t\tchildren = (")
for src in uitest_sources:
add(f"\t\t\t\t{uitest_file_refs[src]} /* {os.path.basename(src)} */,")
add("\t\t\t);")
add("\t\t\tpath = ProximityUITests;")
add("\t\t\tsourceTree = \"<group>\";")
add("\t\t};")
add(f'\t\t{products_group_id} /* Products */ = {{')
add("\t\t\tisa = PBXGroup;")
add("\t\t\tchildren = (")
add(f"\t\t\t\t{app_product_id} /* Proximity.app */,")
add(f"\t\t\t\t{uitest_product_id} /* ProximityUITests.xctest */,")
add("\t\t\t);")
add("\t\t\tname = Products;")
add("\t\t\tsourceTree = \"<group>\";")
add("\t\t};")
add("\t\t/* End PBXGroup section */")
# ---------------- PBXNativeTarget ----------------
add("\t\t/* Begin PBXNativeTarget section */")
add(f'\t\t{app_target_id} /* Proximity */ = {{')
add("\t\t\tisa = PBXNativeTarget;")
add(f"\t\t\tbuildConfigurationList = {app_config_list_id} /* Build configuration list for PBXNativeTarget \\\"Proximity\\\" */;")
add("\t\t\tbuildPhases = (")
add(f"\t\t\t\t{sources_build_phase_id} /* Sources */,")
add(f"\t\t\t\t{frameworks_build_phase_id} /* Frameworks */,")
add(f"\t\t\t\t{resources_build_phase_id} /* Resources */,")
add("\t\t\t);")
add("\t\t\tbuildRules = (")
add("\t\t\t);")
add("\t\t\tdependencies = (")
add("\t\t\t);")
add(f"\t\t\tname = Proximity;")
add(f"\t\t\tproductName = Proximity;")
add(f"\t\t\tproductReference = {app_product_id} /* Proximity.app */;")
add("\t\t\tproductType = \"com.apple.product-type.application\";")
add("\t\t};")
add(f'\t\t{uitest_target_id} /* ProximityUITests */ = {{')
add("\t\t\tisa = PBXNativeTarget;")
add(f"\t\t\tbuildConfigurationList = {uitest_config_list_id} /* Build configuration list for PBXNativeTarget \\\"ProximityUITests\\\" */;")
add("\t\t\tbuildPhases = (")
add(f"\t\t\t\t{uitest_sources_phase_id} /* Sources */,")
add(f"\t\t\t\t{uitest_frameworks_phase_id} /* Frameworks */,")
add(f"\t\t\t\t{uitest_resources_phase_id} /* Resources */,")
add("\t\t\t);")
add("\t\t\tbuildRules = (")
add("\t\t\t);")
add("\t\t\tdependencies = (")
add(f"\t\t\t\t{app_target_dep_id} /* PBXTargetDependency */,")
add("\t\t\t);")
add("\t\t\tname = ProximityUITests;")
add("\t\t\tproductName = ProximityUITests;")
add(f"\t\t\tproductReference = {uitest_product_id} /* ProximityUITests.xctest */;")
add("\t\t\tproductType = \"com.apple.product-type.bundle.ui-testing\";")
add("\t\t};")
add("\t\t/* End PBXNativeTarget section */")
# ---------------- PBXProject ----------------
add("\t\t/* Begin PBXProject section */")
add(f'\t\t{proj_id} /* Project object */ = {{')
add("\t\t\tisa = PBXProject;")
add("\t\t\tattributes = {")
add("\t\t\t\tBuildIndependentTargetsInParallel = 1;")
add("\t\t\t\tLastSwiftUpdateCheck = 1600;")
add("\t\t\t\tLastUpgradeCheck = 1600;")
add("\t\t\t\tTargetAttributes = {")
add(f"\t\t\t\t\t{app_target_id} = {{")
add("\t\t\t\t\t\tCreatedOnToolsVersion = 16.0;")
add("\t\t\t\t\t};")
add(f"\t\t\t\t\t{uitest_target_id} = {{")
add("\t\t\t\t\t\tCreatedOnToolsVersion = 16.0;")
add(f"\t\t\t\t\t\tTestTargetID = {app_target_id};")
add("\t\t\t\t\t};")
add("\t\t\t\t};")
add("\t\t\t};")
add(f"\t\t\tbuildConfigurationList = {proj_config_list_id} /* Build configuration list for PBXProject \\\"Proximity\\\" */;")
add("\t\t\tcompatibilityVersion = \"Xcode 15.0\";")
add("\t\t\tdevelopmentRegion = en;")
add("\t\t\thasScannedForEncodings = 0;")
add("\t\t\tknownRegions = (")
add("\t\t\t\ten,")
add("\t\t\t\tBase,")
add("\t\t\t);")
add(f"\t\t\tmainGroup = {main_group_id} /* main group */;")
add(f"\t\t\tproductRefGroup = {products_group_id} /* Products */;")
add("\t\t\tprojectDirPath = \"\";")
add("\t\t\tprojectRoot = \"\";")
add("\t\t\ttargets = (")
add(f"\t\t\t\t{app_target_id} /* Proximity */,")
add(f"\t\t\t\t{uitest_target_id} /* ProximityUITests */,")
add("\t\t\t);")
add("\t\t};")
add("\t\t/* End PBXProject section */")
# ---------------- PBXResourcesBuildPhase ----------------
add("\t\t/* Begin PBXResourcesBuildPhase section */")
add(f'\t\t{resources_build_phase_id} /* Resources */ = {{')
add("\t\t\tisa = PBXResourcesBuildPhase;")
add("\t\t\tbuildActionMask = 2147483647;")
add("\t\t\tfiles = (")
add("\t\t\t);")
add("\t\t\trunOnlyForDeploymentPostprocessing = 0;")
add("\t\t};")
add(f'\t\t{uitest_resources_phase_id} /* Resources */ = {{')
add("\t\t\tisa = PBXResourcesBuildPhase;")
add("\t\t\tbuildActionMask = 2147483647;")
add("\t\t\tfiles = (")
add("\t\t\t);")
add("\t\t\trunOnlyForDeploymentPostprocessing = 0;")
add("\t\t};")
add("\t\t/* End PBXResourcesBuildPhase section */")
# ---------------- PBXSourcesBuildPhase ----------------
add("\t\t/* Begin PBXSourcesBuildPhase section */")
add(f'\t\t{sources_build_phase_id} /* Sources */ = {{')
add("\t\t\tisa = PBXSourcesBuildPhase;")
add("\t\t\tbuildActionMask = 2147483647;")
add("\t\t\tfiles = (")
for src in app_sources:
add(f"\t\t\t\t{app_build_files[src]} /* {os.path.basename(src)} in Sources */,")
add("\t\t\t);")
add("\t\t\trunOnlyForDeploymentPostprocessing = 0;")
add("\t\t};")
add(f'\t\t{uitest_sources_phase_id} /* Sources */ = {{')
add("\t\t\tisa = PBXSourcesBuildPhase;")
add("\t\t\tbuildActionMask = 2147483647;")
add("\t\t\tfiles = (")
for src in uitest_sources:
add(f"\t\t\t\t{uitest_build_files[src]} /* {os.path.basename(src)} in Sources */,")
add("\t\t\t);")
add("\t\t\trunOnlyForDeploymentPostprocessing = 0;")
add("\t\t};")
add("\t\t/* End PBXSourcesBuildPhase section */")
# ---------------- PBXTargetDependency ----------------
add("\t\t/* Begin PBXTargetDependency section */")
add(f'\t\t{app_target_dep_id} /* PBXTargetDependency */ = {{')
add("\t\t\tisa = PBXTargetDependency;")
add(f"\t\t\ttarget = {app_target_id} /* Proximity */;")
add(f"\t\t\ttargetProxy = {container_proxy_id} /* PBXContainerItemProxy */;")
add("\t\t};")
add("\t\t/* End PBXTargetDependency section */")
# ---------------- XCBuildConfiguration (targets) ----------------
add("\t\t/* Begin XCBuildConfiguration section */")
# App Debug
add(f'\t\t{app_debug_id} /* Debug */ = {{')
add("\t\t\tisa = XCBuildConfiguration;")
add("\t\t\tbuildSettings = {")
add("\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;")
add("\t\t\t\tCODE_SIGN_STYLE = Automatic;")
add("\t\t\t\tCURRENT_PROJECT_VERSION = 1;")
add("\t\t\t\tDEVELOPMENT_TEAM = \"\";")
add("\t\t\t\tENABLE_PREVIEWS = YES;")
add("\t\t\t\tGENERATE_INFOPLIST_FILE = YES;")
add("\t\t\t\tINFOPLIST_KEY_CFBundleDisplayName = Proximity;")
add("\t\t\t\tINFOPLIST_KEY_NFCReaderUsageDescription = \"Proximity uses NFC to connect when you tap phones.\";")
add("\t\t\t\tINFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = \"Proximity uses Bluetooth to detect nearby friends.\";")
add("\t\t\t\tINFOPLIST_KEY_NSLocationWhenInUseUsageDescription = \"Proximity uses your location to find people nearby.\";")
add("\t\t\t\tINFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;")
add("\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;")
add("\t\t\t\tINFOPLIST_KEY_UILaunchScreen_Generation = YES;")
add("\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";")
add("\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";")
add("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;")
add("\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (")
add("\t\t\t\t\t\"$(inherited)\",")
add("\t\t\t\t\t\"@executable_path/Frameworks\",")
add("\t\t\t\t);")
add("\t\t\t\tMARKETING_VERSION = 1.0;")
add("\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.proximity.app;")
add("\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";")
add("\t\t\t\tSDKROOT = iphoneos;")
add("\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;")
add("\t\t\t\tSWIFT_VERSION = 5.0;")
add("\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";")
add("\t\t\t};")
add("\t\t\tname = Debug;")
add("\t\t};")
# App Release
add(f'\t\t{app_release_id} /* Release */ = {{')
add("\t\t\tisa = XCBuildConfiguration;")
add("\t\t\tbuildSettings = {")
add("\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;")
add("\t\t\t\tCODE_SIGN_STYLE = Automatic;")
add("\t\t\t\tCURRENT_PROJECT_VERSION = 1;")
add("\t\t\t\tDEVELOPMENT_TEAM = \"\";")
add("\t\t\t\tENABLE_PREVIEWS = YES;")
add("\t\t\t\tGENERATE_INFOPLIST_FILE = YES;")
add("\t\t\t\tINFOPLIST_KEY_CFBundleDisplayName = Proximity;")
add("\t\t\t\tINFOPLIST_KEY_NFCReaderUsageDescription = \"Proximity uses NFC to connect when you tap phones.\";")
add("\t\t\t\tINFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = \"Proximity uses Bluetooth to detect nearby friends.\";")
add("\t\t\t\tINFOPLIST_KEY_NSLocationWhenInUseUsageDescription = \"Proximity uses your location to find people nearby.\";")
add("\t\t\t\tINFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;")
add("\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;")
add("\t\t\t\tINFOPLIST_KEY_UILaunchScreen_Generation = YES;")
add("\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";")
add("\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";")
add("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;")
add("\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (")
add("\t\t\t\t\t\"$(inherited)\",")
add("\t\t\t\t\t\"@executable_path/Frameworks\",")
add("\t\t\t\t);")
add("\t\t\t\tMARKETING_VERSION = 1.0;")
add("\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.proximity.app;")
add("\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";")
add("\t\t\t\tSDKROOT = iphoneos;")
add("\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;")
add("\t\t\t\tSWIFT_VERSION = 5.0;")
add("\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";")
add("\t\t\t};")
add("\t\t\tname = Release;")
add("\t\t};")
# UITest Debug
add(f'\t\t{uitest_debug_id} /* Debug */ = {{')
add("\t\t\tisa = XCBuildConfiguration;")
add("\t\t\tbuildSettings = {")
add("\t\t\t\tCODE_SIGN_STYLE = Automatic;")
add("\t\t\t\tCURRENT_PROJECT_VERSION = 1;")
add("\t\t\t\tGENERATE_INFOPLIST_FILE = YES;")
add("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;")
add("\t\t\t\tMARKETING_VERSION = 1.0;")
add("\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.proximity.app.ProximityUITests;")
add("\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";")
add("\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;")
add("\t\t\t\tSWIFT_VERSION = 5.0;")
add("\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";")
add("\t\t\t\tTEST_TARGET_NAME = Proximity;")
add("\t\t\t};")
add("\t\t\tname = Debug;")
add("\t\t};")
# UITest Release
add(f'\t\t{uitest_release_id} /* Release */ = {{')
add("\t\t\tisa = XCBuildConfiguration;")
add("\t\t\tbuildSettings = {")
add("\t\t\t\tCODE_SIGN_STYLE = Automatic;")
add("\t\t\t\tCURRENT_PROJECT_VERSION = 1;")
add("\t\t\t\tGENERATE_INFOPLIST_FILE = YES;")
add("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;")
add("\t\t\t\tMARKETING_VERSION = 1.0;")
add("\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.proximity.app.ProximityUITests;")
add("\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";")
add("\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;")
add("\t\t\t\tSWIFT_VERSION = 5.0;")
add("\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";")
add("\t\t\t\tTEST_TARGET_NAME = Proximity;")
add("\t\t\t};")
add("\t\t\tname = Release;")
add("\t\t};")
# Project Debug
add(f'\t\t{proj_debug_id} /* Debug */ = {{')
add("\t\t\tisa = XCBuildConfiguration;")
add("\t\t\tbuildSettings = {")
add("\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;")
add("\t\t\t\tCLANG_ANALYZER_NONNULL = YES;")
add("\t\t\t\tCLANG_ENABLE_MODULES = YES;")
add("\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;")
add("\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;")
add("\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;")
add("\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;")
add("\t\t\t\tCOPY_PHASE_STRIP = NO;")
add("\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;")
add("\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;")
add("\t\t\t\tENABLE_TESTABILITY = YES;")
add("\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;")
add("\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;")
add("\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;")
add("\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;")
add("\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (")
add("\t\t\t\t\t\"DEBUG=1\",")
add("\t\t\t\t\t\"$(inherited)\",")
add("\t\t\t\t);")
add("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;")
add("\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;")
add("\t\t\t\tMTL_FAST_MATH = YES;")
add("\t\t\t\tONLY_ACTIVE_ARCH = YES;")
add("\t\t\t\tSDKROOT = iphoneos;")
add("\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";")
add("\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";")
add("\t\t\t};")
add("\t\t\tname = Debug;")
add("\t\t};")
# Project Release
add(f'\t\t{proj_release_id} /* Release */ = {{')
add("\t\t\tisa = XCBuildConfiguration;")
add("\t\t\tbuildSettings = {")
add("\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;")
add("\t\t\t\tCLANG_ANALYZER_NONNULL = YES;")
add("\t\t\t\tCLANG_ENABLE_MODULES = YES;")
add("\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;")
add("\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;")
add("\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;")
add("\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;")
add("\t\t\t\tCOPY_PHASE_STRIP = NO;")
add("\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";")
add("\t\t\t\tENABLE_NS_ASSERTIONS = NO;")
add("\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;")
add("\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;")
add("\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;")
add("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;")
add("\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;")
add("\t\t\t\tMTL_FAST_MATH = YES;")
add("\t\t\t\tSDKROOT = iphoneos;")
add("\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;")
add("\t\t\t\tVALIDATE_PRODUCT = YES;")
add("\t\t\t};")
add("\t\t\tname = Release;")
add("\t\t};")
add("\t\t/* End XCBuildConfiguration section */")
# ---------------- XCConfigurationList ----------------
add("\t\t/* Begin XCConfigurationList section */")
add(f'\t\t{app_config_list_id} /* Build configuration list for PBXNativeTarget \\\"Proximity\\\" */ = {{')
add("\t\t\tisa = XCConfigurationList;")
add("\t\t\tbuildConfigurations = (")
add(f"\t\t\t\t{app_debug_id} /* Debug */,")
add(f"\t\t\t\t{app_release_id} /* Release */,")
add("\t\t\t);")
add("\t\t\tdefaultConfigurationIsVisible = 0;")
add("\t\t\tdefaultConfigurationName = Release;")
add("\t\t};")
add(f'\t\t{uitest_config_list_id} /* Build configuration list for PBXNativeTarget \\\"ProximityUITests\\\" */ = {{')
add("\t\t\tisa = XCConfigurationList;")
add("\t\t\tbuildConfigurations = (")
add(f"\t\t\t\t{uitest_debug_id} /* Debug */,")
add(f"\t\t\t\t{uitest_release_id} /* Release */,")
add("\t\t\t);")
add("\t\t\tdefaultConfigurationIsVisible = 0;")
add("\t\t\tdefaultConfigurationName = Release;")
add("\t\t};")
add(f'\t\t{proj_config_list_id} /* Build configuration list for PBXProject \\\"Proximity\\\" */ = {{')
add("\t\t\tisa = XCConfigurationList;")
add("\t\t\tbuildConfigurations = (")
add(f"\t\t\t\t{proj_debug_id} /* Debug */,")
add(f"\t\t\t\t{proj_release_id} /* Release */,")
add("\t\t\t);")
add("\t\t\tdefaultConfigurationIsVisible = 0;")
add("\t\t\tdefaultConfigurationName = Release;")
add("\t\t};")
add("\t\t/* End XCConfigurationList section */")
add("\t};")
add(f"\trootObject = {proj_id} /* Project object */;")
add("}")
return "\n".join(lines) + "\n"
# ---------------------------------------------------------------------------
# Write scheme so `xcodebuild test` works
# ---------------------------------------------------------------------------
def write_scheme():
app_target_id = stable_id("target:Proximity")
uitest_target_id = stable_id("target:ProximityUITests")
scheme_dir = os.path.join(OUT_DIR, "xcshareddata", "xcschemes")
os.makedirs(scheme_dir, exist_ok=True)
scheme_path = os.path.join(scheme_dir, "Proximity.xcscheme")
scheme = f'''<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "{app_target_id}"
BuildableName = "Proximity.app"
BlueprintName = "Proximity"
ReferencedContainer = "container:Proximity.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "{uitest_target_id}"
BuildableName = "ProximityUITests.xctest"
BlueprintName = "ProximityUITests"
ReferencedContainer = "container:Proximity.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "{app_target_id}"
BuildableName = "Proximity.app"
BlueprintName = "Proximity"
ReferencedContainer = "container:Proximity.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "{app_target_id}"
BuildableName = "Proximity.app"
BlueprintName = "Proximity"
ReferencedContainer = "container:Proximity.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
'''
with open(scheme_path, "w") as f:
f.write(scheme)
# ---------------------------------------------------------------------------
def main():
os.makedirs(OUT_DIR, exist_ok=True)
pbx = build()
with open(os.path.join(OUT_DIR, "project.pbxproj"), "w") as f:
f.write(pbx)
write_scheme()
print(f"Generated {OUT_DIR}")
print(f" app sources: {len(app_sources)}")
print(f" uitest sources: {len(uitest_sources)}")
if __name__ == "__main__":
main()