UNPKG

xc-mcp

Version:

MCP server that wraps Xcode command-line tools for iOS/macOS development workflows

171 lines 6.46 kB
/** * Build Settings Cache * * Caches Xcode build settings to avoid repeated expensive xcodebuild calls. * Settings include bundle identifier, deployment target, device families, and capabilities. * * Cache TTL: 1 hour (can be invalidated on project file changes) */ import { join } from 'path'; import { executeCommand, buildXcodebuildCommand } from '../utils/command.js'; import { parseBuildSettingsJson } from '../utils/build-settings-parser.js'; export class BuildSettingsCache { cache = new Map(); CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour /** * Get all build settings for a given project, scheme, and configuration */ async getBuildSettings(projectPath, scheme, configuration = 'Debug') { const cacheKey = `${projectPath}:${scheme}:${configuration}`; // Check cache validity const cached = this.cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this.CACHE_TTL_MS) { return cached.settings; } // Execute xcodebuild -showBuildSettings const settings = await this.fetchBuildSettings(projectPath, scheme, configuration); // Store in cache this.cache.set(cacheKey, { settings, timestamp: Date.now(), }); return settings; } /** * Get bundle identifier for quick lookup */ async getBundleIdentifier(projectPath, scheme) { const settings = await this.getBuildSettings(projectPath, scheme); return settings.PRODUCT_BUNDLE_IDENTIFIER; } /** * Get app path for installation/launching */ async getAppPath(projectPath, scheme, configuration = 'Debug') { const settings = await this.getBuildSettings(projectPath, scheme, configuration); return join(settings.CONFIGURATION_BUILD_DIR, `${settings.PRODUCT_NAME}.app`); } /** * Get deployment target version as iOS major version (e.g., 15, 16, 17) */ async getDeploymentTarget(projectPath, scheme) { const settings = await this.getBuildSettings(projectPath, scheme); const match = settings.DEPLOYMENT_TARGET.match(/(\d+)/); return match ? parseInt(match[1]) : 14; // Default to iOS 14 if parsing fails } /** * Get supported device families * Returns: { supportsIPhone: boolean; supportsIPad: boolean } */ async getDeviceFamilies(projectPath, scheme) { const settings = await this.getBuildSettings(projectPath, scheme); const families = settings.TARGETED_DEVICE_FAMILY.split(',').map(f => f.trim()); return { supportsIPhone: families.includes('1'), supportsIPad: families.includes('2'), }; } /** * Get required capabilities from Info.plist keys */ async getRequiredCapabilities(projectPath, scheme) { const settings = await this.getBuildSettings(projectPath, scheme); const capabilities = []; if (settings.NSCameraUsageDescription) capabilities.push('camera'); if (settings.NSLocationWhenInUseUsageDescription || settings.NSLocationAlwaysAndWhenInUseUsageDescription) { capabilities.push('location'); } if (settings.NSMicrophoneUsageDescription) capabilities.push('microphone'); if (settings.NSContactsUsageDescription) capabilities.push('contacts'); if (settings.NSCalendarsUsageDescription) capabilities.push('calendar'); if (settings.NSRemindersUsageDescription) capabilities.push('reminders'); if (settings.NSPhotosUsageDescription) capabilities.push('photos'); if (settings.NSHealthShareUsageDescription || settings.NSHealthUpdateUsageDescription) { capabilities.push('health'); } if (settings.NSBluetoothAlwaysUsageDescription) capabilities.push('bluetooth'); if (settings.NSUserTrackingUsageDescription) capabilities.push('tracking'); if (settings.NSSiriUsageDescription) capabilities.push('siri'); return capabilities; } /** * Invalidate cache for specific project (after project changes) */ invalidateCache(projectPath, scheme) { if (!projectPath) { this.cache.clear(); return; } if (!scheme) { // Clear all entries for this project for (const key of this.cache.keys()) { if (key.startsWith(projectPath)) { this.cache.delete(key); } } return; } // Clear specific entry const cacheKey = `${projectPath}:${scheme}`; for (const key of this.cache.keys()) { if (key.startsWith(cacheKey)) { this.cache.delete(key); } } } /** * Get cache statistics */ getCacheStats() { let oldestTimestamp = null; let newestTimestamp = null; for (const entry of this.cache.values()) { if (oldestTimestamp === null || entry.timestamp < oldestTimestamp) { oldestTimestamp = entry.timestamp; } if (newestTimestamp === null || entry.timestamp > newestTimestamp) { newestTimestamp = entry.timestamp; } } return { size: this.cache.size, oldestEntry: oldestTimestamp, newestEntry: newestTimestamp, }; } /** * Fetch build settings from xcodebuild */ async fetchBuildSettings(projectPath, scheme, configuration) { // Build command using existing utility const command = buildXcodebuildCommand('-showBuildSettings', projectPath, { scheme, configuration, json: true, }); try { const result = await executeCommand(command, { timeout: 30000 }); if (result.code !== 0) { throw new Error(`xcodebuild -showBuildSettings failed: ${result.stderr}`); } // Parse JSON output return parseBuildSettingsJson(result.stdout); } catch (error) { throw new Error(`Failed to fetch build settings: ${error}`); } } } // Export singleton instance export const buildSettingsCache = new BuildSettingsCache(); //# sourceMappingURL=build-settings-cache.js.map