import { readXMLAsync } from '../utils/XML' ; import { findSchemeNames , findSchemePaths } from './Paths' ; import { findSignableTargets , TargetType } from './Target' ; import { getPbxproj , unquote } from './utils/Xcodeproj' ; interface SchemeXML { Scheme ? : { BuildAction ? : { BuildActionEntries ? : { BuildActionEntry ? : BuildActionEntryType [ ] ; } [ ] ; } [ ] ; ArchiveAction ? : { $ ? : { buildConfiguration ? : string ; } ; } [ ] ; } ; } interface BuildActionEntryType { BuildableReference ? : { $ ? : { BlueprintName ? : string ; BuildableName ? : string ; } ; } [ ] ; } export function getSchemesFromXcodeproj ( projectRoot ) { return findSchemeNames ( projectRoot ) ; } export function getRunnableSchemesFromXcodeproj ( projectRoot , { configuration = 'Debug' } = { } ) { const project = getPbxproj ( projectRoot ) ; return findSignableTargets ( project ) . map ( ( [ , target ] ) => { let osType = 'iOS' ; const type = unquote ( target . productType ) ; if ( type === TargetType . APPLICATION ) { const xcConfigurationList = project . hash . project . objects . XCConfigurationList [ target . buildConfigurationList ] ; if ( xcConfigurationList ) { const buildConfiguration = xcConfigurationList . buildConfigurations . find ( ( value ) => value . comment === configuration ) || xcConfigurationList . buildConfigurations [ 0 ] ; if ( buildConfiguration ?. value ) { const xcBuildConfiguration = project . hash . project . objects . XCBuildConfiguration ?. [ buildConfiguration . value ] ; const buildSdkRoot = xcBuildConfiguration . buildSettings . SDKROOT ; if ( buildSdkRoot === 'appletvos' || 'TVOS_DEPLOYMENT_TARGET' in xcBuildConfiguration . buildSettings ) { osType = 'tvOS' ; } else if ( buildSdkRoot === 'iphoneos' ) { osType = 'iOS' ; } } } } else if ( type === TargetType . WATCH ) { osType = 'watchOS' ; } return { name : unquote ( target . name ) , osType , type : unquote ( target . productType ) } ; } ) ; } async function readSchemeAsync ( projectRoot , scheme ) { const allSchemePaths = findSchemePaths ( projectRoot ) ; const re = new RegExp ( `/${ scheme }.xcscheme` , 'i' ) ; const schemePath = allSchemePaths . find ( i => re . exec ( i ) ) ; if ( schemePath ) { return ( ( await readXMLAsync ( { path : schemePath } ) ) as unknown ) as SchemeXML | undefined ; } else { throw new Error ( `scheme '${ scheme }' does not exist, make sure it's marked as shared` ) ; } } export async function getApplicationTargetNameForSchemeAsync ( projectRoot , scheme ) { const schemeXML = await readSchemeAsync ( projectRoot , scheme ) ; const buildActionEntry = schemeXML ?. Scheme ?. BuildAction ?. [ 0 ] ?. BuildActionEntries ?. [ 0 ] ?. BuildActionEntry ; const targetName = buildActionEntry ?. length === 1 ? getBlueprintName ( buildActionEntry [ 0 ] ) : getBlueprintName ( buildActionEntry ?. find ( entry => { return entry . BuildableReference ?. [ 0 ] ?. [ '$' ] ?. BuildableName ?. endsWith ( '.app' ) ; } ) ) ; if ( ! targetName ) { throw new Error ( `${ scheme }.xcscheme seems to be corrupted` ) ; } return targetName ; } export async function getArchiveBuildConfigurationForSchemeAsync ( projectRoot , scheme ) { const schemeXML = await readSchemeAsync ( projectRoot , scheme ) ; const buildConfiguration = schemeXML ?. Scheme ?. ArchiveAction ?. [ 0 ] ?. [ '$' ] ?. buildConfiguration ; if ( ! buildConfiguration ) { throw new Error ( `${ scheme }.xcscheme seems to be corrupted` ) ; } return buildConfiguration ; } function getBlueprintName ( entry ? ) { return entry ?. BuildableReference ?. [ 0 ] ?. [ '$' ] ?. BlueprintName ; }