1082815dcSEvan Baconimport { findSchemeNames, findSchemePaths } from './Paths'; 2082815dcSEvan Baconimport { findSignableTargets, TargetType } from './Target'; 3082815dcSEvan Baconimport { getPbxproj, unquote } from './utils/Xcodeproj'; 4*8a424bebSJames Ideimport { readXMLAsync } from '../utils/XML'; 5082815dcSEvan Bacon 6082815dcSEvan Baconinterface SchemeXML { 7082815dcSEvan Bacon Scheme?: { 8082815dcSEvan Bacon BuildAction?: { 9082815dcSEvan Bacon BuildActionEntries?: { 10082815dcSEvan Bacon BuildActionEntry?: BuildActionEntryType[]; 11082815dcSEvan Bacon }[]; 12082815dcSEvan Bacon }[]; 13082815dcSEvan Bacon ArchiveAction?: { 14082815dcSEvan Bacon $?: { 15082815dcSEvan Bacon buildConfiguration?: string; 16082815dcSEvan Bacon }; 17082815dcSEvan Bacon }[]; 18082815dcSEvan Bacon }; 19082815dcSEvan Bacon} 20082815dcSEvan Bacon 21082815dcSEvan Baconinterface BuildActionEntryType { 22082815dcSEvan Bacon BuildableReference?: { 23082815dcSEvan Bacon $?: { 24082815dcSEvan Bacon BlueprintName?: string; 25082815dcSEvan Bacon BuildableName?: string; 26082815dcSEvan Bacon }; 27082815dcSEvan Bacon }[]; 28082815dcSEvan Bacon} 29082815dcSEvan Bacon 30082815dcSEvan Baconexport function getSchemesFromXcodeproj(projectRoot: string): string[] { 31082815dcSEvan Bacon return findSchemeNames(projectRoot); 32082815dcSEvan Bacon} 33082815dcSEvan Bacon 34082815dcSEvan Baconexport function getRunnableSchemesFromXcodeproj( 35082815dcSEvan Bacon projectRoot: string, 36082815dcSEvan Bacon { configuration = 'Debug' }: { configuration?: 'Debug' | 'Release' } = {} 37082815dcSEvan Bacon): { name: string; osType: string; type: string }[] { 38082815dcSEvan Bacon const project = getPbxproj(projectRoot); 39082815dcSEvan Bacon 40082815dcSEvan Bacon return findSignableTargets(project).map(([, target]) => { 41082815dcSEvan Bacon let osType = 'iOS'; 42082815dcSEvan Bacon const type = unquote(target.productType); 43082815dcSEvan Bacon 44082815dcSEvan Bacon if (type === TargetType.WATCH) { 45082815dcSEvan Bacon osType = 'watchOS'; 46082815dcSEvan Bacon } else if ( 47082815dcSEvan Bacon // (apps) com.apple.product-type.application 48082815dcSEvan Bacon // (app clips) com.apple.product-type.application.on-demand-install-capable 49082815dcSEvan Bacon // NOTE(EvanBacon): This matches against `watchOS` as well so we check for watch first. 50082815dcSEvan Bacon type.startsWith(TargetType.APPLICATION) 51082815dcSEvan Bacon ) { 52082815dcSEvan Bacon // Attempt to resolve the platform SDK for each target so we can filter devices. 53082815dcSEvan Bacon const xcConfigurationList = 54082815dcSEvan Bacon project.hash.project.objects.XCConfigurationList[target.buildConfigurationList]; 55082815dcSEvan Bacon 56082815dcSEvan Bacon if (xcConfigurationList) { 57082815dcSEvan Bacon const buildConfiguration = 58082815dcSEvan Bacon xcConfigurationList.buildConfigurations.find( 59082815dcSEvan Bacon (value: { comment: string; value: string }) => value.comment === configuration 60082815dcSEvan Bacon ) || xcConfigurationList.buildConfigurations[0]; 61082815dcSEvan Bacon if (buildConfiguration?.value) { 62082815dcSEvan Bacon const xcBuildConfiguration = 63082815dcSEvan Bacon project.hash.project.objects.XCBuildConfiguration?.[buildConfiguration.value]; 64082815dcSEvan Bacon 65082815dcSEvan Bacon const buildSdkRoot = xcBuildConfiguration.buildSettings.SDKROOT; 66082815dcSEvan Bacon if ( 67082815dcSEvan Bacon buildSdkRoot === 'appletvos' || 68082815dcSEvan Bacon 'TVOS_DEPLOYMENT_TARGET' in xcBuildConfiguration.buildSettings 69082815dcSEvan Bacon ) { 70082815dcSEvan Bacon // Is a TV app... 71082815dcSEvan Bacon osType = 'tvOS'; 72082815dcSEvan Bacon } else if (buildSdkRoot === 'iphoneos') { 73082815dcSEvan Bacon osType = 'iOS'; 74082815dcSEvan Bacon } 75082815dcSEvan Bacon } 76082815dcSEvan Bacon } 77082815dcSEvan Bacon } 78082815dcSEvan Bacon 79082815dcSEvan Bacon return { 80082815dcSEvan Bacon name: unquote(target.name), 81082815dcSEvan Bacon osType, 82082815dcSEvan Bacon type: unquote(target.productType), 83082815dcSEvan Bacon }; 84082815dcSEvan Bacon }); 85082815dcSEvan Bacon} 86082815dcSEvan Bacon 87082815dcSEvan Baconasync function readSchemeAsync( 88082815dcSEvan Bacon projectRoot: string, 89082815dcSEvan Bacon scheme: string 90082815dcSEvan Bacon): Promise<SchemeXML | undefined> { 91082815dcSEvan Bacon const allSchemePaths = findSchemePaths(projectRoot); 92082815dcSEvan Bacon const re = new RegExp(`/${scheme}.xcscheme`, 'i'); 93082815dcSEvan Bacon const schemePath = allSchemePaths.find((i) => re.exec(i)); 94082815dcSEvan Bacon if (schemePath) { 95082815dcSEvan Bacon return (await readXMLAsync({ path: schemePath })) as unknown as SchemeXML | undefined; 96082815dcSEvan Bacon } else { 97082815dcSEvan Bacon throw new Error(`scheme '${scheme}' does not exist, make sure it's marked as shared`); 98082815dcSEvan Bacon } 99082815dcSEvan Bacon} 100082815dcSEvan Bacon 101082815dcSEvan Baconexport async function getApplicationTargetNameForSchemeAsync( 102082815dcSEvan Bacon projectRoot: string, 103082815dcSEvan Bacon scheme: string 104082815dcSEvan Bacon): Promise<string> { 105082815dcSEvan Bacon const schemeXML = await readSchemeAsync(projectRoot, scheme); 106082815dcSEvan Bacon const buildActionEntry = 107082815dcSEvan Bacon schemeXML?.Scheme?.BuildAction?.[0]?.BuildActionEntries?.[0]?.BuildActionEntry; 108082815dcSEvan Bacon const targetName = 109082815dcSEvan Bacon buildActionEntry?.length === 1 110082815dcSEvan Bacon ? getBlueprintName(buildActionEntry[0]) 111082815dcSEvan Bacon : getBlueprintName( 112082815dcSEvan Bacon buildActionEntry?.find((entry) => { 113082815dcSEvan Bacon return entry.BuildableReference?.[0]?.['$']?.BuildableName?.endsWith('.app'); 114082815dcSEvan Bacon }) 115082815dcSEvan Bacon ); 116082815dcSEvan Bacon if (!targetName) { 117082815dcSEvan Bacon throw new Error(`${scheme}.xcscheme seems to be corrupted`); 118082815dcSEvan Bacon } 119082815dcSEvan Bacon return targetName; 120082815dcSEvan Bacon} 121082815dcSEvan Bacon 122082815dcSEvan Baconexport async function getArchiveBuildConfigurationForSchemeAsync( 123082815dcSEvan Bacon projectRoot: string, 124082815dcSEvan Bacon scheme: string 125082815dcSEvan Bacon): Promise<string> { 126082815dcSEvan Bacon const schemeXML = await readSchemeAsync(projectRoot, scheme); 127082815dcSEvan Bacon const buildConfiguration = schemeXML?.Scheme?.ArchiveAction?.[0]?.['$']?.buildConfiguration; 128082815dcSEvan Bacon if (!buildConfiguration) { 129082815dcSEvan Bacon throw new Error(`${scheme}.xcscheme seems to be corrupted`); 130082815dcSEvan Bacon } 131082815dcSEvan Bacon return buildConfiguration; 132082815dcSEvan Bacon} 133082815dcSEvan Bacon 134082815dcSEvan Baconfunction getBlueprintName(entry?: BuildActionEntryType): string | undefined { 135082815dcSEvan Bacon return entry?.BuildableReference?.[0]?.['$']?.BlueprintName; 136082815dcSEvan Bacon} 137