diff --git a/Bugger.xcodeproj/project.pbxproj b/Bugger.xcodeproj/project.pbxproj index dda12f5..5400340 100644 --- a/Bugger.xcodeproj/project.pbxproj +++ b/Bugger.xcodeproj/project.pbxproj @@ -1,73 +1,74 @@ // !$*UTF8*$! { archiveVersion = 1; - classes = {}; + classes = { + }; objectVersion = 56; objects = { /* Begin PBXBuildFile section */ - C70061E61A4A91DEE4DCD093 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2ADE595D33ADF5BA01B253B; }; - 264AF91CD3111E25300CB673 /* BuggerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7705247A59F50F6B6E6D286F; }; - 93E674C67BBD94AE8F60D9C2 /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8899127BDCD00F1D39E8FD; }; - 934A2D27BD52EA7EA9138148 /* Bug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE27079694EBA15A0DFAAB; }; - 40F0A29391DC7C7D5BD3AC86 /* BugChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE785F676266AFB5EA636597; }; - 8AF76AF5588E865614CBEB3F /* BugPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADA8A640EF741D9404316C39; }; - 6E97B8FF66F15C45F5BBCDC3 /* BugStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649CAAB9009813D6C1FD07E3; }; - 7E7A04B78BD2D7DA8B923E89 /* AppStateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3148982E653318C7E3CD13F; }; - 1EE9930CFF29314317C80B41 /* FeishuAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 264DA84D71E29D64DEC1B038; }; - EA309C97F6828294AE038EE0 /* FeishuError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A9F363FE066CAE6226A050; }; - A53C0F63FE7B64140EE84896 /* FeishuModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BF857A0044A0262A1F56F; }; - 32E0CB6A605B7E033826F85C /* FeishuService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66321820D32BCC5D05265115; }; - 7B0310CEB785902145E910CE /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98E94F5A859B0EEC7326C60; }; - 64973B14EC100A258F5D5DF1 /* PollerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD2692AA96D65949109FEC0E; }; - DB560F1DC0B066BE60979A42 /* TokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9677914DC46FD106A90CA724; }; - 570EEFE5A198913D1E28D539 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 968D99A1DA7131447D075F27; }; - 523A4BEC8BE348013BA23CCB /* DateFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BE6905A92E43B24E24CDE29; }; - 6FBC99D9861B64AF3C21FFC2 /* KeychainHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 527F9B3FF513B72F175A8429; }; - 690FBDF23267CDB564AA9014 /* URL+Feishu.swift in Sources */ = {isa = PBXBuildFile; fileRef = E390AA6A998B6C83BD779018; }; - 50C462B4A25F77CD93FFBB49 /* BugStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C251D0198118D6B5D4B7C69; }; - BD6E0970804F37FAA7DBB468 /* FloatingWidgetWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DC24ADCEDA5C16DFF9FA6EA; }; - 5AEABE301EC2352533870063 /* BugListPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D10F852D16E2F4FC269FD2; }; - E4911DC6264A1006B7FDCF30 /* BugRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F3DB4764E379E663475EBD; }; - EA6166A18AD94D499E170D07 /* OAuthSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29172EAB6F6B4526BDA23C2; }; - 6B62FD564DD3D1A983DFB3EB /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9AD7665552E1BC2E76337C; }; - 2D2411A895B80B698B8E2A82 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A93EEBDA5A740C84995D959; }; + F6750BB1FBEE5BC90DD6BDBC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207CA4381D0CFEC95263A2DE; }; + 15550246A9E8DAF25E690478 /* BuggerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB7AD270E93EA343975AC6; }; + 58F16D20CC26D8248055CC2C /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBEF4F77A6A2E2D041EF581; }; + 4EA9BDBA3F98AF4A41712A18 /* Bug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846CA001267896C54A78441A; }; + DDB9EB345FEBB95DD9976364 /* BugChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B24E1E1D32F7B774D12DD85; }; + A33561284CA44A0045902BCC /* BugPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5359156BD70B765D2BF4E718; }; + E4CBAE27491AF1C52944FA91 /* BugStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646C676356A4125B8E8D5FA5; }; + D2855F82E68FADA8BEA43086 /* AppStateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1DD178095EFCE7BC121C1C4; }; + 4309E8C70AE99466545A3E37 /* FeishuAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A2C50E47966E8ADB0446A0C; }; + CB529549DB1DBB5F02884300 /* FeishuError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A83E76FBB83849E4B6704D3; }; + CAE624453A3F0375D872170A /* FeishuModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE9FBC39502FA635EF1A6A5C; }; + 29EE2FE56882F97E14AAF58F /* FeishuService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21AC33F380B7DC34C39B61A6; }; + 6148104B311B60C79B3B8572 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59CC175DE8E9FE3250757D33; }; + 56D44DCBAF4FE0E1D9E620FA /* PollerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4628E7B599B69B499E09E9; }; + B882D310EB2B5D032375CDDF /* TokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3775F77DD36C27160A8C; }; + C5D9B1FACE813CD43236BB77 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F99094B0F609809EBDB5F4A; }; + 29AE37B117A1422EA21570CA /* DateFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C935E6B4D1813AB01186A0; }; + 64F1EE978536D1CB02B8F0D3 /* KeychainHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF06E1030AA21DA8190169A8; }; + 1FB7E762C0B6568C47B48EF0 /* URL+Feishu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F888AA5F655343B19839B4C2; }; + AECED74595B57A400437B9FD /* BugStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344F52FB34B285D91A3E4670; }; + 485A69E15EF1D805F50DF364 /* FloatingWidgetWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2FE32B404AB4388A066991; }; + 1E3C1F40E2FF64A041856909 /* BugListPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEABFC668F7B082847F65D38; }; + 3467A50268399ABE9804D4D4 /* BugRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1129D6F630904F03D06B5DC4; }; + B066D78DA6F57A882A61C0B2 /* OAuthSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C0A744D4F5DB88B1496176B; }; + 7B0EAA6D83D40A593E36EE31 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D3693AA93F823859D15256D; }; + C3F7FEB179B079BDDD2AA212 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 09B9A4D84F3F604C49171A89; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ EF0182DF85A7CD0319333F24 /* Bugger.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bugger.app; sourceTree = BUILT_PRODUCTS_DIR; }; - B2ADE595D33ADF5BA01B253B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7705247A59F50F6B6E6D286F /* BuggerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuggerApp.swift; sourceTree = ""; }; - AE8899127BDCD00F1D39E8FD /* AppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = ""; }; - C4DE27079694EBA15A0DFAAB /* Bug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bug.swift; sourceTree = ""; }; - FE785F676266AFB5EA636597 /* BugChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugChange.swift; sourceTree = ""; }; - ADA8A640EF741D9404316C39 /* BugPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugPriority.swift; sourceTree = ""; }; - 649CAAB9009813D6C1FD07E3 /* BugStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugStatus.swift; sourceTree = ""; }; - E3148982E653318C7E3CD13F /* AppStateService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateService.swift; sourceTree = ""; }; - 264DA84D71E29D64DEC1B038 /* FeishuAuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuAuthService.swift; sourceTree = ""; }; - 38A9F363FE066CAE6226A050 /* FeishuError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuError.swift; sourceTree = ""; }; - 973BF857A0044A0262A1F56F /* FeishuModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuModels.swift; sourceTree = ""; }; - 66321820D32BCC5D05265115 /* FeishuService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuService.swift; sourceTree = ""; }; - C98E94F5A859B0EEC7326C60 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; - BD2692AA96D65949109FEC0E /* PollerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollerService.swift; sourceTree = ""; }; - 9677914DC46FD106A90CA724 /* TokenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenManager.swift; sourceTree = ""; }; - 968D99A1DA7131447D075F27 /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color+Extensions.swift; sourceTree = ""; }; - 0BE6905A92E43B24E24CDE29 /* DateFormatter+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatter+Extensions.swift; sourceTree = ""; }; - 527F9B3FF513B72F175A8429 /* KeychainHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainHelper.swift; sourceTree = ""; }; - E390AA6A998B6C83BD779018 /* URL+Feishu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL+Feishu.swift; sourceTree = ""; }; - 6C251D0198118D6B5D4B7C69 /* BugStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugStore.swift; sourceTree = ""; }; - 5DC24ADCEDA5C16DFF9FA6EA /* FloatingWidgetWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingWidgetWindow.swift; sourceTree = ""; }; - 17D10F852D16E2F4FC269FD2 /* BugListPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugListPopover.swift; sourceTree = ""; }; - C3F3DB4764E379E663475EBD /* BugRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugRow.swift; sourceTree = ""; }; - E29172EAB6F6B4526BDA23C2 /* OAuthSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthSetupView.swift; sourceTree = ""; }; - 9F9AD7665552E1BC2E76337C /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - 6A93EEBDA5A740C84995D959 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 2EB7B1BD755DF7F2FB5F8392 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 86B3BC083DF59791BE56CFAE /* Bugger.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Bugger.entitlements; sourceTree = ""; }; + 207CA4381D0CFEC95263A2DE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + DDDB7AD270E93EA343975AC6 /* BuggerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuggerApp.swift; sourceTree = ""; }; + 9FBEF4F77A6A2E2D041EF581 /* AppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = ""; }; + 846CA001267896C54A78441A /* Bug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bug.swift; sourceTree = ""; }; + 0B24E1E1D32F7B774D12DD85 /* BugChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugChange.swift; sourceTree = ""; }; + 5359156BD70B765D2BF4E718 /* BugPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugPriority.swift; sourceTree = ""; }; + 646C676356A4125B8E8D5FA5 /* BugStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugStatus.swift; sourceTree = ""; }; + F1DD178095EFCE7BC121C1C4 /* AppStateService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateService.swift; sourceTree = ""; }; + 6A2C50E47966E8ADB0446A0C /* FeishuAuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuAuthService.swift; sourceTree = ""; }; + 2A83E76FBB83849E4B6704D3 /* FeishuError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuError.swift; sourceTree = ""; }; + FE9FBC39502FA635EF1A6A5C /* FeishuModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuModels.swift; sourceTree = ""; }; + 21AC33F380B7DC34C39B61A6 /* FeishuService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeishuService.swift; sourceTree = ""; }; + 59CC175DE8E9FE3250757D33 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 4F4628E7B599B69B499E09E9 /* PollerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollerService.swift; sourceTree = ""; }; + 0E7E3775F77DD36C27160A8C /* TokenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenManager.swift; sourceTree = ""; }; + 0F99094B0F609809EBDB5F4A /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = ""; }; + 87C935E6B4D1813AB01186A0 /* DateFormatter+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extensions.swift"; sourceTree = ""; }; + EF06E1030AA21DA8190169A8 /* KeychainHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainHelper.swift; sourceTree = ""; }; + F888AA5F655343B19839B4C2 /* URL+Feishu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Feishu.swift"; sourceTree = ""; }; + 344F52FB34B285D91A3E4670 /* BugStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugStore.swift; sourceTree = ""; }; + 6E2FE32B404AB4388A066991 /* FloatingWidgetWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingWidgetWindow.swift; sourceTree = ""; }; + CEABFC668F7B082847F65D38 /* BugListPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugListPopover.swift; sourceTree = ""; }; + 1129D6F630904F03D06B5DC4 /* BugRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugRow.swift; sourceTree = ""; }; + 3C0A744D4F5DB88B1496176B /* OAuthSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthSetupView.swift; sourceTree = ""; }; + 2D3693AA93F823859D15256D /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 09B9A4D84F3F604C49171A89 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 73AB234B54673C838E33AC7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E9B6FE6E06295095649B24A7 /* Bugger.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Bugger.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - DE1439ACCFCEB73E6F9981A5 /* Frameworks */ = { + 03303046F4622F1382FE219D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -77,133 +78,133 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 8C4FDD7B635240BD562EBB49 = { + 14ED1DECD6D906F498B9ECB5 /* Root */ = { isa = PBXGroup; children = ( - EE6F40F7464432A6C2A761DA /* Sources */, - 7C4EE8955498EEF65ED6C84A /* Resources */, + 287BE755981C092238309A9A /* Sources */, + 8AD6A16612A2DB7B2F899787 /* Resources */, B23D89B2D2DF3745A003AEB1 /* Products */, ); sourceTree = ""; }; - EE6F40F7464432A6C2A761DA /* Sources */ = { + 287BE755981C092238309A9A /* Sources */ = { isa = PBXGroup; children = ( - B2ADE595D33ADF5BA01B253B /* AppDelegate.swift */, - 7705247A59F50F6B6E6D286F /* BuggerApp.swift */, - B805360275E47AAD7192E4AE /* Models */, - EF310F3F9A8796C845F345F5 /* Services */, - B38F908829C30D6B6F967EEA /* Utils */, - 56714C7161F5C8195EA19816 /* ViewModels */, - F4ED3EC6534567974F720F99 /* Views */, + 207CA4381D0CFEC95263A2DE /* AppDelegate.swift */, + DDDB7AD270E93EA343975AC6 /* BuggerApp.swift */, + 65997CD9BD6FFEF8851BF59E /* Models */, + A8C46168E4DFCE29B056A4E2 /* Services */, + A6646A2065F53ED50AD2CCA7 /* Utils */, + 6AB9730AA11690EFC8F21473 /* ViewModels */, + 31DF436612EC1C3CD1194AA1 /* Views */, ); path = Sources; sourceTree = ""; }; - B805360275E47AAD7192E4AE /* Models */ = { + 65997CD9BD6FFEF8851BF59E /* Models */ = { isa = PBXGroup; children = ( - AE8899127BDCD00F1D39E8FD /* AppConfig.swift */, - C4DE27079694EBA15A0DFAAB /* Bug.swift */, - FE785F676266AFB5EA636597 /* BugChange.swift */, - ADA8A640EF741D9404316C39 /* BugPriority.swift */, - 649CAAB9009813D6C1FD07E3 /* BugStatus.swift */, + 9FBEF4F77A6A2E2D041EF581 /* AppConfig.swift */, + 846CA001267896C54A78441A /* Bug.swift */, + 0B24E1E1D32F7B774D12DD85 /* BugChange.swift */, + 5359156BD70B765D2BF4E718 /* BugPriority.swift */, + 646C676356A4125B8E8D5FA5 /* BugStatus.swift */, ); path = Models; sourceTree = ""; }; - EF310F3F9A8796C845F345F5 /* Services */ = { + A8C46168E4DFCE29B056A4E2 /* Services */ = { isa = PBXGroup; children = ( - E3148982E653318C7E3CD13F /* AppStateService.swift */, - 0B0ED8A1BE17CC4C0B3A787B /* Feishu */, - C98E94F5A859B0EEC7326C60 /* NotificationService.swift */, - BD2692AA96D65949109FEC0E /* PollerService.swift */, - 9677914DC46FD106A90CA724 /* TokenManager.swift */, + F1DD178095EFCE7BC121C1C4 /* AppStateService.swift */, + 59CC175DE8E9FE3250757D33 /* NotificationService.swift */, + 4F4628E7B599B69B499E09E9 /* PollerService.swift */, + 0E7E3775F77DD36C27160A8C /* TokenManager.swift */, + E0E23D3899D89889546FB428 /* Feishu */, ); path = Services; sourceTree = ""; }; - 0B0ED8A1BE17CC4C0B3A787B /* Feishu */ = { + A6646A2065F53ED50AD2CCA7 /* Utils */ = { isa = PBXGroup; children = ( - 264DA84D71E29D64DEC1B038 /* FeishuAuthService.swift */, - 38A9F363FE066CAE6226A050 /* FeishuError.swift */, - 973BF857A0044A0262A1F56F /* FeishuModels.swift */, - 66321820D32BCC5D05265115 /* FeishuService.swift */, - ); - path = Feishu; - sourceTree = ""; - }; - B38F908829C30D6B6F967EEA /* Utils */ = { - isa = PBXGroup; - children = ( - 968D99A1DA7131447D075F27 /* Color+Extensions.swift */, - 0BE6905A92E43B24E24CDE29 /* DateFormatter+Extensions.swift */, - 527F9B3FF513B72F175A8429 /* KeychainHelper.swift */, - E390AA6A998B6C83BD779018 /* URL+Feishu.swift */, + 0F99094B0F609809EBDB5F4A /* Color+Extensions.swift */, + 87C935E6B4D1813AB01186A0 /* DateFormatter+Extensions.swift */, + EF06E1030AA21DA8190169A8 /* KeychainHelper.swift */, + F888AA5F655343B19839B4C2 /* URL+Feishu.swift */, ); path = Utils; sourceTree = ""; }; - 56714C7161F5C8195EA19816 /* ViewModels */ = { + 6AB9730AA11690EFC8F21473 /* ViewModels */ = { isa = PBXGroup; children = ( - 6C251D0198118D6B5D4B7C69 /* BugStore.swift */, + 344F52FB34B285D91A3E4670 /* BugStore.swift */, ); path = ViewModels; sourceTree = ""; }; - F4ED3EC6534567974F720F99 /* Views */ = { + 31DF436612EC1C3CD1194AA1 /* Views */ = { isa = PBXGroup; children = ( - 82B5ED67A4830EDA75C6CDE1 /* FloatingWidget */, - 24C578F83DCF9CEAFCAF2E73 /* MenuBar */, - 0308B80FA0CF6FBE7D8C7EB3 /* OAuth */, - 3053DFBFF1BD7F53471B36BB /* Settings */, + E7E4AD241EEE594C1EEB9724 /* FloatingWidget */, + EA9ED591BB8A18FEBD1B58A3 /* MenuBar */, + 74AD95EF49A7D01C83865FAC /* OAuth */, + B70D2E54AD6B19DD455A8F82 /* Settings */, ); path = Views; sourceTree = ""; }; - 82B5ED67A4830EDA75C6CDE1 /* FloatingWidget */ = { + E0E23D3899D89889546FB428 /* Feishu */ = { isa = PBXGroup; children = ( - 5DC24ADCEDA5C16DFF9FA6EA /* FloatingWidgetWindow.swift */, + 6A2C50E47966E8ADB0446A0C /* FeishuAuthService.swift */, + 2A83E76FBB83849E4B6704D3 /* FeishuError.swift */, + FE9FBC39502FA635EF1A6A5C /* FeishuModels.swift */, + 21AC33F380B7DC34C39B61A6 /* FeishuService.swift */, + ); + path = Feishu; + sourceTree = ""; + }; + E7E4AD241EEE594C1EEB9724 /* FloatingWidget */ = { + isa = PBXGroup; + children = ( + 6E2FE32B404AB4388A066991 /* FloatingWidgetWindow.swift */, ); path = FloatingWidget; sourceTree = ""; }; - 24C578F83DCF9CEAFCAF2E73 /* MenuBar */ = { + EA9ED591BB8A18FEBD1B58A3 /* MenuBar */ = { isa = PBXGroup; children = ( - 17D10F852D16E2F4FC269FD2 /* BugListPopover.swift */, - C3F3DB4764E379E663475EBD /* BugRow.swift */, + CEABFC668F7B082847F65D38 /* BugListPopover.swift */, + 1129D6F630904F03D06B5DC4 /* BugRow.swift */, ); path = MenuBar; sourceTree = ""; }; - 0308B80FA0CF6FBE7D8C7EB3 /* OAuth */ = { + 74AD95EF49A7D01C83865FAC /* OAuth */ = { isa = PBXGroup; children = ( - E29172EAB6F6B4526BDA23C2 /* OAuthSetupView.swift */, + 3C0A744D4F5DB88B1496176B /* OAuthSetupView.swift */, ); path = OAuth; sourceTree = ""; }; - 3053DFBFF1BD7F53471B36BB /* Settings */ = { + B70D2E54AD6B19DD455A8F82 /* Settings */ = { isa = PBXGroup; children = ( - 9F9AD7665552E1BC2E76337C /* SettingsView.swift */, + 2D3693AA93F823859D15256D /* SettingsView.swift */, ); path = Settings; sourceTree = ""; }; - 7C4EE8955498EEF65ED6C84A /* Resources */ = { + 8AD6A16612A2DB7B2F899787 /* Resources */ = { isa = PBXGroup; children = ( - 6A93EEBDA5A740C84995D959 /* Assets.xcassets */, - 2EB7B1BD755DF7F2FB5F8392 /* Info.plist */, - 86B3BC083DF59791BE56CFAE /* Bugger.entitlements */, + 09B9A4D84F3F604C49171A89 /* Assets.xcassets */, + 73AB234B54673C838E33AC7F /* Info.plist */, + E9B6FE6E06295095649B24A7 /* Bugger.entitlements */, ); path = Resources; sourceTree = ""; @@ -221,11 +222,11 @@ /* Begin PBXNativeTarget section */ 42AEFBAE01D2DFD981F7DA7D /* Bugger */ = { isa = PBXNativeTarget; - buildConfigurationList = 74C875FD1C5C02E06FAC0545 /* Build configuration list for PBXNativeTarget "Bugger" */; + buildConfigurationList = F1F749C5DF06081CD2D04369 /* Build configuration list for PBXNativeTarget "Bugger" */; buildPhases = ( - 3B7DFB0747829707F437ED7F /* Sources */, - DE1439ACCFCEB73E6F9981A5 /* Frameworks */, - 3519654520012F56AC9A0E86 /* Resources */, + B1CCC9C70ADA89DF47C9677E /* Sources */, + 03303046F4622F1382FE219D /* Frameworks */, + F05EFBB4EA8CE481794F9936 /* Resources */, ); buildRules = ( ); @@ -243,15 +244,15 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1500; + LastSwiftUpdateCheck = 2600; + LastUpgradeCheck = 2600; TargetAttributes = { 42AEFBAE01D2DFD981F7DA7D = { - CreatedOnToolsVersion = 15.0; + CreatedOnToolsVersion = 26.0; }; }; }; - buildConfigurationList = DD445E777372328CDC08F332 /* Build configuration list for PBXProject "Bugger" */; + buildConfigurationList = F4FFB6AD1F4DDC161F81C0DE /* Build configuration list for PBXProject "Bugger" */; compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -259,7 +260,7 @@ en, Base, ); - mainGroup = 8C4FDD7B635240BD562EBB49; + mainGroup = 14ED1DECD6D906F498B9ECB5; productRefGroup = B23D89B2D2DF3745A003AEB1 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -270,81 +271,76 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 3519654520012F56AC9A0E86 /* Resources */ = { + F05EFBB4EA8CE481794F9936 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2D2411A895B80B698B8E2A82 /* Assets.xcassets in Resources */, + C3F7FEB179B079BDDD2AA212 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 3B7DFB0747829707F437ED7F /* Sources */ = { + B1CCC9C70ADA89DF47C9677E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C70061E61A4A91DEE4DCD093 /* AppDelegate.swift in Sources */, - 264AF91CD3111E25300CB673 /* BuggerApp.swift in Sources */, - 93E674C67BBD94AE8F60D9C2 /* AppConfig.swift in Sources */, - 934A2D27BD52EA7EA9138148 /* Bug.swift in Sources */, - 40F0A29391DC7C7D5BD3AC86 /* BugChange.swift in Sources */, - 8AF76AF5588E865614CBEB3F /* BugPriority.swift in Sources */, - 6E97B8FF66F15C45F5BBCDC3 /* BugStatus.swift in Sources */, - 7E7A04B78BD2D7DA8B923E89 /* AppStateService.swift in Sources */, - 1EE9930CFF29314317C80B41 /* FeishuAuthService.swift in Sources */, - EA309C97F6828294AE038EE0 /* FeishuError.swift in Sources */, - A53C0F63FE7B64140EE84896 /* FeishuModels.swift in Sources */, - 32E0CB6A605B7E033826F85C /* FeishuService.swift in Sources */, - 7B0310CEB785902145E910CE /* NotificationService.swift in Sources */, - 64973B14EC100A258F5D5DF1 /* PollerService.swift in Sources */, - DB560F1DC0B066BE60979A42 /* TokenManager.swift in Sources */, - 570EEFE5A198913D1E28D539 /* Color+Extensions.swift in Sources */, - 523A4BEC8BE348013BA23CCB /* DateFormatter+Extensions.swift in Sources */, - 6FBC99D9861B64AF3C21FFC2 /* KeychainHelper.swift in Sources */, - 690FBDF23267CDB564AA9014 /* URL+Feishu.swift in Sources */, - 50C462B4A25F77CD93FFBB49 /* BugStore.swift in Sources */, - BD6E0970804F37FAA7DBB468 /* FloatingWidgetWindow.swift in Sources */, - 5AEABE301EC2352533870063 /* BugListPopover.swift in Sources */, - E4911DC6264A1006B7FDCF30 /* BugRow.swift in Sources */, - EA6166A18AD94D499E170D07 /* OAuthSetupView.swift in Sources */, - 6B62FD564DD3D1A983DFB3EB /* SettingsView.swift in Sources */, + F6750BB1FBEE5BC90DD6BDBC /* AppDelegate.swift in Sources */, + 15550246A9E8DAF25E690478 /* BuggerApp.swift in Sources */, + 58F16D20CC26D8248055CC2C /* AppConfig.swift in Sources */, + 4EA9BDBA3F98AF4A41712A18 /* Bug.swift in Sources */, + DDB9EB345FEBB95DD9976364 /* BugChange.swift in Sources */, + A33561284CA44A0045902BCC /* BugPriority.swift in Sources */, + E4CBAE27491AF1C52944FA91 /* BugStatus.swift in Sources */, + D2855F82E68FADA8BEA43086 /* AppStateService.swift in Sources */, + 4309E8C70AE99466545A3E37 /* FeishuAuthService.swift in Sources */, + CB529549DB1DBB5F02884300 /* FeishuError.swift in Sources */, + CAE624453A3F0375D872170A /* FeishuModels.swift in Sources */, + 29EE2FE56882F97E14AAF58F /* FeishuService.swift in Sources */, + 6148104B311B60C79B3B8572 /* NotificationService.swift in Sources */, + 56D44DCBAF4FE0E1D9E620FA /* PollerService.swift in Sources */, + B882D310EB2B5D032375CDDF /* TokenManager.swift in Sources */, + C5D9B1FACE813CD43236BB77 /* Color+Extensions.swift in Sources */, + 29AE37B117A1422EA21570CA /* DateFormatter+Extensions.swift in Sources */, + 64F1EE978536D1CB02B8F0D3 /* KeychainHelper.swift in Sources */, + 1FB7E762C0B6568C47B48EF0 /* URL+Feishu.swift in Sources */, + AECED74595B57A400437B9FD /* BugStore.swift in Sources */, + 485A69E15EF1D805F50DF364 /* FloatingWidgetWindow.swift in Sources */, + 1E3C1F40E2FF64A041856909 /* BugListPopover.swift in Sources */, + 3467A50268399ABE9804D4D4 /* BugRow.swift in Sources */, + B066D78DA6F57A882A61C0B2 /* OAuthSetupView.swift in Sources */, + 7B0EAA6D83D40A593E36EE31 /* SettingsView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - F76147CE86058CC1939088C9 /* Debug */ = { + 172CAC4F236E6C94A1763B32 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_MODULES = YES; - COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = -Onone; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; - 97C89C522DCD700C1F32C445 /* Release */ = { + 8FD30172609F662677CE533F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_MODULES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf-with-dsym; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; }; - 36F7B61E0BB35F5EE850C3BE /* Debug */ = { + BCB1273FA062F6C8B2456F08 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -369,7 +365,7 @@ }; name = Debug; }; - C961A2B6F28F76EF702C6931 /* Release */ = { + DE8F0F92492FDD4AE81004CA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -397,25 +393,26 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - DD445E777372328CDC08F332 /* Build configuration list for PBXProject "Bugger" */ = { + F1F749C5DF06081CD2D04369 /* Build configuration list for PBXNativeTarget "Bugger" */ = { isa = XCConfigurationList; buildConfigurations = ( - F76147CE86058CC1939088C9 /* Debug */, - 97C89C522DCD700C1F32C445 /* Release */, + BCB1273FA062F6C8B2456F08 /* Debug */, + DE8F0F92492FDD4AE81004CA /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 74C875FD1C5C02E06FAC0545 /* Build configuration list for PBXNativeTarget "Bugger" */ = { + F4FFB6AD1F4DDC161F81C0DE /* Build configuration list for PBXProject "Bugger" */ = { isa = XCConfigurationList; buildConfigurations = ( - 36F7B61E0BB35F5EE850C3BE /* Debug */, - C961A2B6F28F76EF702C6931 /* Release */, + 172CAC4F236E6C94A1763B32 /* Debug */, + 8FD30172609F662677CE533F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + }; rootObject = 46F86FAA6BBF9AC94A7E4595 /* Project object */; } diff --git a/Bugger.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Bugger.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 94b2795..919434a 100644 --- a/Bugger.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Bugger.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,4 +1,7 @@ + + diff --git a/Bugger.xcodeproj/xcshareddata/xcschemes/Bugger.xcscheme b/Bugger.xcodeproj/xcshareddata/xcschemes/Bugger.xcscheme index 3846110..7d035da 100644 --- a/Bugger.xcodeproj/xcshareddata/xcschemes/Bugger.xcscheme +++ b/Bugger.xcodeproj/xcshareddata/xcschemes/Bugger.xcscheme @@ -1,6 +1,6 @@ str: - digest = hashlib.md5(seed.encode()).hexdigest().upper() - return digest[:24] + return hashlib.md5(seed.encode()).hexdigest().upper()[:24] -def collect_swift_files() -> list[Path]: - return sorted(SOURCE_ROOT.rglob("*.swift")) +def quote_path(name: str) -> str: + """Quote a path value if it contains characters unsafe in NeXTSTEP plist format. + + In NeXTSTEP plist, unquoted strings can only contain [a-zA-Z0-9_./$-]. + Characters like +, -, *, etc. act as operators and must be quoted. + """ + safe_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_./$-") + if all(c in safe_chars for c in name): + return name + return f'"{name}"' -def collect_resources() -> list[Path]: - resources = [] - for path in sorted(RESOURCE_ROOT.rglob("*")): - if path.is_file(): - resources.append(path) - return resources +def collect_swift_files() -> dict[str, str]: + """Return {relative_path_in_sources: absolute_path} for all .swift files.""" + files = {} + for path in sorted(SOURCE_ROOT.rglob("*.swift")): + rel = str(path.relative_to(SOURCE_ROOT)) + files[rel] = str(path) + return files -def pbxproj() -> str: +def build_pbxproj() -> str: swift_files = collect_swift_files() - resources = collect_resources() + sorted_files = sorted(swift_files.keys()) - project_id = uuid("project") + # ---- UUIDs ---- + # File references + f_uuids = {f: uuid(f"fr-{f}") for f in sorted_files} + b_uuids = {f: uuid(f"bf-{f}") for f in sorted_files} + + # Resources + asset_ref = uuid("res-assets") + info_ref = uuid("res-info-plist") + ent_ref = uuid("res-entitlements") + asset_build = uuid("res-build-assets") + + # Product + product_ref = uuid("product-ref") + + # Build phases + frameworks_phase = uuid("fw-phase") + resources_phase = uuid("res-phase") + sources_phase = uuid("src-phase") + + # Target target_id = uuid("target") - sources_phase_id = uuid("sources-phase") - resources_phase_id = uuid("resources-phase") - frameworks_phase_id = uuid("frameworks-phase") - product_ref_id = uuid("product-ref") - project_config_list_id = uuid("project-config-list") - target_config_list_id = uuid("target-config-list") - debug_config_id = uuid("debug-config") - release_config_id = uuid("release-config") - target_debug_config_id = uuid("target-debug-config") - target_release_config_id = uuid("target-release-config") + target_cfg_list = uuid("targ-cfg-list") + target_dbg = uuid("targ-dbg") + target_rel = uuid("targ-rel") - main_group_id = uuid("main-group") - products_group_id = uuid("products-group") - sources_group_id = uuid("sources-group") - resources_group_id = uuid("resources-group") + # Project + project_id = uuid("project") + project_cfg_list = uuid("proj-cfg-list") + project_dbg = uuid("proj-dbg") + project_rel = uuid("proj-rel") - file_refs: dict[Path, str] = {} - build_files: list[tuple[str, str, str]] = [] + # Groups + root_group = uuid("root-group") + sources_group = uuid("sources-group") + products_group = uuid("products-group") + resources_group = uuid("resources-group") - for path in swift_files: - ref_id = uuid(f"file-{path}") - build_id = uuid(f"build-{path}") - file_refs[path] = ref_id - build_files.append((build_id, ref_id, "Sources")) + # Build group hierarchy from directory structure + all_groups = set() + for f in sorted_files: + parts = f.split("/")[:-1] + for i in range(len(parts)): + all_groups.add("/".join(parts[:i + 1])) + sorted_groups = sorted(all_groups, key=lambda g: (g.count("/"), g)) + group_uuids = {g: uuid(f"grp-{g}") for g in sorted_groups} - resource_refs: dict[Path, str] = {} - for path in resources: - ref_id = uuid(f"res-{path}") - build_id = uuid(f"res-build-{path}") - resource_refs[path] = ref_id - build_files.append((build_id, ref_id, "Resources")) + # Determine which files/children go where + top_files = [f for f in sorted_files if "/" not in f] + top_dirs = {g for g in sorted_groups if "/" not in g} - def group_for(path: Path, parent: str) -> str: - rel = path.relative_to(ROOT) - parts = rel.parts[:-1] - group_ids = [main_group_id] - current = "" - for part in parts: - current = f"{current}/{part}" - group_ids.append(uuid(f"group-{current}")) - return group_ids[-1] + def dir_files(group: str) -> list[str]: + """Files directly inside this group directory.""" + prefix = group + "/" + return sorted(f for f in sorted_files + if f.startswith(prefix) and "/" not in f[len(prefix):]) - groups: dict[str, list[str]] = {} - child_groups: dict[str, set[str]] = {} + def subdirs(group: str) -> list[str]: + """Subdirectory groups directly under this group.""" + prefix = group + "/" + return sorted(sg for sg in sorted_groups + if sg.startswith(prefix) and "/" not in sg[len(prefix):]) - def add_to_group(group_id: str, child_id: str): - groups.setdefault(group_id, []).append(child_id) + # ---- Build output ---- + b: list[str] = [] - add_to_group(main_group_id, sources_group_id) - add_to_group(main_group_id, resources_group_id) - add_to_group(main_group_id, products_group_id) - add_to_group(products_group_id, product_ref_id) + def L(line: str = "", indent: int = 0): + b.append("\t" * indent + line) - def ensure_group_chain(rel_parts: tuple[str, ...], root_group: str): - current = root_group - built = [] - for part in rel_parts: - built.append(part) - gid = uuid(f"group-{'/'.join(built)}") - if gid not in groups and gid != current: - add_to_group(current, gid) - current = gid - return current + L("// !$*UTF8*$!") + L("{") + L("archiveVersion = 1;", 1) + L("classes = {", 1) + L("};", 1) + L("objectVersion = 56;", 1) + L("objects = {", 1) + L() - for path in swift_files: - rel = path.relative_to(SOURCE_ROOT) - parent = ensure_group_chain(rel.parts[:-1], sources_group_id) - add_to_group(parent, file_refs[path]) + # -- PBXBuildFile -- + L("/* Begin PBXBuildFile section */") + for f in sorted_files: + name = f.split("/")[-1] + L(f'{b_uuids[f]} /* {name} in Sources */ = {{isa = PBXBuildFile; fileRef = {f_uuids[f]}; }};', 2) + L(f'{asset_build} /* Assets.xcassets in Resources */ = {{isa = PBXBuildFile; fileRef = {asset_ref}; }};', 2) + L("/* End PBXBuildFile section */") + L() - for path in resources: - rel = path.relative_to(RESOURCE_ROOT) - if len(rel.parts) > 1: - parent = ensure_group_chain(rel.parts[:-1], resources_group_id) - else: - parent = resources_group_id - add_to_group(parent, resource_refs[path]) + # -- PBXFileReference -- + L("/* Begin PBXFileReference section */") + L(f'{product_ref} /* {PROJECT_NAME}.app */ = {{isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = {PROJECT_NAME}.app; sourceTree = BUILT_PRODUCTS_DIR; }};', 2) + for f in sorted_files: + name = f.split("/")[-1] + L(f'{f_uuids[f]} /* {name} */ = {{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = {quote_path(name)}; sourceTree = ""; }};', 2) + L(f'{asset_ref} /* Assets.xcassets */ = {{isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }};', 2) + L(f'{info_ref} /* Info.plist */ = {{isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }};', 2) + L(f'{ent_ref} /* Bugger.entitlements */ = {{isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Bugger.entitlements; sourceTree = ""; }};', 2) + L("/* End PBXFileReference section */") + L() - lines: list[str] = [] - lines.append("// !$*UTF8*$!") - lines.append("{") - lines.append("\tarchiveVersion = 1;") - lines.append("\tclasses = {};") - lines.append("\tobjectVersion = 56;") - lines.append("\tobjects = {") + # -- PBXFrameworksBuildPhase -- + L("/* Begin PBXFrameworksBuildPhase section */") + L(f'{frameworks_phase} /* Frameworks */ = {{', 2) + L("isa = PBXFrameworksBuildPhase;", 3) + L("buildActionMask = 2147483647;", 3) + L("files = (", 3) + L(");", 3) + L("runOnlyForDeploymentPostprocessing = 0;", 3) + L("};", 2) + L("/* End PBXFrameworksBuildPhase section */") + L() - lines.append(f"\n/* Begin PBXBuildFile section */") - for build_id, ref_id, kind in build_files: - if kind == "Sources": - lines.append(f"\t\t{build_id} /* {Path(ref_id).name} in Sources */ = {{isa = PBXBuildFile; fileRef = {ref_id}; }};") - else: - lines.append(f"\t\t{build_id} /* {ref_id} in Resources */ = {{isa = PBXBuildFile; fileRef = {ref_id}; }};") - lines.append("/* End PBXBuildFile section */\n") + # -- PBXGroup -- + L("/* Begin PBXGroup section */") - lines.append("/* Begin PBXFileReference section */") - lines.append(f"\t\t{product_ref_id} /* {PROJECT_NAME}.app */ = {{isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = {PROJECT_NAME}.app; sourceTree = BUILT_PRODUCTS_DIR; }};") - for path, ref_id in file_refs.items(): - rel = path.relative_to(ROOT) - lines.append(f"\t\t{ref_id} /* {path.name} */ = {{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = {path.name}; sourceTree = \"\"; }};") - for path, ref_id in resource_refs.items(): - rel = path.relative_to(ROOT) - suffix = path.suffix.lower() - if suffix == ".plist": - file_type = "text.plist.xml" - elif suffix == ".entitlements": - file_type = "text.plist.entitlements" - elif path.name == "Contents.json": - file_type = "text.json" - else: - file_type = "folder.assetcatalog" if path.parent.suffix == ".xcassets" and path.name == "Contents.json" and path.parent.parent.name == "Assets.xcassets" else "text.json" - if path.name == "Contents.json" and "Assets.xcassets" in str(path): - if path.parent == RESOURCE_ROOT / "Assets.xcassets": - continue - if path.parent.name.endswith(".appiconset"): - file_type = "text.json" - elif path.parent == RESOURCE_ROOT / "Assets.xcassets": - file_type = "folder.assetcatalog" - # asset catalog handled as single folder reference below - if "Assets.xcassets" in str(path) and path != RESOURCE_ROOT / "Assets.xcassets": - continue - if path == RESOURCE_ROOT / "Assets.xcassets": - lines.append(f"\t\t{ref_id} /* Assets.xcassets */ = {{isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"\"; }};") - continue - lines.append(f"\t\t{ref_id} /* {path.name} */ = {{isa = PBXFileReference; lastKnownFileType = {file_type}; path = {path.name}; sourceTree = \"\"; }};") - lines.append("/* End PBXFileReference section */\n") + # Root group + L(f'{root_group} /* Root */ = {{', 2) + L("isa = PBXGroup;", 3) + L("children = (", 3) + L(f'{sources_group} /* Sources */,', 4) + L(f'{resources_group} /* Resources */,', 4) + L(f'{products_group} /* Products */,', 4) + L(");", 3) + L('sourceTree = "";', 3) + L("};", 2) - # Simpler approach: rewrite resource refs cleanly - resource_file_refs = [] - asset_ref = uuid("asset-catalog") - info_ref = uuid("info-plist") - ent_ref = uuid("entitlements") - resource_file_refs = [ - (RESOURCE_ROOT / "Assets.xcassets", asset_ref, "folder.assetcatalog", "Assets.xcassets"), - (RESOURCE_ROOT / "Info.plist", info_ref, "text.plist.xml", "Info.plist"), - (RESOURCE_ROOT / "Bugger.entitlements", ent_ref, "text.plist.entitlements", "Bugger.entitlements"), - ] + # Sources group + L(f'{sources_group} /* Sources */ = {{', 2) + L("isa = PBXGroup;", 3) + L("children = (", 3) + for f in top_files: + L(f'{f_uuids[f]} /* {f} */,', 4) + for d in sorted(top_dirs): + L(f'{group_uuids[d]} /* {d} */,', 4) + L(");", 3) + L("path = Sources;", 3) + L('sourceTree = "";', 3) + L("};", 2) - lines = [] - lines.append("// !$*UTF8*$!") - lines.append("{") - lines.append("\tarchiveVersion = 1;") - lines.append("\tclasses = {};") - lines.append("\tobjectVersion = 56;") - lines.append("\tobjects = {") + # Subdirectory groups + for g in sorted_groups: + name = g.split("/")[-1] + L(f'{group_uuids[g]} /* {name} */ = {{', 2) + L("isa = PBXGroup;", 3) + L("children = (", 3) + for f in dir_files(g): + L(f'{f_uuids[f]} /* {f.split("/")[-1]} */,', 4) + for sg in subdirs(g): + L(f'{group_uuids[sg]} /* {sg.split("/")[-1]} */,', 4) + L(");", 3) + L(f"path = {name};", 3) + L('sourceTree = "";', 3) + L("};", 2) - lines.append("\n/* Begin PBXBuildFile section */") - swift_build_entries = [] - for path in swift_files: - ref_id = uuid(f"file-{path}") - build_id = uuid(f"build-{path}") - swift_build_entries.append((build_id, ref_id, path.name)) - lines.append(f"\t\t{build_id} /* {path.name} in Sources */ = {{isa = PBXBuildFile; fileRef = {ref_id}; }};") + # Resources group + L(f'{resources_group} /* Resources */ = {{', 2) + L("isa = PBXGroup;", 3) + L("children = (", 3) + L(f'{asset_ref} /* Assets.xcassets */,', 4) + L(f'{info_ref} /* Info.plist */,', 4) + L(f'{ent_ref} /* Bugger.entitlements */,', 4) + L(");", 3) + L("path = Resources;", 3) + L('sourceTree = "";', 3) + L("};", 2) - asset_build_id = uuid("asset-build") - lines.append(f"\t\t{asset_build_id} /* Assets.xcassets in Resources */ = {{isa = PBXBuildFile; fileRef = {asset_ref}; }};") - lines.append("/* End PBXBuildFile section */\n") + # Products group + L(f'{products_group} /* Products */ = {{', 2) + L("isa = PBXGroup;", 3) + L("children = (", 3) + L(f'{product_ref} /* {PROJECT_NAME}.app */,', 4) + L(");", 3) + L("name = Products;", 3) + L('sourceTree = "";', 3) + L("};", 2) - lines.append("/* Begin PBXFileReference section */") - lines.append(f"\t\t{product_ref_id} /* {PROJECT_NAME}.app */ = {{isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = {PROJECT_NAME}.app; sourceTree = BUILT_PRODUCTS_DIR; }};") - swift_ref_entries = [] - for path in swift_files: - ref_id = uuid(f"file-{path}") - swift_ref_entries.append((ref_id, path)) - lines.append(f"\t\t{ref_id} /* {path.name} */ = {{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = {path.name}; sourceTree = \"\"; }};") - lines.append(f"\t\t{asset_ref} /* Assets.xcassets */ = {{isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"\"; }};") - lines.append(f"\t\t{info_ref} /* Info.plist */ = {{isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"\"; }};") - lines.append(f"\t\t{ent_ref} /* Bugger.entitlements */ = {{isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Bugger.entitlements; sourceTree = \"\"; }};") - lines.append("/* End PBXFileReference section */\n") + L("/* End PBXGroup section */") + L() - def render_groups(): - group_defs = [] + # -- PBXNativeTarget -- + L("/* Begin PBXNativeTarget section */") + L(f'{target_id} /* {PROJECT_NAME} */ = {{', 2) + L("isa = PBXNativeTarget;", 3) + L(f'buildConfigurationList = {target_cfg_list} /* Build configuration list for PBXNativeTarget "{PROJECT_NAME}" */;', 3) + L("buildPhases = (", 3) + L(f'{sources_phase} /* Sources */,', 4) + L(f'{frameworks_phase} /* Frameworks */,', 4) + L(f'{resources_phase} /* Resources */,', 4) + L(");", 3) + L("buildRules = (", 3) + L(");", 3) + L("dependencies = (", 3) + L(");", 3) + L(f"name = {PROJECT_NAME};", 3) + L(f"productName = {PROJECT_NAME};", 3) + L(f"productReference = {product_ref} /* {PROJECT_NAME}.app */;", 3) + L('productType = "com.apple.product-type.application";', 3) + L("};", 2) + L("/* End PBXNativeTarget section */") + L() - def walk(dir_path: Path, group_name: str, group_id: str, parent_set: list[str]): - children = [] - for child in sorted(dir_path.iterdir()): - if child.is_dir() and child.name != ".DS_Store": - child_id = uuid(f"group-{child}") - children.append(child_id) - walk(child, child.name, child_id, []) - elif child.suffix == ".swift": - ref_id = uuid(f"file-{child}") - children.append(ref_id) - if dir_path == RESOURCE_ROOT: - children = [asset_ref, info_ref, ent_ref] - if dir_path == ROOT: - children = [sources_group_id, resources_group_id, products_group_id] - if dir_path == SOURCE_ROOT: - children = [] - for child in sorted(dir_path.iterdir()): - if child.is_dir(): - children.append(uuid(f"group-{child}")) - elif child.suffix == ".swift": - children.append(uuid(f"file-{child}")) - child_lines = "\n".join([f"\t\t\t\t{id_}," for id_ in children]) - path_line = f"path = {group_name};" if group_name not in ("", PROJECT_NAME) else "" - if group_id == main_group_id: - group_defs.append( - f"\t\t{group_id} = {{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n{child_lines}\n\t\t\t);\n\t\t\tsourceTree = \"\";\n\t\t}};" - ) - elif group_id == products_group_id: - group_defs.append( - f"\t\t{group_id} = {{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t{product_ref_id},\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"\";\n\t\t}};" - ) - elif group_id == sources_group_id: - group_defs.append( - f"\t\t{group_id} = {{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n{child_lines}\n\t\t\t);\n\t\t\tpath = Sources;\n\t\t\tsourceTree = \"\";\n\t\t}};" - ) - elif group_id == resources_group_id: - group_defs.append( - f"\t\t{group_id} = {{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t{asset_ref},\n\t\t\t\t{info_ref},\n\t\t\t\t{ent_ref},\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"\";\n\t\t}};" - ) - else: - rel = dir_path.relative_to(SOURCE_ROOT) if str(dir_path).startswith(str(SOURCE_ROOT)) else dir_path.name - group_defs.append( - f"\t\t{group_id} = {{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n{child_lines}\n\t\t\t);\n\t\t\tpath = {dir_path.name};\n\t\t\tsourceTree = \"\";\n\t\t}};" - ) + # -- PBXProject -- + L("/* Begin PBXProject section */") + L(f'{project_id} /* Project object */ = {{', 2) + L("isa = PBXProject;", 3) + L("attributes = {", 3) + L("BuildIndependentTargetsInParallel = 1;", 4) + L("LastSwiftUpdateCheck = 2600;", 4) + L("LastUpgradeCheck = 2600;", 4) + L(f"TargetAttributes = {{", 4) + L(f"{target_id} = {{", 5) + L("CreatedOnToolsVersion = 26.0;", 6) + L("};", 5) + L("};", 4) + L("};", 3) + L(f'buildConfigurationList = {project_cfg_list} /* Build configuration list for PBXProject "{PROJECT_NAME}" */;', 3) + L('compatibilityVersion = "Xcode 14.0";', 3) + L("developmentRegion = en;", 3) + L("hasScannedForEncodings = 0;", 3) + L("knownRegions = (", 3) + L("en,", 4) + L("Base,", 4) + L(");", 3) + L(f"mainGroup = {root_group};", 3) + L(f"productRefGroup = {products_group} /* Products */;", 3) + L('projectDirPath = "";', 3) + L('projectRoot = "";', 3) + L("targets = (", 3) + L(f'{target_id} /* {PROJECT_NAME} */,', 4) + L(");", 3) + L("};", 2) + L("/* End PBXProject section */") + L() - walk(ROOT, PROJECT_NAME, main_group_id, []) - for child in sorted(SOURCE_ROOT.iterdir()): - if child.is_dir(): - walk(child, child.name, uuid(f"group-{child}"), []) - return "\n".join(group_defs) + # -- PBXResourcesBuildPhase -- + L("/* Begin PBXResourcesBuildPhase section */") + L(f'{resources_phase} /* Resources */ = {{', 2) + L("isa = PBXResourcesBuildPhase;", 3) + L("buildActionMask = 2147483647;", 3) + L("files = (", 3) + L(f'{asset_build} /* Assets.xcassets in Resources */,', 4) + L(");", 3) + L("runOnlyForDeploymentPostprocessing = 0;", 3) + L("};", 2) + L("/* End PBXResourcesBuildPhase section */") + L() - lines.append("/* Begin PBXFrameworksBuildPhase section */") - lines.append(f"\t\t{frameworks_phase_id} /* Frameworks */ = {{") - lines.append("\t\t\tisa = PBXFrameworksBuildPhase;") - lines.append("\t\t\tbuildActionMask = 2147483647;") - lines.append("\t\t\tfiles = (") - lines.append("\t\t\t);") - lines.append("\t\t\trunOnlyForDeploymentPostprocessing = 0;") - lines.append("\t\t};") - lines.append("/* End PBXFrameworksBuildPhase section */\n") + # -- PBXSourcesBuildPhase -- + L("/* Begin PBXSourcesBuildPhase section */") + L(f'{sources_phase} /* Sources */ = {{', 2) + L("isa = PBXSourcesBuildPhase;", 3) + L("buildActionMask = 2147483647;", 3) + L("files = (", 3) + for f in sorted_files: + name = f.split("/")[-1] + L(f'{b_uuids[f]} /* {name} in Sources */,', 4) + L(");", 3) + L("runOnlyForDeploymentPostprocessing = 0;", 3) + L("};", 2) + L("/* End PBXSourcesBuildPhase section */") + L() - lines.append("/* Begin PBXGroup section */") - lines.append(render_groups()) - lines.append("/* End PBXGroup section */\n") + # -- XCBuildConfiguration -- + L("/* Begin XCBuildConfiguration section */") - lines.append("/* Begin PBXNativeTarget section */") - lines.append(f"\t\t{target_id} /* {PROJECT_NAME} */ = {{") - lines.append("\t\t\tisa = PBXNativeTarget;") - lines.append(f"\t\t\tbuildConfigurationList = {target_config_list_id} /* Build configuration list for PBXNativeTarget \"{PROJECT_NAME}\" */;") - lines.append("\t\t\tbuildPhases = (") - lines.append(f"\t\t\t\t{sources_phase_id} /* Sources */,") - lines.append(f"\t\t\t\t{frameworks_phase_id} /* Frameworks */,") - lines.append(f"\t\t\t\t{resources_phase_id} /* Resources */,") - lines.append("\t\t\t);") - lines.append("\t\t\tbuildRules = (") - lines.append("\t\t\t);") - lines.append("\t\t\tdependencies = (") - lines.append("\t\t\t);") - lines.append(f"\t\t\tname = {PROJECT_NAME};") - lines.append(f"\t\t\tproductName = {PROJECT_NAME};") - lines.append(f"\t\t\tproductReference = {product_ref_id} /* {PROJECT_NAME}.app */;") - lines.append("\t\t\tproductType = \"com.apple.product-type.application\";") - lines.append("\t\t};") - lines.append("/* End PBXNativeTarget section */\n") + # Project Debug + L(f'{project_dbg} /* Debug */ = {{', 2) + L("isa = XCBuildConfiguration;", 3) + L("buildSettings = {", 3) + L("ALWAYS_SEARCH_USER_PATHS = NO;", 4) + L("CLANG_ENABLE_MODULES = YES;", 4) + L("DEBUG_INFORMATION_FORMAT = dwarf;", 4) + L("SDKROOT = macosx;", 4) + L("SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;", 4) + L('SWIFT_OPTIMIZATION_LEVEL = "-Onone";', 4) + L("};", 3) + L("name = Debug;", 3) + L("};", 2) - lines.append("/* Begin PBXProject section */") - lines.append(f"\t\t{project_id} /* Project object */ = {{") - lines.append("\t\t\tisa = PBXProject;") - lines.append("\t\t\tattributes = {") - lines.append("\t\t\t\tBuildIndependentTargetsInParallel = 1;") - lines.append("\t\t\t\tLastSwiftUpdateCheck = 1500;") - lines.append("\t\t\t\tLastUpgradeCheck = 1500;") - lines.append("\t\t\t\tTargetAttributes = {") - lines.append(f"\t\t\t\t\t{target_id} = {{") - lines.append("\t\t\t\t\t\tCreatedOnToolsVersion = 15.0;") - lines.append("\t\t\t\t\t};") - lines.append("\t\t\t\t};") - lines.append("\t\t\t};") - lines.append(f"\t\t\tbuildConfigurationList = {project_config_list_id} /* Build configuration list for PBXProject \"{PROJECT_NAME}\" */;") - lines.append("\t\t\tcompatibilityVersion = \"Xcode 14.0\";") - lines.append("\t\t\tdevelopmentRegion = en;") - lines.append("\t\t\thasScannedForEncodings = 0;") - lines.append("\t\t\tknownRegions = (") - lines.append("\t\t\t\ten,") - lines.append("\t\t\t\tBase,") - lines.append("\t\t\t);") - lines.append(f"\t\t\tmainGroup = {main_group_id};") - lines.append(f"\t\t\tproductRefGroup = {products_group_id} /* Products */;") - lines.append("\t\t\tprojectDirPath = \"\";") - lines.append("\t\t\tprojectRoot = \"\";") - lines.append("\t\t\ttargets = (") - lines.append(f"\t\t\t\t{target_id} /* {PROJECT_NAME} */,") - lines.append("\t\t\t);") - lines.append("\t\t};") - lines.append("/* End PBXProject section */\n") + # Project Release + L(f'{project_rel} /* Release */ = {{', 2) + L("isa = XCBuildConfiguration;", 3) + L("buildSettings = {", 3) + L("ALWAYS_SEARCH_USER_PATHS = NO;", 4) + L("CLANG_ENABLE_MODULES = YES;", 4) + L('DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";', 4) + L("SDKROOT = macosx;", 4) + L("SWIFT_COMPILATION_MODE = wholemodule;", 4) + L("};", 3) + L("name = Release;", 3) + L("};", 2) - lines.append("/* Begin PBXResourcesBuildPhase section */") - lines.append(f"\t\t{resources_phase_id} /* Resources */ = {{") - lines.append("\t\t\tisa = PBXResourcesBuildPhase;") - lines.append("\t\t\tbuildActionMask = 2147483647;") - lines.append("\t\t\tfiles = (") - lines.append(f"\t\t\t\t{asset_build_id} /* Assets.xcassets in Resources */,") - lines.append("\t\t\t);") - lines.append("\t\t\trunOnlyForDeploymentPostprocessing = 0;") - lines.append("\t\t};") - lines.append("/* End PBXResourcesBuildPhase section */\n") - - lines.append("/* Begin PBXSourcesBuildPhase section */") - lines.append(f"\t\t{sources_phase_id} /* Sources */ = {{") - lines.append("\t\t\tisa = PBXSourcesBuildPhase;") - lines.append("\t\t\tbuildActionMask = 2147483647;") - lines.append("\t\t\tfiles = (") - for build_id, _, name in swift_build_entries: - lines.append(f"\t\t\t\t{build_id} /* {name} in Sources */,") - lines.append("\t\t\t);") - lines.append("\t\t\trunOnlyForDeploymentPostprocessing = 0;") - lines.append("\t\t};") - lines.append("/* End PBXSourcesBuildPhase section */\n") - - common_settings = [ + # Target Debug + target_settings = [ ("ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon"), ("CODE_SIGN_ENTITLEMENTS", "Resources/Bugger.entitlements"), ("CODE_SIGN_STYLE", "Automatic"), ("COMBINE_HIDPI_IMAGES", "YES"), ("CURRENT_PROJECT_VERSION", "1"), - ("DEVELOPMENT_TEAM", ""), + ("DEVELOPMENT_TEAM", '""'), ("ENABLE_HARDENED_RUNTIME", "YES"), - ("FEISHU_APP_ID", ""), - ("FEISHU_APP_SECRET", ""), - ("FEISHU_BASE_DOMAIN", "xorbitlab.feishu.cn"), + ("FEISHU_APP_ID", '"$(FEISHU_APP_ID)"'), + ("FEISHU_APP_SECRET", '"$(FEISHU_APP_SECRET)"'), + ("FEISHU_BASE_DOMAIN", '"xorbitlab.feishu.cn"'), ("GENERATE_INFOPLIST_FILE", "NO"), ("INFOPLIST_FILE", "Resources/Info.plist"), - ("LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/../Frameworks"), + ("LD_RUNPATH_SEARCH_PATHS", + '"$(inherited) @executable_path/../Frameworks"'), ("MACOSX_DEPLOYMENT_TARGET", "14.0"), ("MARKETING_VERSION", "1.0"), ("PRODUCT_BUNDLE_IDENTIFIER", BUNDLE_ID), - ("PRODUCT_NAME", "$(TARGET_NAME)"), + ("PRODUCT_NAME", '"$(TARGET_NAME)"'), ("SWIFT_EMIT_LOC_STRINGS", "YES"), ("SWIFT_VERSION", "5.0"), ] - def config_block(config_id: str, name: str, settings: list[tuple[str, str]]) -> str: - setting_lines = "\n".join([f"\t\t\t\t{key} = {value};" for key, value in settings]) - return ( - f"\t\t{config_id} /* {name} */ = {{\n" - f"\t\t\tisa = XCBuildConfiguration;\n" - f"\t\t\tbuildSettings = {{\n{setting_lines}\n" - f"\t\t\t}};\n" - f"\t\t\tname = {name};\n" - f"\t\t}};" - ) + L(f'{target_dbg} /* Debug */ = {{', 2) + L("isa = XCBuildConfiguration;", 3) + L("buildSettings = {", 3) + for k, v in target_settings: + L(f"{k} = {v};", 4) + L("};", 3) + L("name = Debug;", 3) + L("};", 2) - lines.append("/* Begin XCBuildConfiguration section */") - lines.append(config_block(debug_config_id, "Debug", [("ALWAYS_SEARCH_USER_PATHS", "NO"), ("CLANG_ENABLE_MODULES", "YES"), ("COPY_PHASE_STRIP", "NO"), ("DEBUG_INFORMATION_FORMAT", "dwarf"), ("GCC_DYNAMIC_NO_PIC", "NO"), ("GCC_OPTIMIZATION_LEVEL", "0"), ("ONLY_ACTIVE_ARCH", "YES"), ("SDKROOT", "macosx"), ("SWIFT_ACTIVE_COMPILATION_CONDITIONS", "DEBUG"), ("SWIFT_OPTIMIZATION_LEVEL", "-Onone")])) - lines.append(config_block(release_config_id, "Release", [("ALWAYS_SEARCH_USER_PATHS", "NO"), ("CLANG_ENABLE_MODULES", "YES"), ("COPY_PHASE_STRIP", "NO"), ("DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym"), ("SDKROOT", "macosx"), ("SWIFT_COMPILATION_MODE", "wholemodule")])) - lines.append(config_block(target_debug_config_id, "Debug", common_settings)) - lines.append(config_block(target_release_config_id, "Release", common_settings)) - lines.append("/* End XCBuildConfiguration section */\n") + # Target Release + L(f'{target_rel} /* Release */ = {{', 2) + L("isa = XCBuildConfiguration;", 3) + L("buildSettings = {", 3) + for k, v in target_settings: + L(f"{k} = {v};", 4) + L("};", 3) + L("name = Release;", 3) + L("};", 2) - lines.append("/* Begin XCConfigurationList section */") - lines.append(f"\t\t{project_config_list_id} /* Build configuration list for PBXProject \"{PROJECT_NAME}\" */ = {{") - lines.append("\t\t\tisa = XCConfigurationList;") - lines.append("\t\t\tbuildConfigurations = (") - lines.append(f"\t\t\t\t{debug_config_id} /* Debug */,") - lines.append(f"\t\t\t\t{release_config_id} /* Release */,") - lines.append("\t\t\t);") - lines.append("\t\t\tdefaultConfigurationIsVisible = 0;") - lines.append("\t\t\tdefaultConfigurationName = Release;") - lines.append("\t\t};") - lines.append(f"\t\t{target_config_list_id} /* Build configuration list for PBXNativeTarget \"{PROJECT_NAME}\" */ = {{") - lines.append("\t\t\tisa = XCConfigurationList;") - lines.append("\t\t\tbuildConfigurations = (") - lines.append(f"\t\t\t\t{target_debug_config_id} /* Debug */,") - lines.append(f"\t\t\t\t{target_release_config_id} /* Release */,") - lines.append("\t\t\t);") - lines.append("\t\t\tdefaultConfigurationIsVisible = 0;") - lines.append("\t\t\tdefaultConfigurationName = Release;") - lines.append("\t\t};") - lines.append("/* End XCConfigurationList section */") - lines.append("\t};") - lines.append(f"\trootObject = {project_id} /* Project object */;") - lines.append("}") - return "\n".join(lines) + L("/* End XCBuildConfiguration section */") + L() + + # -- XCConfigurationList -- + L("/* Begin XCConfigurationList section */") + L(f'{target_cfg_list} /* Build configuration list for PBXNativeTarget "{PROJECT_NAME}" */ = {{', 2) + L("isa = XCConfigurationList;", 3) + L("buildConfigurations = (", 3) + L(f'{target_dbg} /* Debug */,', 4) + L(f'{target_rel} /* Release */,', 4) + L(");", 3) + L("defaultConfigurationIsVisible = 0;", 3) + L("defaultConfigurationName = Release;", 3) + L("};", 2) + L(f'{project_cfg_list} /* Build configuration list for PBXProject "{PROJECT_NAME}" */ = {{', 2) + L("isa = XCConfigurationList;", 3) + L("buildConfigurations = (", 3) + L(f'{project_dbg} /* Debug */,', 4) + L(f'{project_rel} /* Release */,', 4) + L(");", 3) + L("defaultConfigurationIsVisible = 0;", 3) + L("defaultConfigurationName = Release;", 3) + L("};", 2) + L("/* End XCConfigurationList section */") + L() + + L("};", 1) + L(f"rootObject = {project_id} /* Project object */;", 1) + L("}") + + return "\n".join(b) + "\n" -def main(): - project_dir = ROOT / f"{PROJECT_NAME}.xcodeproj" - project_dir.mkdir(exist_ok=True) - (project_dir / "project.pbxproj").write_text(pbxproj()) - scheme_dir = project_dir / "xcshareddata" / "xcschemes" - scheme_dir.mkdir(parents=True, exist_ok=True) - (scheme_dir / f"{PROJECT_NAME}.xcscheme").write_text(f""" +def build_xcscheme() -> str: + target_id = uuid("target") + return f""" @@ -456,7 +449,7 @@ def main(): runnableDebuggingMode = "0"> @@ -464,8 +457,37 @@ def main(): -""") - print(f"Generated {project_dir}") +""" + + +def main(): + PROJECT_DIR.mkdir(parents=True, exist_ok=True) + + # pbxproj + pbxproj = build_pbxproj() + (PROJECT_DIR / "project.pbxproj").write_text(pbxproj) + print(f"Wrote {PROJECT_DIR / 'project.pbxproj'} ({len(pbxproj)} bytes)") + + # Workspace + ws_dir = PROJECT_DIR / "project.xcworkspace" + ws_dir.mkdir(parents=True, exist_ok=True) + (ws_dir / "contents.xcworkspacedata").write_text( + '\n' + '\n' + ' \n' + ' \n' + '\n' + ) + + # Scheme + scheme_dir = PROJECT_DIR / "xcshareddata" / "xcschemes" + scheme_dir.mkdir(parents=True, exist_ok=True) + (scheme_dir / f"{PROJECT_NAME}.xcscheme").write_text(build_xcscheme()) + + print("Generated project successfully.") + print(f" Sources: {len(collect_swift_files())} Swift files") if __name__ == "__main__":