├── Tests ├── Fixtures │ ├── test.xcconfig │ ├── TestProject │ │ ├── .lldbinit │ │ ├── Mintfile │ │ ├── SomeFile │ │ ├── Tool │ │ │ └── main.swift │ │ ├── excluded-file │ │ ├── App_iOS │ │ │ ├── .DS_Store │ │ │ ├── Resource.abc │ │ │ ├── excluded-file │ │ │ ├── module.modulemap │ │ │ ├── Base.lproj │ │ │ │ ├── .DS_Store │ │ │ │ ├── excluded-file │ │ │ │ ├── Localizable.strings │ │ │ │ ├── LocalizedStoryboard.storyboard │ │ │ │ ├── Localizable.stringsdict │ │ │ │ └── LaunchScreen.storyboard │ │ │ ├── Resource.abcd │ │ │ │ └── File.json │ │ │ ├── inputList.xcfilelist │ │ │ ├── outputList.xcfilelist │ │ │ ├── Documentation.docc │ │ │ │ ├── Resources │ │ │ │ │ └── resource.png │ │ │ │ └── Documentation.md │ │ │ ├── en.lproj │ │ │ │ ├── LocalizedStoryboard.strings │ │ │ │ ├── Localizable.strings │ │ │ │ └── Localizable.stringsdict │ │ │ ├── Settings.bundle │ │ │ │ ├── en.lproj │ │ │ │ │ └── Root.strings │ │ │ │ └── Root.plist │ │ │ ├── FolderWithDot2.0 │ │ │ │ └── SwiftFileInDotPath.swift │ │ │ ├── App.entitlements │ │ │ ├── Model.xcdatamodeld │ │ │ │ ├── .xccurrentversion │ │ │ │ ├── Model.xcdatamodel │ │ │ │ │ └── contents │ │ │ │ ├── Model 2.xcdatamodel │ │ │ │ │ └── contents │ │ │ │ └── Model 3.xcdatamodel │ │ │ │ │ └── contents │ │ │ ├── ViewController.swift │ │ │ ├── Configuration.storekit │ │ │ ├── AppDelegate.swift │ │ │ ├── App_iOS.xctestplan │ │ │ ├── Info.plist │ │ │ └── Assets.xcassets │ │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── CopyFiles │ │ │ └── Empty.h │ │ ├── Group │ │ │ └── File1.swift │ │ ├── Group2 │ │ │ └── File2.swift │ │ ├── Headers │ │ │ ├── Header1.h │ │ │ └── Folder1 │ │ │ │ └── Header2.h │ │ ├── Resources │ │ │ ├── example.mp4 │ │ │ ├── ResourceFolder │ │ │ │ ├── a.jpg │ │ │ │ └── j.jpg │ │ │ ├── MyBundle.bundle │ │ │ │ └── file.text │ │ │ ├── SceneKitCatalog.scnassets │ │ │ │ └── a.jpg │ │ │ └── GoogleService-Info.plist │ │ ├── .gitignore │ │ ├── Folder │ │ │ ├── Folder1 │ │ │ │ └── file.file │ │ │ └── Folder2 │ │ │ │ └── file.file │ │ ├── FileGroup │ │ │ └── UnderFileGroup │ │ │ │ └── MoreUnder.swift │ │ ├── Framework │ │ │ ├── Project.xcodeproj │ │ │ │ └── project.pbxproj │ │ │ ├── FrameworkFile.swift │ │ │ ├── MyFramework.h │ │ │ └── Info.plist │ │ ├── scripts │ │ │ └── script.sh │ │ ├── CrossOverlayFramework │ │ │ ├── Project.xcodeproj │ │ │ │ └── project.pbxproj │ │ │ ├── FrameworkFile.swift │ │ │ ├── CrossOverlayFramework.swiftcrossimport │ │ │ │ └── Framework.swiftoverlay │ │ │ ├── MyFramework.h │ │ │ └── Info.plist │ │ ├── App_supportedDestinations │ │ │ ├── TestResources │ │ │ │ ├── File_ios.swift │ │ │ │ ├── File_macOS.swift │ │ │ │ ├── File_tvOs.swift │ │ │ │ ├── TVOS │ │ │ │ │ └── File_B.swift │ │ │ │ ├── iOs │ │ │ │ │ └── File_A.swift │ │ │ │ ├── macos │ │ │ │ │ └── File_C.swift │ │ │ │ ├── File_MACCATALYST.swift │ │ │ │ └── macCatalyst │ │ │ │ │ └── File_D.swift │ │ │ ├── Sources │ │ │ │ ├── MyAppApp.swift │ │ │ │ ├── tvOS │ │ │ │ │ └── ContentView.swift │ │ │ │ └── iOS │ │ │ │ │ └── ContentView.swift │ │ │ ├── Info.generated.plist │ │ │ └── Storyboards │ │ │ │ └── LaunchScreen.storyboard │ │ ├── SyncedFolder │ │ │ └── SyncedFile.swift │ │ ├── NestedFiles │ │ │ └── Foo │ │ │ │ └── Nested.swift │ │ ├── Configs │ │ │ ├── base.xcconfig │ │ │ └── config.xcconfig │ │ ├── StandaloneFiles │ │ │ ├── Standalone.swift │ │ │ └── StandaloneAssets.xcassets │ │ │ │ └── Contents.json │ │ ├── Utilities │ │ │ └── MyPlayground.playground │ │ │ │ ├── Contents.swift │ │ │ │ ├── playground.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ │ └── contents.xcplayground │ │ ├── Cartfile │ │ ├── App_macOS │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ ├── ViewController.swift │ │ │ ├── AppDelegate.swift │ │ │ └── App-Info.plist │ │ ├── StaticLibrary_ObjC │ │ │ ├── StaticLibrary_ObjC.h │ │ │ ├── Module │ │ │ │ └── module.modulemap │ │ │ └── StaticLibrary_ObjC.m │ │ ├── iMessageApp │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ └── Info.plist │ │ ├── ExtensionKit Extension │ │ │ ├── EntryPoint.swift │ │ │ ├── Intent.swift │ │ │ └── Info.plist │ │ ├── iMessageExtension │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── iMessage App Icon.stickersiconset │ │ │ │ │ └── Contents.json │ │ │ └── Info.plist │ │ ├── iMessageStickers │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ └── Info.plist │ │ ├── Cartfile.resolved │ │ ├── StaticLibrary_Swift │ │ │ └── StaticLibrary.swift │ │ ├── XPC Service │ │ │ ├── XPC_Service.h │ │ │ ├── XPC_Service.m │ │ │ ├── XPC_ServiceProtocol.h │ │ │ ├── Info.plist │ │ │ └── main.m │ │ ├── Vendor │ │ │ └── SomeXPCService.xpc │ │ │ │ └── Contents │ │ │ │ ├── MacOS │ │ │ │ └── XPC Service │ │ │ │ └── Info.plist │ │ ├── carthage_dynamic.xcconfig │ │ ├── Project.xcodeproj │ │ │ └── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── fixtures.xcconfig │ │ ├── AnotherProject │ │ │ ├── AnotherProject.xcodeproj │ │ │ │ └── project.xcworkspace │ │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ │ └── xcshareddata │ │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── project.yml │ │ ├── carthage_static.xcconfig │ │ ├── DriverKit Driver │ │ │ ├── Driver.entitlements │ │ │ ├── Driver.iig │ │ │ ├── Driver.cpp │ │ │ └── Info.plist │ │ ├── Network Extension │ │ │ ├── main.swift │ │ │ ├── NetworkExtension.entitlements │ │ │ ├── FilterDataProvider.swift │ │ │ └── Info.plist │ │ ├── Workspace.xcworkspace │ │ │ ├── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── swiftpm │ │ │ │ │ └── Package.resolved │ │ │ └── contents.xcworkspacedata │ │ ├── environments.yml │ │ ├── App_Clip │ │ │ ├── ViewController.swift │ │ │ ├── AppDelegate.swift │ │ │ ├── Clip.entitlements │ │ │ ├── Info.plist │ │ │ ├── Base.lproj │ │ │ │ └── LaunchScreen.storyboard │ │ │ └── Assets.xcassets │ │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── App_watchOS Extension │ │ │ ├── Assets.xcassets │ │ │ │ └── Complication.complicationset │ │ │ │ │ ├── Circular.imageset │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Extra Large.imageset │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Modular.imageset │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Utilitarian.imageset │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ ├── InterfaceController.swift │ │ │ ├── PushNotificationPayload.apns │ │ │ ├── NotificationController.swift │ │ │ └── Info.plist │ │ ├── build.sh │ │ ├── EndpointSecurity Extension │ │ │ ├── EndpointSecurity.entitlements │ │ │ ├── main.swift │ │ │ └── Info.plist │ │ ├── String Catalogs │ │ │ └── LocalizableStrings.xcstrings │ │ ├── App_Clip_Tests │ │ │ ├── Info.plist │ │ │ └── TestProjectTests.swift │ │ ├── App_iOS_Tests │ │ │ ├── Info.plist │ │ │ └── TestProjectTests.swift │ │ ├── App_Clip_UITests │ │ │ ├── Info.plist │ │ │ └── TestProjectUITests.swift │ │ ├── App_iOS_UITests │ │ │ ├── Info.plist │ │ │ └── TestProjectUITests.swift │ │ ├── App_macOS_Tests │ │ │ ├── Info.plist │ │ │ └── TestProjectTests.swift │ │ ├── xcode12_13_and_14_workaround.xcconfig │ │ └── App_watchOS │ │ │ ├── Info.plist │ │ │ └── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── SPM │ │ ├── FooFeature │ │ │ ├── Sources │ │ │ │ ├── FooUI │ │ │ │ │ └── FooUI.swift │ │ │ │ └── FooDomain │ │ │ │ │ └── FooDomain.swift │ │ │ └── Package.swift │ │ ├── SPM │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ ├── App.xctestplan │ │ │ ├── AppDelegate.swift │ │ │ └── Info.plist │ │ ├── StaticLibrary │ │ │ └── StaticLibrary.swift │ │ ├── SPM.xcodeproj │ │ │ └── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── SPMTests │ │ │ └── SPMTests.swift │ │ └── project.yml │ ├── paths_test │ │ ├── relative_file_groups │ │ │ ├── inc.yml │ │ │ └── TestFile.md │ │ ├── same_relative_path_test │ │ │ ├── parent1 │ │ │ │ ├── parent1.yml │ │ │ │ └── same │ │ │ │ │ ├── same.yml │ │ │ │ │ └── target1 │ │ │ │ │ └── target1.yml │ │ │ ├── parent2 │ │ │ │ ├── parent2.yml │ │ │ │ └── same │ │ │ │ │ ├── same.yml │ │ │ │ │ └── target2 │ │ │ │ │ └── target2.yml │ │ │ └── same_relative_path_test.yml │ │ ├── relative_local_package │ │ │ ├── inc.yml │ │ │ └── LocalPackage │ │ │ │ ├── Sources │ │ │ │ └── LocalPackage │ │ │ │ │ └── LocalPackage.swift │ │ │ │ ├── .gitignore │ │ │ │ └── Package.swift │ │ ├── recursive_test │ │ │ └── recursive_test.yml │ │ └── included_paths_test.yml │ ├── legacy_paths_test │ │ ├── recursive_include.yml │ │ └── legacy_included_paths_test.yml │ ├── duplicated_include │ │ ├── different_path │ │ │ └── duplicated_import_root.yml │ │ ├── duplicated_import_transitive.yml │ │ ├── duplicated_import_root.yml │ │ └── duplicated_import_sut.yml │ ├── legacy_paths_test.yml │ ├── CarthageProject │ │ ├── .gitignore │ │ ├── Cartfile │ │ ├── Cartfile.resolved │ │ ├── Project.xcodeproj │ │ │ └── project.xcworkspace │ │ │ │ └── contents.xcworkspacedata │ │ ├── project.yml │ │ └── Carthage │ │ │ └── Build │ │ │ ├── .Result.version │ │ │ ├── .Alamofire.version │ │ │ ├── .ReactiveSwift.version │ │ │ ├── .CarthageTestFixture.version │ │ │ └── .ReactiveCocoa.version │ ├── scheme_test │ │ ├── TestProject.xcodeproj │ │ │ ├── project.xcworkspace │ │ │ │ └── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── someUser.xcuserdatad │ │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ └── test_project.yml │ ├── included_additional.yml │ ├── yaml.yml │ ├── included.yml │ ├── invalid_configs │ │ ├── invalid_configs_value_non_mapping_settings.yml │ │ ├── invalid_configs_value_non_mapping_targets.yml │ │ ├── invalid_configs_value_non_mapping_setting_groups.yml │ │ └── invalid_configs_value_non_mapping_aggregate_targets.yml │ ├── variables_test.yml │ ├── include_test.json │ ├── include_test.yml │ ├── paths_test.yml │ └── settings_test.yml ├── LinuxMain.swift ├── XcodeGenKitTests │ └── BreakpointGeneratorTests.swift ├── XcodeGenCoreTests │ ├── AtomicTests.swift │ └── ArrayExtensionsTests.swift ├── FixtureTests │ └── FixtureTests.swift └── ProjectSpecTests │ └── InvalidConfigsFormatTests.swift ├── Package.resources ├── _config.yml ├── .gitattributes ├── Sources ├── XcodeGenKit │ ├── SettingPresets │ ├── SettingsPresetFile.swift │ ├── Version.swift │ ├── InfoPlistGenerator.swift │ ├── ProjectGenerator.swift │ └── FileWriter.swift ├── XcodeGen │ └── main.swift ├── ProjectSpec │ ├── BuildSettingsContainer.swift │ ├── Encoding.swift │ ├── Platform.swift │ ├── SourceType.swift │ ├── NSRegularExpressionExtensions.swift │ ├── Array+Extension.swift │ ├── VersionExtensions.swift │ ├── ProjectTarget.swift │ ├── Yaml.swift │ ├── GroupOrdering.swift │ ├── CacheFile.swift │ ├── ProjectReference.swift │ ├── TestPlan.swift │ ├── Plist.swift │ ├── SupportedDestination.swift │ ├── Dictionary+Extension.swift │ ├── Config.swift │ ├── BuildSettingsExtractor.swift │ ├── TargetReference.swift │ ├── BuildToolPlugin.swift │ └── Linkage.swift ├── XcodeGenCLI │ ├── Arguments.swift │ ├── XcodeGenCLI.swift │ ├── GenerationError.swift │ └── Commands │ │ ├── CacheCommand.swift │ │ └── DumpCommand.swift └── XcodeGenCore │ └── Atomic.swift ├── SettingPresets ├── Products │ ├── library.static.yml │ ├── app-extension.yml │ ├── bundle.ui-testing.yml │ ├── bundle.unit-test.yml │ ├── tv-app-extension.yml │ ├── app-extension.intents-service.yml │ ├── app-extension.messages.yml │ ├── watchkit2-extension.yml │ ├── framework.yml │ └── framework.static.yml ├── Product_Platform │ ├── application_macOS.yml │ ├── application_watchOS.yml │ ├── application_visionOS.yml │ ├── application_iOS.yml │ ├── bundle.unit-test_macOS.yml │ ├── app-extension_macOS.yml │ └── application_tvOS.yml ├── Platforms │ ├── watchOS.yml │ ├── tvOS.yml │ ├── visionOS.yml │ ├── iOS.yml │ └── macOS.yml ├── SupportedDestinations │ ├── macCatalyst.yml │ ├── watchOS.yml │ ├── tvOS.yml │ ├── macOS.yml │ ├── visionOS.yml │ └── iOS.yml ├── Configs │ ├── release.yml │ └── debug.yml └── base.yml ├── Assets └── Logo_animated.gif ├── scripts ├── build-fixtures.sh ├── gen-fixtures.sh ├── diff-fixtures.sh ├── install.sh └── archive.sh ├── .gitignore ├── .swiftformat ├── RELEASE.md ├── Docs └── Examples.md ├── .github └── workflows │ └── ci.yml ├── LICENSE └── Makefile /Tests/Fixtures/test.xcconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Package.resources: -------------------------------------------------------------------------------- 1 | SettingPresets 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/.lldbinit: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | CHANGELOG merge=union 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Mintfile: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/SomeFile: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Tool/main.swift: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/excluded-file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/.DS_Store: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Resource.abc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/excluded-file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/CopyFiles/Empty.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Group/File1.swift: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Group2/File2.swift: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Headers/Header1.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Resources/example.mp4: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/.gitignore: -------------------------------------------------------------------------------- 1 | Carthage 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/module.modulemap: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Folder/Folder1/file.file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Folder/Folder2/file.file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Headers/Folder1/Header2.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Sources/XcodeGenKit/SettingPresets: -------------------------------------------------------------------------------- 1 | ../../SettingPresets/ -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Base.lproj/.DS_Store: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Resource.abcd/File.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/inputList.xcfilelist: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/outputList.xcfilelist: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Resources/ResourceFolder/a.jpg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Resources/ResourceFolder/j.jpg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Base.lproj/excluded-file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Resources/MyBundle.bundle/file.text: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SettingPresets/Products/library.static.yml: -------------------------------------------------------------------------------- 1 | SKIP_INSTALL: 'YES' 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/FileGroup/UnderFileGroup/MoreUnder.swift: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Resources/SceneKitCatalog.scnassets/a.jpg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Framework/Project.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/scripts/script.sh: -------------------------------------------------------------------------------- 1 | echo "You ran a script" 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Documentation.docc/Resources/resource.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/en.lproj/LocalizedStoryboard.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/FooFeature/Sources/FooUI/FooUI.swift: -------------------------------------------------------------------------------- 1 | public struct FooUI {} 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/CrossOverlayFramework/Project.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/relative_file_groups/inc.yml: -------------------------------------------------------------------------------- 1 | fileGroups: 2 | - TestFile.md 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/FooFeature/Sources/FooDomain/FooDomain.swift: -------------------------------------------------------------------------------- 1 | public struct FooDomain {} 2 | -------------------------------------------------------------------------------- /Assets/Logo_animated.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonaskolb/XcodeGen/HEAD/Assets/Logo_animated.gif -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/application_macOS.yml: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon 2 | -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/application_watchOS.yml: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/File_ios.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/SyncedFolder/SyncedFile.swift: -------------------------------------------------------------------------------- 1 | 2 | struct SyncedStruct { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/parent1/parent1.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - same/same.yml 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/parent2/parent2.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - same/same.yml 3 | -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/application_visionOS.yml: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/File_macOS.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/File_tvOs.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/TVOS/File_B.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/iOs/File_A.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/macos/File_C.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/NestedFiles/Foo/Nested.swift: -------------------------------------------------------------------------------- 1 | func nested() -> String { 2 | "Nested" 3 | } 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/relative_file_groups/TestFile.md: -------------------------------------------------------------------------------- 1 | This is a test file for relative file groups 2 | -------------------------------------------------------------------------------- /scripts/build-fixtures.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd Tests/Fixtures/TestProject 5 | ./build.sh 6 | -------------------------------------------------------------------------------- /SettingPresets/Platforms/watchOS.yml: -------------------------------------------------------------------------------- 1 | SDKROOT: watchos 2 | SKIP_INSTALL: 'YES' 3 | TARGETED_DEVICE_FAMILY: 4 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/File_MACCATALYST.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/TestResources/macCatalyst/File_D.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Configs/base.xcconfig: -------------------------------------------------------------------------------- 1 | BASE_SETTING = baseSetting 2 | OVERWRITTEN_SETTING = base 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/parent1/same/same.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - target1/target1.yml 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/parent2/same/same.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - target2/target2.yml 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/relative_local_package/inc.yml: -------------------------------------------------------------------------------- 1 | packages: 2 | LocalPackage: 3 | path: LocalPackage 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/StandaloneFiles/Standalone.swift: -------------------------------------------------------------------------------- 1 | func standaloneHello() -> String { 2 | "Hello" 3 | } 4 | -------------------------------------------------------------------------------- /SettingPresets/SupportedDestinations/macCatalyst.yml: -------------------------------------------------------------------------------- 1 | SUPPORTS_MACCATALYST: YES 2 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO 3 | -------------------------------------------------------------------------------- /SettingPresets/SupportedDestinations/watchOS.yml: -------------------------------------------------------------------------------- 1 | SUPPORTED_PLATFORMS: watchos watchsimulator 2 | TARGETED_DEVICE_FAMILY: '4' 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/legacy_paths_test/recursive_include.yml: -------------------------------------------------------------------------------- 1 | targets: 2 | IncludedTarget: 3 | sources: 4 | - source 5 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // LinuxMain.swift 2 | fatalError("Run the tests with `swift test --enable-test-discovery`.") 3 | 4 | -------------------------------------------------------------------------------- /SettingPresets/SupportedDestinations/tvOS.yml: -------------------------------------------------------------------------------- 1 | SUPPORTED_PLATFORMS: appletvos appletvsimulator 2 | TARGETED_DEVICE_FAMILY: '3' 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Utilities/MyPlayground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | var str = "Hello, playground" 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/StaticLibrary/StaticLibrary.swift: -------------------------------------------------------------------------------- 1 | import Codability 2 | 3 | func doThing() { 4 | _ = AnyCodable.self 5 | } 6 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Documentation.docc/Documentation.md: -------------------------------------------------------------------------------- 1 | # ``App_Clip`` 2 | 3 | Test 4 | 5 | ## Overview 6 | 7 | Test 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/duplicated_include/different_path/duplicated_import_root.yml: -------------------------------------------------------------------------------- 1 | name: DuplicatedImportRoot 2 | fileGroups: 3 | - Third 4 | -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/application_iOS.yml: -------------------------------------------------------------------------------- 1 | CODE_SIGN_IDENTITY: iPhone Developer 2 | ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Cartfile: -------------------------------------------------------------------------------- 1 | github "antitypical/Result" 2 | github "SwiftyJSON/SwiftyJSON" 3 | github "pointfreeco/swift-nonempty" 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SettingPresets/SupportedDestinations/macOS.yml: -------------------------------------------------------------------------------- 1 | SUPPORTED_PLATFORMS: macosx 2 | SUPPORTS_MACCATALYST: NO 3 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Configs/config.xcconfig: -------------------------------------------------------------------------------- 1 | #include "base.xcconfig" 2 | 3 | CONFIG_SETTING = configSetting 4 | OVERWRITTEN_SETTING = new 5 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/StaticLibrary_ObjC/StaticLibrary_ObjC.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | @interface SLObjC: NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/Fixtures/duplicated_include/duplicated_import_transitive.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - duplicated_import_root.yml 3 | name: DuplicatedImportTransitive 4 | -------------------------------------------------------------------------------- /SettingPresets/Products/app-extension.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"] 2 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/ExtensionKit Extension/EntryPoint.swift: -------------------------------------------------------------------------------- 1 | import AppIntents 2 | 3 | @main 4 | struct EntryPoint: AppIntentsExtension { 5 | } 6 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Framework/FrameworkFile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct FrameworkStruct { 4 | 5 | public init() {} 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageExtension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageStickers/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/Fixtures/legacy_paths_test.yml: -------------------------------------------------------------------------------- 1 | include: 2 | path: legacy_paths_test/legacy_included_paths_test.yml 3 | relativePaths: false 4 | name: NewName 5 | -------------------------------------------------------------------------------- /SettingPresets/Platforms/tvOS.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"] 2 | SDKROOT: appletvos 3 | TARGETED_DEVICE_FAMILY: 3 4 | -------------------------------------------------------------------------------- /SettingPresets/Platforms/visionOS.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"] 2 | SDKROOT: xros 3 | TARGETED_DEVICE_FAMILY: 7 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/.gitignore: -------------------------------------------------------------------------------- 1 | Carthage/Build/iOS 2 | Carthage/Build/watchOS 3 | Carthage/Build/tvOS 4 | Carthage/Build/Mac 5 | Carthage/Checkouts 6 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/StaticLibrary_ObjC/Module/module.modulemap: -------------------------------------------------------------------------------- 1 | module StaticLibrary_ObjC { 2 | header "StaticLibrary_ObjC.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /SettingPresets/Platforms/iOS.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"] 2 | SDKROOT: iphoneos 3 | TARGETED_DEVICE_FAMILY: '1,2' 4 | -------------------------------------------------------------------------------- /SettingPresets/Platforms/macOS.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/../Frameworks"] 2 | SDKROOT: macosx 3 | COMBINE_HIDPI_IMAGES: 'YES' 4 | -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/bundle.unit-test_macOS.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks"] 2 | -------------------------------------------------------------------------------- /SettingPresets/SupportedDestinations/visionOS.yml: -------------------------------------------------------------------------------- 1 | SUPPORTED_PLATFORMS: xros xrsimulator 2 | TARGETED_DEVICE_FAMILY: '7' 3 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO 4 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/CrossOverlayFramework/FrameworkFile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct FrameworkStruct { 4 | 5 | public init() {} 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/StandaloneFiles/StandaloneAssets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Base.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | Project 4 | 5 | Created by ryohey on 2017/11/03. 6 | 7 | */ 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | Project 4 | 5 | Created by ryohey on 2017/11/03. 6 | 7 | */ 8 | -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/app-extension_macOS.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks"] 2 | -------------------------------------------------------------------------------- /SettingPresets/Product_Platform/application_tvOS.yml: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME: App Icon & Top Shelf Image 2 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME: LaunchImage 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "SwiftyJSON/SwiftyJSON" "5.0.0" 2 | github "antitypical/Result" "5.0.0" 3 | github "pointfreeco/swift-nonempty" "0.2.2" 4 | -------------------------------------------------------------------------------- /SettingPresets/Products/bundle.ui-testing.yml: -------------------------------------------------------------------------------- 1 | BUNDLE_LOADER: $(TEST_HOST) 2 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"] 3 | -------------------------------------------------------------------------------- /SettingPresets/Products/bundle.unit-test.yml: -------------------------------------------------------------------------------- 1 | BUNDLE_LOADER: $(TEST_HOST) 2 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"] 3 | -------------------------------------------------------------------------------- /SettingPresets/Products/tv-app-extension.yml: -------------------------------------------------------------------------------- 1 | SKIP_INSTALL: 'YES' 2 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"] 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/relative_local_package/LocalPackage/Sources/LocalPackage/LocalPackage.swift: -------------------------------------------------------------------------------- 1 | // The Swift Programming Language 2 | // https://docs.swift.org/swift-book 3 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/StaticLibrary_Swift/StaticLibrary.swift: -------------------------------------------------------------------------------- 1 | 2 | public struct SLSwift { 3 | public func description() -> String { 4 | "Hello, World!" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/parent1/same/target1/target1.yml: -------------------------------------------------------------------------------- 1 | targets: 2 | target1: 3 | type: framework 4 | platform: macOS 5 | sources: 6 | - source 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/parent2/same/target2/target2.yml: -------------------------------------------------------------------------------- 1 | targets: 2 | target2: 3 | type: framework 4 | platform: macOS 5 | sources: 6 | - source 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Settings.bundle/en.lproj/Root.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonaskolb/XcodeGen/HEAD/Tests/Fixtures/TestProject/App_iOS/Settings.bundle/en.lproj/Root.strings -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/XPC Service/XPC_Service.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "XPC_ServiceProtocol.h" 3 | 4 | @interface XPC_Service : NSObject 5 | @end 6 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Cartfile: -------------------------------------------------------------------------------- 1 | github "antitypical/Result" 2 | github "rpassis/CarthageTestFixture" 3 | github "ReactiveCocoa/ReactiveSwift" ~> 4.0 4 | github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 5 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/CrossOverlayFramework/CrossOverlayFramework.swiftcrossimport/Framework.swiftoverlay: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | version: 1 4 | modules: 5 | - name: _CrossOverlayFramework_Framework 6 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/FolderWithDot2.0/SwiftFileInDotPath.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | func printHelloWorld() { 5 | print("Hello World!") 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Vendor/SomeXPCService.xpc/Contents/MacOS/XPC Service: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonaskolb/XcodeGen/HEAD/Tests/Fixtures/TestProject/Vendor/SomeXPCService.xpc/Contents/MacOS/XPC Service -------------------------------------------------------------------------------- /SettingPresets/Products/app-extension.intents-service.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../../../../Frameworks"] 2 | -------------------------------------------------------------------------------- /Sources/XcodeGen/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ProjectSpec 3 | import XcodeGenCLI 4 | import Version 5 | 6 | let version = Version("2.44.1") 7 | let cli = XcodeGenCLI(version: version) 8 | cli.execute() 9 | -------------------------------------------------------------------------------- /SettingPresets/Products/app-extension.messages.yml: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME: iMessage App Icon 2 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"] 3 | -------------------------------------------------------------------------------- /SettingPresets/Products/watchkit2-extension.yml: -------------------------------------------------------------------------------- 1 | LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"] 2 | ASSETCATALOG_COMPILER_COMPLICATION_NAME: Complication 3 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/BuildSettingsContainer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol BuildSettingsContainer { 4 | 5 | var settings: Settings { get } 6 | var configFiles: [String: String] { get } 7 | } 8 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Encoding.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | public protocol JSONEncodable { 5 | // returns JSONDictionary or JSONArray or JSONRawType or nil 6 | func toJSONValue() -> Any 7 | } 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "ReactiveCocoa/ReactiveCocoa" "8.0.2" 2 | github "ReactiveCocoa/ReactiveSwift" "4.0.0" 3 | github "antitypical/Result" "4.1.0" 4 | github "rpassis/CarthageTestFixture" "1.0" 5 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Framework/MyFramework.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyFramework.h 3 | // MyFramework 4 | // 5 | // Created by Yonas Kolb on 21/7/17. 6 | // Copyright © 2017 Yonas Kolb. All rights reserved. 7 | // 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/StaticLibrary_ObjC/StaticLibrary_ObjC.m: -------------------------------------------------------------------------------- 1 | #include "StaticLibrary_ObjC.h" 2 | 3 | @implementation SLObjC 4 | 5 | - (NSString *)description { 6 | return @"Hello, World!"; 7 | } 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .swiftpm 3 | /.build 4 | /Packages 5 | xcuserdata 6 | *.xccheckout 7 | *.xcuserstate 8 | XcodeGen.xcodeproj 9 | xcodegen.zip 10 | xcodegen.artifactbundle.zip 11 | .vscode/launch.json 12 | DerivedData 13 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/CrossOverlayFramework/MyFramework.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyFramework.h 3 | // MyFramework 4 | // 5 | // Created by Yonas Kolb on 21/7/17. 6 | // Copyright © 2017 Yonas Kolb. All rights reserved. 7 | // 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/carthage_dynamic.xcconfig: -------------------------------------------------------------------------------- 1 | #include "xcode12_13_and_14_workaround.xcconfig" 2 | 3 | IPHONEOS_DEPLOYMENT_TARGET=14.0 4 | MACOSX_DEPLOYMENT_TARGET=10.15 5 | TVOS_DEPLOYMENT_TARGET=14.0 6 | WATCHOS_DEPLOYMENT_TARGET=7.0 7 | -------------------------------------------------------------------------------- /SettingPresets/SupportedDestinations/iOS.yml: -------------------------------------------------------------------------------- 1 | SUPPORTED_PLATFORMS: iphoneos iphonesimulator 2 | TARGETED_DEVICE_FAMILY: '1,2' 3 | SUPPORTS_MACCATALYST: NO 4 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: YES 5 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: YES 6 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Platform.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Platform: String, Hashable, CaseIterable { 4 | case auto 5 | case iOS 6 | case tvOS 7 | case macOS 8 | case watchOS 9 | case visionOS 10 | } 11 | -------------------------------------------------------------------------------- /Sources/XcodeGenCLI/Arguments.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PathKit 3 | import SwiftCLI 4 | 5 | extension Path: SwiftCLI.ConvertibleFromString { 6 | 7 | public init?(input: String) { 8 | self.init(input) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Project.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/fixtures.xcconfig: -------------------------------------------------------------------------------- 1 | #include "xcode12_13_and_14_workaround.xcconfig" 2 | 3 | // Common settings for fixtures 4 | CODE_SIGN_IDENTITY = 5 | CODE_SIGNING_REQUIRED = NO 6 | CODE_SIGN_ENTITLEMENTS = 7 | CODE_SIGNING_ALLOWED = NO 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Project.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/scheme_test/TestProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/relative_local_package/LocalPackage/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/Sources/MyAppApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct MyAppApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Utilities/MyPlayground.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/AnotherProject/AnotherProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/carthage_static.xcconfig: -------------------------------------------------------------------------------- 1 | #include "xcode12_13_and_14_workaround.xcconfig" 2 | 3 | MACH_O_TYPE = staticlib 4 | 5 | IPHONEOS_DEPLOYMENT_TARGET=14.0 6 | MACOSX_DEPLOYMENT_TARGET=10.15 7 | TVOS_DEPLOYMENT_TARGET=14.0 8 | WATCHOS_DEPLOYMENT_TARGET=7.0 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Utilities/MyPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/SourceType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Yonas Kolb on 1/5/20. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum SourceType: String { 11 | case group 12 | case file 13 | case folder 14 | case syncedFolder 15 | } 16 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/ExtensionKit Extension/Intent.swift: -------------------------------------------------------------------------------- 1 | import AppIntents 2 | 3 | struct Intent: AppIntent { 4 | static var title: LocalizedStringResource = "Intent" 5 | 6 | func perform() async throws -> some IntentResult { 7 | return .result() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/XPC Service/XPC_Service.m: -------------------------------------------------------------------------------- 1 | #import "XPC_Service.h" 2 | 3 | @implementation XPC_Service 4 | 5 | - (void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply { 6 | NSString *response = [aString uppercaseString]; 7 | reply(response); 8 | } 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/DriverKit Driver/Driver.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Network Extension/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // NetworkExtension 4 | // 5 | // Created by Vlad Gorlov on 18.06.21. 6 | // 7 | 8 | import Foundation 9 | import NetworkExtension 10 | 11 | autoreleasepool { 12 | NEProvider.startSystemExtensionMode() 13 | } 14 | 15 | dispatchMain() 16 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/same_relative_path_test/same_relative_path_test.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - parent1/parent1.yml 3 | - parent2/parent2.yml 4 | targets: 5 | app: 6 | type: application 7 | platform: macOS 8 | sources: 9 | - source 10 | dependencies: 11 | - target: target1 12 | - target: target2 13 | -------------------------------------------------------------------------------- /scripts/gen-fixtures.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | swift run xcodegen --spec Tests/Fixtures/TestProject/AnotherProject/project.yml 5 | swift run xcodegen --spec Tests/Fixtures/TestProject/project.yml 6 | swift run xcodegen --spec Tests/Fixtures/CarthageProject/project.yml 7 | swift run xcodegen --spec Tests/Fixtures/SPM/project.yml 8 | -------------------------------------------------------------------------------- /SettingPresets/Products/framework.yml: -------------------------------------------------------------------------------- 1 | CURRENT_PROJECT_VERSION: 1 2 | DEFINES_MODULE: 'YES' 3 | CODE_SIGN_IDENTITY: "" 4 | DYLIB_COMPATIBILITY_VERSION: 1 5 | DYLIB_CURRENT_VERSION: 1 6 | VERSIONING_SYSTEM: "apple-generic" 7 | INSTALL_PATH: "$(LOCAL_LIBRARY_DIR)/Frameworks" 8 | DYLIB_INSTALL_NAME_BASE: "@rpath" 9 | SKIP_INSTALL: 'YES' 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/App.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | group.com.app 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/included_additional.yml: -------------------------------------------------------------------------------- 1 | name: Included_Additional 2 | settingGroups: 3 | test: 4 | MY_SETTING5: ADDITIONAL 5 | packages: 6 | SwiftPM: 7 | url: https://github.com/apple/swift-package-manager 8 | branch: swift-5.0-branch 9 | targets: 10 | IncludedTarget: 11 | dependencies: 12 | - package: SwiftPM 13 | -------------------------------------------------------------------------------- /SettingPresets/Products/framework.static.yml: -------------------------------------------------------------------------------- 1 | CURRENT_PROJECT_VERSION: 1 2 | DEFINES_MODULE: 'YES' 3 | CODE_SIGN_IDENTITY: "" 4 | DYLIB_COMPATIBILITY_VERSION: 1 5 | DYLIB_CURRENT_VERSION: 1 6 | VERSIONING_SYSTEM: "apple-generic" 7 | INSTALL_PATH: "$(LOCAL_LIBRARY_DIR)/Frameworks" 8 | DYLIB_INSTALL_NAME_BASE: "@rpath" 9 | SKIP_INSTALL: 'YES' 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Workspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Model.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Model 2.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/NSRegularExpressionExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public extension NSRegularExpression { 4 | 5 | func isMatch(to string: String) -> Bool { 6 | let range = NSRange(location: 0, length: string.utf16.count) 7 | return self.firstMatch(in: string, options: [], range: range) != nil 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/duplicated_include/duplicated_import_root.yml: -------------------------------------------------------------------------------- 1 | name: DuplicatedImportRoot 2 | fileGroups: 3 | - First 4 | - Second 5 | targetTemplates: 6 | IncludedTemplate: 7 | type: application 8 | platform: iOS 9 | sources: 10 | - template 11 | preBuildScripts: 12 | - script: swiftlint 13 | name: Swiftlint 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Project.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Workspace.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/environments.yml: -------------------------------------------------------------------------------- 1 | settings: 2 | configs: 3 | Test: 4 | BUNDLE_ID_SUFFIX: .test 5 | Staging: 6 | BUNDLE_ID_SUFFIX: .staging 7 | configs: 8 | Test Debug: debug 9 | Staging Debug: debug 10 | Production Debug: debug 11 | Test Release: release 12 | Staging Release: release 13 | Production Release: release 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/yaml.yml: -------------------------------------------------------------------------------- 1 | true: true 2 | false: false 3 | yes: YES 4 | no: NO 5 | yesQuote: "YES" 6 | noQuote: "NO" 7 | int: 1 8 | intQuote: "1" 9 | float: 3.2 10 | floatQuote: "10.10" 11 | string: hello 12 | stringQuote: "hello" 13 | space: " " 14 | empty: 15 | emptyQuote: "" 16 | emptyDictionary: {} 17 | arrayLiteral: [1,2] 18 | arrayList: 19 | - 1 20 | - 2 21 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Array+Extension.swift: -------------------------------------------------------------------------------- 1 | extension Array where Element == [String: Any?] { 2 | func removingEmptyArraysDictionariesAndNils() -> [[String: Any]] { 3 | var new: [[String: Any]] = [] 4 | forEach { element in 5 | new.append(element.removingEmptyArraysDictionariesAndNils()) 6 | } 7 | return new 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Utilities/MyPlayground.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/AnotherProject/AnotherProject.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Fixtures/duplicated_include/duplicated_import_sut.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - duplicated_import_transitive.yml 3 | - duplicated_import_root.yml 4 | - duplicated_import_root.yml 5 | - path: different_path/duplicated_import_root.yml 6 | relativePaths: false 7 | name: DuplicatedImportDependent 8 | targets: 9 | IncludedTarget: 10 | templates: 11 | - IncludedTemplate -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --exclude .build 2 | --exclude .swiftpm 3 | --swiftversion 5.1 4 | --disable redundantSelf 5 | --disable sortedImports 6 | --disable blankLinesAtStartOfScope 7 | --disable blankLinesAtEndOfScope 8 | --disable unusedArguments 9 | --disable hoistPatternLet 10 | --disable numberFormatting 11 | --disable redundantRawValues 12 | --disable andOperator 13 | --ifdef noindent 14 | --ranges nospace 15 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/project.yml: -------------------------------------------------------------------------------- 1 | name: Project 2 | options: 3 | findCarthageFrameworks: true 4 | targets: 5 | Framework: 6 | type: framework 7 | platform: [iOS, tvOS, watchOS, macOS] 8 | dependencies: 9 | - carthage: Result 10 | - carthage: Alamofire 11 | - carthage: CarthageTestFixture 12 | - carthage: ReactiveSwift 13 | - carthage: ReactiveCocoa 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/Sources/tvOS/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | var body: some View { 5 | VStack { 6 | Image(systemName: "house") 7 | .imageScale(.large) 8 | .foregroundColor(.accentColor) 9 | Text("Hello, world! tvOS") 10 | } 11 | .padding() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/included.yml: -------------------------------------------------------------------------------- 1 | name: Included 2 | settingGroups: 3 | test: 4 | MY_SETTING1: VALUE1 5 | MY_SETTING2: VALUE2 6 | MY_SETTING4: ${SETTING4} 7 | toReplace: 8 | MY_SETTING1: VALUE1 9 | targets: 10 | IncludedTarget: 11 | type: application 12 | platform: iOS 13 | sources: 14 | - Target 15 | targetTemplates: 16 | IncludedTemplate: 17 | sources: 18 | - template 19 | -------------------------------------------------------------------------------- /scripts/diff-fixtures.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [[ `git status --porcelain Tests/Fixtures` ]]; then 5 | echo "" 6 | echo "⚠️ Generated fixtures have changed." 7 | echo "⚠️ If this is a valid change please run the tests and commit the updates." 8 | echo "" 9 | git --no-pager diff --color=always Tests/Fixtures 10 | exit 1 11 | else 12 | echo "✅ Generated fixtures have not changed." 13 | fi 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/Sources/iOS/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | var body: some View { 5 | VStack { 6 | Image(systemName: "globe") 7 | .imageScale(.large) 8 | .foregroundColor(.accentColor) 9 | Text("Hello, world! on iOS") 10 | } 11 | .padding() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/ExtensionKit Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EXAppExtensionAttributes 6 | 7 | EXExtensionPointIdentifier 8 | com.apple.appintents-extension 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip/ViewController.swift: -------------------------------------------------------------------------------- 1 | import Contacts 2 | import UIKit 3 | 4 | class ViewController: UIViewController { 5 | 6 | override func viewDidLoad() { 7 | super.viewDidLoad() 8 | _ = CNContact() 9 | } 10 | 11 | override func didReceiveMemoryWarning() { 12 | super.didReceiveMemoryWarning() 13 | // Dispose of any resources that can be recreated. 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | import Contacts 2 | import UIKit 3 | 4 | class ViewController: UIViewController { 5 | 6 | override func viewDidLoad() { 7 | super.viewDidLoad() 8 | _ = CNContact() 9 | } 10 | 11 | override func didReceiveMemoryWarning() { 12 | super.didReceiveMemoryWarning() 13 | // Dispose of any resources that can be recreated. 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class ViewController: NSViewController { 4 | 5 | override func viewDidLoad() { 6 | super.viewDidLoad() 7 | 8 | // Do any additional setup after loading the view. 9 | } 10 | 11 | override var representedObject: Any? { 12 | didSet { 13 | // Update the view, if already loaded. 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SettingPresets/Configs/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Settings take from the following file and sorted 3 | # /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_ProjectSettings.xctemplate/TemplateInfo.plist 4 | DEBUG_INFORMATION_FORMAT: dwarf-with-dsym 5 | ENABLE_NS_ASSERTIONS: NO 6 | MTL_ENABLE_DEBUG_INFO: NO 7 | 8 | # Swift Settings 9 | SWIFT_COMPILATION_MODE: wholemodule 10 | SWIFT_OPTIMIZATION_LEVEL: -O 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/DriverKit Driver/Driver.iig: -------------------------------------------------------------------------------- 1 | // 2 | // Driver.iig 3 | // Driver 4 | // 5 | // Created by Vlad Gorlov on 18.06.21. 6 | // 7 | 8 | #ifndef Driver_h 9 | #define Driver_h 10 | 11 | #include 12 | #include 13 | 14 | class Driver: public IOService 15 | { 16 | public: 17 | virtual kern_return_t 18 | Start(IOService * provider) override; 19 | }; 20 | 21 | #endif /* Driver_h */ 22 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # name: 4 | # install.sh 5 | # 6 | # description: 7 | # Install XcodeGen to /usr/local 8 | # 9 | # - /usr/local/bin/xcodegen 10 | # - /usr/local/share/xcodegen/SettingPresets 11 | # 12 | # parameters: 13 | # - 1: install location 14 | 15 | PREFIX=${PREFIX:-${1:-/usr/local}} 16 | BASE_DIR=$(cd `dirname $0`; pwd) 17 | 18 | cp -r $BASE_DIR/share "${PREFIX}" 19 | cp -r $BASE_DIR/bin "${PREFIX}" 20 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | @NSApplicationMain 4 | class AppDelegate: NSObject, NSApplicationDelegate { 5 | 6 | func applicationDidFinishLaunching(_ aNotification: Notification) { 7 | // Insert code here to initialize your application 8 | } 9 | 10 | func applicationWillTerminate(_ aNotification: Notification) { 11 | // Insert code here to tear down your application 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "screenWidth" : "{130,145}", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "screenWidth" : "{146,165}", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "screenWidth" : "{130,145}", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "screenWidth" : "{146,165}", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "screenWidth" : "{130,145}", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "screenWidth" : "{146,165}", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "screenWidth" : "{130,145}", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "screenWidth" : "{146,165}", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/XPC Service/XPC_ServiceProtocol.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | // The protocol that this service will vend as its API. This header file will also need to be visible to the process hosting the service. 4 | @protocol XPC_ServiceProtocol 5 | 6 | // Replace the API of this protocol with an API appropriate to the service you are vending. 7 | - (void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo " 5 | ⚙️ Building iOS app" 6 | xcodebuild -quiet -workspace Workspace.xcworkspace -scheme "App_iOS Test" -configuration "Test Debug" -xcconfig fixtures.xcconfig 7 | echo "✅ Successfully built iOS app" 8 | 9 | echo " 10 | ⚙️ Building macOS app" 11 | xcodebuild -quiet -workspace Workspace.xcworkspace -scheme "App_macOS" -configuration "Test Debug" -xcconfig fixtures.xcconfig 12 | echo "✅ Successfully built macOS app" 13 | -------------------------------------------------------------------------------- /Tests/Fixtures/invalid_configs/invalid_configs_value_non_mapping_settings.yml: -------------------------------------------------------------------------------- 1 | name: InvalidConfigsValueNonMappingSettings 2 | 3 | # This fixture tests validation of `settings.configs` at the top level. 4 | # Here, `invalid_key0` and `invalid_key1` are scalar values (not mappings), 5 | # so parsing should throw SpecParsingError.invalidConfigsMappingFormat. 6 | settings: 7 | configs: 8 | invalid_key0: value0 9 | debug: 10 | valid_key: value1 11 | invalid_key1: value2 12 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/DriverKit Driver/Driver.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Driver.cpp 3 | // Driver 4 | // 5 | // Created by Vlad Gorlov on 18.06.21. 6 | // 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "Driver.h" 14 | 15 | kern_return_t 16 | IMPL(Driver, Start) 17 | { 18 | kern_return_t ret; 19 | ret = Start(provider, SUPERDISPATCH); 20 | os_log(OS_LOG_DEFAULT, "Hello World"); 21 | return ret; 22 | } 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/variables_test.yml: -------------------------------------------------------------------------------- 1 | include: [included.yml] 2 | name: NewName 3 | settingGroups: 4 | test: 5 | MY_SETTING1: ${SETTING1} 6 | targets: 7 | SomeTarget: 8 | type: framework 9 | platform: iOS 10 | templates: 11 | - Variables 12 | templateAttributes: 13 | templateVariable: templateVariable 14 | variable: doesNotWin 15 | targetTemplates: 16 | Variables: 17 | sources: 18 | - ${target_name} 19 | - ${variable} 20 | - ${templateVariable} 21 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Framework 2 | import UIKit 3 | 4 | @UIApplicationMain 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | 7 | var window: UIWindow? 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 10 | // Override point for customization after application launch. 11 | _ = FrameworkStruct() 12 | return true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip/Clip.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.parent-application-identifiers 6 | 7 | $(AppIdentifierPrefix)com.project.appwithclip 8 | 9 | com.apple.security.application-groups 10 | group.com.app 11 | 12 | 13 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/EndpointSecurity Extension/EndpointSecurity.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.endpoint-security.client 6 | 7 | com.apple.security.application-groups 8 | 9 | $(TeamIdentifierPrefix)com.example.app-group 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/EndpointSecurity Extension/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // EndpointSecurity 4 | // 5 | // Created by Vlad Gorlov on 18.06.21. 6 | // 7 | 8 | import Foundation 9 | import EndpointSecurity 10 | 11 | var client: OpaquePointer? 12 | 13 | // Create the client 14 | let res = es_new_client(&client) { (client, message) in 15 | // Do processing on the message received 16 | } 17 | 18 | if res != ES_NEW_CLIENT_RESULT_SUCCESS { 19 | exit(EXIT_FAILURE) 20 | } 21 | 22 | dispatchMain() 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Workspace.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "43ca8cdf31efddefed0c1a55ebf00893877282d5a9bda294cd149edd4e559706", 3 | "pins" : [ 4 | { 5 | "identity" : "swinject", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/Swinject/Swinject", 8 | "state" : { 9 | "revision" : "b1d92a53159fe45e162c307183aec9be15e4e7ae", 10 | "version" : "2.8.0" 11 | } 12 | } 13 | ], 14 | "version" : 3 15 | } 16 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Base.lproj/LocalizedStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # The release process for XcodeGen 2 | 3 | 1. Make sure `CHANGELOG.md` is up to date: 4 | - All relevant entries have been added with the PR link and author 5 | - The new version number is added at the top after `Master` 6 | 1. Update the version at the top of `Makefile` 7 | 1. Run `make release` 8 | 1. Push commit and tag to github 9 | 1. Create release from tag on GitHub using the version number and relevant changelog contents 10 | 1. Run `make archive` and upload `xcodegen.zip` and `xcodegen.artifactbundle.zip` to the github release 11 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM/App.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "521B6958-2D62-4961-B353-91EF8F252F4B", 5 | "name" : "Configuration 1", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "codeCoverage" : false, 13 | "targetForVariableExpansion" : { 14 | "containerPath" : "container:SPM.xcodeproj", 15 | "identifier" : "C99E3C420D63D5219CE57E33", 16 | "name" : "App" 17 | } 18 | }, 19 | "testTargets" : [ 20 | 21 | ], 22 | "version" : 1 23 | } 24 | -------------------------------------------------------------------------------- /Tests/Fixtures/scheme_test/test_project.yml: -------------------------------------------------------------------------------- 1 | name: TestProject 2 | schemes: 3 | User_ProjectScheme: 4 | build: 5 | targets: 6 | ExternalTarget: all 7 | management: 8 | shared: false 9 | orderHint: 1 10 | isShown: true 11 | targets: 12 | ExternalTarget: 13 | type: framework 14 | platform: iOS 15 | scheme: 16 | management: 17 | shared: true 18 | isShown: false 19 | Shared_TargetScheme: 20 | type: bundle 21 | platform: iOS 22 | scheme: 23 | management: 24 | orderHint: 0 -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Model.xcdatamodeld/Model.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Model.xcdatamodeld/Model 2.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Model.xcdatamodeld/Model 3.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/FooFeature/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "FooFeature", 8 | products: [ 9 | .library(name: "FooDomain", targets: [ 10 | "FooDomain" 11 | ]), 12 | .library(name: "FooUI", targets: [ 13 | "FooUI" 14 | ]) 15 | ], 16 | targets: [ 17 | .target(name: "FooDomain"), 18 | .target(name: "FooUI") 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SPM 4 | // 5 | // Created by Yonas Kolb on 13/8/19. 6 | // Copyright © 2019 BeemIt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Codability 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | true 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Fixtures/invalid_configs/invalid_configs_value_non_mapping_targets.yml: -------------------------------------------------------------------------------- 1 | name: InvalidConfigsValueNonMappingTargets 2 | 3 | # This fixture tests nested validation of `settings.configs` under a target. 4 | # Here, `invalid_key0` and `invalid_key1` are scalar values (not mappings), 5 | # so parsing should throw SpecParsingError.invalidConfigsMappingFormat. 6 | targets: 7 | invalid_target: 8 | type: application 9 | platform: iOS 10 | settings: 11 | configs: 12 | invalid_key0: value0 13 | debug: 14 | valid_key: value1 15 | invalid_key1: value2 16 | -------------------------------------------------------------------------------- /SettingPresets/Configs/debug.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Settings take from the following file and sorted 3 | # /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_ProjectSettings.xctemplate/TemplateInfo.plist 4 | DEBUG_INFORMATION_FORMAT: dwarf 5 | ENABLE_TESTABILITY: YES 6 | GCC_DYNAMIC_NO_PIC: NO 7 | GCC_OPTIMIZATION_LEVEL: '0' 8 | GCC_PREPROCESSOR_DEFINITIONS: ["$(inherited)", "DEBUG=1"] 9 | MTL_ENABLE_DEBUG_INFO: INCLUDE_SOURCE 10 | ONLY_ACTIVE_ARCH: YES 11 | 12 | # Swift Settings 13 | SWIFT_ACTIVE_COMPILATION_CONDITIONS: DEBUG 14 | SWIFT_OPTIMIZATION_LEVEL: -Onone 15 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Configuration.storekit: -------------------------------------------------------------------------------- 1 | { 2 | "products" : [ 3 | { 4 | "displayPrice" : "0.00", 5 | "familyShareable" : false, 6 | "internalID" : "6D7919A3", 7 | "localizations" : [ 8 | { 9 | "description" : "", 10 | "displayName" : "", 11 | "locale" : "en_US" 12 | } 13 | ], 14 | "productID" : "com.xcodegen.0", 15 | "referenceName" : null, 16 | "type" : "Consumable" 17 | } 18 | ], 19 | "settings" : { 20 | 21 | }, 22 | "subscriptionGroups" : [ 23 | 24 | ], 25 | "version" : { 26 | "major" : 1, 27 | "minor" : 0 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Framework 2 | import UIKit 3 | 4 | @UIApplicationMain 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | 7 | var window: UIWindow? 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 10 | 11 | // file from a framework 12 | _ = FrameworkStruct() 13 | 14 | // Standalone files added to project by path-to-file. 15 | _ = standaloneHello() 16 | 17 | // file in a synced folder 18 | _ = SyncedStruct() 19 | 20 | return true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/include_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "included.yml" 4 | ], 5 | "name": "NewName", 6 | "settingGroups": { 7 | "test": { 8 | "MY_SETTING1": "NEW VALUE", 9 | "MY_SETTING3": "VALUE3" 10 | }, 11 | "new": { 12 | "MY_SETTING": "VALUE" 13 | }, 14 | "toReplace:REPLACE": { 15 | "MY_SETTING2": "VALUE2" 16 | } 17 | }, 18 | "targets": { 19 | "NewTarget": { 20 | "type": "application", 21 | "platform": "iOS" 22 | }, 23 | "IncludedTarget": { 24 | "name": "IncludedTargetNew", 25 | "platform": "tvOS", 26 | "sources:REPLACE": "NewSource" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/Fixtures/invalid_configs/invalid_configs_value_non_mapping_setting_groups.yml: -------------------------------------------------------------------------------- 1 | name: InvalidConfigsValueNonMappingSettingGroups 2 | 3 | # This fixture tests validation of `settings.configs` under an aggregate target. 4 | # Here, `invalid_key0` and `invalid_key1` are scalar values (not mappings), 5 | # so parsing should throw SpecParsingError.invalidConfigsMappingFormat. 6 | settingGroups: 7 | invalid_preset: 8 | configs: 9 | invalid_key0: value0 10 | debug: 11 | valid_key: value1 12 | invalid_key1: value2 13 | targets: 14 | invalid_target: 15 | type: application 16 | platform: iOS 17 | settings: 18 | groups: 19 | - invalid_preset 20 | -------------------------------------------------------------------------------- /scripts/archive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PACKAGE_NAME=${EXECUTABLE_NAME:?} 3 | TMP=$(mktemp -d)/$PACKAGE_NAME 4 | BINDIR=$TMP/bin 5 | SHAREDIR=$TMP/share/$PACKAGE_NAME 6 | ZIPFILE=$TMP/${EXECUTABLE_NAME:?}.zip 7 | INSTALLSH=scripts/install.sh 8 | LICENSE=LICENSE 9 | 10 | # copy 11 | 12 | mkdir -p $BINDIR 13 | cp -f "$1" $BINDIR 14 | 15 | mkdir -p $SHAREDIR 16 | cp -R SettingPresets $SHAREDIR/SettingPresets 17 | 18 | cp $INSTALLSH $TMP 19 | 20 | cp $LICENSE $TMP 21 | 22 | # zip 23 | 24 | (cd $TMP/..; zip -r $ZIPFILE $PACKAGE_NAME) 25 | 26 | # print sha 27 | 28 | SHA=$(cat $ZIPFILE | shasum -a 256 | sed 's/ .*//') 29 | echo "SHA: $SHA" 30 | mv $ZIPFILE . 31 | 32 | # cleanup 33 | 34 | rm -rf $TMP 35 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import WatchKit 3 | 4 | class InterfaceController: WKInterfaceController { 5 | 6 | override func awake(withContext context: Any?) { 7 | super.awake(withContext: context) 8 | // Configure interface objects here. 9 | } 10 | 11 | override func willActivate() { 12 | // This method is called when watch view controller is about to be visible to user 13 | super.willActivate() 14 | } 15 | 16 | override func didDeactivate() { 17 | // This method is called when watch view controller is no longer visible 18 | super.didDeactivate() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/String Catalogs/LocalizableStrings.xcstrings: -------------------------------------------------------------------------------- 1 | { 2 | "sourceLanguage" : "en", 3 | "strings" : { 4 | "sampleText" : { 5 | "comment" : "Sample string in an asset catalog", 6 | "extractionState" : "manual", 7 | "localizations" : { 8 | "en" : { 9 | "stringUnit" : { 10 | "state" : "translated", 11 | "value" : "This is a localized string" 12 | } 13 | }, 14 | "es" : { 15 | "stringUnit" : { 16 | "state" : "translated", 17 | "value" : "Esta es una cadena de texto localizable." 18 | } 19 | } 20 | } 21 | } 22 | }, 23 | "version" : "1.0" 24 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/Assets.xcassets/Complication.complicationset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "idiom" : "watch", 5 | "filename" : "Circular.imageset", 6 | "role" : "circular" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "filename" : "Extra Large.imageset", 11 | "role" : "extra-large" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "filename" : "Modular.imageset", 16 | "role" : "modular" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "filename" : "Utilitarian.imageset", 21 | "role" : "utilitarian" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Carthage/Build/.Result.version: -------------------------------------------------------------------------------- 1 | { 2 | "Mac" : [ 3 | { 4 | "name" : "Result", 5 | "hash" : "a608337c467d3ef16f53035c587dab84152fc14b3b4c8b597c5a973d3bfd0d63" 6 | } 7 | ], 8 | "watchOS" : [ 9 | { 10 | "name" : "Result", 11 | "hash" : "24680c05790afb8bd5d1ae8f1960905210546943e13fdea8e2994c2f8efdc0a5" 12 | } 13 | ], 14 | "tvOS" : [ 15 | { 16 | "name" : "Result", 17 | "hash" : "dca6d7e70e25cfa2b3363a0839dd13271b4da374f22d6cd357cf45b39299c17b" 18 | } 19 | ], 20 | "commitish" : "4.1.0", 21 | "iOS" : [ 22 | { 23 | "name" : "Result", 24 | "hash" : "5ad79b86004be535ee374685fbbd95db8d91f8c0458d3534aacf426930c7bbf6" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/AnotherProject/project.yml: -------------------------------------------------------------------------------- 1 | name: AnotherProject 2 | include: [../environments.yml] 3 | options: 4 | useBaseInternationalization: false 5 | configFiles: 6 | Test Debug: ../Configs/config.xcconfig 7 | targets: 8 | BundleX: 9 | type: bundle 10 | platform: iOS 11 | settings: 12 | GENERATE_INFOPLIST_FILE: YES 13 | BundleY: 14 | type: bundle 15 | platform: iOS 16 | settings: 17 | GENERATE_INFOPLIST_FILE: YES 18 | ExternalTarget: 19 | type: framework 20 | platform: iOS 21 | settings: 22 | GENERATE_INFOPLIST_FILE: YES 23 | IncludedLegacy: 24 | type: "" 25 | platform: iOS 26 | legacy: 27 | toolPath: /usr/bin/true 28 | workingDirectory: . 29 | 30 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Network Extension/NetworkExtension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.application-groups 8 | 9 | $(TeamIdentifierPrefix)com.example.app-group 10 | 11 | com.apple.developer.networking.networkextension 12 | 13 | packet-tunnel-provider 14 | app-proxy-provider 15 | content-filter-provider 16 | dns-proxy 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Carthage/Build/.Alamofire.version: -------------------------------------------------------------------------------- 1 | { 2 | "Mac" : [ 3 | { 4 | "name" : "Alamofire", 5 | "hash" : "c8ee4910ecef2738eccd34a7abcae7d6561c0d709fbc301f660391018bf654b5" 6 | } 7 | ], 8 | "watchOS" : [ 9 | { 10 | "name" : "Alamofire", 11 | "hash" : "960f25b2b83d186bf62a05e59af8a6f7c99ac20201f6d948106c52917f29b7da" 12 | } 13 | ], 14 | "tvOS" : [ 15 | { 16 | "name" : "Alamofire", 17 | "hash" : "b8994794ee1378f98ceb7d294c84990d60fdabcc5f5a7d8386e71315ff583f1f" 18 | } 19 | ], 20 | "commitish" : "4.7.2", 21 | "iOS" : [ 22 | { 23 | "name" : "Alamofire", 24 | "hash" : "a000ec6f95d6fd12843007ba8c22e69c64e922b45ccfe34e1d38200b54e5e391" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Sources/ProjectSpec/VersionExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Yonas Kolb on 7/2/20. 6 | // 7 | 8 | import Foundation 9 | import Version 10 | 11 | extension Version: Swift.ExpressibleByStringLiteral { 12 | 13 | public static func parse(_ string: String) throws -> Version { 14 | if let version = Version(tolerant: string) { 15 | return version 16 | } else { 17 | throw SpecParsingError.invalidVersion(string) 18 | } 19 | } 20 | 21 | public static func parse(_ double: Double) throws -> Version { 22 | return try Version.parse(String(double)) 23 | } 24 | 25 | public init(stringLiteral value: String) { 26 | self.init(tolerant: value)! 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Fixtures/include_test.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - included.yml 3 | - path: included_additional.yml 4 | enable: ${INCLUDE_ADDITIONAL_YAML} 5 | packages: 6 | Yams: 7 | url: https://github.com/jpsim/Yams 8 | majorVersion: 2.0.0 9 | name: NewName 10 | settingGroups: 11 | test: 12 | MY_SETTING1: NEW VALUE 13 | MY_SETTING3: VALUE3 14 | new: 15 | MY_SETTING: VALUE 16 | toReplace:REPLACE: 17 | MY_SETTING2: VALUE2 18 | targets: 19 | NewTarget: 20 | type: application 21 | platform: iOS 22 | templates: [IncludedTemplate] 23 | sources: 24 | - target 25 | IncludedTarget: 26 | name: IncludedTargetNew 27 | platform: tvOS 28 | sources:REPLACE: NewSource 29 | dependencies: 30 | - package: Yams 31 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Carthage/Build/.ReactiveSwift.version: -------------------------------------------------------------------------------- 1 | { 2 | "Mac" : [ 3 | { 4 | "name" : "ReactiveSwift", 5 | "hash" : "ed8de3ce3e03c940ba43f99c5de552b9af89c2d850bb8afb6e5f9f493489f45d" 6 | } 7 | ], 8 | "watchOS" : [ 9 | { 10 | "name" : "ReactiveSwift", 11 | "hash" : "c015d0bc75885b9358870ef16cb9e67b5ef8bcaa3a5ec90d513d623a1091f7eb" 12 | } 13 | ], 14 | "tvOS" : [ 15 | { 16 | "name" : "ReactiveSwift", 17 | "hash" : "4d4c3af69ff5b35bec1b76638989b29cb2d785536b2e124c5cfd6660d3eb2640" 18 | } 19 | ], 20 | "commitish" : "4.0.0", 21 | "iOS" : [ 22 | { 23 | "name" : "ReactiveSwift", 24 | "hash" : "bdf6f01656281b630b9f323625fc5134bb4fe4e061ac1885e3dc6498f9cad7dc" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Sources/ProjectSpec/ProjectTarget.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XcodeProj 3 | 4 | public protocol ProjectTarget: BuildSettingsContainer { 5 | 6 | var name: String { get } 7 | var type: PBXProductType { get } 8 | var buildScripts: [BuildScript] { get } 9 | var buildToolPlugins: [BuildToolPlugin] { get } 10 | var scheme: TargetScheme? { get } 11 | var attributes: [String: Any] { get } 12 | } 13 | 14 | extension Target { 15 | 16 | public var buildScripts: [BuildScript] { 17 | preBuildScripts + postCompileScripts + postBuildScripts 18 | } 19 | } 20 | 21 | extension Project { 22 | 23 | public var projectTargets: [ProjectTarget] { 24 | targets.map { $0 as ProjectTarget } + aggregateTargets.map { $0 as ProjectTarget } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/invalid_configs/invalid_configs_value_non_mapping_aggregate_targets.yml: -------------------------------------------------------------------------------- 1 | name: InvalidConfigsValueNonMappingAggregateTargets 2 | 3 | # This fixture tests validation of `settings.configs` under an aggregate target. 4 | # Here, `invalid_key0` and `invalid_key1` are scalar values (not mappings), 5 | # so parsing should throw SpecParsingError.invalidConfigsMappingFormat. 6 | targets: 7 | valid_target1: 8 | type: application 9 | platform: iOS 10 | valid_target2: 11 | type: application 12 | platform: iOS 13 | 14 | aggregateTargets: 15 | invalid_target: 16 | targets: 17 | - valid_target1 18 | - valid_target2 19 | settings: 20 | configs: 21 | invalid_key0: value0 22 | debug: 23 | valid_key: value1 24 | invalid_key1: value2 25 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/scheme_test/TestProject.xcodeproj/xcuserdata/someUser.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ExternalTarget.xcscheme_^#shared#^_ 8 | 9 | isShown 10 | 11 | 12 | Shared_TargetScheme.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | User_ProjectScheme.xcscheme 18 | 19 | isShown 20 | 21 | orderHint 22 | 1 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip_UITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS_UITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/XcodeGenKitTests/BreakpointGeneratorTests.swift: -------------------------------------------------------------------------------- 1 | import ProjectSpec 2 | import Spectre 3 | import TestSupport 4 | import XCTest 5 | 6 | class BreakpointGeneratorTests: XCTestCase { 7 | 8 | func testBreakpoints() { 9 | describe { 10 | 11 | $0.it("generates breakpoint") { 12 | let breakpoint = Breakpoint(type: .exception(.init())) 13 | let project = Project(basePath: "", name: "test", targets: [], breakpoints: [breakpoint]) 14 | let xcodeProject = try project.generateXcodeProject() 15 | let xcbreakpoint = try unwrap(xcodeProject.sharedData?.breakpoints?.breakpoints.first) 16 | try expect(xcbreakpoint.breakpointExtensionID.rawValue) == "Xcode.Breakpoint.ExceptionBreakpoint" 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test.yml: -------------------------------------------------------------------------------- 1 | include: paths_test/included_paths_test.yml 2 | name: NewName 3 | configFiles: 4 | NewConfig: config 5 | targets: 6 | NewTarget: 7 | type: application 8 | platform: iOS 9 | templates: [Template1] 10 | configFiles: 11 | Config: config 12 | sources: 13 | - source 14 | dependencies: 15 | - framework: Framework 16 | info: 17 | path: info 18 | entitlements: 19 | path: entitlements 20 | preBuildScripts: 21 | - path: preBuildScript 22 | postCompileScripts: 23 | - path: postCompileScript 24 | postBuildScripts: 25 | - path: postBuildScript 26 | aggregateTargets: 27 | NewAggregateTarget: 28 | targets: 29 | - NewTarget 30 | configFiles: 31 | Config: config 32 | buildScripts: 33 | - path: buildScript 34 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/relative_local_package/LocalPackage/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.7 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "LocalPackage", 8 | products: [ 9 | // Products define the executables and libraries a package produces, making them visible to other packages. 10 | .library( 11 | name: "LocalPackage", 12 | targets: ["LocalPackage"] 13 | ) 14 | ], 15 | targets: [ 16 | // Targets are the basic building blocks of a package, defining a module or a test suite. 17 | // Targets can depend on other targets in this package and products from dependencies. 18 | .target(name: "LocalPackage") 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Yaml.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PathKit 3 | import Yams 4 | 5 | public func loadYamlDictionary(path: Path) throws -> [String: Any] { 6 | let string: String = try path.read() 7 | if string == "" { 8 | return [:] 9 | } 10 | 11 | let resolver = Resolver.default 12 | .removing(.null) // remove rule so that empty quotes are treated as empty strings 13 | 14 | guard let yaml = try Yams.load(yaml: string, resolver) else { 15 | return [:] 16 | } 17 | return yaml as? [String: Any] ?? [:] 18 | } 19 | 20 | public func dumpYamlDictionary(_ dictionary: [String: Any], path: Path) throws { 21 | let uncluttered = (dictionary as [String: Any?]).removingEmptyArraysDictionariesAndNils() 22 | let string: String = try Yams.dump(object: uncluttered) 23 | try path.write(string) 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/xcode12_13_and_14_workaround.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // See https://github.com/Carthage/Carthage/issues/3019 3 | // 4 | // Skips building ARM slices for simulators until Carthage can support it 5 | // 6 | 7 | EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64=arm64 arm64e armv7 armv7s armv6 armv8 8 | EXCLUDED_ARCHS_1200=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)) 9 | EXCLUDED_ARCHS_1300=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)) 10 | EXCLUDED_ARCHS_1400=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)) 11 | EXCLUDED_ARCHS=$(inherited) $(EXCLUDED_ARCHS_$(XCODE_VERSION_MAJOR)) 12 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/recursive_test/recursive_test.yml: -------------------------------------------------------------------------------- 1 | options: 2 | carthageBuildPath: carthage_build 3 | carthageExecutablePath: carthage_executable 4 | configFiles: 5 | RecursiveConfig: config 6 | targets: 7 | RecursiveTarget: 8 | type: application 9 | platform: macOS 10 | configFiles: 11 | Config: config 12 | sources: 13 | - source 14 | dependencies: 15 | - framework: Framework 16 | info: 17 | path: info 18 | entitlements: 19 | path: entitlements 20 | prebuildScripts: 21 | - path: prebuildScript 22 | postCompileScripts: 23 | - path: postCompileScript 24 | postBuildScripts: 25 | - path: postBuildScript 26 | aggregateTargets: 27 | RecursiveAggregateTarget: 28 | targets: 29 | - RecursiveTarget 30 | configFiles: 31 | Config: config 32 | buildScripts: 33 | - path: buildScript 34 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/CrossOverlayFramework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPMTests/SPMTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class SPMTests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Base.lproj/Localizable.stringsdict: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StringKey 6 | 7 | NSStringLocalizedFormatKey 8 | %#@VARIABLE@ 9 | Variable 10 | 11 | NSStringFormatSpecTypeKey 12 | NSStringPluralRuleType 13 | NSStringFormatValueTypeKey 14 | 15 | zero 16 | 17 | one 18 | 19 | two 20 | 21 | few 22 | 23 | many 24 | 25 | other 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/en.lproj/Localizable.stringsdict: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StringKey 6 | 7 | NSStringLocalizedFormatKey 8 | %#@VARIABLE@ 9 | Variable 10 | 11 | NSStringFormatSpecTypeKey 12 | NSStringPluralRuleType 13 | NSStringFormatValueTypeKey 14 | 15 | zero 16 | 17 | one 18 | 19 | two 20 | 21 | few 22 | 23 | many 24 | 25 | other 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/Info.generated.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | TestApp 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1.0.0 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Network Extension/FilterDataProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterDataProvider.swift 3 | // NetworkExtension 4 | // 5 | // Created by Vlad Gorlov on 18.06.21. 6 | // 7 | 8 | import NetworkExtension 9 | 10 | class FilterDataProvider: NEFilterDataProvider { 11 | 12 | override func startFilter(completionHandler: @escaping (Error?) -> Void) { 13 | // Add code to initialize the filter. 14 | completionHandler(nil) 15 | } 16 | 17 | override func stopFilter(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { 18 | // Add code to clean up filter resources. 19 | completionHandler() 20 | } 21 | 22 | override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict { 23 | // Add code to determine if the flow should be dropped or not, downloading new rules if required. 24 | return .allow() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip_Tests/TestProjectTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class TestProjectTests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS_Tests/TestProjectTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class TestProjectTests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS_Tests/TestProjectTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class TestProjectTests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip_UITests/TestProjectUITests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class TestProjectUITests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS_UITests/TestProjectUITests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class TestProjectUITests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/legacy_paths_test/legacy_included_paths_test.yml: -------------------------------------------------------------------------------- 1 | configFiles: 2 | IncludedConfig: config 3 | include: 4 | - path: legacy_paths_test/recursive_include.yml 5 | relativePaths: false 6 | options: 7 | carthageBuildPath: carthage_build 8 | carthageExecutablePath: carthage_executable 9 | targets: 10 | IncludedTarget: 11 | type: application 12 | platform: tvOS 13 | configFiles: 14 | Config: config 15 | dependencies: 16 | - framework: Framework 17 | info: 18 | path: info 19 | entitlements: 20 | path: entitlements 21 | preBuildScripts: 22 | - path: preBuildScript 23 | postCompileScripts: 24 | - path: postCompileScript 25 | postBuildScripts: 26 | - path: postBuildScript 27 | aggregateTargets: 28 | IncludedAggregateTarget: 29 | targets: 30 | - IncludedTarget 31 | configFiles: 32 | Config: config 33 | buildScripts: 34 | - path: buildScript 35 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/PushNotificationPayload.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps": { 3 | "alert": { 4 | "body": "Test message", 5 | "title": "Optional title" 6 | }, 7 | "category": "myCategory", 8 | "thread-id":"5280" 9 | }, 10 | 11 | "WatchKit Simulator Actions": [ 12 | { 13 | "title": "First Button", 14 | "identifier": "firstButtonAction" 15 | } 16 | ], 17 | 18 | "customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App." 19 | } 20 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/GroupOrdering.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | /// Describes an order of groups. 5 | public struct GroupOrdering: Equatable { 6 | 7 | /// A group name pattern. 8 | public var pattern: String 9 | 10 | /// A group name regex. 11 | public var regex: NSRegularExpression? 12 | 13 | /// Subgroups orders. 14 | public var order: [String] 15 | 16 | public init(pattern: String = "", order: [String] = []) { 17 | self.pattern = pattern 18 | self.regex = try? NSRegularExpression(pattern: pattern) 19 | self.order = order 20 | } 21 | 22 | } 23 | 24 | extension GroupOrdering: JSONObjectConvertible { 25 | 26 | public init(jsonDictionary: JSONDictionary) throws { 27 | pattern = jsonDictionary.json(atKeyPath: "pattern") ?? "" 28 | regex = try? NSRegularExpression(pattern: pattern) 29 | order = jsonDictionary.json(atKeyPath: "order") ?? [] 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/CacheFile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XcodeGenCore 3 | import Version 4 | 5 | public class CacheFile { 6 | 7 | public let string: String 8 | 9 | init?(version: Version, projectDictionary: [String: Any], project: Project) throws { 10 | 11 | guard #available(OSX 10.13, *) else { return nil } 12 | 13 | let files = Set(project.allTrackedFiles) 14 | .map { ((try? $0.relativePath(from: project.basePath)) ?? $0).string } 15 | .sorted { $0.localizedStandardCompare($1) == .orderedAscending } 16 | .joined(separator: "\n") 17 | 18 | let data = try JSONSerialization.data(withJSONObject: projectDictionary, options: [.sortedKeys, .prettyPrinted]) 19 | let spec = String(data: data, encoding: .utf8)! 20 | 21 | string = """ 22 | # XCODEGEN VERSION 23 | \(version) 24 | 25 | # SPEC 26 | \(spec) 27 | 28 | # FILES 29 | \(files)" 30 | 31 | """ 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/ProjectReference.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | public struct ProjectReference: Hashable { 5 | public var name: String 6 | public var path: String 7 | 8 | public init(name: String, path: String) { 9 | self.name = name 10 | self.path = path 11 | } 12 | } 13 | 14 | extension ProjectReference: PathContainer { 15 | 16 | static var pathProperties: [PathProperty] { 17 | [ 18 | .dictionary([ 19 | .string("path"), 20 | ]), 21 | ] 22 | } 23 | } 24 | 25 | extension ProjectReference: NamedJSONDictionaryConvertible { 26 | public init(name: String, jsonDictionary: JSONDictionary) throws { 27 | self.name = name 28 | self.path = try jsonDictionary.json(atKeyPath: "path") 29 | } 30 | } 31 | 32 | extension ProjectReference: JSONEncodable { 33 | public func toJSONValue() -> Any { 34 | [ 35 | "path": path, 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/XPC Service/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | XPC Service 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | XPCService 24 | 25 | ServiceType 26 | Application 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Sources/XcodeGenCLI/XcodeGenCLI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ProjectSpec 3 | import SwiftCLI 4 | import Version 5 | 6 | public class XcodeGenCLI { 7 | let cli: CLI 8 | 9 | public init(version: Version) { 10 | let generateCommand = GenerateCommand(version: version) 11 | 12 | cli = CLI( 13 | name: "xcodegen", 14 | version: version.description, 15 | description: "Generates Xcode projects", 16 | commands: [ 17 | generateCommand, 18 | CacheCommand(version: version), 19 | DumpCommand(version: version), 20 | ] 21 | ) 22 | cli.parser.routeBehavior = .searchWithFallback(generateCommand) 23 | } 24 | 25 | public func execute(arguments: [String]? = nil) { 26 | let status: Int32 27 | if let arguments = arguments { 28 | status = cli.go(with: arguments) 29 | } else { 30 | status = cli.go() 31 | } 32 | exit(status) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/TestPlan.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | public struct TestPlan: Hashable { 5 | public var path: String 6 | public var defaultPlan: Bool 7 | 8 | public init(path: String, defaultPlan: Bool = false) { 9 | self.defaultPlan = defaultPlan 10 | self.path = path 11 | } 12 | } 13 | 14 | 15 | extension TestPlan: JSONObjectConvertible { 16 | 17 | public init(jsonDictionary: JSONDictionary) throws { 18 | path = try jsonDictionary.json(atKeyPath: "path") 19 | defaultPlan = jsonDictionary.json(atKeyPath: "defaultPlan") ?? false 20 | } 21 | } 22 | 23 | extension TestPlan: JSONEncodable { 24 | public func toJSONValue() -> Any { 25 | [ 26 | "path": path, 27 | "defaultPlan": defaultPlan, 28 | ] as [String : Any] 29 | } 30 | } 31 | 32 | extension TestPlan: PathContainer { 33 | 34 | static var pathProperties: [PathProperty] { 35 | [ 36 | .string("path"), 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Docs/Examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | These are a bunch of real world examples of XcodeGen project specs. Feel free to add your own via PR. 4 | 5 | - [toshi0383/Bitrise-iOS](https://github.com/toshi0383/Bitrise-iOS/blob/master/project.yml) 6 | - [johndpope/swift-models](https://github.com/johndpope/swift-models/tree/stable/Inference) 7 | - [atelier-socle/AppRepositoryTemplate](https://github.com/atelier-socle/AppRepositoryTemplate/blob/master/project.yml) 8 | - [atelier-socle/FrameworkRepositoryTemplate](https://github.com/atelier-socle/FrameworkRepositoryTemplate/blob/master/project.yml) 9 | - [scelis/XcodeGen-TestStickers](https://github.com/scelis/XcodeGen-TestStickers/blob/master/project.yml) 10 | - [minvws/nl-covid19-notification-app-ios](https://github.com/minvws/nl-covid19-notification-app-ios/blob/master/project.yml) 11 | - [pvinis/react-native-xcodegen](https://github.com/pvinis/react-native-xcodegen/blob/master/templates) 12 | - [covid19cz/erouska-ios](https://github.com/covid19cz/erouska-ios/blob/develop/project.yml) 13 | - [markst/hotreloading-vscode-ios](https://github.com/markst/hotreloading-vscode-ios) 14 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/EndpointSecurity Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | EndpointSecurity 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSSystemExtensionUsageDescription 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: {} 4 | pull_request: {} 5 | jobs: 6 | run: 7 | runs-on: macos-15 8 | name: Xcode ${{ matrix.xcode }} 9 | strategy: 10 | matrix: 11 | xcode: ["16.0", "16.3"] 12 | env: 13 | DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer 14 | steps: 15 | - uses: actions/checkout@master 16 | - name: Resolve 17 | run: swift package resolve 18 | - name: Build 19 | run: swift build 20 | - name: Test 21 | run: set -o pipefail && swift test 2>&1 | xcpretty 22 | - name: Gen fixtures 23 | run: scripts/gen-fixtures.sh 24 | - name: Check fixtures 25 | run: scripts/diff-fixtures.sh 26 | - name: Build fixtures 27 | env: 28 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | run: scripts/build-fixtures.sh 30 | run-linux: 31 | runs-on: ubuntu-latest 32 | name: Linux 33 | steps: 34 | - uses: actions/checkout@master 35 | - name: Build and run tests 36 | run: swift test --enable-test-discovery 37 | -------------------------------------------------------------------------------- /Tests/XcodeGenCoreTests/AtomicTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicTests.swift 3 | // 4 | // 5 | // Created by Vladislav Lisianskii on 27.03.2022. 6 | // 7 | 8 | import XCTest 9 | @testable import XcodeGenCore 10 | 11 | final class AtomicTests: XCTestCase { 12 | 13 | @Atomic private var atomicDictionary = [String: Int]() 14 | 15 | func testSimultaneousWriteOrder() { 16 | let group = DispatchGroup() 17 | 18 | for index in (0..<100) { 19 | group.enter() 20 | DispatchQueue.global().async { 21 | self.$atomicDictionary.with { atomicDictionary in 22 | atomicDictionary["\(index)"] = index 23 | } 24 | group.leave() 25 | } 26 | } 27 | 28 | group.notify(queue: .main, execute: { 29 | var expectedValue = [String: Int]() 30 | for index in (0..<100) { 31 | expectedValue["\(index)"] = index 32 | } 33 | XCTAssertEqual( 34 | self.atomicDictionary, 35 | expectedValue 36 | ) 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | IMessage 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionMainStoryboard 26 | MainInterface 27 | NSExtensionPointIdentifier 28 | com.apple.message-payload-provider 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yonas Kolb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS/App-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | CustomSetting 24 | $CUSTOM_SETTING 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | App_iOS 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.2 21 | CFBundleVersion 22 | 1 23 | UISupportedInterfaceOrientations 24 | 25 | UIInterfaceOrientationPortrait 26 | UIInterfaceOrientationPortraitUpsideDown 27 | 28 | WKCompanionAppBundleIdentifier 29 | com.project.app 30 | WKWatchKitApp 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Plist.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | public struct Plist: Equatable { 5 | 6 | public let path: String 7 | public let properties: [String: Any] 8 | 9 | public init(path: String, attributes: [String: Any] = [:]) { 10 | self.path = path 11 | properties = attributes 12 | } 13 | 14 | public static func == (lhs: Plist, rhs: Plist) -> Bool { 15 | lhs.path == rhs.path && 16 | NSDictionary(dictionary: lhs.properties).isEqual(to: rhs.properties) 17 | } 18 | } 19 | 20 | extension Plist: JSONObjectConvertible { 21 | 22 | public init(jsonDictionary: JSONDictionary) throws { 23 | path = try jsonDictionary.json(atKeyPath: "path") 24 | properties = jsonDictionary.json(atKeyPath: "properties") ?? [:] 25 | } 26 | } 27 | 28 | extension Plist: JSONEncodable { 29 | public func toJSONValue() -> Any { 30 | [ 31 | "path": path, 32 | "properties": properties, 33 | ] as [String : Any] 34 | } 35 | } 36 | 37 | extension Plist: PathContainer { 38 | 39 | static var pathProperties: [PathProperty] { 40 | [ 41 | .string("path"), 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/App_iOS.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "AC4AE1EF-F65C-4037-8994-D607D2E5841E", 5 | "name" : "Configuration 1", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "commandLineArgumentEntries" : [ 13 | { 14 | "argument" : "MyDisabledArgument", 15 | "enabled" : false 16 | }, 17 | { 18 | "argument" : "MyEnabledArgument" 19 | } 20 | ], 21 | "mainThreadCheckerEnabled" : false, 22 | "targetForVariableExpansion" : { 23 | "containerPath" : "container:Project.xcodeproj", 24 | "identifier" : "0867B0DACEF28C11442DE8F7", 25 | "name" : "App_iOS" 26 | } 27 | }, 28 | "testTargets" : [ 29 | { 30 | "target" : { 31 | "containerPath" : "container:Project.xcodeproj", 32 | "identifier" : "DC2F16BAA6E13B44AB62F888", 33 | "name" : "App_iOS_Tests" 34 | } 35 | }, 36 | { 37 | "target" : { 38 | "containerPath" : "container:Project.xcodeproj", 39 | "identifier" : "F674B2CFC4738EEC49BAD0DA", 40 | "name" : "App_iOS_UITests" 41 | } 42 | } 43 | ], 44 | "version" : 1 45 | } 46 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Tests/XcodeGenCoreTests/ArrayExtensionsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import XcodeGenCore 3 | 4 | class ArrayExtensionsTests: XCTestCase { 5 | 6 | func testSearchingForFirstIndex() { 7 | let array = SortedArray([1, 2, 3, 4 ,5]) 8 | XCTAssertEqual(array.firstIndex(where: { $0 > 2 }), 2) 9 | } 10 | 11 | func testIndexCannotBeFound() { 12 | let array = SortedArray([1, 2, 3, 4, 5]) 13 | XCTAssertEqual(array.firstIndex(where: { $0 > 10 }), nil) 14 | } 15 | 16 | func testEmptyArray() { 17 | let array = SortedArray([Int]()) 18 | XCTAssertEqual(array.firstIndex(where: { $0 > 0 }), nil) 19 | } 20 | 21 | func testSearchingReturnsFirstIndexWhenMultipleElementsHaveSameValue() { 22 | let array = SortedArray([1, 2, 3, 3 ,3]) 23 | XCTAssertEqual(array.firstIndex(where: { $0 == 3 }), 2) 24 | } 25 | } 26 | 27 | 28 | class SortedArrayTests: XCTestCase { 29 | 30 | func testSortingOnInitialization() { 31 | let array = [1, 5, 4, 2] 32 | let sortedArray = SortedArray(array) 33 | XCTAssertEqual([1, 2, 4, 5], sortedArray.value) 34 | } 35 | 36 | func testEmpty() { 37 | XCTAssertEqual([Int](), SortedArray([Int]()).value) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/NotificationController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UserNotifications 3 | import WatchKit 4 | 5 | class NotificationController: WKUserNotificationInterfaceController { 6 | 7 | override init() { 8 | // Initialize variables here. 9 | super.init() 10 | 11 | // Configure interface objects here. 12 | } 13 | 14 | override func willActivate() { 15 | // This method is called when watch view controller is about to be visible to user 16 | super.willActivate() 17 | } 18 | 19 | override func didDeactivate() { 20 | // This method is called when watch view controller is no longer visible 21 | super.didDeactivate() 22 | } 23 | 24 | /* 25 | override func didReceive(_ notification: UNNotification, withCompletion completionHandler: @escaping (WKUserNotificationInterfaceType) -> Swift.Void) { 26 | // This method is called when a notification needs to be presented. 27 | // Implement it if you use a dynamic notification interface. 28 | // Populate your dynamic notification interface as quickly as possible. 29 | // 30 | // After populating your dynamic notification interface call the completion block. 31 | completionHandler(.custom) 32 | } 33 | */ 34 | } 35 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | App_watchOS Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | WKAppBundleIdentifier 28 | com.project.app.watch 29 | 30 | NSExtensionPointIdentifier 31 | com.apple.watchkit 32 | 33 | WKExtensionDelegateClassName 34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 35 | 36 | 37 | -------------------------------------------------------------------------------- /Sources/XcodeGenCore/Atomic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Atomic.swift 3 | // 4 | // 5 | // Created by Vladislav Lisianskii on 23.02.2022. 6 | // 7 | 8 | import Foundation 9 | 10 | @propertyWrapper 11 | public final class Atomic { 12 | 13 | private var value: Value 14 | 15 | private let queue = DispatchQueue( 16 | label: "com.xcodegencore.atomic.\(UUID().uuidString)", 17 | qos: .utility, 18 | attributes: .concurrent, 19 | autoreleaseFrequency: .inherit, 20 | target: .global() 21 | ) 22 | 23 | public init(wrappedValue: Value) { 24 | self.value = wrappedValue 25 | } 26 | 27 | public var wrappedValue: Value { 28 | get { 29 | queue.sync { value } 30 | } 31 | set { 32 | queue.async(flags: .barrier) { [weak self] in 33 | self?.value = newValue 34 | } 35 | } 36 | } 37 | 38 | /// Allows us to get the actual `Atomic` instance with the $ 39 | /// prefix. 40 | public var projectedValue: Atomic { 41 | return self 42 | } 43 | 44 | /// Modifies the protected value using `closure`. 45 | public func with( 46 | _ closure: (inout Value) throws -> R 47 | ) rethrows -> R { 48 | try queue.sync(flags: .barrier) { 49 | try closure(&value) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/SupportedDestination.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum SupportedDestination: String, CaseIterable { 4 | case iOS 5 | case tvOS 6 | case macOS 7 | case macCatalyst 8 | case watchOS 9 | case visionOS 10 | } 11 | 12 | extension SupportedDestination { 13 | 14 | public var string: String { 15 | switch self { 16 | case .iOS: 17 | return "ios" 18 | case .tvOS: 19 | return "tvos" 20 | case .macOS: 21 | return "macos" 22 | case .macCatalyst: 23 | return "maccatalyst" 24 | case .watchOS: 25 | return "watchos" 26 | case .visionOS: 27 | return "xros" 28 | } 29 | } 30 | 31 | // This is used to: 32 | // 1. Get the first one and apply SettingPresets 'Platforms' and 'Product_Platform' if the platform is 'auto' 33 | // 2. Sort, loop and merge together SettingPresets 'SupportedDestinations' 34 | public var priority: Int { 35 | switch self { 36 | case .iOS: 37 | return 0 38 | case .tvOS: 39 | return 1 40 | case .watchOS: 41 | return 2 42 | case .visionOS: 43 | return 3 44 | case .macOS: 45 | return 4 46 | case .macCatalyst: 47 | return 5 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Dictionary+Extension.swift: -------------------------------------------------------------------------------- 1 | extension Dictionary where Key == String, Value == Any? { 2 | func removingEmptyArraysDictionariesAndNils() -> [String: Any] { 3 | var new: [String: Any] = [:] 4 | filter(outNil).forEach { pair in 5 | let value: Any 6 | if let array = pair.value as? [[String: Any?]] { 7 | value = array.removingEmptyArraysDictionariesAndNils() 8 | } else if let dictionary = pair.value as? [String: Any?] { 9 | value = dictionary.removingEmptyArraysDictionariesAndNils() 10 | } else { 11 | value = pair.value! // nil is filtered out :) 12 | } 13 | new[pair.key] = value 14 | } 15 | return new 16 | .filter(outEmptyArrays) 17 | .filter(outEmptyDictionaries) 18 | } 19 | 20 | func outEmptyArrays(_ pair: (key: String, value: Any)) -> Bool { 21 | guard let array = pair.value as? [Any] else { return true } 22 | return !array.isEmpty 23 | } 24 | 25 | func outEmptyDictionaries(_ pair: (key: String, value: Any)) -> Bool { 26 | guard let dictionary = pair.value as? [String: Any] else { return true } 27 | return !dictionary.isEmpty 28 | } 29 | 30 | func outNil(_ pair: (key: String, value: Any?)) -> Bool { 31 | return pair.value != nil 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/FixtureTests/FixtureTests.swift: -------------------------------------------------------------------------------- 1 | import PathKit 2 | import ProjectSpec 3 | import Spectre 4 | import XcodeGenKit 5 | import XcodeProj 6 | import XCTest 7 | import TestSupport 8 | 9 | class FixtureTests: XCTestCase { 10 | 11 | func testProjectFixture() throws { 12 | try skipIfNecessary() 13 | describe { 14 | $0.it("generates Test Project") { 15 | try generateXcodeProject(specPath: fixturePath + "TestProject/AnotherProject/project.yml") 16 | try generateXcodeProject(specPath: fixturePath + "TestProject/project.yml") 17 | } 18 | $0.it("generates Carthage Project") { 19 | try generateXcodeProject(specPath: fixturePath + "CarthageProject/project.yml") 20 | } 21 | $0.it("generates SPM Project") { 22 | try generateXcodeProject(specPath: fixturePath + "SPM/project.yml") 23 | } 24 | } 25 | } 26 | } 27 | 28 | private func generateXcodeProject(specPath: Path, file: String = #file, line: Int = #line) throws { 29 | let project = try Project(path: specPath) 30 | let generator = ProjectGenerator(project: project) 31 | let writer = FileWriter(project: project) 32 | let xcodeProject = try generator.generateXcodeProject(userName: "someUser") 33 | try writer.writeXcodeProject(xcodeProject) 34 | try writer.writePlists() 35 | } 36 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Resources/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AD_UNIT_ID_FOR_BANNER_TEST 6 | ca-app-pub-0000000000000000/1111111111 7 | AD_UNIT_ID_FOR_INTERSTITIAL_TEST 8 | ca-app-pub-9999999999999999/2222222222 9 | CLIENT_ID 10 | AAAAAAA 11 | REVERSED_CLIENT_ID 12 | BBBBBBB 13 | API_KEY 14 | CCCCCCC 15 | GCM_SENDER_ID 16 | 000000000000000 17 | PLIST_VERSION 18 | 1 19 | BUNDLE_ID 20 | com.project.app 21 | PROJECT_ID 22 | abcdef:app-000000000 23 | STORAGE_BUCKET 24 | abcdef:app-000000000.appspot.com 25 | IS_ADS_ENABLED 26 | 27 | IS_ANALYTICS_ENABLED 28 | 29 | IS_APPINVITE_ENABLED 30 | 31 | IS_GCM_ENABLED 32 | 33 | IS_SIGNIN_ENABLED 34 | 35 | GOOGLE_APP_ID 36 | 000000000000000 37 | DATABASE_URL 38 | https://github.com/yonaskolb/XcodeGen/ 39 | 40 | 41 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/Config.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | public struct Config: Hashable { 5 | public var name: String 6 | public var type: ConfigType? 7 | 8 | public init(name: String, type: ConfigType? = nil) { 9 | self.name = name 10 | self.type = type 11 | } 12 | 13 | public static var defaultConfigs: [Config] = [Config(name: ConfigType.debug.name, type: .debug), Config(name: ConfigType.release.name, type: .release)] 14 | } 15 | 16 | public enum ConfigType: String, Hashable { 17 | case debug 18 | case release 19 | 20 | public var name: String { 21 | rawValue.prefix(1).uppercased() + rawValue.dropFirst() 22 | } 23 | } 24 | 25 | extension Config { 26 | 27 | public func matchesVariant(_ variant: String, for type: ConfigType) -> Bool { 28 | guard self.type == type else { return false } 29 | let nameWithoutType = self.name.lowercased() 30 | .replacingOccurrences(of: type.name.lowercased(), with: "") 31 | .trimmingCharacters(in: CharacterSet(charactersIn: " -_()")) 32 | return nameWithoutType == variant.lowercased() 33 | } 34 | } 35 | 36 | public extension Collection where Element == Config { 37 | func first(including configVariant: String, for type: ConfigType) -> Config? { 38 | first { $0.matchesVariant(configVariant, for: type) } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Sources/XcodeGenCLI/GenerationError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PathKit 3 | import ProjectSpec 4 | import Rainbow 5 | import SwiftCLI 6 | 7 | enum GenerationError: Error, CustomStringConvertible, ProcessError { 8 | case missingProjectSpec(Path) 9 | case projectSpecParsingError(Error) 10 | case cacheGenerationError(Error) 11 | case validationError(SpecValidationError) 12 | case generationError(Error) 13 | case missingUsername 14 | case writingError(Error) 15 | 16 | var description: String { 17 | switch self { 18 | case let .missingProjectSpec(path): 19 | return "No project spec found at \(path.absolute())" 20 | case let .projectSpecParsingError(error): 21 | return "Parsing project spec failed: \(error)" 22 | case let .cacheGenerationError(error): 23 | return "Couldn't generate cache file: \(error)" 24 | case let .validationError(error): 25 | return error.description 26 | case let .generationError(error): 27 | return String(describing: error) 28 | case .missingUsername: 29 | return "Couldn't find current username" 30 | case let .writingError(error): 31 | return String(describing: error) 32 | } 33 | } 34 | 35 | var message: String? { 36 | description.red 37 | } 38 | 39 | var exitStatus: Int32 { 40 | 1 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_watchOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "86x86", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "quickLook", 41 | "subtype" : "38mm" 42 | }, 43 | { 44 | "size" : "98x98", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "quickLook", 48 | "subtype" : "42mm" 49 | }, 50 | { 51 | "idiom" : "watch-marketing", 52 | "size" : "1024x1024", 53 | "scale" : "1x" 54 | } 55 | ], 56 | "info" : { 57 | "version" : 1, 58 | "author" : "xcode" 59 | } 60 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Network Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | NetworkExtension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSSystemExtensionUsageDescription 26 | 27 | NetworkExtension 28 | 29 | NEMachServiceName 30 | $(TeamIdentifierPrefix)com.example.app-group.MySystemExtension 31 | NEProviderClasses 32 | 33 | com.apple.networkextension.filter-data 34 | $(PRODUCT_MODULE_NAME).FilterDataProvider 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Sources/XcodeGenKit/SettingsPresetFile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ProjectSpec 3 | import XcodeProj 4 | 5 | public enum SettingsPresetFile { 6 | case config(ConfigType) 7 | case platform(Platform) 8 | case supportedDestination(SupportedDestination) 9 | case product(PBXProductType) 10 | case productPlatform(PBXProductType, Platform) 11 | case base 12 | 13 | var path: String { 14 | switch self { 15 | case let .config(config): return "Configs/\(config.rawValue)" 16 | case let .platform(platform): return "Platforms/\(platform.rawValue)" 17 | case let .supportedDestination(supportedDestination): return "SupportedDestinations/\(supportedDestination.rawValue)" 18 | case let .product(product): return "Products/\(product.name)" 19 | case let .productPlatform(product, platform): return "Product_Platform/\(product.name)_\(platform.rawValue)" 20 | case .base: return "base" 21 | } 22 | } 23 | 24 | var name: String { 25 | switch self { 26 | case let .config(config): return "\(config.rawValue) config" 27 | case let .platform(platform): return platform.rawValue 28 | case let .supportedDestination(supportedDestination): return supportedDestination.rawValue 29 | case let .product(product): return product.name 30 | case let .productPlatform(product, platform): return "\(platform) \(product)" 31 | case .base: return "base" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/XcodeGenKit/Version.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ProjectSpec 3 | 4 | extension Project { 5 | 6 | public var xcodeVersion: String { 7 | XCodeVersion.parse(options.xcodeVersion ?? "14.3") 8 | } 9 | 10 | var schemeVersion: String { 11 | "1.7" 12 | } 13 | 14 | var compatibilityVersion: String { 15 | "Xcode 14.0" 16 | } 17 | 18 | var objectVersion: UInt { 19 | 77 20 | } 21 | 22 | var minimizedProjectReferenceProxies: Int { 23 | 1 24 | } 25 | } 26 | 27 | public struct XCodeVersion { 28 | 29 | public static func parse(_ version: String) -> String { 30 | if version.contains(".") { 31 | let parts = version.split(separator: ".").map(String.init) 32 | var string = "" 33 | let major = parts[0] 34 | if major.count == 1 { 35 | string = "0\(major)" 36 | } else { 37 | string = major 38 | } 39 | 40 | let minor = parts[1] 41 | string += minor 42 | 43 | if parts.count > 2 { 44 | let patch = parts[2] 45 | string += patch 46 | } else { 47 | string += "0" 48 | } 49 | return string 50 | } else if version.count == 2 { 51 | return "\(version)00" 52 | } else if version.count == 1 { 53 | return "0\(version)00" 54 | } else { 55 | return version 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tests/Fixtures/paths_test/included_paths_test.yml: -------------------------------------------------------------------------------- 1 | name: IncludedPathsTest 2 | include: 3 | - recursive_test/recursive_test.yml 4 | - same_relative_path_test/same_relative_path_test.yml 5 | - path: relative_local_package/inc.yml 6 | relativePaths: true 7 | - path: relative_file_groups/inc.yml 8 | relativePaths: true 9 | configFiles: 10 | IncludedConfig: config 11 | projectReferences: 12 | ProjX: { path: ../TestProject/Project.xcodeproj } 13 | targets: 14 | IncludedTarget: 15 | type: application 16 | platform: tvOS 17 | configFiles: 18 | Config: config 19 | sources: 20 | - simplesource 21 | - path: source 22 | excludes: [file] 23 | dependencies: 24 | - framework: Framework 25 | info: 26 | path: info 27 | entitlements: 28 | path: entitlements 29 | preBuildScripts: 30 | - path: preBuildScript 31 | postCompileScripts: 32 | - path: postCompileScript 33 | postBuildScripts: 34 | - path: postBuildScript 35 | scheme: 36 | testPlans: 37 | - path: TestPlan.xctestplan 38 | aggregateTargets: 39 | IncludedAggregateTarget: 40 | targets: 41 | - IncludedTarget 42 | configFiles: 43 | Config: config 44 | buildScripts: 45 | - path: buildScript 46 | targetTemplates: 47 | Template1: 48 | sources: 49 | - template_source 50 | schemes: 51 | Scheme: 52 | build: 53 | targets: 54 | NewTarget: all 55 | test: 56 | testPlans: 57 | - path: TestPlan.xctestplan 58 | -------------------------------------------------------------------------------- /Sources/XcodeGenCLI/Commands/CacheCommand.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PathKit 3 | import ProjectSpec 4 | import SwiftCLI 5 | import XcodeGenKit 6 | import XcodeProj 7 | import Version 8 | 9 | class CacheCommand: ProjectCommand { 10 | 11 | @Key("--cache-path", description: "Where the cache file will be loaded from and save to. Defaults to ~/.xcodegen/cache/{SPEC_PATH_HASH}") 12 | var cacheFilePath: Path? 13 | 14 | init(version: Version) { 15 | super.init(version: version, 16 | name: "cache", 17 | shortDescription: "Write the project cache") 18 | } 19 | 20 | override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws { 21 | 22 | let cacheFilePath = self.cacheFilePath ?? Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute() 23 | 24 | var cacheFile: CacheFile? 25 | 26 | // generate cache 27 | do { 28 | cacheFile = try specLoader.generateCacheFile() 29 | } catch { 30 | throw GenerationError.projectSpecParsingError(error) 31 | } 32 | 33 | // write cache 34 | if let cacheFile = cacheFile { 35 | do { 36 | try cacheFilePath.parent().mkpath() 37 | try cacheFilePath.write(cacheFile.string) 38 | success("Wrote cache to \(cacheFilePath)") 39 | } catch { 40 | info("Failed to write cache: \(error.localizedDescription)") 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/Vendor/SomeXPCService.xpc/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 17G65 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleDisplayName 10 | XPC Service 11 | CFBundleExecutable 12 | XPC Service 13 | CFBundleIdentifier 14 | com.project.XPCService 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | XPC Service 19 | CFBundlePackageType 20 | XPC! 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSupportedPlatforms 24 | 25 | MacOSX 26 | 27 | CFBundleVersion 28 | 1 29 | DTCompiler 30 | com.apple.compilers.llvm.clang.1_0 31 | DTPlatformBuild 32 | 9F2000 33 | DTPlatformVersion 34 | GM 35 | DTSDKBuild 36 | 17E189 37 | DTSDKName 38 | macosx10.13 39 | DTXcode 40 | 0941 41 | DTXcodeBuild 42 | 9F2000 43 | XPCService 44 | 45 | ServiceType 46 | Application 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/BuildSettingsExtractor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | /// A helper for extracting and validating the `Settings` object from a JSON dictionary. 5 | struct BuildSettingsParser { 6 | let jsonDictionary: JSONDictionary 7 | 8 | /// Attempts to extract and parse the `Settings` from the dictionary. 9 | /// 10 | /// - Returns: A valid `Settings` object 11 | func parse() throws -> Settings { 12 | do { 13 | return try jsonDictionary.json(atKeyPath: "settings") 14 | } catch let specParsingError as SpecParsingError { 15 | // Re-throw `SpecParsingError` to prevent the misuse of settings.configs. 16 | throw specParsingError 17 | } catch { 18 | // Ignore all errors except `SpecParsingError` 19 | return .empty 20 | } 21 | } 22 | 23 | /// Attempts to extract and parse setting groups from the dictionary with fallback defaults. 24 | /// 25 | /// - Returns: Parsed setting groups or default groups if parsing fails 26 | func parseSettingGroups() throws -> [String: Settings] { 27 | do { 28 | return try jsonDictionary.json(atKeyPath: "settingGroups", invalidItemBehaviour: .fail) 29 | } catch let specParsingError as SpecParsingError { 30 | // Re-throw `SpecParsingError` to prevent the misuse of settingGroups. 31 | throw specParsingError 32 | } catch { 33 | // Ignore all errors except `SpecParsingError` 34 | return jsonDictionary.json(atKeyPath: "settingPresets") ?? [:] 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageStickers/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/TargetReference.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | public struct TargetReference: Hashable { 5 | public var name: String 6 | public var location: Location 7 | 8 | public enum Location: Hashable { 9 | case local 10 | case project(String) 11 | } 12 | 13 | public init(name: String, location: Location) { 14 | self.name = name 15 | self.location = location 16 | } 17 | } 18 | 19 | extension TargetReference { 20 | public init(_ string: String) throws { 21 | let paths = string.split(separator: "/") 22 | switch paths.count { 23 | case 2: 24 | location = .project(String(paths[0])) 25 | name = String(paths[1]) 26 | case 1: 27 | location = .local 28 | name = String(paths[0]) 29 | default: 30 | throw SpecParsingError.invalidTargetReference(string) 31 | } 32 | } 33 | 34 | public static func local(_ name: String) -> TargetReference { 35 | TargetReference(name: name, location: .local) 36 | } 37 | } 38 | 39 | extension TargetReference: ExpressibleByStringLiteral { 40 | public init(stringLiteral value: String) { 41 | try! self.init(value) 42 | } 43 | } 44 | 45 | extension TargetReference: CustomStringConvertible { 46 | public var reference: String { 47 | switch location { 48 | case .local: return name 49 | case .project(let root): 50 | return "\(root)/\(name)" 51 | } 52 | } 53 | 54 | public var description: String { 55 | reference 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/DriverKit Driver/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | IOKitPersonalities 22 | 23 | Driver 24 | 25 | CFBundleIdentifier 26 | $(PRODUCT_BUNDLE_IDENTIFIER) 27 | CFBundleIdentifierKernel 28 | com.apple.kpi.iokit 29 | IOClass 30 | IOUserService 31 | IOMatchCategory 32 | com.apple.null.driver 33 | IOProviderClass 34 | IOUserResources 35 | IOResourceMatch 36 | IOKit 37 | IOUserClass 38 | Driver 39 | IOUserServerName 40 | com.apple.null.driver 41 | 42 | 43 | OSBundleUsageDescription 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOOL_NAME = XcodeGen 2 | export EXECUTABLE_NAME = xcodegen 3 | VERSION = 2.44.1 4 | 5 | PREFIX = /usr/local 6 | INSTALL_PATH = $(PREFIX)/bin/$(EXECUTABLE_NAME) 7 | SHARE_PATH = $(PREFIX)/share/$(EXECUTABLE_NAME) 8 | CURRENT_PATH = $(PWD) 9 | REPO = https://github.com/yonaskolb/$(TOOL_NAME) 10 | SWIFT_BUILD_FLAGS = --disable-sandbox -c release --arch arm64 --arch x86_64 11 | BUILD_PATH = $(shell swift build $(SWIFT_BUILD_FLAGS) --show-bin-path) 12 | EXECUTABLE_PATH = $(BUILD_PATH)/$(EXECUTABLE_NAME) 13 | 14 | .PHONY: install build uninstall format_code release 15 | 16 | install: build 17 | mkdir -p $(PREFIX)/bin 18 | cp -f $(EXECUTABLE_PATH) $(INSTALL_PATH) 19 | mkdir -p $(SHARE_PATH) 20 | cp -R $(CURRENT_PATH)/SettingPresets $(SHARE_PATH)/SettingPresets 21 | 22 | build: 23 | swift build $(SWIFT_BUILD_FLAGS) 24 | 25 | uninstall: 26 | rm -f $(INSTALL_PATH) 27 | rm -rf $(SHARE_PATH) 28 | 29 | format_code: 30 | swiftformat . 31 | 32 | release: 33 | sed -i '' 's|\(let version = Version("\)\(.*\)\(")\)|\1$(VERSION)\3|' Sources/XcodeGen/main.swift 34 | sed -i '' 's|\(.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "\)\(.*\)\(")\)|\1$(VERSION)\3|' README.md 35 | 36 | git add . 37 | git commit -m "Update to $(VERSION)" 38 | #git tag $(VERSION) 39 | 40 | publish: archive 41 | echo "published $(VERSION)" 42 | 43 | archive: build 44 | ./scripts/archive.sh "$(EXECUTABLE_PATH)" 45 | swift package plugin --allow-writing-to-package-directory generate-artifact-bundle \ 46 | --package-version $(VERSION) \ 47 | --executable-name $(EXECUTABLE_NAME) \ 48 | --build-config release \ 49 | --include-resource-path LICENSE 50 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.2 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 42.1 19 | CFBundleVersion 20 | 2 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Sources/ProjectSpec/BuildToolPlugin.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | 4 | /// Specifies the use of a plug-in product in a target. 5 | public struct BuildToolPlugin: Equatable { 6 | 7 | /// The name of the plug-in target. 8 | public var plugin: String 9 | /// The name of the package that defines the plug-in target. 10 | public var package: String 11 | 12 | public init( 13 | plugin: String, 14 | package: String 15 | ) { 16 | self.plugin = plugin 17 | self.package = package 18 | } 19 | } 20 | 21 | extension BuildToolPlugin: JSONObjectConvertible { 22 | 23 | public init(jsonDictionary: JSONDictionary) throws { 24 | if let plugin: String = jsonDictionary.json(atKeyPath: "plugin") { 25 | self.plugin = plugin 26 | } else { 27 | throw SpecParsingError.invalidDependency(jsonDictionary) 28 | } 29 | 30 | if let package: String = jsonDictionary.json(atKeyPath: "package") { 31 | self.package = package 32 | } else { 33 | throw SpecParsingError.invalidDependency(jsonDictionary) 34 | } 35 | } 36 | } 37 | 38 | extension BuildToolPlugin { 39 | public var uniqueID: String { 40 | return "\(plugin)/\(package)" 41 | } 42 | } 43 | 44 | extension BuildToolPlugin: Hashable { 45 | public func hash(into hasher: inout Hasher) { 46 | hasher.combine(plugin) 47 | hasher.combine(package) 48 | } 49 | } 50 | 51 | extension BuildToolPlugin: JSONEncodable { 52 | public func toJSONValue() -> Any { 53 | [ 54 | "plugin": plugin, 55 | "package": package 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tests/Fixtures/settings_test.yml: -------------------------------------------------------------------------------- 1 | settings: 2 | BUILD_SETTING_1: value 3 | BUILD_SETTING_2: value 2 4 | 5 | settings: 6 | base: 7 | BUILD_SETTING_1: value 1 8 | configs: 9 | my_config: 10 | BUILD_SETTING_2: value 2 11 | groups: 12 | - my_settings 13 | 14 | name: SettingsTest 15 | settingGroups: 16 | preset1: 17 | SETTING: value 18 | preset2: 19 | configs: 20 | config1: 21 | SETTING1: value 22 | preset3: 23 | base: 24 | SETTING: value 25 | configs: 26 | config1: 27 | SETTING1: value 28 | preset4: 29 | base: 30 | SETTING: value 31 | preset5: 32 | groups: 33 | - preset1 34 | base: 35 | SETTING: value 36 | preset6: 37 | groups: 38 | - preset1 39 | base: 40 | SETTING: value 41 | configs: 42 | config1: 43 | SETTING1: value 44 | preset7: 45 | base: 46 | SETTING: value 47 | configs: 48 | config1: 49 | groups: 50 | - preset1 51 | base: 52 | SETTING: value 53 | preset8: 54 | configs: 55 | config1: 56 | configs: 57 | config1: 58 | SETTING1: value 59 | settings: 60 | base: 61 | SETTING 5: value 5 62 | groups: 63 | - preset7 64 | configs: 65 | config1: 66 | SETTING 6: value 6 67 | configs: 68 | config1: debug 69 | config2: release 70 | targets: 71 | Target: 72 | type: application 73 | platform: iOS 74 | settings: 75 | groups: 76 | - preset7 77 | base: 78 | SETTING 2: value 2 79 | configs: 80 | config1: 81 | groups: 82 | - preset1 83 | base: 84 | SETTING 3: value 3 85 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Settings.bundle/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StringsTable 6 | Root 7 | PreferenceSpecifiers 8 | 9 | 10 | Type 11 | PSGroupSpecifier 12 | Title 13 | Group 14 | 15 | 16 | Type 17 | PSTextFieldSpecifier 18 | Title 19 | Name 20 | Key 21 | name_preference 22 | DefaultValue 23 | 24 | IsSecure 25 | 26 | KeyboardType 27 | Alphabet 28 | AutocapitalizationType 29 | None 30 | AutocorrectionType 31 | No 32 | 33 | 34 | Type 35 | PSToggleSwitchSpecifier 36 | Title 37 | Enabled 38 | Key 39 | enabled_preference 40 | DefaultValue 41 | 42 | 43 | 44 | Type 45 | PSSliderSpecifier 46 | Key 47 | slider_preference 48 | DefaultValue 49 | 0.5 50 | MinimumValue 51 | 0 52 | MaximumValue 53 | 1 54 | MinimumValueImage 55 | 56 | MaximumValueImage 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageExtension/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x45", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x45", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "67x50", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "74x55", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ios-marketing", 40 | "size" : "1024x1024", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "size" : "27x20", 45 | "idiom" : "universal", 46 | "scale" : "2x", 47 | "platform" : "ios" 48 | }, 49 | { 50 | "size" : "27x20", 51 | "idiom" : "universal", 52 | "scale" : "3x", 53 | "platform" : "ios" 54 | }, 55 | { 56 | "size" : "32x24", 57 | "idiom" : "universal", 58 | "scale" : "2x", 59 | "platform" : "ios" 60 | }, 61 | { 62 | "size" : "32x24", 63 | "idiom" : "universal", 64 | "scale" : "3x", 65 | "platform" : "ios" 66 | }, 67 | { 68 | "size" : "1024x768", 69 | "idiom" : "ios-marketing", 70 | "scale" : "1x", 71 | "platform" : "ios" 72 | } 73 | ], 74 | "info" : { 75 | "version" : 1, 76 | "author" : "xcode" 77 | } 78 | } -------------------------------------------------------------------------------- /Sources/ProjectSpec/Linkage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XcodeProj 3 | 4 | public enum Linkage { 5 | case dynamic 6 | case `static` 7 | case none 8 | } 9 | 10 | extension Target { 11 | 12 | public var defaultLinkage: Linkage { 13 | switch type { 14 | case .none, 15 | .appExtension, 16 | .application, 17 | .bundle, 18 | .commandLineTool, 19 | .instrumentsPackage, 20 | .intentsServiceExtension, 21 | .messagesApplication, 22 | .messagesExtension, 23 | .metalLibrary, 24 | .ocUnitTestBundle, 25 | .onDemandInstallCapableApplication, 26 | .stickerPack, 27 | .tvExtension, 28 | .uiTestBundle, 29 | .unitTestBundle, 30 | .watchApp, 31 | .watchExtension, 32 | .watch2App, 33 | .watch2AppContainer, 34 | .watch2Extension, 35 | .xcodeExtension, 36 | .xpcService, 37 | .systemExtension, 38 | .driverExtension, 39 | .extensionKitExtension: 40 | return .none 41 | case .framework, .xcFramework: 42 | // Check the MACH_O_TYPE for "Static Framework" 43 | if settings.buildSettings.machOType == "staticlib" { 44 | return .static 45 | } else { 46 | return .dynamic 47 | } 48 | case .dynamicLibrary: 49 | return .dynamic 50 | case .staticLibrary, .staticFramework: 51 | return .static 52 | } 53 | } 54 | } 55 | 56 | private extension BuildSettings { 57 | 58 | var machOType: String? { 59 | self["MACH_O_TYPE"] as? String 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_supportedDestinations/Storyboards/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/project.yml: -------------------------------------------------------------------------------- 1 | name: SPM 2 | packages: 3 | Codability: 4 | url: https://github.com/yonaskolb/Codability 5 | majorVersion: 0.2.1 6 | SwiftRoaring: 7 | url: https://github.com/piotte13/SwiftRoaring 8 | majorVersion: 1.0.4 9 | Prefire: 10 | url: https://github.com/BarredEwe/Prefire 11 | majorVersion: 1.4.1 12 | SwiftLocation: 13 | url: https://github.com/malcommac/SwiftLocation 14 | exactVersion: 6.0 15 | XcodeGen: 16 | path: ../../.. #XcodeGen itself 17 | group: SPM 18 | FooFeature: 19 | path: FooFeature 20 | aggregateTargets: 21 | AggTarget: 22 | buildToolPlugins: 23 | - plugin: PrefirePlaybookPlugin 24 | package: Prefire 25 | targets: 26 | App: 27 | type: application 28 | platform: iOS 29 | sources: [SPM] 30 | scheme: 31 | testTargets: 32 | - package: XcodeGen/XcodeGenKitTests 33 | - Tests 34 | buildToolPlugins: 35 | - plugin: PrefirePlaybookPlugin 36 | package: Prefire 37 | dependencies: 38 | - package: Codability 39 | weak: true 40 | - package: SwiftRoaring 41 | product: SwiftRoaringDynamic 42 | embed: true 43 | - target: StaticLibrary 44 | - package: XcodeGen 45 | - package: SwiftLocation 46 | - package: FooFeature 47 | products: 48 | - FooDomain 49 | - FooUI 50 | Tests: 51 | type: bundle.unit-test 52 | platform: iOS 53 | sources: [SPMTests] 54 | dependencies: 55 | - target: App 56 | StaticLibrary: 57 | type: library.static 58 | platform: iOS 59 | sources: StaticLibrary 60 | dependencies: 61 | - package: Codability 62 | - package: SwiftRoaring 63 | product: SwiftRoaringDynamic 64 | - package: XcodeGen 65 | - package: FooFeature 66 | products: 67 | - FooDomain 68 | - FooUI 69 | -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/XPC Service/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "XPC_Service.h" 3 | 4 | @interface ServiceDelegate : NSObject 5 | @end 6 | 7 | @implementation ServiceDelegate 8 | 9 | - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { 10 | // This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection. 11 | 12 | // Configure the connection. 13 | // First, set the interface that the exported object implements. 14 | newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPC_ServiceProtocol)]; 15 | 16 | // Next, set the object that the connection exports. All messages sent on the connection to this service will be sent to the exported object to handle. The connection retains the exported object. 17 | XPC_Service *exportedObject = [XPC_Service new]; 18 | newConnection.exportedObject = exportedObject; 19 | 20 | // Resuming the connection allows the system to deliver more incoming messages. 21 | [newConnection resume]; 22 | 23 | // Returning YES from this method tells the system that you have accepted this connection. If you want to reject the connection for some reason, call -invalidate on the connection and return NO. 24 | return YES; 25 | } 26 | 27 | @end 28 | 29 | int main(int argc, const char *argv[]) 30 | { 31 | // Create the delegate for the service. 32 | ServiceDelegate *delegate = [ServiceDelegate new]; 33 | 34 | // Set up the one NSXPCListener for this service. It will handle all incoming connections. 35 | NSXPCListener *listener = [NSXPCListener serviceListener]; 36 | listener.delegate = delegate; 37 | 38 | // Resuming the serviceListener starts this service. This method does not return. 39 | [listener resume]; 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /SettingPresets/base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Settings take from the following file and sorted 3 | # /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_ProjectSettings.xctemplate/TemplateInfo.plist 4 | ALWAYS_SEARCH_USER_PATHS: NO 5 | CLANG_ANALYZER_NONNULL: YES 6 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION: YES_AGGRESSIVE 7 | CLANG_CXX_LANGUAGE_STANDARD: gnu++14 8 | CLANG_CXX_LIBRARY: libc++ 9 | CLANG_ENABLE_MODULES: YES 10 | CLANG_ENABLE_OBJC_ARC: YES 11 | CLANG_ENABLE_OBJC_WEAK: YES 12 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING: YES 13 | CLANG_WARN_BOOL_CONVERSION: YES 14 | CLANG_WARN_COMMA: YES 15 | CLANG_WARN_CONSTANT_CONVERSION: YES 16 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS: YES 17 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE: YES_ERROR 18 | CLANG_WARN_DOCUMENTATION_COMMENTS: YES 19 | CLANG_WARN_EMPTY_BODY: YES 20 | CLANG_WARN_ENUM_CONVERSION: YES 21 | CLANG_WARN_INFINITE_RECURSION: YES 22 | CLANG_WARN_INT_CONVERSION: YES 23 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION: YES 24 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF: YES 25 | CLANG_WARN_OBJC_LITERAL_CONVERSION: YES 26 | CLANG_WARN_OBJC_ROOT_CLASS: YES_ERROR 27 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: YES 28 | CLANG_WARN_RANGE_LOOP_ANALYSIS: YES 29 | CLANG_WARN_STRICT_PROTOTYPES: YES 30 | CLANG_WARN_SUSPICIOUS_MOVE: YES 31 | CLANG_WARN_UNGUARDED_AVAILABILITY: YES_AGGRESSIVE 32 | CLANG_WARN_UNREACHABLE_CODE: YES 33 | CLANG_WARN__DUPLICATE_METHOD_MATCH: YES 34 | COPY_PHASE_STRIP: NO 35 | ENABLE_STRICT_OBJC_MSGSEND: YES 36 | GCC_C_LANGUAGE_STANDARD: gnu11 37 | GCC_NO_COMMON_BLOCKS: YES 38 | GCC_WARN_64_TO_32_BIT_CONVERSION: YES 39 | GCC_WARN_ABOUT_RETURN_TYPE: YES_ERROR 40 | GCC_WARN_UNDECLARED_SELECTOR: YES 41 | GCC_WARN_UNINITIALIZED_AUTOS: YES_AGGRESSIVE 42 | GCC_WARN_UNUSED_FUNCTION: YES 43 | GCC_WARN_UNUSED_VARIABLE: YES 44 | MTL_FAST_MATH: YES 45 | 46 | # Target Settings 47 | PRODUCT_NAME: $(TARGET_NAME) 48 | 49 | # Swift Settings 50 | SWIFT_VERSION: '5.0' 51 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Carthage/Build/.CarthageTestFixture.version: -------------------------------------------------------------------------------- 1 | { 2 | "Mac" : [ 3 | { 4 | "name" : "CarthageTestFixture", 5 | "hash" : "071b5d10a76f763129c32223af9f0e34674f2d6766e1b8dd0658c634c750d88f" 6 | }, 7 | { 8 | "name" : "DependencyFixtureB", 9 | "hash" : "65d7482cfefd8952762f57de885e9929aae8e6fe6a1c556e6bad75811f8d096f" 10 | }, 11 | { 12 | "name" : "DependencyFixtureA", 13 | "hash" : "ada254978598a68cce0e7cc919549e4888793ad2e9082fca38a533b9731710d5" 14 | } 15 | ], 16 | "watchOS" : [ 17 | { 18 | "name" : "CarthageTestFixture", 19 | "hash" : "6f5aeb82a53091a5573be1b16b58156a2444f8a568e4a4b4914a5658a11ce91c" 20 | }, 21 | { 22 | "name" : "DependencyFixtureB", 23 | "hash" : "a252cd58636f621f39daa48f89e881665d4d1da1debaf46d7ba926f59720e928" 24 | }, 25 | { 26 | "name" : "DependencyFixtureA", 27 | "hash" : "1f2df2a6f33953eb61d56c195dcf2b102d458dcec4ad93b70d1f802cf1d01081" 28 | } 29 | ], 30 | "tvOS" : [ 31 | { 32 | "name" : "CarthageTestFixture", 33 | "hash" : "4d0c084ea320ef9d2e216f60956bdb50e4975887f19f4817b26657a3796ab411" 34 | }, 35 | { 36 | "name" : "DependencyFixtureA", 37 | "hash" : "f257e4d0348770f210196dedaf16b51d54b255ec3e9e7b1cce50ea74e57d61b3" 38 | }, 39 | { 40 | "name" : "DependencyFixtureB", 41 | "hash" : "47377bbe103c06a25150aece9f48ac4b42065bc0c4543a7249395482676a3c38" 42 | } 43 | ], 44 | "commitish" : "1.0", 45 | "iOS" : [ 46 | { 47 | "name" : "CarthageTestFixture", 48 | "hash" : "7c2a74fc024a50478bec9ca8833302b2850d1971aadc6c9d890266ea29af6966" 49 | }, 50 | { 51 | "name" : "DependencyFixtureA", 52 | "hash" : "d9979f6f61ad275ae8d4fdf575df66fde694f584cc7f58f2dcb86e9fe81b9a3a" 53 | }, 54 | { 55 | "name" : "DependencyFixtureB", 56 | "hash" : "1c418bb70678e1f47cffc0ad8d8e6e014372ad9936fff6302abb86511980ac01" 57 | } 58 | ] 59 | } -------------------------------------------------------------------------------- /Sources/XcodeGenKit/InfoPlistGenerator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PathKit 3 | import ProjectSpec 4 | 5 | public class InfoPlistGenerator { 6 | 7 | /** 8 | Default info plist attributes taken from: 9 | /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_DefinitionsInfoPlist.xctemplate/TemplateInfo.plist 10 | */ 11 | private func generateDefaultInfoPlist(for target: Target) -> [String: Any] { 12 | var dictionary: [String: Any] = [:] 13 | dictionary["CFBundleIdentifier"] = "$(PRODUCT_BUNDLE_IDENTIFIER)" 14 | dictionary["CFBundleInfoDictionaryVersion"] = "6.0" 15 | 16 | dictionary["CFBundleName"] = "$(PRODUCT_NAME)" 17 | dictionary["CFBundleDevelopmentRegion"] = "$(DEVELOPMENT_LANGUAGE)" 18 | dictionary["CFBundleShortVersionString"] = "1.0" 19 | dictionary["CFBundleVersion"] = "1" 20 | 21 | // Bundles should not contain any CFBundleExecutable otherwise they will be rejected when uploading. 22 | if target.type != .bundle { 23 | dictionary["CFBundleExecutable"] = "$(EXECUTABLE_NAME)" 24 | } 25 | 26 | return dictionary 27 | } 28 | 29 | public func generateProperties(for target: Target) -> [String: Any] { 30 | var targetInfoPlist = generateDefaultInfoPlist(for: target) 31 | switch target.type { 32 | case .uiTestBundle, 33 | .unitTestBundle: 34 | targetInfoPlist["CFBundlePackageType"] = "BNDL" 35 | case .application, 36 | .watch2App: 37 | targetInfoPlist["CFBundlePackageType"] = "APPL" 38 | case .framework: 39 | targetInfoPlist["CFBundlePackageType"] = "FMWK" 40 | case .bundle: 41 | targetInfoPlist["CFBundlePackageType"] = "BNDL" 42 | case .xpcService, 43 | .appExtension: 44 | targetInfoPlist["CFBundlePackageType"] = "XPC!" 45 | default: break 46 | } 47 | return targetInfoPlist 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tests/Fixtures/CarthageProject/Carthage/Build/.ReactiveCocoa.version: -------------------------------------------------------------------------------- 1 | { 2 | "Mac" : [ 3 | { 4 | "name" : "ReactiveMapKit", 5 | "hash" : "ca2fed26f8381df41034bab95e4751c59a2e6790fb2a6def8f4bc9cdee0c5733" 6 | }, 7 | { 8 | "name" : "ReactiveMapKit", 9 | "hash" : "ca2fed26f8381df41034bab95e4751c59a2e6790fb2a6def8f4bc9cdee0c5733" 10 | }, 11 | { 12 | "name" : "ReactiveCocoa", 13 | "hash" : "944bf96ea2e3431d7c70daef2c5664586439ca59e92092983acb1f36e3cb7f50" 14 | }, 15 | { 16 | "name" : "ReactiveCocoa", 17 | "hash" : "944bf96ea2e3431d7c70daef2c5664586439ca59e92092983acb1f36e3cb7f50" 18 | } 19 | ], 20 | "watchOS" : [ 21 | { 22 | "name" : "ReactiveCocoa", 23 | "hash" : "f7634a0c6323009b856c6cd6b0d76e9727006289b0edc1cb8a97b85f6549e16c" 24 | } 25 | ], 26 | "tvOS" : [ 27 | { 28 | "name" : "ReactiveMapKit", 29 | "hash" : "19c5331921afd44ec907b2dda06a522cf7f106580d689b39c0ffea989efa558a" 30 | }, 31 | { 32 | "name" : "ReactiveMapKit", 33 | "hash" : "19c5331921afd44ec907b2dda06a522cf7f106580d689b39c0ffea989efa558a" 34 | }, 35 | { 36 | "name" : "ReactiveCocoa", 37 | "hash" : "6fd1ed75f76420c86c66d2900d37f66ea6c9378007bf9c52aa8ee0806ae24477" 38 | }, 39 | { 40 | "name" : "ReactiveCocoa", 41 | "hash" : "6fd1ed75f76420c86c66d2900d37f66ea6c9378007bf9c52aa8ee0806ae24477" 42 | } 43 | ], 44 | "commitish" : "8.0.2", 45 | "iOS" : [ 46 | { 47 | "name" : "ReactiveMapKit", 48 | "hash" : "8d1f84508d7403d8acdb4c188377777273ceba4eb6237728f97dcc18d41b94c8" 49 | }, 50 | { 51 | "name" : "ReactiveMapKit", 52 | "hash" : "8d1f84508d7403d8acdb4c188377777273ceba4eb6237728f97dcc18d41b94c8" 53 | }, 54 | { 55 | "name" : "ReactiveCocoa", 56 | "hash" : "3ecd35da18aeade9d027be56daa4a925fbf7fc05fd7d559260ef2fc7945fb840" 57 | }, 58 | { 59 | "name" : "ReactiveCocoa", 60 | "hash" : "3ecd35da18aeade9d027be56daa4a925fbf7fc05fd7d559260ef2fc7945fb840" 61 | } 62 | ] 63 | } -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_Clip/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/App_iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Tests/Fixtures/TestProject/iMessageStickers/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Sources/XcodeGenKit/ProjectGenerator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONUtilities 3 | import PathKit 4 | import ProjectSpec 5 | import XcodeProj 6 | import Yams 7 | 8 | public class ProjectGenerator { 9 | 10 | let project: Project 11 | 12 | public init(project: Project) { 13 | self.project = project 14 | } 15 | 16 | public func generateXcodeProject(in projectDirectory: Path? = nil, userName: String) throws -> XcodeProj { 17 | 18 | // generate PBXProj 19 | let pbxProjGenerator = PBXProjGenerator(project: project, 20 | projectDirectory: projectDirectory) 21 | let pbxProj = try pbxProjGenerator.generate() 22 | 23 | // generate Workspace 24 | let workspace = try generateWorkspace() 25 | 26 | // generate Schemes 27 | let schemeGenerator = SchemeGenerator(project: project, pbxProj: pbxProj) 28 | let (sharedSchemes, userSchemes, schemeManagement) = try schemeGenerator.generateSchemes() 29 | 30 | // generate Breakpoints 31 | let breakpointGenerator = BreakpointGenerator(project: project) 32 | let xcbreakpointlist = try breakpointGenerator.generateBreakpointList() 33 | 34 | // generate shared data 35 | let sharedData = XCSharedData(schemes: sharedSchemes, breakpoints: xcbreakpointlist) 36 | 37 | // generate user data 38 | let userData = userSchemes.isEmpty && schemeManagement == nil ? [] : [ 39 | XCUserData(userName: userName, schemes: userSchemes, schemeManagement: schemeManagement) 40 | ] 41 | 42 | return XcodeProj( 43 | workspace: workspace, 44 | pbxproj: pbxProj, 45 | sharedData: sharedData, 46 | userData: userData 47 | ) 48 | } 49 | 50 | func generateWorkspace() throws -> XCWorkspace { 51 | let selfReference = XCWorkspaceDataFileRef(location: .current("")) 52 | let dataElement = XCWorkspaceDataElement.file(selfReference) 53 | let workspaceData = XCWorkspaceData(children: [dataElement]) 54 | return XCWorkspace(data: workspaceData) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/XcodeGenKit/FileWriter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PathKit 3 | import ProjectSpec 4 | import XcodeProj 5 | 6 | public class FileWriter { 7 | 8 | let project: Project 9 | 10 | public init(project: Project) { 11 | self.project = project 12 | } 13 | 14 | public func writeXcodeProject(_ xcodeProject: XcodeProj, to projectPath: Path? = nil) throws { 15 | let projectPath = projectPath ?? project.defaultProjectPath 16 | let tempPath = try Path.processUniqueTemporary() + "XcodeGen" 17 | try? tempPath.delete() 18 | if projectPath.exists { 19 | try projectPath.copy(tempPath) 20 | } 21 | try xcodeProject.write(path: tempPath, override: true) 22 | try? projectPath.delete() 23 | try tempPath.copy(projectPath) 24 | try? tempPath.delete() 25 | } 26 | 27 | public func writePlists() throws { 28 | 29 | let infoPlistGenerator = InfoPlistGenerator() 30 | for target in project.targets { 31 | // write Info.plist 32 | if let plist = target.info { 33 | let properties = infoPlistGenerator.generateProperties(for: target).merged(plist.properties) 34 | try writePlist(properties, path: plist.path) 35 | } 36 | 37 | // write entitlements 38 | if let plist = target.entitlements { 39 | try writePlist(plist.properties, path: plist.path) 40 | } 41 | } 42 | } 43 | 44 | private func writePlist(_ plist: [String: Any], path: String) throws { 45 | let path = project.basePath + path 46 | if path.exists, let data: Data = try? path.read(), 47 | let existingPlist = (try? PropertyListSerialization.propertyList(from: data, format: nil)) as? [String: Any], NSDictionary(dictionary: plist).isEqual(to: existingPlist) { 48 | // file is the same 49 | return 50 | } 51 | let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) 52 | try? path.delete() 53 | try path.parent().mkpath() 54 | try path.write(data) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/ProjectSpecTests/InvalidConfigsFormatTests.swift: -------------------------------------------------------------------------------- 1 | import ProjectSpec 2 | import Testing 3 | import TestSupport 4 | import PathKit 5 | 6 | struct InvalidConfigsMappingFormatTests { 7 | struct InvalidConfigsTestArguments { 8 | var fixturePath: Path 9 | var expectedError: SpecParsingError 10 | } 11 | 12 | private static var testArguments: [InvalidConfigsTestArguments] { 13 | let invalidConfigsFixturePath: Path = fixturePath + "invalid_configs" 14 | return [ 15 | InvalidConfigsTestArguments( 16 | fixturePath: invalidConfigsFixturePath + "invalid_configs_value_non_mapping_settings.yml", 17 | expectedError: SpecParsingError.invalidConfigsMappingFormat(keys: ["invalid_key0", "invalid_key1"]) 18 | ), 19 | InvalidConfigsTestArguments( 20 | fixturePath: invalidConfigsFixturePath + "invalid_configs_value_non_mapping_targets.yml", 21 | expectedError: SpecParsingError.invalidConfigsMappingFormat(keys: ["invalid_key0", "invalid_key1"]) 22 | ), 23 | InvalidConfigsTestArguments( 24 | fixturePath: invalidConfigsFixturePath + "invalid_configs_value_non_mapping_aggregate_targets.yml", 25 | expectedError: SpecParsingError.invalidConfigsMappingFormat(keys: ["invalid_key0", "invalid_key1"]) 26 | ), 27 | InvalidConfigsTestArguments( 28 | fixturePath: invalidConfigsFixturePath + "invalid_configs_value_non_mapping_setting_groups.yml", 29 | expectedError: SpecParsingError.invalidConfigsMappingFormat(keys: ["invalid_key0", "invalid_key1"]) 30 | ) 31 | ] 32 | } 33 | 34 | @Test("throws invalidConfigsMappingFormat for non-mapping configs entries", arguments: testArguments) 35 | func testInvalidConfigsMappingFormat(_ arguments: InvalidConfigsTestArguments) throws { 36 | #expect { 37 | try Project(path: arguments.fixturePath) 38 | } throws: { actualError in 39 | (actualError as any CustomStringConvertible).description 40 | == arguments.expectedError.description 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/Fixtures/SPM/SPM/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Sources/XcodeGenCLI/Commands/DumpCommand.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftCLI 3 | import PathKit 4 | import ProjectSpec 5 | import Yams 6 | import Version 7 | import XcodeGenKit 8 | 9 | class DumpCommand: ProjectCommand { 10 | 11 | @Key("--type", "-t", description: "The type of dump to output. Either \(DumpType.allCases.map { "\"\($0.rawValue)\"" }.joined(separator: ", ")). Defaults to \(DumpType.defaultValue.rawValue). The \"parsed\" types parse the project into swift and then back again.") 12 | private var dumpType: DumpType? 13 | 14 | @Key("--file", "-f", description: "The path of a file to write to. If not supplied will output to stdout") 15 | private var file: Path? 16 | 17 | init(version: Version) { 18 | super.init(version: version, 19 | name: "dump", 20 | shortDescription: "Dumps the resolved project spec to stdout or a file") 21 | } 22 | 23 | override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws { 24 | let type = dumpType ?? .defaultValue 25 | 26 | let output: String 27 | switch type { 28 | case .swiftDump: 29 | var string = "" 30 | dump(project, to: &string) 31 | output = string 32 | case .json: 33 | let data = try JSONSerialization.data(withJSONObject: specLoader.projectDictionary!, options: .prettyPrinted) 34 | output = String(data: data, encoding: .utf8)! 35 | case .yaml: 36 | output = try Yams.dump(object: specLoader.projectDictionary!) 37 | case .parsedJSON: 38 | let data = try JSONSerialization.data(withJSONObject: project.toJSONDictionary(), options: .prettyPrinted) 39 | output = String(data: data, encoding: .utf8)! 40 | case .parsedYaml: 41 | output = try Yams.dump(object: project.toJSONDictionary()) 42 | case .summary: 43 | output = project.debugDescription 44 | } 45 | 46 | if let file = file { 47 | try file.parent().mkpath() 48 | try file.write(output) 49 | } else { 50 | success(output) 51 | } 52 | } 53 | } 54 | 55 | private enum DumpType: String, ConvertibleFromString, CaseIterable { 56 | case swiftDump = "swift-dump" 57 | case json 58 | case yaml 59 | case parsedJSON = "parsed-json" 60 | case parsedYaml = "parsed-yaml" 61 | case summary 62 | 63 | static var defaultValue: DumpType { .yaml } 64 | } 65 | --------------------------------------------------------------------------------