import { PBXNativeTarget , PBXTargetDependency , XCBuildConfiguration , XcodeProject } from 'xcode' ; import { getApplicationTargetNameForSchemeAsync } from './BuildScheme' ; import { getBuildConfigurationForListIdAndName , getPbxproj , isNotComment , NativeTargetSectionEntry , } from './utils/Xcodeproj' ; import { trimQuotes } from './utils/string' ; export enum TargetType { APPLICATION = 'com.apple.product-type.application' , EXTENSION = 'com.apple.product-type.app-extension' , WATCH = 'com.apple.product-type.application.watchapp' , STICKER_PACK_EXTENSION = 'com.apple.product-type.app-extension.messages-sticker-pack' , OTHER = 'other' } export interface Target { name : string ; type : TargetType ; dependencies ? : Target [ ] ; } export function getXCBuildConfigurationFromPbxproj ( project , { targetName , buildConfiguration = 'Release' , } = { } ) { const [ , nativeTarget ] = targetName ? findNativeTargetByName ( project , targetName ) : findFirstNativeTarget ( project ) ; const [ , xcBuildConfiguration ] = getBuildConfigurationForListIdAndName ( project , { configurationListId : nativeTarget . buildConfigurationList , buildConfiguration } ) ; return xcBuildConfiguration ?? null ; } export async function findApplicationTargetWithDependenciesAsync ( projectRoot , scheme ) { const applicationTargetName = await getApplicationTargetNameForSchemeAsync ( projectRoot , scheme ) ; const project = getPbxproj ( projectRoot ) ; const [ , applicationTarget ] = findNativeTargetByName ( project , applicationTargetName ) ; const dependencies = getTargetDependencies ( project , applicationTarget ) ; return { name : trimQuotes ( applicationTarget . name ) , type : TargetType . APPLICATION , dependencies } ; } function getTargetDependencies ( project , parentTarget ) { if ( ! parentTarget . dependencies || parentTarget . dependencies . length === 0 ) { return undefined ; } return parentTarget . dependencies . map ( ( { value } ) => { const { target : targetId } = project . getPBXGroupByKeyAndType ( value , 'PBXTargetDependency' ) as PBXTargetDependency ; const [ , target ] = findNativeTargetById ( project , targetId ) ; const type = isTargetOfType ( target , TargetType . EXTENSION ) ? TargetType . EXTENSION : TargetType . OTHER ; return { name : trimQuotes ( target . name ) , type , dependencies : getTargetDependencies ( project , target ) } ; } ) ; } export function isTargetOfType ( target , targetType ) { return trimQuotes ( target . productType ) === targetType ; } export function getNativeTargets ( project ) { const section = project . pbxNativeTargetSection ( ) ; return Object . entries ( section ) . filter ( isNotComment ) ; } export function findSignableTargets ( project ) { const targets = getNativeTargets ( project ) ; const applicationTargets = targets . filter ( ( [ , target ] ) => isTargetOfType ( target , TargetType . APPLICATION ) || isTargetOfType ( target , TargetType . EXTENSION ) || isTargetOfType ( target , TargetType . WATCH ) || isTargetOfType ( target , TargetType . STICKER_PACK_EXTENSION ) ) ; if ( applicationTargets . length === 0 ) { throw new Error ( `Could not find any signable targets in project.pbxproj` ) ; } return applicationTargets ; } export function findFirstNativeTarget ( project ) { const targets = getNativeTargets ( project ) ; const applicationTargets = targets . filter ( ( [ , target ] ) => isTargetOfType ( target , TargetType . APPLICATION ) ) ; if ( applicationTargets . length === 0 ) { throw new Error ( `Could not find any application target in project.pbxproj` ) ; } return applicationTargets [ 0 ] ; } export function findNativeTargetByName ( project , targetName ) { const nativeTargets = getNativeTargets ( project ) ; const nativeTargetEntry = nativeTargets . find ( ( [ , i ] ) => trimQuotes ( i . name ) === targetName ) ; if ( ! nativeTargetEntry ) { throw new Error ( `Could not find target '${ targetName }' in project.pbxproj` ) ; } return nativeTargetEntry ; } function findNativeTargetById ( project , targetId ) { const nativeTargets = getNativeTargets ( project ) ; const nativeTargetEntry = nativeTargets . find ( ( [ key ] ) => key === targetId ) ; if ( ! nativeTargetEntry ) { throw new Error ( `Could not find target with id '${ targetId }' in project.pbxproj` ) ; } return nativeTargetEntry ; }