From 92aa76643c4bdaffbcf37a3fb94a0ab566f43570 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 20 Oct 2023 16:02:03 +0200 Subject: [PATCH 01/61] Remove release action, add documentation for release and branching --- .github/workflows/release.yml | 123 ----------------------- Documentation/branching_model.md | 29 ++++++ Documentation/images/branching_model.png | Bin 0 -> 52317 bytes Documentation/releasing_an_update.md | 97 ++++++++++++++---- 4 files changed, 104 insertions(+), 145 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 Documentation/branching_model.md create mode 100644 Documentation/images/branching_model.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index d8844c1..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Release Update - -# This workflow merges a PR and, optionally, releases a GitHub Release with an auto bumped version. - -# Using the merge-bump- label on a PR, you can choose to bump the major, minor, or patch version -# Using the merge-no-bump label on a PR, you can merge the PR with no version change - -# You should only attach **one** of these labels, anymore will result in undefined behavior as to which bump will be performed. - - -# Run only on labeled Pull Requests -on: - pull_request_target: - types: - - labeled - -permissions: - contents: write - pull-requests: write - repository-projects: read - -jobs: - release: - - # Limit job to requests with a merge-* label on them - if: contains(${{ github.event.pull_request.labels.*.name }}, 'merge-') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - # Fetches the last tag in the repo - - name: πŸ”™ Get Previous Tag - id: previous_tag - uses: "WyriHaximus/github-action-get-previous-tag@v1" - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - - # Generates the next major, minor, and patch version given the previous tag - - name: ⏭ Get Next Versions - id: next_versions - uses: "WyriHaximus/github-action-next-semvers@v1" - with: - version: ${{ steps.previous_tag.outputs.tag }} - - # Selects the next version based off the label attached to the PR - # NOTE: it will select _the first_ tag it sees. Do **not** attach more than one `merge-bump-` labels - - name: βœ… Select Next Version - id: next_version - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HEAD_REF: ${{ github.head_ref }} - MAJOR_VERSION: ${{ steps.next_versions.outputs.major }} - MINOR_VERSION: ${{ steps.next_versions.outputs.minor }} - PATCH_VERSION: ${{ steps.next_versions.outputs.patch }} - NO_VERSION: no-bump - run: | - LABEL=$(gh pr view $HEAD_REF --json labels -q '.labels[] | select(.name | contains("merge-bump-")) | .name' | head -n 1) - echo "Found label: $LABEL" - - if [[ $LABEL == "merge-bump-major" ]]; then - echo "next_tag=$MAJOR_VERSION" >> $GITHUB_ENV - elif [[ $LABEL == "merge-bump-minor" ]]; then - echo "next_tag=$MINOR_VERSION" >> $GITHUB_ENV - elif [[ $LABEL == "merge-bump-patch" ]]; then - echo "next_tag=$PATCH_VERSION" >> $GITHUB_ENV - else - echo "Setting no-bump as no bump label was found" - echo "next_tag=$NO_VERSION" >> $GITHUB_ENV - fi - - # Merges the underlying PR - - name: γŠ—οΈ Merge PR - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HEAD_REF: ${{ github.head_ref }} - run: gh pr merge --merge $HEAD_REF - - # Delete the underlying PR branch - - name: ␑ Delete PR Branch - if: github.event.pull_request.head.repo.fork == false - env: - HEAD_REF: ${{ github.event.pull_request.head.ref }} - run: git push --delete origin $HEAD_REF - - # Checkout main again, the PR has merged so we want to pull that update down before bumping the version - - uses: actions/checkout@v3 - if: contains(toJSON(github.event.pull_request.labels.*.name), 'merge-bump-') - with: - ref: main - - # Apply the version bump by editing the Versions.swift file - - name: πŸ‘Š Bump Version - if: contains(toJSON(github.event.pull_request.labels.*.name), 'merge-bump-') - run: | - sed -i 's/.*static let version.*/\tstatic let version = "'"${{ env.next_tag }}"'"/g' Sources/GenIR/Versions.swift - # Confirm the tag is in place - if ! grep -Fq '${{ env.next_tag }}' Sources/GenIR/Versions.swift ; then - echo "Failed to find tag in Versions.swift: ${{ env.next_tag }}. Bailing out." - exit 1 - fi - - # Commit the change to main - - name: πŸ’ Commit Version Change - if: contains(toJSON(github.event.pull_request.labels.*.name), 'merge-bump-') - run: | - git config user.name 'github-actions[bot]' - git config user.email 'github-actions[bot]@github.com' - - git add Sources/GenIR/Versions.swift - git commit -m "Gen IR version: ${{ env.next_tag }}" - git push - - # Create a new GitHub release with the new version - - name: πŸš€ Release New Version - uses: softprops/action-gh-release@v1 - if: contains(toJSON(github.event.pull_request.labels.*.name), 'merge-bump-') - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ env.next_tag }} - name: ${{ env.next_tag }} \ No newline at end of file diff --git a/Documentation/branching_model.md b/Documentation/branching_model.md new file mode 100644 index 0000000..5172fda --- /dev/null +++ b/Documentation/branching_model.md @@ -0,0 +1,29 @@ +# Gen IR Branching Model + +![Branching Model](images/branching_model.png) + +Gen IR uses a modified version of [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) as the basis of it’s branching model. This model is tried, tested, and has remained so over 10+ years for a project of this type. + +Modifications to the model: + +- `develop` becomes `unstable` +- No `hotfix` branch for now. We can just follow the regular flow +- Unstable releases are tagged on `unstable` if and when we want to make a new release + - These should be set as `prelease` in the GitHub Release! + +## Versioning + +We use the [SemVar 2.0.0](https://semvar.org) versioning scheme. + +Releases should increment this (see [Releasing an Update](releasing_an_update.md)) + +## New Feature Development + +- Create a new branch off `unstable` and name it appropriately + - `git checkout unstable && git pull && git checkout -b sensibly_named_branch` +- Add your new feature commits +- Create a merge request to the `unstable` branch + +### Unstable releases + +If a feature should be released for testing & early adopters, you will need to do some additional steps. See [Releasing an Update - Unstable](releasing_an_update.md#unstable) for more. diff --git a/Documentation/images/branching_model.png b/Documentation/images/branching_model.png new file mode 100644 index 0000000000000000000000000000000000000000..a277abb4443187629249a931ed68d4009d9a9f13 GIT binary patch literal 52317 zcmeEP1zc3y)(51eQ(B~vZlpm#>6Gr47&;|H=@N?;P$_9Ck#0o<3rPtn=|O6M0lqWB zcv0_t*LUmb_Z~lmIs5F{wbuHt*lV8?p{62_jY*D)fPjE~{@hs&1Oy}~@cAA(D)4)Q zrt>NSg6}DJSzUKWZ)-aTD+C5!nS)OZJY2RQH+Kf!vkW{uX3owWmUd<~u4ayI98Om5 zKoM}?5oBp+ZD(b9P=|+$hnJn3i=Br{i<^sqSBh5v_{Go7!OJ74e^B4d#>(liL1mDy zor8lJ1CJa(2N%#3v$C0mos&Dr&6a^z8n{01RCxFJ0h2Hx~yt|3M8` zPoQ~jE@2KX7T|`wm4!VBXbn~c_{R+t$=SJDS%bWRLT+v$SSPR{Te+Lr91Pb{&q6UmKN!I-XXpZgwE2!&&igaB=V-+;I1CwmK-awDPpGu!5EH$}(_C z0S&|c@yZ;o5pZF5xGFws-aZQYLQ={mw#pt#yxLCAX2J)pIhi>gwq|4JZtVc_`noxI zqmJJ4u4c}*${@>6OJeDLFa{w$V3x2sS^69l@bMmw)5aCv`(X`Ofu^1BVRry^K7G%| z!_Lym?clqI72QD~2X{N?FAFU|PEJ-9hlD$cYyLRRukes93HIpIV(FGTX@d^We*1KXa=u*Sm0)BW(lm{r|*9%I!cv; zWrQ!LD+mO%cZ6t1Z^J7&ddpZjz}OGIj)(mKy!7vX_w#DFe&+r^(;V-?8hDyHcpR?d zL4ljQ4}4**oGhha^Z^#c3FHLSlD0H+v;8Ee+`xscyQ2ee3-%EZc?TJg1IYE$+VNVN zSqWKN0OfA(t{{7>BXG4amvNLH3tY zX;+YkljUb7{-ur*2;_Xo&KIrR-F*&$&dkFd^d+-@Aq3yb2v8yihy&0RfaKpu2EUb% zCEu?}h7gZAuK=LxzE3jvz7-6fLjn5plJU2&Jb)x-FtLMkv@=Wq++frA*JMA+#s{B& zMSS?alpbza#Xlk?0OuZz^Lxhh4>I~8xxWBw0S5pPr7i6|8F&P2U>~_S_&5MmKPU%g za^%*xV&dQcsAm|Yd)eB#TWLC*eZpKXz(4@Xjv%|4oAZIrwzl(z3C%YQ{rgaF4RUga zizG~0IGCARIjDi$?A#BmBMU&ESpk|u8ZemL?Et;5^i>6Sm|#kqIoR32jHz#`OC8=Y z2f4e09AQHQIy|CtxVQuaWMqKygH`wyodfIG>JW)PNzwO_{|Lx^26EmnIZ@#7@`$p5 z^X5@y122OM$5FikyZ8I%!Y9oAV<>v4QU5h_IhxryeJ5`LO9^AgBTV$$X25S54!=um zM>|VPn4R*g{QC>U!yx#dAiKa<+VofG4zKaU&>e1F{cFMlUJK0W`5&tMCsE~VW9?Vi z3l1Sa4txC!9)`dHI2?5_;9~LT?WiN55BDaHQU_l4x0qYsdKf}qYNmg{!+>}38$QiX z;b9yy@qdShaa0U=4sFgqF9!b%fAfFEZJ&JrxJ~tk*$sxQfF1Fz)BZIof79h3_S*kJ z*Fx|o;Bc4&aS(QdMTEb$@8#sAVZ{H&HL>Ehf>JKZCQ$&3zbE_>tNEZ!@ideJ)oY-NTLHPZxTy3m`3sSNhZCS=b&F%#%I1f>HX@ zmA9Qc>}#;iGb`Zpk#7N4zkCUH0sqc#Zh9OQJGkfegNohXYO?dfvw&bL2CUZS;LX41 z#=@i!pwYL%p>Nv%M|^_c91f3=Fo&>!@ZpaT*HR!;~v)0r#@ddGS!~Mq}RA&BG%bO1#ID^AG-1z*9EbqUl{ZD(a z-@ifio0j))4hL?A{9()IAKWEAj8=VNg?tlNh9#f^{{8<_#r~tIVsK;WP`ZAGR5AF0 zqc7O&>%jGY`Y_62kCADZ#VCj_xYJ%l?^68a}{T`{jrGOS1)@WdjGV zKWw%9CSHD>O8a$>e;6*|J^r?GgVp%GRBL!EK+Plh-~T#c{1B-BV0!k?6*B#li1TFv z{@#BObA? z;N(4OUBS!XB>zV|h#ghD?7;e-zZ^Z#rl?2Cxj@5QG+9lSiuoBj_Z ziNf{BQL{|&pU<2A9!2w~%rf{q|C>da_p7J__s<~0pK<5EQilIlgBmX9M`izK+b81t zIzt2=w}u-Ef7mvJK@u!!za9ReR{i(vZunTgvFpESA6D~kx9k5wvl`CB|JAtu zwR8A0;JVOZ?B_>@^)H<$;Go)H@>rOk4r1Yu3b*{>81k1v?6HAlly9^U02j%fdb z&;*ylBhX}RW#;bT3Y_WSGIup|vakg{9NOAP#Na!Qa(q4;`PF{;XFva^Kg#hdVS&$< z;13fQ5c+n<^;anTEztds!ayo7Ts)2hettX(11DDw?4={}3EQpE{E?nsb|m)q4@D4; zdJLbB7XO30INu*p|C{Z1ez;qdB-;or00KWU}EAdTgl;qY>Cz@zA&{vLAk&!k4V z1^ySr`<2I`eJO&6S@!=EK|rzmXU}l_zZ604|7pQr5y1l|{K1q zCvcDX9~8as;h#f{{bPxfpC@`GO7^YC{g>p$eDaOI1i;VnvTx3pe2%ak4CYs~|6iCF z^9@G*O7sg38b3_%iyNNv`@hi0!P)dfwmtf41c5`p>x zg5&j(2(to4sdAPnY2)-vI4SNE~=U1+Lej5P~ zT#>?Y1MV;U$hh(AyeD+HoB0Lr!O~6sSsUSxW=(zJA$+y9`W?IfznAKHU?6;@_kV?z zfaN-3L>!t6M@@*|So}|^J%4}?zx(ja_p}YuaDRK+6hB;#!bJvt{k`5Ue3U@_zhc`3 z-u>Zl;0MRyF@-`CqXy57!Y4`E+LaKv{8on=b*pz3nf0P0s%m)4^NU z4&So#4U_%Eis^gabMgOUU;n!l)t`ID-Qm;l4xe%df8yQ0ddA(qtMuUS_xKL2_bI*R zdo+NvEPpP8#?eX zXY30d2)yD6?y!ArQvXi_etJ6OZ^UZ<05}|Kz;6PxKad9e6n6iOH{8OvJ%4$X@X>c-Wcl~ZnE37a6^?<73YO&PdPAU!|&jKpc|n0?75Ld27H z7#WwM(jips7LT;vD}V0ClJ?{zB*pGRuaKh>aR!b=&uWF_gZ}u!egz?9GZP#LR>+x+ z=m?ryZI9C&02lrLGoyex%OnFs{dbPLu4twfm zH9sT}7wQ+{=^%Nkv?C}0Z+lAgMy3J^!{JOaB7b{W zXw9Vd+0XP2Q%_iXT4eGuFAOV$aOwv7Nkk&3jZ3Jp1`hozPN_Wa(3v|H9pUm#%V4t1 z%o)EiP|Bmxq-#BTV2L&yZDhwm^Gm5yVRaBMVjGA(zRTxz9Xp1Y*Q4)WKfz>>tS`j*rPc6zS9J5ZyCvnX`Ge;pH6#u>~f)$VI*OiaXNay`r5Tjw|meOdPEFN=q59` zaNM&NrAn=DhmE7Cts75YH8Q<3PU}%D3aF$7*{oxwZ8=2!NtmhOT2FEM8d+VmERRq= z=WLA2(tE5x*&FmqA&iHFy+#PE@WG@{&wZR&3x7yIms+Rxv>uOSM|h7R!?TG$R1dby zX9PPN&J&o5L)Do(qwazXgs)}uj8+m6ePCgJQ7B%$x~mGc*`+noN4?$azcw0!Xv0Y@ zqQP*7;h4a=eofzrGjFD5JT^}F((y?dJ;dQZZ})~VMgp=D_axiRwTyJbiedsu zK8srxiQt6uAaOxgk>yHN99ou@JcG;dpHMq~}M;j>H6p zNDztP&|90_6^MA2ZQ@#1Rlae|LP~Cm%Lr+|&Mu)YR|bisqtp}!(t)=MgU!rAuIV2 ztOOcSQv}B)mQ`V%EASl{(i6DFl*7+v>PU8jS=F<&v{HyT;o2po_y@yyqLsbu&F4Rl z3SB;^-6Vi5f<_`Bxt$u{n`>f#@^d%7N5nRc{ru; zNE(qsafy?bh3xuBPGcGqXi~j+q|TdSe{aoUG}}K{(TWRcPpr>^%5`y?n5t3LMnRfeh_ z!``mX+ShA1#~@i~^V89`Z*>G^v$f!>aY6vraW4(97OGGB+fPDByD@@tRH>W0ww{HY zrORcHv4O~U3F~I*s&^QPHRNgE2E_{OxlJ{_?sIV@-KnU3)<@FD1RqG(r-9Ub8i)nO zm9fB&5XAfROfp-M+E?4@62+dQ><`P_Ab-O~_9FL!-AIzK=gMU$G{y!|>YjnpjAJww z9I3g?k?4RqX#-7>iZ{{C8-Pu!y-`VeQc!#Cdqc3tpJiI7ZF9Ytmq$+($Dkf*ICe}_ zz7d?xc0ytnFyf-zMH*7eKOS;IPlm#4?8_1I z?S0HQgi-88={h?^m2v?0*t;RAnr^WS3mq@?L@?di1|O7$J(4HH9&_{(LayVqkBHc? zp3mP#9DPZcxj~8z4mCZcRbWWPh};!|QnowJ(*1F##}KRHnXu%*$6Kba-fWfl?Vv7M zkIl6pmprT_AvdaDuw#P+8avZ3g>kG?^^5OZ!mU)@HxcRX=-yeex2dmW7Kh891tW|r z2N?pO5DY2XuC*XccH2>rNHIauo3h zOB5_NQy*J)UL9oM7|sda?@`{#P`^QP?HV4g$7!LKrF(sAOH21k$I?&u&g}&it^4dO z+X$pyi(#_3q4_HH(Y~T&rLC4?;D$$Q?G;T)44yNpl(}MR4H@$YMra?-An@cM2}qy| z?-dOOOgkD%yS86V!H8~c zvh)5{O6Ev{Nz;3Y?4}1LDA&TJ>upO3)N0SpQljijFztuDvO4X&a+;Vw%yFkjrgyF) zq+X{pK`aJkKNls;V#lj!+1AGXRxz?0rV=l-7XSL8e&)hQf>G1amBHI7X2@oG^)UuTtF8!&+Oc#z zUvt$CM(rVDPvZ8lK+`O8>Ha1%GnbICZ-)IH!4k~qOe}?bVQSv^HmYSsj4lzNrSE@L>Q1lg~e$i8fK$PbJ=10 zl+(+loZ$3#^jdir6m8wN9_ExP_JAdDv^ZwxYHMs#l52M%f>8ghm#=L=1}Jg+(*gCa zs+S`Ul|bSN7;3v4pE3~S!e75Q7v$*D&Xxl+m3p%3sLp2kGfe)LG6(MXo(T?YW zO`f>U50Y>;_3+GBfzNeaTf$Cb?|S|*ZFBMspKkTK(%Nvm32B#?Wh7iHHD1Md0eM#Z z5vD+-+ZdZTiJsOjf=Qq)IW!&|8vS#=#hw-KKGJO`^^dM?-pZXRo_G)%YfqSy@?L}W zEZow+f)LXHor?$b1e2Bn0aQBv&BHO9X}z4%3e6F_ZCT;IIbyAvO)kMZgbCDVJD3;~ zm`aONBH`HD2tkJpLFLmBT7e-T%k~O*_keAb&Aaa2vp1r2pr|JHWY*8Ll(OF-F|JgV z7_yOA@y8tYfyz;@pV@00@;cA-#Ky#9N(~*ZCFbxlR&=Nos>5gjsa}at4o=4vTc5yW zh3GvLzl?VJ+|BF8DxMcim>6;dSWWY_7#Yvgzr8?`^PX@j>@~wQe$&0=mrkN{efS~t zhlrhVy)kE^*dp_E=cB#~Td4MV4myMtO~RXvO^IWq{q#xCLJ*t+In~i(^)HL_6XDGk zrVcY`-Fq}=8QBa#bw;veIM1XP19Od{q^DNWEk&Zia7-kXxk zw|z1r-Zls4UfhGbd=ZU-$hVmjqNT>gcL*7!sv`oh0+Ct-t;by?3q=fhpyn!~z1Z>| zQpidXL5bTbw6P6yA6uNSfX`GwZ&Z39727L6yrbDijS-+Zm!)+<8<~#)+92zzn)HU@ zjma&S?KFVv7V#Pf3R7&WF}uNj?-90;8y;w>NjqIy9Ba|N~Tt$*uRG0dm;*`Y723BVhE&hvhU5`w3iy%6;Wv94oXpV)s5-nUNL?_pcToWh(ZI_DF5?1Nh^cX->XvKs-= z6|dZFEyhzBkqGO8+E+jB85sqfaH7(mi|NZphAOo+^m@NbzDh_oP`P!XkGjE7)8Uk< zAz;K&==f{cDFp>7vG95w6BIZhO>|c83H?JpN`wIfJ%s(p@v#%PA`pAR#=Jn3d=wck z(nHeOT?wGoAuV88a4~*a*p(6_n|DY`2;9W+=$GgvcVkQ}p5RKj_X!uonW(j-mYVS+v*ek@1cynWzAu2!fF0$RH}W&i!WYx>St zEzj~{dOFEpY*BF~%Cn(0?i7h>sVdIL2OlE65%<%t5>muEnA4S^x|M|mOCS|T zE%_J~W5uUWvnQAnT;4~wrH5dJoRk{$e@2%bT4t$)^omirw2eaW@-;83pK()hY zQgR!e1524AILF%XE=B60pT1%=Gsz5DT-itIU!RkCL|Gi@V1ZO?+}kF~qa_W|8mFs_GZ8|kR(8mt;H;! z%kZBKc+h#afe_IHwg|gbe*0&#>_2FE5M*!xAcwAc@*J6^4XSmH%gKU`rfp5ALr@vm zTBxPX8Y(eViqO?C(sOq(V&PAsTbhb(?WJ> zjc1Zem-UKust2Au4q&Mwu?~k~CF{BI}RUWVW6!G(;Uo|;{-3%z<#HhyfAv1i^lKXO@+JTFE|nP-8J?si_dxWhAjta@+ zt}LrRRZG6fu%X4>=c%cw~jWhR0 zxA>SKJG4Yg?7S%ZISuy;IlQ}(S{$d{Q4thd7F$j)7g`eNC2uIm-;M~{Lk}+m`Fl6L z+3aV!!i&<78Pi~HanTmK)YE2i^?Q58)?Tthr~=+mX~jYaHfT?wHlrl}*>Fxl9;D@z4+NTgT5*r7I2zDcAv)Lkh&*K82||#o@GYopE}uIZ}lPA#6jsiIDZaht)>*l z^{-jIEF1wFP3C0K-CdoonPo`FrOiM|mwEz;{3%*Ul0mE+(OCwPkh{y=sls~=BzrM? zCn43cHG0VVCu(Uwgpcc&=Ab}?(In#h!{viV@yIcz)aj76mkj#lO=>P1Al6F7J!5Ok z2_AjXZsO0oyB2XjbLDj|ttS1cYsUbdm52vy2gyKWwZu576)_4~2zFN+K2$tmaj5qa3E}REY_5R79VBBSIkQqe!KS#Gp@~sJNEKmAikav}V58ptb%&%I(0t%eZJ-x@^{sYS{1hQ#`=tBD=hRzm1cdj(tH{6L*;>3#CR{+RDUbR zC0%4MGVYu04Maqf#p?7#9n148ZXVO(&_}j?5^gU!2)7^z6(^U#mt4sDM*g`AK$T|I6H*4 zADTIayLA#GGPY$@U^1bfQWP3$JgSsbb2pRw9+Uvl5f27J4x1l!+*y|5YCM?B<5?a8 zF^r&#NJ7^hPgMkMw3rK5D)ta#>7>*(+B~upS{>KfQM(m77|MJSpX(&jgTXrSo!gnQ z4FDSELa}mp)Go-vz;c;pHtj)qjU%1CPk7}Fp4UnV3?rb@1t*a- zN8olpFb2TN4r0LvI#sApwt|O`KbhWK!bs#8d!v;;s>yHI*92wmF+@@7%AP7+u(C1h z;}aIE&b;MwIdQTanN-m&Mf3dA<=FE?vlNUT2HhK{bLi5Mvlq$r+4+rTjT>L>mYOsd&cX^fxZOb(fN}#*pwbJUJ20((y|<%0MD}1zrc8D~`BP zbG4LonQTf58huZHfFdU59{qdHwn>i6i*`O0NdEeU1y6D?W%ZBgRZaD@#hs!H*lHu#gcIK(8DP6;t;zOv`4`eEsKL$^`0!6CCQx&;@EDbiven?s=w57-thD4~Bv(3Kg>(WN`|^hZa0jUokk;gBf1N z0gFSkG^lb5xuFunroZp;Sf4UCB!|Z_2c$H^S(~~_x52AvbvLkYY6{fuWvCk-s1p4w zSOep_U5)>!zk%1IG)4wR6+c|RA}ASu=0vl3a%+x|4`GInWlU97bw({EDJcR|7$cwnXZpBqnO51IxkoFL7yP>FW(HqR%(r{!zR=O)FPQ04 z8s&H0AAAj4c^tVO(9rLpc7a#GfOT8dR10lZ5LGhCz(#V zIy+j{t!9V>VDIYm0qeL?>9BzHg%onHSL)dt3?i+e7UGrWG-xUYWmfyh%p-s6o<`W zM?1Pj-Xdk&)NaKj`QSZ~S|)7|dpxx|Z@Q6z#~&vXp&L=Fh#z=`)2g`XxVZdh!W$Kw z*5$QXE~Ip`6__0*ILX#ADVF zUK9yGeXgNE9MXpK&x%b8_H{YL9I4I_LQ_A)t?7r5X3~^vpHN=tekWoLx`#-g}IT=ruGmoySs#U#};-QykeY0Y6yXV z4?^xcO}ZyB2o>1lL!lf=s1BR@+|~B|C&v{tC8AnY0~b8vA66mAFce;r#)!Z;nbvfCu+E2zx&+kGf(XQ!9`8V={U6NK=A)9i0=F;K`s5;59BX$C z4%~f!eqyBoKheo0kv-+usQX21!fX`?GlVLYGQ;ON$qUdq3EL<^yk}Q#%ScO~N~o{I zNyFSL2!XJ_+A#>xuw%S+u2^Y~z*XJ=Ma#--PG$6wCqJ4{iT}nHwL|(|Lvc}T+6%jQ zUK4|Myudk}^!u?Ms6)ER2CEz@=B|_rHT4_oS7W0fqe&~>G2;{&o*;-vZXp4{Mt2G- z6FWV9wXffoQEt3Ulpf%OCqDDOy&*z|W?!GKig0#2E`(E8mh-lQX{Q z($F$>T*q`UI4?E4!F&3YR-qoT3YEz2%PgiPN6J)T7m`8uZB+A-R!QeEf=51!j0Gcm zq)d?gI?vgUcu)v?1=Mqg#PNi^h<#rDGq4%a2pmyP{)~ zyzS2CU1Nmo(uH}(2bm+(8yDU_dv^_hl38_B)Xhbc2%6#J3C_nlXuu4JwjMoJ>8Gl#Fcvr{ic641<0RC&Vc%=?IDVvy-+}UU zf<_*tV3vKqQc>;2J37YTL<5M?n`-wVdYioAY%TZImejNdffY%s4lmkt9OpL(?>p(< z*)W7^-bo~X_HID0j;+?dpl^BjMq<+GjztyEfFVN`?QC-vMjI#m4>Ys<#z@!W6KNPC zZ;h%KAiM2eLNj)`r2twGnT510uoy{SxWv4Qz94ahUcu^&N()=!u&^b+S3rjvC`)<( zSH$4VUL~@P03A=W-}?%dHJL7WE`aQyW^g-L|FMI{Ovxh6g171Qv=yZMo97q;mFI|> zg)O#0>vOUDptwb5Do4jgDCOk#B2ICULdboFJ`h8sD+@*kRVLcsXCo)%E<}PeMMWKn{id|iP;pI$OQLHi>`M&S#?OjlQ@Y`%FA}| z?a!LJj?=O0Pi%ok1;oU4E;3LJS1hSDF|t>V^KT}Kt?KA7*6R+NX zQ+~c1g|`Zd?y`sj-3e43>?KzVKAn%yapzh72xE4ui^o;5520Fj6N4Sy?&0}N4OTsA zKfMT!+8q<2tq#?v4_dJRF}4?dJYKU`QhBD@?E?+X_OlZUQFH_AXnl0ud){@CYa4AP zGS%}Zm`RmpAG_;W@3-|_ChOxyf4Y!U&@i!{i<=!EOI(FnJewk3UT54d-yq97s}U4_ z-l)aXdHtHp?rdw3qIbY)W17A0T$-L(%&hxjUQ{=j=~lKYm#?I$5QIFWt8qG)b$v0Z zPjEn>xX9ao{<7V}^9wOaeKK#%>JW+QfU)enPwtv$@-jmD(36&+pkrAx!b&W|QzY?f ztf0?^tpWFTFBT_DpykJ=`H>T%f{``RmtC!=*CkBDHj&ksJIutnjjRy%L}e&AN4DKV z_Z`;^Z1w8~I>`1)#V5v|RJ0hz)p~E7x-^<4VHoIwZ7dpyXc4;GjInz?OR+I1^H`%r z37X~lxaxV8%hx2N$BIjOuZ8oj%0>~RR1rAQpfr?4@kUdMTrcxSTwGfoq~Q=fgX^K# zH(}`8uPh01RT;4ssiYok7885Xo+;RrZeZ3wq&xnC)?o-iQ=d|yqr0ijRnK51XL-Ya zs8TLao)Uc)pacQtHicv6udp1Dol$~Xl&5av9c)V{_b6w)e?n9|JH!t z4(T~1-m)sLvYYqylR9kbZ7$bQyw&=9H)YJ{FFO025}?^MirDw*ZdRm6B1 z`zvSFi9rPYK_2)m`_Ixw$MtYg+>D%pj?F%OY9!4OC^U+-ZxZLP!#s{2+Hh@V>q%w| zx20pPZJWg%j&TIvT^aFt1%~Sv6=Z$S)F({V7HIZ6E)yum$59bS}mioAZD`V{QEi@g9tQ_=)?}kV-#u08S?W0C>`c7Bw(QfD^ult2GW2H)R*jHQQ$r}>aJ%W2Vv=#RQ6Sr^SLx6}N0yX+gjku!A*Zc*-ZkrQvAPvzFJ#1}$+{-{ zU-XS6AiBMLO@<&LJ>2-VTJE*1mifNfWnXR8+e_YeijPwjrjCzR+0#JzAU5Is zHPjE@RvZokz2cHO{ib&$Q}(!|3!UZ{xR^5k&H1*hrh@}R{r zx0%rzwK5g#l9KWl3Zq16V|@xNt6XPhmS6gj-&%gtt%pFfR~5lIzGn1syr)&nCT_I8 znAO96(RRMGM*rS~Nn_GZjaQtVU=Jw>MiZ zw>R4a>>J3%j*dm;%vYL$8{aS^aBCkUh6h` zhj$1m>o%2ol#Y~)oR9&!T2?3QfP7;lrfF|#ooXXr4#+)#!LGVT^2T0pKiCfr-iw1mSSr0!OYYZ*wkRx z2nD4rZrCnogji)IvczdG@UW>apGdgOiwYgm{(4%CtT73HC>B=xyG#8=XBKSZc9zhP zQ5ylDC}T#ju5sig^#uogoMVr%gHE^;n#+$$`J2r)XPrzD3t63N2-re3tZ|RoDdZUu zy;^{cXrnXZumkS7{(Kq=85q}pL2S*Lgo2$S@J8PwsdmgJZqJBPaoLHsQ5=c}a=1j4 zzV@gW(*11NzOWtZNr^+1P2nklEP+6yEVN~R2<~vWHzj-E$l13pvzo`GGgb>Ywr7h4 z2;6u$>yhZepOuU@QmcpIVAboN212F1-RcdD^nSOj-a%;lucNj z%rt>y?^%NPgpUeE6mPR&R905f&~f#pSo-jI@iyK$De413j=aUz{=Boyy`Qb2Lb+Xz zfsJw~xlA|0Jhj+f1-a&^ninRIUf>o7AMgsx@-u1_>qS-sNo;`)S zRk+9=f4^ZhX=m3-TlTF-O$QV1y%)uG;i{$*PP9B!+e{_iYspXC^R}Yn%lv41k|~U; zuy=2~2*3$vk!Xiyd2n~UtsEnD8}LXHB!JS`st(Okyv~cw;pM>S65M~YX4&s=CA$W} zi7GEk(5QE#FGAP!+G_XOG0&yqxX|gTMahK4JM6>S3T8K!hXYJ+yt9}i9N23%k5LXb5#*N%mN;(X$&JWcCc>}C=ce6J z7goJu=;te{Zyv94r!`;%`*uV?zWXXgg+johezqF_t-_s=XQ!Qa2O@Ny&azmYU&wD= z=Wcxea-7a_;YJJUQ&g9sr+IE_H9BF}?LspFuf2{?Ge8NpwQL>@>#;&QSOTq^*K_~T z+mXTQ_Ea$;%i;I(j%Xf_#v)if2`vUfG=L~YLMI$`6Z5>j4 z9XZWskspay;ir97Y10b`ZuNrk+I*5up)-0GlboOpTCw%7oY!r{@ z-#GttCG7sIwRJ)fcE9`aE`)LA-dd2o@zw3hCc#`+PQlLQ6}y_Mhb8_|McUnOmR1#C zaW!+26D}=@CMJ2T!0bQmo5**}qR_yCwa>f(98*l`RP)S|48%)3bXjCwlx3~|ZuAc7 zrhDnsbE4HN;?TU}#di*t4{zIL>jsqGN@^!7?M0soLr-Kxyg^dqPAl+SPBn|M-SqPZs}Yu3~VpW-4u zmg>S~UkuBVtr@gYVgXvrNCwgY2XAY7oX*5MdlDCko~x}_G*`S;n}#0JrcHB`;*{wF zWF-Ue>m`ir!KJ)mBm!W>ZLT&?b#4D2HoWAML@ zk*Qe=&tC!-U&mj~3D}P$;BstItyxRFSOf6VYYs6~Z6n_vws$Az>M$Q#1(7U9=8H26 zo^8~^0CFCUe3dE|HLFohNg`>UyoQvNxs8W@UlPEbvK1Qdqcr4QICE>L1pNhvSg5wiO#}Fl&FLh8-5!4FEn1{> zybLn;iv)1`gd@`!0p7w=h8+341lnn#nZQf&e}b| z4(e1!h5&SU3S-Ya@vBo$`^X8QeBI!zb1MK7jVU(10aC2Q-NXjLAwWqG3n7e22Z3b) z$jS_Xt?4b;nw}@%vTFie=T`|45JDOrs<;q zTy-$Z#xU5h4Y7Pa+-h08z>}oZgm$Wp0cnsPSdz+}S9C>ZvRvdouVguj1V=V2 zq_-3+OV9lPC>Y&BMKJcl@Hcg!s#XrB(D#Wo@I15>B6<;j~RQVc#@m*y)k`zy~Lt$oehndCX>B1f$3uX z>#^I?$@B)I=nIAccz_)46pC3hst1U;7j;ljQ~JnaZ!_G@%@Q6ic$<4yGXhgqd(d}n znfH;;w6VK>?fvLw^G(&*b|-^)b%Q6M4+bvrBD!5g=9Zi2LX^T1&2DPl z8dFs{j|#Z5{r&xsMUt2yNd4*6)zvDi*;=f$R7tjUM!>uu`8D%hdg>J1GaT^RT|Ylj zxBt=lkUd}CaF%ZMDRQRWvF#i8YZ7Qc;M764jCQ$b(Yu*$#tT)9>P6+u9|U`L>*95Y ziB(U+1WxiK3eUYB7rBg6w$HIv2Z{$Xqz}@7TPF;9#@|-&%!}r%&*fJ{Umw3j_(Tz0 zJVb=k@H`c%VULGQ)OWq7%F(l|#G}Ld!(e901*a30%Y&Ht%{qUL;UwG#mJF1{5GML35afiJ-=C#-4 zX0b%t2a!Gt4!{w#4icKrmOgJf7h3i>Pt8A9Q9*EgjlcUtwtI3;@KWRr)&OFD zONJS7KH^2)suy^oL_O8uSp|iTsrm;bA-{3HHxJa&LUC6@P}j*3Ys`dvrjfM@4Y6-? z7-EPAjNnx|qcH6y#nfN%ZH^r4#asU<%!ZkSYx3s7aRX0>V6l|VmaKeIvpI*rNBdyv zOYiQZC{fSjcB^mR%E#1*i1R>*4{3(EJF)OQmc^gN!TsWJn3{@JqOdm%M6A3`O#(fdG0CjA?kn$5lo!A29LYlfm4Qs!nx?H|k3<0b zOuVMKsV>%mco*7y(uJvYurd|SnMUT{{=`*b_3dOX>JU-E(>V?!22~z0m9a$|wPoZx z(^Q6{-u^w3dT}_lG4Y5}=#z{P8qvqN#&l>>HE$QqBuAy3C*ES85GtU7s6wb%Z{|O# zE?<*XhrV`}upP)_;oE-5y|?ezU+;IC@{BLS-hd~1>>VMj-MSs=Cr~S})jL$RDm`VP z$D4dNS=BM$kF-e@wrf8c*q2wOmgXdSH`RGFEb~gJ%sh!>X27aGSIH>ehyF@lHa%)& z42+eM@t(qTjJ@~?{u|DotmmDT79jcpd7W$nQtHNg9k`i`RqWNPRtgEn_GfQ^ z8E0&mwMw`K=dc$WW)YU&?KErDVl?MmDRFAcHk`ljdnK~O)(QPN*#6bU(s-5jnPMvj zg;|MW$|$)fmh7+jN^j_-GVB{9tSP4#JFpe7d8epE2I9(B-SJ-(yXR##(jMIvMC)w5 zjQZqKCP}w<1u_F0a>QggD5C>UCJ$#Yt3&N#dK`iKFSEkfw+jbyUGD_%gO>=^vbFS0#ys)*TC za+dSP3#)4iS+|o2)-;(02QGe$0c*D~Ub~ivpp?2C zCyJC(=@{fj+H-VS#~OS#sQU*8o26=-$hdu(4JF*dkQ=z3^VHlz zq06|St_slQxKEfZcZB@iB@i(%`Rihr^8Rd$;8p25<+v6;n& zbiIx+fjiqmI0`oj^rm0?ZCsUQQSa0hI#Voy6Yb-~Inv1r0n!@Xil6%S6PHY!T}rpt zX@-tfgBiE6XOPw)Pw8$2r$28Ed|BQOgyp4Lb^?jIZshDmM!6&edg?*^=B2ugm{x&B zY@5p}VYr0m#mdf(-PyUdpwm$Ko)A=zcbG}f0l@Ui)-gxjoe;3TWpoob%-g?)C>$JDpxv!bmby|tb5 z^XjKle4JuhsMUvCP8u&BY^RJ+>&rxnjZv$$hQn8 zhWag0KOVRW1WFNknOfSOk(!RDGN|3v#c&dn%EG)Qfw`xobFUzHKmCfIi+{7W(>p{F z4wJgpLP1kOEIqGT+NNT?p=aC|rp;Lql^$D3Y=Gjya%>lr$a1zQ-!7R*EOStLOJZqo&QhAn7)hfi{*&W})2UUcvVf*W7 zGy#Dh$NOsKuEyjQjoF;KG;`;iZbX#B@r*Gl&K;D3!a}nr2%rydbLg0c(^6B-I^RT; zcurhYanHG*?HaJ%l~ZrNrJ#1ti~+p9hmU%fslcI~zFSB9b(q9F)_(j%n9%5cxVAxt z0LN8qJ^W}@{0oh<$4JW-{nKY(5k58*=CgZ)W!;^EiAyaO)N3|xO3Q)NeWpd?Nsh!# zHsi>YLYt^)KU^pd>gFaA&G6;$>a|IF$N=G$`YV3Ttiwh*m#Q(rbvh=7I$S4)BOk?& zkyeuJ*(jTkzJF1Q@Aj;ZV~VMjL#$8rol1cB?f~*|+DSEoRIFYOm$%B#;~U>#+jwYZ zZhyHrD<3KdPru<M3T>dajBM^+`Ct zbojqY&NHluu3N((NL4!0i?kqJP(fPgARUn=AfZSNUAnX=NJpdzN>v1<1PDZW2%#uQ z5Ru+{?;s^WzTthpcK)2}ocx~b*;%t!_FDITo)KfWWNMvo8#(DR;F^WaY$Hxu@rpW~ zC%cFb+!Y+=5^~Jhcc9tjD}ea&LESvHFUU@6Tlf8N*VVRom1WE%E?_2^CB9B(L%>ev zbQ6K;=wM{(LoQ83zr|691n+*Hagw`w`=|g9FcW`}U6*v-TNy!#m*YjR9FL=*Q5MkE zb)$8zI#h$D(Q>cK1}0o&?`(d>sY)-*>iFDP)K=0Q(QYAaqItzw`u(6@l>e&H|5!b{ z2H__Dx3;Pqy+Iea9mG+8drOSlq7-~OwAWYdb`)R!nqH&DfSvg3;M22PA7_Trp=NBz z{CoQl#XbEb>;#W#P&NCT)KqV2cFnEjx;k^OXV2zZQ1Rhip|T@#B_bNnbnj4{3>!8) zVyQ${WADm`jEjxK5X@qqYAe5mrZOTImw-~n`xA;UGr)10e7^&^`C~nhjqL?xc&}oH z=F`p~Vu=$b&s|8Doa@SQjOblfvomC1P_GTEH-2pX=m;+VMB2d>j&722_(&3i_#>R* zzuK>8uN_Io%o|xWFI`bOkq_N|m>%b1)uf46d@;Vf{VGJ?w{K+TJ(Us7x8xZn_2@)- zdcNaBEA+8l$+7==_UMC;_{rWc?}`?VZh1KmtX}pX**3QZ^?tNsXq|}QO5K+hFAW_6 zc;D^m1D83dN6Hb@S)bsc@vj+3`*&wJpzcV#@jCKG0x_yi2)l3c50YRD}ok6_tGK`bD9t+Q|2rt?vdXHd!Rdy(2-+<|O;4^r0Fof{ ztv_ZUn__dX7k=2m!|b&8&dK_fQ{l9Vop*6}=AyNZiRt;z?E2ja*#I!Bby-j9$grRL z=S;xaaVWDYl=hu6OzrDJBmn{K)a82t7=%`3FrH%QVeb`+VExq@off_x0jQPdvV-Mx zQHk+rVG$*au_g5NroovaEz@VdqSk$!c&fckXS1$$8W-zc2%PI6%SKi2os3)Yk`~1E zRp}?w&x&{Wi*o|)Se$B%Wo^sFaw=`z2{p$x#MIg2Z-`oc8yh3(LSHd$o%$$jpdDTE z3clzGjHi{LBd@H8YpZ4&VeSyv%>RXH5hrF;;1v)YHff}{?7qL7x9YOaR-|d{x|n3_zrOfBY%Sl_=kbp0MTex)$Q7 zL<2XWzc|w%sSS)F4;bUp9|1y(tI6@O2>|Zn#5$q8wHIruR>S~>iy(`Myd-Qsf8$4H z8A7_Zb6=EzUD@GK8Gv6xFE8O&+U3;f%YB9J(bA+50E<{qUsB_8(g0^t@&bg~S2eXs z{sToX*;_;)+1r|#;z|1U0h(JN;67xk5dhOHF9ZbcUl)sB&Iz zK`U6($K2#?EB$C86f!zCen&5*_+CwQZM*dRr#kb;7P-NBf?yIy7q;h)hp3`h_c&GJ zYf%Ig+2ue>tiZ?J?;K)jzp80>i*%?Krvn$DI**4z0uh-x36SJ&&cC)g&irCFk7@$Y0 zdpL%e;2mwweV#3KyA9kxE>Mtthlf%a{5=C1H{V7&RY$-0KF{35BCgmX_M1~FNS8m` zPfoC#PMOmU`8#7bBjQV@|8=g{uTRlwZz6L3Eih&c&ZoN*z&i0ToFzXEK|4}^#tnqc zMm|`t(xM5oaz=Kc-co8M%2DQN=4ResF_Q=gAej4>92 ze&F|mon?fgN@sbp%fQ!I->~*DM>^eu?PQsY&XC93Dkr)bFnYodJP;r*=H0fB65}cC zy@*S=Sgg`Nr`wvq!1wuC7*wT7_6eeWz3qnhWR*n$^CX(TcZo z>=(v|Rhh8I%Wk<&RC!pW*XHxIe$QdzRMX@~9_B2EWx_?{42av62);%_6L9-yGi)oj ze=wY;n8QiY_Cp7RQ6C3)l&C##*HCt2!iiyLrhRD<2SQtpwb^wJG&FAD!c*F4r6({{ zU;cJ}P-=$6gzrm@*pANTs4*Lp7gD6+Dk*L1cdJv(_^K!`%-}X{nWAe-I8Rxn;iO4h zOokuOA(J5C(|Ry4Jgy_EF`(?n#@bRXhHPB3evaws6`^6#u14EQ`4F=(e0M5A(~iO4 zHuMx4^Np`dudQ7A(Vn?D+(fm5$hlU+qPeV48^1+%Fk8U#Ex|Ve%A?0G3B){5pCwXj zIPaHk`@)F0JHyltdDtT-h{Fv>uDm_$dM0R6eMgsBfkn1vm5dEp@#<-Kw2IwBo@d)< z9A`36hUK9)$10{Mu?Pf{zG^wEALN+E#@P@~Tgeuk>ZGqU*gXhYZA+h-U%qZjY34PT zH@YHM0xBbWa@|Ng4^#O1WJkTcq4H^$zz8A;Y}F7<`w9E!6^?ovJ*@Y*b)o8FW@F(^ z`Hb$4xVa>?t10|y`1UlBiIB+)7h^}2#W)l0D;;x7ow+`Qq7~OYjZ9JA)nP_24 z;8LK)TFVfD6Az=N6yzbk3mWgM#K{JVf3#H(V>3_+IDho-`-EQ4`fhEV^;$~jhb9ks z1s;(L8VfB4xuoXJ_Pu|qWh5w~fyG_8dgz5NruBxbBP;rc`Ui6ZJYz!lsl+&&TAcFB zG)Vz$MnFqLPB;-<$t74&?Fm7B@0?0*4^%M!gL40q4UHFiv!psIp?t+|LsP)~`eR`q z4;UW(0XK8kho;7Dl9S|sgILNsM3gP#Q^DUQ!e#XIWewWt+7kN3p>zQ@77tne`__#y-hSbT*GDWMru9NW^Xacu~|tjISN|ik`w=%wIc=}@H!BDLuF==0Gd2&&dNjjkvGOHZomK`rP&70K{v=3RiLkoOoEp)fA{7#|^xZ5l>9 z_@h+W44UJvy4cu4D17^XP!I3+;7))TY6@{eqvf$l@sYvjxCae-B{Uc;$FWQ+^O=!C18W= zKP2j&Az#$&E70&6!`~!|W&Pxm53TP9E6tbt`;iVv);9+Vm(DmLj(;=@tlOHkCehc2 zS6GwpzgTX2w<7(X^5;A#`4L6fJ$k(Q04tZ%G3oVfype>IZIjUR6DNg>?Q&8uHUq(x zHDgm1d<#lGjgHa-j1qgbIzD)C*cx-(*?1-7hV-3QUxqb=cW3zipTyR^UMhGbMiEe% zd)zB3ZA5SB;bfS&mn!IY)_+5TY}@kc58i{{%zCw1Wd;Ai-{7=PJ{9Tnmcf9TJu^5J zTau-9B99LW^>WQ5>CRlAtf+QYB)j(`3V3^WO34#ow2|y&%sODn3?D;Y{wt?|Q(7zU zpf(TLAaypWAghWwP3(_yV#vI;^k)CGSJ^Y-eUR6lUY`@lDZF{^70>@b~eFrJw{|^Sl z>nLx$gZK3kuP~_VE|-IJEtY219g=KB-&_|{s3*S&e5Sf4-Oiz1NJ;O@?^79IXi>m- zY{Jt`R;hw~YS3&tI8Hu?!JodMMuAUuN@Me2EaZeh;oA9-ir2;T(KRA|rSoq}!uq|? zQsLt)vgvvdo#`CU_!YsxuAVSLH#aw|LXl-&(tED}AD`%3#-O&b#M1lyId8JNPgbS! zPU9AZl@p`WTZeI)%qzEv9i#-jTlTUP&n%pdli9)TCl{J!V^?auZ$iJtL7vA|UeV{O zd+WZ9)6LBNd9|nD!3ReP->FA|J1c}`RFI003#beIU436Q8m zF&)oPsr@JdfzW-q%m*A3^roT~M2kHp5!=nDa}6-`jA^@`C>q7c#EoZ_pc;aYUAs4Q zkIU_&+U^_3(=5a~-~_5=+Zu~ybj>_1_ffd%zgiDM1sqnPwyly`Tm=l!F%d>I=JI1Q zrzurw7(rG>#x@AKAy{(ci?Q(h7h;qqe7hhdGj}54qB2imwtdz9{p463o52p3tznt7 z_cIF?0#d5&c`P-{%++y!4`srOyUY6flSh*>;6Q{UW|P1_a$ zneJ$dYfJ-6NZj@KAIOR4Cu{uJgUp3J?1r3e%Lp<+O=15)fA(|cf3#N zv3i`iE+j2D-uS4CX%i>?I@5LoXsRf#{Yv7&WzV-v1K))EIj zq~JptM3&&h7jMJuYy>(5I6!%!ocXuD7}5XpIj?nUMA0h%jZ`6=r&dY}X5;=ymRt*# z2U_Asm3@wpI%5kC}IEk xTT6222t(=e!#*0EwiWdfqr8T`y`dq#pcbsk37l92e` -- Update the `gen-ir` formulae url.tag & url.revision keys to match the release the previous step made -- Open a PR with these changes _and these changes only!_. - - If any other changes are detected, or more than one commit is made, homebrew's automation will fail -- When checks pass, add the `pr-pull` label to the PR -- Automation will make a new release +A release has been made, congratulations. However there's additional steps for distributing the release via `brew`. -Users can now run `brew update && brew upgrade` to update `gen-ir` and `brew install gen-ir` will install the latest version. +## Distributing a release + +Gen IR uses a Homebrew Tap for distribution. In order for the Tap to see the new release, you need to update the [Gen IR Formula](https://github.com/veracode/homebrew-tap/blob/main/Formula/gen-ir.rb). + +> Note: You may have to update more than one formula! If you're releasing a new major or minor version, you'll need to ensure versioning of the formula is correct. See the section [Versioning Tap Releases](#versioning-tap-releases) for more information. + +First, if you haven't already, checkout the `veracode/homebrew-tap` repo: + +```shell +git clone git@github.com:veracode/homebrew-tap.git +``` + +Then, do the following to increment the formula: + +- Create a new branch - replacing `` with the released version: + - `git checkout -b gen_ir_` +- Update the `gen-ir.rb` formula: + - Change `url.tag`'s value to the tag's name + - Change `url.revision` to the commit hash pointed to by the tag +- Open a merge request with _only these changes!_ + - If you have more than one commit, or change more than this single file - homebrews automation will refuse to merge the request. +- When the `test-bot` check passes, add the `pr-pull` label to the request +- Automation will make the new release + +Users can now run `brew update && brew upgrade gen-ir` to update to the latest version. + +## Versioning Tap Releases + +It is likely that you will need to do One More Thing, which is to ensure the formula is versioned correctly. + +Gen IR has the following policy on versions: + +- Gen IR will maintain formulae for one version behind _and_ any current prerelease versions +- Any versioned formulae **must** use `keg_only :versioned_formula` + - This means brew will _only_ install into the Cellar, and will not link into the brew prefix +- Gen IR _will not_ maintain formulae for patch versions + +So, if you have released a new major or minor version you should: + +- Create a new versioned formula for the previous release to yours +- Remove any now-deprecated formula(e) + +### Creating Versioned Formulae + +Creating a versioned formula can happen in one of two ways - I suggest using `brew extract` for this, but you can also manually find and copy the formula file, ensuring you add `keg_only :versioned_formula`. + +Rather unhelpfully, you can only use `brew extract` to extract a formula from one tap to another - not to an existing tap with the same formula. In this case you can substitute in any tap, but in this case I will use mine: `ninjalikescheez/tap` + +```shell +# assuming you're inside the veracode/homebrew-tap checkout +brew extract --version= veracode/tap/gen-ir ninjalikescheez/tap +cp /opt/homebrew/Library/Taps/ninjalikescheez/homebrew-tap/Formula/gen-ir@.rb Formula/ +``` + +Edit the file to add the `keg_only :versioned_formula` tag. + +TODO: Try this irl and see if the brew automation is able to auto-add the bottles or if we have to do it ourselves. From 7d4c767943bc15f2ae05f4155503972cd4774c0e Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 23 Oct 2023 11:04:59 +0200 Subject: [PATCH 02/61] Address linting issues --- .gitignore | 3 ++- .swiftlint.yml | 4 ++++ .../Tests/PBXProjParserTests/PBXProjParserTests.swift | 2 +- Sources/GenIR/BuildCacheManipulator.swift | 2 +- Sources/GenIR/Extensions/FileManager+Extension.swift | 2 ++ Sources/GenIR/Extensions/String+Extension.swift | 1 + Sources/GenIR/OutputPostprocessor.swift | 6 +----- Sources/GenIR/StdoutLogHandler.swift | 7 ++++--- Sources/GenIR/Targets/Targets.swift | 2 +- 9 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index cbd71e6..cc3876c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ output/ *.xcarchive/ *.bc *.dia -_build/ \ No newline at end of file +_build/ +**/.build/* \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index addd3ae..44b675c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -4,7 +4,11 @@ excluded: - .vscode/ - PBXProjParser/.build/ # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet - GenIRLogging/.build/ + - TestAssets/ line_length: warning: 150 ignores_comments: true + +disabled_rules: + - todo \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift b/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift index 5b33bf0..3d9d527 100644 --- a/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift +++ b/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift @@ -36,7 +36,7 @@ final class PBXProjParserTests: XCTestCase { "DoubleTargetTest": [], // TODO: should we also disregard Cocoapods doing their stupid bundle as a native target even though it isn't "MyBundle": ["MyBundle.bundle"], - "Pods-DoubleTargetTest": ["MyBundle.framework", "MyBundle.bundle"], + "Pods-DoubleTargetTest": ["MyBundle.framework", "MyBundle.bundle"] ] XCTAssert( diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index cb5e678..35def63 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -23,7 +23,7 @@ struct BuildCacheManipulator { self.buildCachePath = buildCachePath self.buildSettings = buildSettings buildProductsPath = archive - shouldDeploySkipInstallHack = buildSettings["SKIP_INSTALL"] == "NO" + shouldDeploySkipInstallHack = self.buildSettings["SKIP_INSTALL"] == "NO" guard FileManager.default.directoryExists(at: buildCachePath) else { throw Error.directoryNotFound("Build cache path doesn't exist at expected path: \(buildCachePath)") diff --git a/Sources/GenIR/Extensions/FileManager+Extension.swift b/Sources/GenIR/Extensions/FileManager+Extension.swift index 5f1a4b8..964df3f 100644 --- a/Sources/GenIR/Extensions/FileManager+Extension.swift +++ b/Sources/GenIR/Extensions/FileManager+Extension.swift @@ -155,7 +155,9 @@ extension FileManager { if let type = attributes[.type] as? FileAttributeType, type == .typeSymbolicLink { let destination = try destinationOfSymbolicLink(atPath: path.filePath) + // swiftlint:disable identifier_name let actualDestinationCausePathingSucksInFoundation = path.deletingLastPathComponent().appendingPathComponent(destination) + // swiftlint:enable identifier_name return fileExists(atPath: actualDestinationCausePathingSucksInFoundation.filePath) } diff --git a/Sources/GenIR/Extensions/String+Extension.swift b/Sources/GenIR/Extensions/String+Extension.swift index 1eb1774..ca6e499 100644 --- a/Sources/GenIR/Extensions/String+Extension.swift +++ b/Sources/GenIR/Extensions/String+Extension.swift @@ -31,6 +31,7 @@ extension String { return self[startIndex..() diff --git a/Sources/GenIR/StdoutLogHandler.swift b/Sources/GenIR/StdoutLogHandler.swift index 96e850a..f95a688 100644 --- a/Sources/GenIR/StdoutLogHandler.swift +++ b/Sources/GenIR/StdoutLogHandler.swift @@ -22,9 +22,10 @@ struct StdOutLogHandler: LogHandler { var logLevel: Logging.Logger.Level = .info - init(label: String) { } + init(_: String) { } // swiftlint:disable function_parameter_count + // periphery:ignore:parameters source func log( level: Logger.Level, message: Logger.Message, @@ -36,7 +37,7 @@ struct StdOutLogHandler: LogHandler { ) { let levelPrefix = prefix(for: level) let timestamp = timestamp(for: level) - let lineInfo = lineInfo(for: level, source: source, file: file, function: function, line: line) + let lineInfo = lineInfo(for: level, file: file, function: function, line: line) print("\(timestamp)\(lineInfo)\(levelPrefix)\(message)") } @@ -70,7 +71,7 @@ struct StdOutLogHandler: LogHandler { } } - private func lineInfo(for level: Logger.Level, source: String, file: String, function: String, line: UInt) -> String { + private func lineInfo(for level: Logger.Level, file: String, function: String, line: UInt) -> String { switch level { case .trace, .debug, .notice, .warning, .error, .critical: return "[\(file):\(line) \(function)] " diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift index 3d63854..32862d9 100644 --- a/Sources/GenIR/Targets/Targets.swift +++ b/Sources/GenIR/Targets/Targets.swift @@ -118,4 +118,4 @@ extension Targets: Collection { func makeIterator() -> CollectionType.Iterator { targets.makeIterator() } -} \ No newline at end of file +} From b865fd61e8dd4f0a82a35ba0b1b8e5fb557614dd Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 23 Oct 2023 11:05:18 +0200 Subject: [PATCH 03/61] Add workflows for linting --- .github/workflows/SwiftLint.yml | 18 ++++++++++++++++++ .github/workflows/build.yml | 5 +---- .github/workflows/periphery.yml | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/SwiftLint.yml create mode 100644 .github/workflows/periphery.yml diff --git a/.github/workflows/SwiftLint.yml b/.github/workflows/SwiftLint.yml new file mode 100644 index 0000000..12300cf --- /dev/null +++ b/.github/workflows/SwiftLint.yml @@ -0,0 +1,18 @@ +name: SwiftLint + +on: + pull_request: + paths: + - '.github/workflows/swiftlint.yml' + - '.swiftlint.yml' + - '**/*.swift' + +jobs: + SwiftLint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Lint changed files + uses: norio-nomura/action-swiftlint@3.2.1 + env: + DIFF_BASE: ${{ github.base_ref }} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 81350e9..8a13885 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,10 +7,7 @@ on: jobs: build: - runs-on: macos-12 - # This doesn't seem to work, even when the internet seems to indicate it should... - env: - DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer + runs-on: macos-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml new file mode 100644 index 0000000..8689e82 --- /dev/null +++ b/.github/workflows/periphery.yml @@ -0,0 +1,21 @@ +name: periphery + +on: + pull_request: + paths: + - '**/*.swift' + +jobs: + periphery-scan: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Homebrew + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master + - name: Install periphery + run: | + brew install peripheryapp/periphery/periphery + - name: Scan for unused code + run: | + periphery scan From a87ead8104e577776320adeee3e799f5abe87896 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 23 Oct 2023 11:05:45 +0200 Subject: [PATCH 04/61] Add drawio file for the branching model to make it easier to change in the future --- Documentation/files/branching_model.drawio | 235 +++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 Documentation/files/branching_model.drawio diff --git a/Documentation/files/branching_model.drawio b/Documentation/files/branching_model.drawio new file mode 100644 index 0000000..8b38980 --- /dev/null +++ b/Documentation/files/branching_model.drawio @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 98c8a30d1d3ae82449fb21fda606463e5886efe4 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 23 Oct 2023 11:11:28 +0200 Subject: [PATCH 05/61] Use macos-13, apparently it's still in beta and not -latest... --- .github/workflows/build.yml | 2 +- .github/workflows/periphery.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a13885..d4c66cc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ on: jobs: build: - runs-on: macos-latest + runs-on: macos-13 steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml index 8689e82..b645c49 100644 --- a/.github/workflows/periphery.yml +++ b/.github/workflows/periphery.yml @@ -7,7 +7,7 @@ on: jobs: periphery-scan: - runs-on: macos-latest + runs-on: macos-13 steps: - uses: actions/checkout@v1 - name: Set up Homebrew From b41c23ba8bbb2540d4b51b43385747bf79b19d98 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 23 Oct 2023 11:31:32 +0200 Subject: [PATCH 06/61] Remove brew extract command in favour of easier instructions --- Documentation/releasing_an_update.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Documentation/releasing_an_update.md b/Documentation/releasing_an_update.md index 3627bc1..df8f7ce 100644 --- a/Documentation/releasing_an_update.md +++ b/Documentation/releasing_an_update.md @@ -74,16 +74,8 @@ So, if you have released a new major or minor version you should: ### Creating Versioned Formulae -Creating a versioned formula can happen in one of two ways - I suggest using `brew extract` for this, but you can also manually find and copy the formula file, ensuring you add `keg_only :versioned_formula`. +Using the history of the `homebrew-tap` find the version of the Gen IR formula you're looking for, then copy the file to the `Formula` folder renaming it like so: `gen-ir@` -Rather unhelpfully, you can only use `brew extract` to extract a formula from one tap to another - not to an existing tap with the same formula. In this case you can substitute in any tap, but in this case I will use mine: `ninjalikescheez/tap` +Edit the file to add the `keg_only :versioned_formula` tag after the `bottle`. -```shell -# assuming you're inside the veracode/homebrew-tap checkout -brew extract --version= veracode/tap/gen-ir ninjalikescheez/tap -cp /opt/homebrew/Library/Taps/ninjalikescheez/homebrew-tap/Formula/gen-ir@.rb Formula/ -``` - -Edit the file to add the `keg_only :versioned_formula` tag. - -TODO: Try this irl and see if the brew automation is able to auto-add the bottles or if we have to do it ourselves. +> Note: it is a good idea to run `brew style Formulae/gen-ir@.rb` before you push the commit! Brew is _very_ particular about the layout of a formula and the test-bot will fail if your key isn't in the right spot. From e27bfe846b86ef2c9ee116cfe50530c8ae32d33a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 23 Oct 2023 14:52:04 +0200 Subject: [PATCH 07/61] Clean up documentation --- Documentation/branching_model.md | 20 +- Documentation/files/branching_model.drawio | 235 --------------------- Documentation/images/branching_model.png | Bin 52317 -> 0 bytes Documentation/releasing_an_update.md | 18 +- 4 files changed, 27 insertions(+), 246 deletions(-) delete mode 100644 Documentation/files/branching_model.drawio delete mode 100644 Documentation/images/branching_model.png diff --git a/Documentation/branching_model.md b/Documentation/branching_model.md index 5172fda..21484dd 100644 --- a/Documentation/branching_model.md +++ b/Documentation/branching_model.md @@ -2,13 +2,12 @@ ![Branching Model](images/branching_model.png) -Gen IR uses a modified version of [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) as the basis of it’s branching model. This model is tried, tested, and has remained so over 10+ years for a project of this type. +Gen IR uses the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. This model is tried, tested, and has remained so over 10+ years for a project of this type. Modifications to the model: -- `develop` becomes `unstable` -- No `hotfix` branch for now. We can just follow the regular flow -- Unstable releases are tagged on `unstable` if and when we want to make a new release +- No `hotfix` branch for now. We can just follow the regular release flow +- Unstable releases are tagged on `develop` - These should be set as `prelease` in the GitHub Release! ## Versioning @@ -19,11 +18,14 @@ Releases should increment this (see [Releasing an Update](releasing_an_update.md ## New Feature Development -- Create a new branch off `unstable` and name it appropriately - - `git checkout unstable && git pull && git checkout -b sensibly_named_branch` +- Create a new branch off `develop` and name it appropriately + - `git checkout develop && git pull && git checkout -b sensibly_named_branch` - Add your new feature commits -- Create a merge request to the `unstable` branch +- Create a merge request to the `develop` branch -### Unstable releases +## New Release -If a feature should be released for testing & early adopters, you will need to do some additional steps. See [Releasing an Update - Unstable](releasing_an_update.md#unstable) for more. +- Create a branch off `develop` and name it `release/` +- Test, QA, fix +- Merge to `main` and create a release +- Merge to `develop`, remove the `develop` tag and retag the new commit with `develop` diff --git a/Documentation/files/branching_model.drawio b/Documentation/files/branching_model.drawio deleted file mode 100644 index 8b38980..0000000 --- a/Documentation/files/branching_model.drawio +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Documentation/images/branching_model.png b/Documentation/images/branching_model.png deleted file mode 100644 index a277abb4443187629249a931ed68d4009d9a9f13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52317 zcmeEP1zc3y)(51eQ(B~vZlpm#>6Gr47&;|H=@N?;P$_9Ck#0o<3rPtn=|O6M0lqWB zcv0_t*LUmb_Z~lmIs5F{wbuHt*lV8?p{62_jY*D)fPjE~{@hs&1Oy}~@cAA(D)4)Q zrt>NSg6}DJSzUKWZ)-aTD+C5!nS)OZJY2RQH+Kf!vkW{uX3owWmUd<~u4ayI98Om5 zKoM}?5oBp+ZD(b9P=|+$hnJn3i=Br{i<^sqSBh5v_{Go7!OJ74e^B4d#>(liL1mDy zor8lJ1CJa(2N%#3v$C0mos&Dr&6a^z8n{01RCxFJ0h2Hx~yt|3M8` zPoQ~jE@2KX7T|`wm4!VBXbn~c_{R+t$=SJDS%bWRLT+v$SSPR{Te+Lr91Pb{&q6UmKN!I-XXpZgwE2!&&igaB=V-+;I1CwmK-awDPpGu!5EH$}(_C z0S&|c@yZ;o5pZF5xGFws-aZQYLQ={mw#pt#yxLCAX2J)pIhi>gwq|4JZtVc_`noxI zqmJJ4u4c}*${@>6OJeDLFa{w$V3x2sS^69l@bMmw)5aCv`(X`Ofu^1BVRry^K7G%| z!_Lym?clqI72QD~2X{N?FAFU|PEJ-9hlD$cYyLRRukes93HIpIV(FGTX@d^We*1KXa=u*Sm0)BW(lm{r|*9%I!cv; zWrQ!LD+mO%cZ6t1Z^J7&ddpZjz}OGIj)(mKy!7vX_w#DFe&+r^(;V-?8hDyHcpR?d zL4ljQ4}4**oGhha^Z^#c3FHLSlD0H+v;8Ee+`xscyQ2ee3-%EZc?TJg1IYE$+VNVN zSqWKN0OfA(t{{7>BXG4amvNLH3tY zX;+YkljUb7{-ur*2;_Xo&KIrR-F*&$&dkFd^d+-@Aq3yb2v8yihy&0RfaKpu2EUb% zCEu?}h7gZAuK=LxzE3jvz7-6fLjn5plJU2&Jb)x-FtLMkv@=Wq++frA*JMA+#s{B& zMSS?alpbza#Xlk?0OuZz^Lxhh4>I~8xxWBw0S5pPr7i6|8F&P2U>~_S_&5MmKPU%g za^%*xV&dQcsAm|Yd)eB#TWLC*eZpKXz(4@Xjv%|4oAZIrwzl(z3C%YQ{rgaF4RUga zizG~0IGCARIjDi$?A#BmBMU&ESpk|u8ZemL?Et;5^i>6Sm|#kqIoR32jHz#`OC8=Y z2f4e09AQHQIy|CtxVQuaWMqKygH`wyodfIG>JW)PNzwO_{|Lx^26EmnIZ@#7@`$p5 z^X5@y122OM$5FikyZ8I%!Y9oAV<>v4QU5h_IhxryeJ5`LO9^AgBTV$$X25S54!=um zM>|VPn4R*g{QC>U!yx#dAiKa<+VofG4zKaU&>e1F{cFMlUJK0W`5&tMCsE~VW9?Vi z3l1Sa4txC!9)`dHI2?5_;9~LT?WiN55BDaHQU_l4x0qYsdKf}qYNmg{!+>}38$QiX z;b9yy@qdShaa0U=4sFgqF9!b%fAfFEZJ&JrxJ~tk*$sxQfF1Fz)BZIof79h3_S*kJ z*Fx|o;Bc4&aS(QdMTEb$@8#sAVZ{H&HL>Ehf>JKZCQ$&3zbE_>tNEZ!@ideJ)oY-NTLHPZxTy3m`3sSNhZCS=b&F%#%I1f>HX@ zmA9Qc>}#;iGb`Zpk#7N4zkCUH0sqc#Zh9OQJGkfegNohXYO?dfvw&bL2CUZS;LX41 z#=@i!pwYL%p>Nv%M|^_c91f3=Fo&>!@ZpaT*HR!;~v)0r#@ddGS!~Mq}RA&BG%bO1#ID^AG-1z*9EbqUl{ZD(a z-@ifio0j))4hL?A{9()IAKWEAj8=VNg?tlNh9#f^{{8<_#r~tIVsK;WP`ZAGR5AF0 zqc7O&>%jGY`Y_62kCADZ#VCj_xYJ%l?^68a}{T`{jrGOS1)@WdjGV zKWw%9CSHD>O8a$>e;6*|J^r?GgVp%GRBL!EK+Plh-~T#c{1B-BV0!k?6*B#li1TFv z{@#BObA? z;N(4OUBS!XB>zV|h#ghD?7;e-zZ^Z#rl?2Cxj@5QG+9lSiuoBj_Z ziNf{BQL{|&pU<2A9!2w~%rf{q|C>da_p7J__s<~0pK<5EQilIlgBmX9M`izK+b81t zIzt2=w}u-Ef7mvJK@u!!za9ReR{i(vZunTgvFpESA6D~kx9k5wvl`CB|JAtu zwR8A0;JVOZ?B_>@^)H<$;Go)H@>rOk4r1Yu3b*{>81k1v?6HAlly9^U02j%fdb z&;*ylBhX}RW#;bT3Y_WSGIup|vakg{9NOAP#Na!Qa(q4;`PF{;XFva^Kg#hdVS&$< z;13fQ5c+n<^;anTEztds!ayo7Ts)2hettX(11DDw?4={}3EQpE{E?nsb|m)q4@D4; zdJLbB7XO30INu*p|C{Z1ez;qdB-;or00KWU}EAdTgl;qY>Cz@zA&{vLAk&!k4V z1^ySr`<2I`eJO&6S@!=EK|rzmXU}l_zZ604|7pQr5y1l|{K1q zCvcDX9~8as;h#f{{bPxfpC@`GO7^YC{g>p$eDaOI1i;VnvTx3pe2%ak4CYs~|6iCF z^9@G*O7sg38b3_%iyNNv`@hi0!P)dfwmtf41c5`p>x zg5&j(2(to4sdAPnY2)-vI4SNE~=U1+Lej5P~ zT#>?Y1MV;U$hh(AyeD+HoB0Lr!O~6sSsUSxW=(zJA$+y9`W?IfznAKHU?6;@_kV?z zfaN-3L>!t6M@@*|So}|^J%4}?zx(ja_p}YuaDRK+6hB;#!bJvt{k`5Ue3U@_zhc`3 z-u>Zl;0MRyF@-`CqXy57!Y4`E+LaKv{8on=b*pz3nf0P0s%m)4^NU z4&So#4U_%Eis^gabMgOUU;n!l)t`ID-Qm;l4xe%df8yQ0ddA(qtMuUS_xKL2_bI*R zdo+NvEPpP8#?eX zXY30d2)yD6?y!ArQvXi_etJ6OZ^UZ<05}|Kz;6PxKad9e6n6iOH{8OvJ%4$X@X>c-Wcl~ZnE37a6^?<73YO&PdPAU!|&jKpc|n0?75Ld27H z7#WwM(jips7LT;vD}V0ClJ?{zB*pGRuaKh>aR!b=&uWF_gZ}u!egz?9GZP#LR>+x+ z=m?ryZI9C&02lrLGoyex%OnFs{dbPLu4twfm zH9sT}7wQ+{=^%Nkv?C}0Z+lAgMy3J^!{JOaB7b{W zXw9Vd+0XP2Q%_iXT4eGuFAOV$aOwv7Nkk&3jZ3Jp1`hozPN_Wa(3v|H9pUm#%V4t1 z%o)EiP|Bmxq-#BTV2L&yZDhwm^Gm5yVRaBMVjGA(zRTxz9Xp1Y*Q4)WKfz>>tS`j*rPc6zS9J5ZyCvnX`Ge;pH6#u>~f)$VI*OiaXNay`r5Tjw|meOdPEFN=q59` zaNM&NrAn=DhmE7Cts75YH8Q<3PU}%D3aF$7*{oxwZ8=2!NtmhOT2FEM8d+VmERRq= z=WLA2(tE5x*&FmqA&iHFy+#PE@WG@{&wZR&3x7yIms+Rxv>uOSM|h7R!?TG$R1dby zX9PPN&J&o5L)Do(qwazXgs)}uj8+m6ePCgJQ7B%$x~mGc*`+noN4?$azcw0!Xv0Y@ zqQP*7;h4a=eofzrGjFD5JT^}F((y?dJ;dQZZ})~VMgp=D_axiRwTyJbiedsu zK8srxiQt6uAaOxgk>yHN99ou@JcG;dpHMq~}M;j>H6p zNDztP&|90_6^MA2ZQ@#1Rlae|LP~Cm%Lr+|&Mu)YR|bisqtp}!(t)=MgU!rAuIV2 ztOOcSQv}B)mQ`V%EASl{(i6DFl*7+v>PU8jS=F<&v{HyT;o2po_y@yyqLsbu&F4Rl z3SB;^-6Vi5f<_`Bxt$u{n`>f#@^d%7N5nRc{ru; zNE(qsafy?bh3xuBPGcGqXi~j+q|TdSe{aoUG}}K{(TWRcPpr>^%5`y?n5t3LMnRfeh_ z!``mX+ShA1#~@i~^V89`Z*>G^v$f!>aY6vraW4(97OGGB+fPDByD@@tRH>W0ww{HY zrORcHv4O~U3F~I*s&^QPHRNgE2E_{OxlJ{_?sIV@-KnU3)<@FD1RqG(r-9Ub8i)nO zm9fB&5XAfROfp-M+E?4@62+dQ><`P_Ab-O~_9FL!-AIzK=gMU$G{y!|>YjnpjAJww z9I3g?k?4RqX#-7>iZ{{C8-Pu!y-`VeQc!#Cdqc3tpJiI7ZF9Ytmq$+($Dkf*ICe}_ zz7d?xc0ytnFyf-zMH*7eKOS;IPlm#4?8_1I z?S0HQgi-88={h?^m2v?0*t;RAnr^WS3mq@?L@?di1|O7$J(4HH9&_{(LayVqkBHc? zp3mP#9DPZcxj~8z4mCZcRbWWPh};!|QnowJ(*1F##}KRHnXu%*$6Kba-fWfl?Vv7M zkIl6pmprT_AvdaDuw#P+8avZ3g>kG?^^5OZ!mU)@HxcRX=-yeex2dmW7Kh891tW|r z2N?pO5DY2XuC*XccH2>rNHIauo3h zOB5_NQy*J)UL9oM7|sda?@`{#P`^QP?HV4g$7!LKrF(sAOH21k$I?&u&g}&it^4dO z+X$pyi(#_3q4_HH(Y~T&rLC4?;D$$Q?G;T)44yNpl(}MR4H@$YMra?-An@cM2}qy| z?-dOOOgkD%yS86V!H8~c zvh)5{O6Ev{Nz;3Y?4}1LDA&TJ>upO3)N0SpQljijFztuDvO4X&a+;Vw%yFkjrgyF) zq+X{pK`aJkKNls;V#lj!+1AGXRxz?0rV=l-7XSL8e&)hQf>G1amBHI7X2@oG^)UuTtF8!&+Oc#z zUvt$CM(rVDPvZ8lK+`O8>Ha1%GnbICZ-)IH!4k~qOe}?bVQSv^HmYSsj4lzNrSE@L>Q1lg~e$i8fK$PbJ=10 zl+(+loZ$3#^jdir6m8wN9_ExP_JAdDv^ZwxYHMs#l52M%f>8ghm#=L=1}Jg+(*gCa zs+S`Ul|bSN7;3v4pE3~S!e75Q7v$*D&Xxl+m3p%3sLp2kGfe)LG6(MXo(T?YW zO`f>U50Y>;_3+GBfzNeaTf$Cb?|S|*ZFBMspKkTK(%Nvm32B#?Wh7iHHD1Md0eM#Z z5vD+-+ZdZTiJsOjf=Qq)IW!&|8vS#=#hw-KKGJO`^^dM?-pZXRo_G)%YfqSy@?L}W zEZow+f)LXHor?$b1e2Bn0aQBv&BHO9X}z4%3e6F_ZCT;IIbyAvO)kMZgbCDVJD3;~ zm`aONBH`HD2tkJpLFLmBT7e-T%k~O*_keAb&Aaa2vp1r2pr|JHWY*8Ll(OF-F|JgV z7_yOA@y8tYfyz;@pV@00@;cA-#Ky#9N(~*ZCFbxlR&=Nos>5gjsa}at4o=4vTc5yW zh3GvLzl?VJ+|BF8DxMcim>6;dSWWY_7#Yvgzr8?`^PX@j>@~wQe$&0=mrkN{efS~t zhlrhVy)kE^*dp_E=cB#~Td4MV4myMtO~RXvO^IWq{q#xCLJ*t+In~i(^)HL_6XDGk zrVcY`-Fq}=8QBa#bw;veIM1XP19Od{q^DNWEk&Zia7-kXxk zw|z1r-Zls4UfhGbd=ZU-$hVmjqNT>gcL*7!sv`oh0+Ct-t;by?3q=fhpyn!~z1Z>| zQpidXL5bTbw6P6yA6uNSfX`GwZ&Z39727L6yrbDijS-+Zm!)+<8<~#)+92zzn)HU@ zjma&S?KFVv7V#Pf3R7&WF}uNj?-90;8y;w>NjqIy9Ba|N~Tt$*uRG0dm;*`Y723BVhE&hvhU5`w3iy%6;Wv94oXpV)s5-nUNL?_pcToWh(ZI_DF5?1Nh^cX->XvKs-= z6|dZFEyhzBkqGO8+E+jB85sqfaH7(mi|NZphAOo+^m@NbzDh_oP`P!XkGjE7)8Uk< zAz;K&==f{cDFp>7vG95w6BIZhO>|c83H?JpN`wIfJ%s(p@v#%PA`pAR#=Jn3d=wck z(nHeOT?wGoAuV88a4~*a*p(6_n|DY`2;9W+=$GgvcVkQ}p5RKj_X!uonW(j-mYVS+v*ek@1cynWzAu2!fF0$RH}W&i!WYx>St zEzj~{dOFEpY*BF~%Cn(0?i7h>sVdIL2OlE65%<%t5>muEnA4S^x|M|mOCS|T zE%_J~W5uUWvnQAnT;4~wrH5dJoRk{$e@2%bT4t$)^omirw2eaW@-;83pK()hY zQgR!e1524AILF%XE=B60pT1%=Gsz5DT-itIU!RkCL|Gi@V1ZO?+}kF~qa_W|8mFs_GZ8|kR(8mt;H;! z%kZBKc+h#afe_IHwg|gbe*0&#>_2FE5M*!xAcwAc@*J6^4XSmH%gKU`rfp5ALr@vm zTBxPX8Y(eViqO?C(sOq(V&PAsTbhb(?WJ> zjc1Zem-UKust2Au4q&Mwu?~k~CF{BI}RUWVW6!G(;Uo|;{-3%z<#HhyfAv1i^lKXO@+JTFE|nP-8J?si_dxWhAjta@+ zt}LrRRZG6fu%X4>=c%cw~jWhR0 zxA>SKJG4Yg?7S%ZISuy;IlQ}(S{$d{Q4thd7F$j)7g`eNC2uIm-;M~{Lk}+m`Fl6L z+3aV!!i&<78Pi~HanTmK)YE2i^?Q58)?Tthr~=+mX~jYaHfT?wHlrl}*>Fxl9;D@z4+NTgT5*r7I2zDcAv)Lkh&*K82||#o@GYopE}uIZ}lPA#6jsiIDZaht)>*l z^{-jIEF1wFP3C0K-CdoonPo`FrOiM|mwEz;{3%*Ul0mE+(OCwPkh{y=sls~=BzrM? zCn43cHG0VVCu(Uwgpcc&=Ab}?(In#h!{viV@yIcz)aj76mkj#lO=>P1Al6F7J!5Ok z2_AjXZsO0oyB2XjbLDj|ttS1cYsUbdm52vy2gyKWwZu576)_4~2zFN+K2$tmaj5qa3E}REY_5R79VBBSIkQqe!KS#Gp@~sJNEKmAikav}V58ptb%&%I(0t%eZJ-x@^{sYS{1hQ#`=tBD=hRzm1cdj(tH{6L*;>3#CR{+RDUbR zC0%4MGVYu04Maqf#p?7#9n148ZXVO(&_}j?5^gU!2)7^z6(^U#mt4sDM*g`AK$T|I6H*4 zADTIayLA#GGPY$@U^1bfQWP3$JgSsbb2pRw9+Uvl5f27J4x1l!+*y|5YCM?B<5?a8 zF^r&#NJ7^hPgMkMw3rK5D)ta#>7>*(+B~upS{>KfQM(m77|MJSpX(&jgTXrSo!gnQ z4FDSELa}mp)Go-vz;c;pHtj)qjU%1CPk7}Fp4UnV3?rb@1t*a- zN8olpFb2TN4r0LvI#sApwt|O`KbhWK!bs#8d!v;;s>yHI*92wmF+@@7%AP7+u(C1h z;}aIE&b;MwIdQTanN-m&Mf3dA<=FE?vlNUT2HhK{bLi5Mvlq$r+4+rTjT>L>mYOsd&cX^fxZOb(fN}#*pwbJUJ20((y|<%0MD}1zrc8D~`BP zbG4LonQTf58huZHfFdU59{qdHwn>i6i*`O0NdEeU1y6D?W%ZBgRZaD@#hs!H*lHu#gcIK(8DP6;t;zOv`4`eEsKL$^`0!6CCQx&;@EDbiven?s=w57-thD4~Bv(3Kg>(WN`|^hZa0jUokk;gBf1N z0gFSkG^lb5xuFunroZp;Sf4UCB!|Z_2c$H^S(~~_x52AvbvLkYY6{fuWvCk-s1p4w zSOep_U5)>!zk%1IG)4wR6+c|RA}ASu=0vl3a%+x|4`GInWlU97bw({EDJcR|7$cwnXZpBqnO51IxkoFL7yP>FW(HqR%(r{!zR=O)FPQ04 z8s&H0AAAj4c^tVO(9rLpc7a#GfOT8dR10lZ5LGhCz(#V zIy+j{t!9V>VDIYm0qeL?>9BzHg%onHSL)dt3?i+e7UGrWG-xUYWmfyh%p-s6o<`W zM?1Pj-Xdk&)NaKj`QSZ~S|)7|dpxx|Z@Q6z#~&vXp&L=Fh#z=`)2g`XxVZdh!W$Kw z*5$QXE~Ip`6__0*ILX#ADVF zUK9yGeXgNE9MXpK&x%b8_H{YL9I4I_LQ_A)t?7r5X3~^vpHN=tekWoLx`#-g}IT=ruGmoySs#U#};-QykeY0Y6yXV z4?^xcO}ZyB2o>1lL!lf=s1BR@+|~B|C&v{tC8AnY0~b8vA66mAFce;r#)!Z;nbvfCu+E2zx&+kGf(XQ!9`8V={U6NK=A)9i0=F;K`s5;59BX$C z4%~f!eqyBoKheo0kv-+usQX21!fX`?GlVLYGQ;ON$qUdq3EL<^yk}Q#%ScO~N~o{I zNyFSL2!XJ_+A#>xuw%S+u2^Y~z*XJ=Ma#--PG$6wCqJ4{iT}nHwL|(|Lvc}T+6%jQ zUK4|Myudk}^!u?Ms6)ER2CEz@=B|_rHT4_oS7W0fqe&~>G2;{&o*;-vZXp4{Mt2G- z6FWV9wXffoQEt3Ulpf%OCqDDOy&*z|W?!GKig0#2E`(E8mh-lQX{Q z($F$>T*q`UI4?E4!F&3YR-qoT3YEz2%PgiPN6J)T7m`8uZB+A-R!QeEf=51!j0Gcm zq)d?gI?vgUcu)v?1=Mqg#PNi^h<#rDGq4%a2pmyP{)~ zyzS2CU1Nmo(uH}(2bm+(8yDU_dv^_hl38_B)Xhbc2%6#J3C_nlXuu4JwjMoJ>8Gl#Fcvr{ic641<0RC&Vc%=?IDVvy-+}UU zf<_*tV3vKqQc>;2J37YTL<5M?n`-wVdYioAY%TZImejNdffY%s4lmkt9OpL(?>p(< z*)W7^-bo~X_HID0j;+?dpl^BjMq<+GjztyEfFVN`?QC-vMjI#m4>Ys<#z@!W6KNPC zZ;h%KAiM2eLNj)`r2twGnT510uoy{SxWv4Qz94ahUcu^&N()=!u&^b+S3rjvC`)<( zSH$4VUL~@P03A=W-}?%dHJL7WE`aQyW^g-L|FMI{Ovxh6g171Qv=yZMo97q;mFI|> zg)O#0>vOUDptwb5Do4jgDCOk#B2ICULdboFJ`h8sD+@*kRVLcsXCo)%E<}PeMMWKn{id|iP;pI$OQLHi>`M&S#?OjlQ@Y`%FA}| z?a!LJj?=O0Pi%ok1;oU4E;3LJS1hSDF|t>V^KT}Kt?KA7*6R+NX zQ+~c1g|`Zd?y`sj-3e43>?KzVKAn%yapzh72xE4ui^o;5520Fj6N4Sy?&0}N4OTsA zKfMT!+8q<2tq#?v4_dJRF}4?dJYKU`QhBD@?E?+X_OlZUQFH_AXnl0ud){@CYa4AP zGS%}Zm`RmpAG_;W@3-|_ChOxyf4Y!U&@i!{i<=!EOI(FnJewk3UT54d-yq97s}U4_ z-l)aXdHtHp?rdw3qIbY)W17A0T$-L(%&hxjUQ{=j=~lKYm#?I$5QIFWt8qG)b$v0Z zPjEn>xX9ao{<7V}^9wOaeKK#%>JW+QfU)enPwtv$@-jmD(36&+pkrAx!b&W|QzY?f ztf0?^tpWFTFBT_DpykJ=`H>T%f{``RmtC!=*CkBDHj&ksJIutnjjRy%L}e&AN4DKV z_Z`;^Z1w8~I>`1)#V5v|RJ0hz)p~E7x-^<4VHoIwZ7dpyXc4;GjInz?OR+I1^H`%r z37X~lxaxV8%hx2N$BIjOuZ8oj%0>~RR1rAQpfr?4@kUdMTrcxSTwGfoq~Q=fgX^K# zH(}`8uPh01RT;4ssiYok7885Xo+;RrZeZ3wq&xnC)?o-iQ=d|yqr0ijRnK51XL-Ya zs8TLao)Uc)pacQtHicv6udp1Dol$~Xl&5av9c)V{_b6w)e?n9|JH!t z4(T~1-m)sLvYYqylR9kbZ7$bQyw&=9H)YJ{FFO025}?^MirDw*ZdRm6B1 z`zvSFi9rPYK_2)m`_Ixw$MtYg+>D%pj?F%OY9!4OC^U+-ZxZLP!#s{2+Hh@V>q%w| zx20pPZJWg%j&TIvT^aFt1%~Sv6=Z$S)F({V7HIZ6E)yum$59bS}mioAZD`V{QEi@g9tQ_=)?}kV-#u08S?W0C>`c7Bw(QfD^ult2GW2H)R*jHQQ$r}>aJ%W2Vv=#RQ6Sr^SLx6}N0yX+gjku!A*Zc*-ZkrQvAPvzFJ#1}$+{-{ zU-XS6AiBMLO@<&LJ>2-VTJE*1mifNfWnXR8+e_YeijPwjrjCzR+0#JzAU5Is zHPjE@RvZokz2cHO{ib&$Q}(!|3!UZ{xR^5k&H1*hrh@}R{r zx0%rzwK5g#l9KWl3Zq16V|@xNt6XPhmS6gj-&%gtt%pFfR~5lIzGn1syr)&nCT_I8 znAO96(RRMGM*rS~Nn_GZjaQtVU=Jw>MiZ zw>R4a>>J3%j*dm;%vYL$8{aS^aBCkUh6h` zhj$1m>o%2ol#Y~)oR9&!T2?3QfP7;lrfF|#ooXXr4#+)#!LGVT^2T0pKiCfr-iw1mSSr0!OYYZ*wkRx z2nD4rZrCnogji)IvczdG@UW>apGdgOiwYgm{(4%CtT73HC>B=xyG#8=XBKSZc9zhP zQ5ylDC}T#ju5sig^#uogoMVr%gHE^;n#+$$`J2r)XPrzD3t63N2-re3tZ|RoDdZUu zy;^{cXrnXZumkS7{(Kq=85q}pL2S*Lgo2$S@J8PwsdmgJZqJBPaoLHsQ5=c}a=1j4 zzV@gW(*11NzOWtZNr^+1P2nklEP+6yEVN~R2<~vWHzj-E$l13pvzo`GGgb>Ywr7h4 z2;6u$>yhZepOuU@QmcpIVAboN212F1-RcdD^nSOj-a%;lucNj z%rt>y?^%NPgpUeE6mPR&R905f&~f#pSo-jI@iyK$De413j=aUz{=Boyy`Qb2Lb+Xz zfsJw~xlA|0Jhj+f1-a&^ninRIUf>o7AMgsx@-u1_>qS-sNo;`)S zRk+9=f4^ZhX=m3-TlTF-O$QV1y%)uG;i{$*PP9B!+e{_iYspXC^R}Yn%lv41k|~U; zuy=2~2*3$vk!Xiyd2n~UtsEnD8}LXHB!JS`st(Okyv~cw;pM>S65M~YX4&s=CA$W} zi7GEk(5QE#FGAP!+G_XOG0&yqxX|gTMahK4JM6>S3T8K!hXYJ+yt9}i9N23%k5LXb5#*N%mN;(X$&JWcCc>}C=ce6J z7goJu=;te{Zyv94r!`;%`*uV?zWXXgg+johezqF_t-_s=XQ!Qa2O@Ny&azmYU&wD= z=Wcxea-7a_;YJJUQ&g9sr+IE_H9BF}?LspFuf2{?Ge8NpwQL>@>#;&QSOTq^*K_~T z+mXTQ_Ea$;%i;I(j%Xf_#v)if2`vUfG=L~YLMI$`6Z5>j4 z9XZWskspay;ir97Y10b`ZuNrk+I*5up)-0GlboOpTCw%7oY!r{@ z-#GttCG7sIwRJ)fcE9`aE`)LA-dd2o@zw3hCc#`+PQlLQ6}y_Mhb8_|McUnOmR1#C zaW!+26D}=@CMJ2T!0bQmo5**}qR_yCwa>f(98*l`RP)S|48%)3bXjCwlx3~|ZuAc7 zrhDnsbE4HN;?TU}#di*t4{zIL>jsqGN@^!7?M0soLr-Kxyg^dqPAl+SPBn|M-SqPZs}Yu3~VpW-4u zmg>S~UkuBVtr@gYVgXvrNCwgY2XAY7oX*5MdlDCko~x}_G*`S;n}#0JrcHB`;*{wF zWF-Ue>m`ir!KJ)mBm!W>ZLT&?b#4D2HoWAML@ zk*Qe=&tC!-U&mj~3D}P$;BstItyxRFSOf6VYYs6~Z6n_vws$Az>M$Q#1(7U9=8H26 zo^8~^0CFCUe3dE|HLFohNg`>UyoQvNxs8W@UlPEbvK1Qdqcr4QICE>L1pNhvSg5wiO#}Fl&FLh8-5!4FEn1{> zybLn;iv)1`gd@`!0p7w=h8+341lnn#nZQf&e}b| z4(e1!h5&SU3S-Ya@vBo$`^X8QeBI!zb1MK7jVU(10aC2Q-NXjLAwWqG3n7e22Z3b) z$jS_Xt?4b;nw}@%vTFie=T`|45JDOrs<;q zTy-$Z#xU5h4Y7Pa+-h08z>}oZgm$Wp0cnsPSdz+}S9C>ZvRvdouVguj1V=V2 zq_-3+OV9lPC>Y&BMKJcl@Hcg!s#XrB(D#Wo@I15>B6<;j~RQVc#@m*y)k`zy~Lt$oehndCX>B1f$3uX z>#^I?$@B)I=nIAccz_)46pC3hst1U;7j;ljQ~JnaZ!_G@%@Q6ic$<4yGXhgqd(d}n znfH;;w6VK>?fvLw^G(&*b|-^)b%Q6M4+bvrBD!5g=9Zi2LX^T1&2DPl z8dFs{j|#Z5{r&xsMUt2yNd4*6)zvDi*;=f$R7tjUM!>uu`8D%hdg>J1GaT^RT|Ylj zxBt=lkUd}CaF%ZMDRQRWvF#i8YZ7Qc;M764jCQ$b(Yu*$#tT)9>P6+u9|U`L>*95Y ziB(U+1WxiK3eUYB7rBg6w$HIv2Z{$Xqz}@7TPF;9#@|-&%!}r%&*fJ{Umw3j_(Tz0 zJVb=k@H`c%VULGQ)OWq7%F(l|#G}Ld!(e901*a30%Y&Ht%{qUL;UwG#mJF1{5GML35afiJ-=C#-4 zX0b%t2a!Gt4!{w#4icKrmOgJf7h3i>Pt8A9Q9*EgjlcUtwtI3;@KWRr)&OFD zONJS7KH^2)suy^oL_O8uSp|iTsrm;bA-{3HHxJa&LUC6@P}j*3Ys`dvrjfM@4Y6-? z7-EPAjNnx|qcH6y#nfN%ZH^r4#asU<%!ZkSYx3s7aRX0>V6l|VmaKeIvpI*rNBdyv zOYiQZC{fSjcB^mR%E#1*i1R>*4{3(EJF)OQmc^gN!TsWJn3{@JqOdm%M6A3`O#(fdG0CjA?kn$5lo!A29LYlfm4Qs!nx?H|k3<0b zOuVMKsV>%mco*7y(uJvYurd|SnMUT{{=`*b_3dOX>JU-E(>V?!22~z0m9a$|wPoZx z(^Q6{-u^w3dT}_lG4Y5}=#z{P8qvqN#&l>>HE$QqBuAy3C*ES85GtU7s6wb%Z{|O# zE?<*XhrV`}upP)_;oE-5y|?ezU+;IC@{BLS-hd~1>>VMj-MSs=Cr~S})jL$RDm`VP z$D4dNS=BM$kF-e@wrf8c*q2wOmgXdSH`RGFEb~gJ%sh!>X27aGSIH>ehyF@lHa%)& z42+eM@t(qTjJ@~?{u|DotmmDT79jcpd7W$nQtHNg9k`i`RqWNPRtgEn_GfQ^ z8E0&mwMw`K=dc$WW)YU&?KErDVl?MmDRFAcHk`ljdnK~O)(QPN*#6bU(s-5jnPMvj zg;|MW$|$)fmh7+jN^j_-GVB{9tSP4#JFpe7d8epE2I9(B-SJ-(yXR##(jMIvMC)w5 zjQZqKCP}w<1u_F0a>QggD5C>UCJ$#Yt3&N#dK`iKFSEkfw+jbyUGD_%gO>=^vbFS0#ys)*TC za+dSP3#)4iS+|o2)-;(02QGe$0c*D~Ub~ivpp?2C zCyJC(=@{fj+H-VS#~OS#sQU*8o26=-$hdu(4JF*dkQ=z3^VHlz zq06|St_slQxKEfZcZB@iB@i(%`Rihr^8Rd$;8p25<+v6;n& zbiIx+fjiqmI0`oj^rm0?ZCsUQQSa0hI#Voy6Yb-~Inv1r0n!@Xil6%S6PHY!T}rpt zX@-tfgBiE6XOPw)Pw8$2r$28Ed|BQOgyp4Lb^?jIZshDmM!6&edg?*^=B2ugm{x&B zY@5p}VYr0m#mdf(-PyUdpwm$Ko)A=zcbG}f0l@Ui)-gxjoe;3TWpoob%-g?)C>$JDpxv!bmby|tb5 z^XjKle4JuhsMUvCP8u&BY^RJ+>&rxnjZv$$hQn8 zhWag0KOVRW1WFNknOfSOk(!RDGN|3v#c&dn%EG)Qfw`xobFUzHKmCfIi+{7W(>p{F z4wJgpLP1kOEIqGT+NNT?p=aC|rp;Lql^$D3Y=Gjya%>lr$a1zQ-!7R*EOStLOJZqo&QhAn7)hfi{*&W})2UUcvVf*W7 zGy#Dh$NOsKuEyjQjoF;KG;`;iZbX#B@r*Gl&K;D3!a}nr2%rydbLg0c(^6B-I^RT; zcurhYanHG*?HaJ%l~ZrNrJ#1ti~+p9hmU%fslcI~zFSB9b(q9F)_(j%n9%5cxVAxt z0LN8qJ^W}@{0oh<$4JW-{nKY(5k58*=CgZ)W!;^EiAyaO)N3|xO3Q)NeWpd?Nsh!# zHsi>YLYt^)KU^pd>gFaA&G6;$>a|IF$N=G$`YV3Ttiwh*m#Q(rbvh=7I$S4)BOk?& zkyeuJ*(jTkzJF1Q@Aj;ZV~VMjL#$8rol1cB?f~*|+DSEoRIFYOm$%B#;~U>#+jwYZ zZhyHrD<3KdPru<M3T>dajBM^+`Ct zbojqY&NHluu3N((NL4!0i?kqJP(fPgARUn=AfZSNUAnX=NJpdzN>v1<1PDZW2%#uQ z5Ru+{?;s^WzTthpcK)2}ocx~b*;%t!_FDITo)KfWWNMvo8#(DR;F^WaY$Hxu@rpW~ zC%cFb+!Y+=5^~Jhcc9tjD}ea&LESvHFUU@6Tlf8N*VVRom1WE%E?_2^CB9B(L%>ev zbQ6K;=wM{(LoQ83zr|691n+*Hagw`w`=|g9FcW`}U6*v-TNy!#m*YjR9FL=*Q5MkE zb)$8zI#h$D(Q>cK1}0o&?`(d>sY)-*>iFDP)K=0Q(QYAaqItzw`u(6@l>e&H|5!b{ z2H__Dx3;Pqy+Iea9mG+8drOSlq7-~OwAWYdb`)R!nqH&DfSvg3;M22PA7_Trp=NBz z{CoQl#XbEb>;#W#P&NCT)KqV2cFnEjx;k^OXV2zZQ1Rhip|T@#B_bNnbnj4{3>!8) zVyQ${WADm`jEjxK5X@qqYAe5mrZOTImw-~n`xA;UGr)10e7^&^`C~nhjqL?xc&}oH z=F`p~Vu=$b&s|8Doa@SQjOblfvomC1P_GTEH-2pX=m;+VMB2d>j&722_(&3i_#>R* zzuK>8uN_Io%o|xWFI`bOkq_N|m>%b1)uf46d@;Vf{VGJ?w{K+TJ(Us7x8xZn_2@)- zdcNaBEA+8l$+7==_UMC;_{rWc?}`?VZh1KmtX}pX**3QZ^?tNsXq|}QO5K+hFAW_6 zc;D^m1D83dN6Hb@S)bsc@vj+3`*&wJpzcV#@jCKG0x_yi2)l3c50YRD}ok6_tGK`bD9t+Q|2rt?vdXHd!Rdy(2-+<|O;4^r0Fof{ ztv_ZUn__dX7k=2m!|b&8&dK_fQ{l9Vop*6}=AyNZiRt;z?E2ja*#I!Bby-j9$grRL z=S;xaaVWDYl=hu6OzrDJBmn{K)a82t7=%`3FrH%QVeb`+VExq@off_x0jQPdvV-Mx zQHk+rVG$*au_g5NroovaEz@VdqSk$!c&fckXS1$$8W-zc2%PI6%SKi2os3)Yk`~1E zRp}?w&x&{Wi*o|)Se$B%Wo^sFaw=`z2{p$x#MIg2Z-`oc8yh3(LSHd$o%$$jpdDTE z3clzGjHi{LBd@H8YpZ4&VeSyv%>RXH5hrF;;1v)YHff}{?7qL7x9YOaR-|d{x|n3_zrOfBY%Sl_=kbp0MTex)$Q7 zL<2XWzc|w%sSS)F4;bUp9|1y(tI6@O2>|Zn#5$q8wHIruR>S~>iy(`Myd-Qsf8$4H z8A7_Zb6=EzUD@GK8Gv6xFE8O&+U3;f%YB9J(bA+50E<{qUsB_8(g0^t@&bg~S2eXs z{sToX*;_;)+1r|#;z|1U0h(JN;67xk5dhOHF9ZbcUl)sB&Iz zK`U6($K2#?EB$C86f!zCen&5*_+CwQZM*dRr#kb;7P-NBf?yIy7q;h)hp3`h_c&GJ zYf%Ig+2ue>tiZ?J?;K)jzp80>i*%?Krvn$DI**4z0uh-x36SJ&&cC)g&irCFk7@$Y0 zdpL%e;2mwweV#3KyA9kxE>Mtthlf%a{5=C1H{V7&RY$-0KF{35BCgmX_M1~FNS8m` zPfoC#PMOmU`8#7bBjQV@|8=g{uTRlwZz6L3Eih&c&ZoN*z&i0ToFzXEK|4}^#tnqc zMm|`t(xM5oaz=Kc-co8M%2DQN=4ResF_Q=gAej4>92 ze&F|mon?fgN@sbp%fQ!I->~*DM>^eu?PQsY&XC93Dkr)bFnYodJP;r*=H0fB65}cC zy@*S=Sgg`Nr`wvq!1wuC7*wT7_6eeWz3qnhWR*n$^CX(TcZo z>=(v|Rhh8I%Wk<&RC!pW*XHxIe$QdzRMX@~9_B2EWx_?{42av62);%_6L9-yGi)oj ze=wY;n8QiY_Cp7RQ6C3)l&C##*HCt2!iiyLrhRD<2SQtpwb^wJG&FAD!c*F4r6({{ zU;cJ}P-=$6gzrm@*pANTs4*Lp7gD6+Dk*L1cdJv(_^K!`%-}X{nWAe-I8Rxn;iO4h zOokuOA(J5C(|Ry4Jgy_EF`(?n#@bRXhHPB3evaws6`^6#u14EQ`4F=(e0M5A(~iO4 zHuMx4^Np`dudQ7A(Vn?D+(fm5$hlU+qPeV48^1+%Fk8U#Ex|Ve%A?0G3B){5pCwXj zIPaHk`@)F0JHyltdDtT-h{Fv>uDm_$dM0R6eMgsBfkn1vm5dEp@#<-Kw2IwBo@d)< z9A`36hUK9)$10{Mu?Pf{zG^wEALN+E#@P@~Tgeuk>ZGqU*gXhYZA+h-U%qZjY34PT zH@YHM0xBbWa@|Ng4^#O1WJkTcq4H^$zz8A;Y}F7<`w9E!6^?ovJ*@Y*b)o8FW@F(^ z`Hb$4xVa>?t10|y`1UlBiIB+)7h^}2#W)l0D;;x7ow+`Qq7~OYjZ9JA)nP_24 z;8LK)TFVfD6Az=N6yzbk3mWgM#K{JVf3#H(V>3_+IDho-`-EQ4`fhEV^;$~jhb9ks z1s;(L8VfB4xuoXJ_Pu|qWh5w~fyG_8dgz5NruBxbBP;rc`Ui6ZJYz!lsl+&&TAcFB zG)Vz$MnFqLPB;-<$t74&?Fm7B@0?0*4^%M!gL40q4UHFiv!psIp?t+|LsP)~`eR`q z4;UW(0XK8kho;7Dl9S|sgILNsM3gP#Q^DUQ!e#XIWewWt+7kN3p>zQ@77tne`__#y-hSbT*GDWMru9NW^Xacu~|tjISN|ik`w=%wIc=}@H!BDLuF==0Gd2&&dNjjkvGOHZomK`rP&70K{v=3RiLkoOoEp)fA{7#|^xZ5l>9 z_@h+W44UJvy4cu4D17^XP!I3+;7))TY6@{eqvf$l@sYvjxCae-B{Uc;$FWQ+^O=!C18W= zKP2j&Az#$&E70&6!`~!|W&Pxm53TP9E6tbt`;iVv);9+Vm(DmLj(;=@tlOHkCehc2 zS6GwpzgTX2w<7(X^5;A#`4L6fJ$k(Q04tZ%G3oVfype>IZIjUR6DNg>?Q&8uHUq(x zHDgm1d<#lGjgHa-j1qgbIzD)C*cx-(*?1-7hV-3QUxqb=cW3zipTyR^UMhGbMiEe% zd)zB3ZA5SB;bfS&mn!IY)_+5TY}@kc58i{{%zCw1Wd;Ai-{7=PJ{9Tnmcf9TJu^5J zTau-9B99LW^>WQ5>CRlAtf+QYB)j(`3V3^WO34#ow2|y&%sODn3?D;Y{wt?|Q(7zU zpf(TLAaypWAghWwP3(_yV#vI;^k)CGSJ^Y-eUR6lUY`@lDZF{^70>@b~eFrJw{|^Sl z>nLx$gZK3kuP~_VE|-IJEtY219g=KB-&_|{s3*S&e5Sf4-Oiz1NJ;O@?^79IXi>m- zY{Jt`R;hw~YS3&tI8Hu?!JodMMuAUuN@Me2EaZeh;oA9-ir2;T(KRA|rSoq}!uq|? zQsLt)vgvvdo#`CU_!YsxuAVSLH#aw|LXl-&(tED}AD`%3#-O&b#M1lyId8JNPgbS! zPU9AZl@p`WTZeI)%qzEv9i#-jTlTUP&n%pdli9)TCl{J!V^?auZ$iJtL7vA|UeV{O zd+WZ9)6LBNd9|nD!3ReP->FA|J1c}`RFI003#beIU436Q8m zF&)oPsr@JdfzW-q%m*A3^roT~M2kHp5!=nDa}6-`jA^@`C>q7c#EoZ_pc;aYUAs4Q zkIU_&+U^_3(=5a~-~_5=+Zu~ybj>_1_ffd%zgiDM1sqnPwyly`Tm=l!F%d>I=JI1Q zrzurw7(rG>#x@AKAy{(ci?Q(h7h;qqe7hhdGj}54qB2imwtdz9{p463o52p3tznt7 z_cIF?0#d5&c`P-{%++y!4`srOyUY6flSh*>;6Q{UW|P1_a$ zneJ$dYfJ-6NZj@KAIOR4Cu{uJgUp3J?1r3e%Lp<+O=15)fA(|cf3#N zv3i`iE+j2D-uS4CX%i>?I@5LoXsRf#{Yv7&WzV-v1K))EIj zq~JptM3&&h7jMJuYy>(5I6!%!ocXuD7}5XpIj?nUMA0h%jZ`6=r&dY}X5;=ymRt*# z2U_Asm3@wpI%5kC}IEk xTT6222t(=e!#*0EwiWdfqr8T`y`dq#pcbsk37l92e` + - `git push --tags` Then, in the GitHub UI: - Go to the [Releases](https://github.com/veracode/gen-ir/releases) page - Click `Draft a new release` +- Set the title to the version name - From the drop down list, choose your newly created tag - Click the `Generate release notes` button to create a change log - Ensure `Set as the latest release` is checked - Click the `Publish` button +- Click `Draft a new release` +- Set the title to `develop` +- From the drop down list, choose your newly created `develop` tag +- Click the `Generate release notes` button to create a change log +- Ensure `Set as pre-release` is checked +- Click the `Publish` button + A release has been made, congratulations. However there's additional steps for distributing the release via `brew`. ## Distributing a release From 76b023ca0c7286de784c0a46db298656a2c2c8b1 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 4 Sep 2023 11:56:39 +0200 Subject: [PATCH 08/61] Add test for container tags in xcworkspaces --- .../App/App.xcodeproj/project.pbxproj | 380 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../App/App/AppApp.swift | 17 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../App/App/Assets.xcassets/Contents.json | 6 + .../App/App/ContentView.swift | 30 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../Modules/FrameworkA/FrameworkA.swift | 12 + .../FrameworkA.xcodeproj/project.pbxproj | 350 ++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../FrameworkA/FrameworkA/FrameworkA.h | 18 + .../FrameworkB.xcodeproj/project.pbxproj | 386 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../FrameworkB/FrameworkB/FrameworkB.h | 18 + .../FrameworkB/FrameworkB/FrameworkB.swift | 12 + .../contents.xcworkspacedata | 17 + .../project.pbxproj | 338 +++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../GenIRTests/WorkspaceContainerTests.swift | 21 + 23 files changed, 1688 insertions(+) create mode 100644 TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj create mode 100644 TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceContainerTest/App/App/AppApp.swift create mode 100644 TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/Contents.json create mode 100644 TestAssets/WorkspaceContainerTest/App/App/ContentView.swift create mode 100644 TestAssets/WorkspaceContainerTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.swift create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA/FrameworkA.h create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.h create mode 100644 TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.swift create mode 100644 TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/project.pbxproj create mode 100644 TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Tests/GenIRTests/WorkspaceContainerTests.swift diff --git a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8e3249d --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj @@ -0,0 +1,380 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CE2A6FC42AA5CB1C0045CA22 /* AppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2A6FC32AA5CB1C0045CA22 /* AppApp.swift */; }; + CE2A6FC62AA5CB1C0045CA22 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2A6FC52AA5CB1C0045CA22 /* ContentView.swift */; }; + CE2A6FC82AA5CB1D0045CA22 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE2A6FC72AA5CB1D0045CA22 /* Assets.xcassets */; }; + CE2A6FCB2AA5CB1D0045CA22 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE2A6FCA2AA5CB1D0045CA22 /* Preview Assets.xcassets */; }; + CEC8C1362AA5CCB10063BAAD /* FrameworkA.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEC8C1342AA5CCB10063BAAD /* FrameworkA.framework */; }; + CEC8C1372AA5CCB10063BAAD /* FrameworkA.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEC8C1342AA5CCB10063BAAD /* FrameworkA.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CEC8C1382AA5CCB10063BAAD /* FrameworkB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEC8C1352AA5CCB10063BAAD /* FrameworkB.framework */; }; + CEC8C1392AA5CCB10063BAAD /* FrameworkB.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEC8C1352AA5CCB10063BAAD /* FrameworkB.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + CEC8C13A2AA5CCB10063BAAD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CEC8C1392AA5CCB10063BAAD /* FrameworkB.framework in Embed Frameworks */, + CEC8C1372AA5CCB10063BAAD /* FrameworkA.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + CE2A6FC02AA5CB1C0045CA22 /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CE2A6FC32AA5CB1C0045CA22 /* AppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppApp.swift; sourceTree = ""; }; + CE2A6FC52AA5CB1C0045CA22 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + CE2A6FC72AA5CB1D0045CA22 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CE2A6FCA2AA5CB1D0045CA22 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + CE2A70002AA5CB820045CA22 /* FrameworkA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FrameworkA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE2A70012AA5CB820045CA22 /* FrameworkB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FrameworkB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CEC8C1342AA5CCB10063BAAD /* FrameworkA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FrameworkA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CEC8C1352AA5CCB10063BAAD /* FrameworkB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FrameworkB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE2A6FBD2AA5CB1C0045CA22 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CEC8C1382AA5CCB10063BAAD /* FrameworkB.framework in Frameworks */, + CEC8C1362AA5CCB10063BAAD /* FrameworkA.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE2A6FB72AA5CB1C0045CA22 = { + isa = PBXGroup; + children = ( + CE2A6FC22AA5CB1C0045CA22 /* App */, + CE2A6FC12AA5CB1C0045CA22 /* Products */, + CE2A6FFF2AA5CB820045CA22 /* Frameworks */, + ); + sourceTree = ""; + }; + CE2A6FC12AA5CB1C0045CA22 /* Products */ = { + isa = PBXGroup; + children = ( + CE2A6FC02AA5CB1C0045CA22 /* App.app */, + ); + name = Products; + sourceTree = ""; + }; + CE2A6FC22AA5CB1C0045CA22 /* App */ = { + isa = PBXGroup; + children = ( + CE2A6FC32AA5CB1C0045CA22 /* AppApp.swift */, + CE2A6FC52AA5CB1C0045CA22 /* ContentView.swift */, + CE2A6FC72AA5CB1D0045CA22 /* Assets.xcassets */, + CE2A6FC92AA5CB1D0045CA22 /* Preview Content */, + ); + path = App; + sourceTree = ""; + }; + CE2A6FC92AA5CB1D0045CA22 /* Preview Content */ = { + isa = PBXGroup; + children = ( + CE2A6FCA2AA5CB1D0045CA22 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + CE2A6FFF2AA5CB820045CA22 /* Frameworks */ = { + isa = PBXGroup; + children = ( + CEC8C1342AA5CCB10063BAAD /* FrameworkA.framework */, + CEC8C1352AA5CCB10063BAAD /* FrameworkB.framework */, + CE2A70002AA5CB820045CA22 /* FrameworkA.framework */, + CE2A70012AA5CB820045CA22 /* FrameworkB.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CE2A6FBF2AA5CB1C0045CA22 /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE2A6FCE2AA5CB1D0045CA22 /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + CE2A6FBC2AA5CB1C0045CA22 /* Sources */, + CE2A6FBD2AA5CB1C0045CA22 /* Frameworks */, + CE2A6FBE2AA5CB1C0045CA22 /* Resources */, + CEC8C13A2AA5CCB10063BAAD /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = App; + productName = App; + productReference = CE2A6FC02AA5CB1C0045CA22 /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE2A6FB82AA5CB1C0045CA22 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + CE2A6FBF2AA5CB1C0045CA22 = { + CreatedOnToolsVersion = 14.3; + }; + }; + }; + buildConfigurationList = CE2A6FBB2AA5CB1C0045CA22 /* Build configuration list for PBXProject "App" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE2A6FB72AA5CB1C0045CA22; + productRefGroup = CE2A6FC12AA5CB1C0045CA22 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE2A6FBF2AA5CB1C0045CA22 /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE2A6FBE2AA5CB1C0045CA22 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE2A6FCB2AA5CB1D0045CA22 /* Preview Assets.xcassets in Resources */, + CE2A6FC82AA5CB1D0045CA22 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE2A6FBC2AA5CB1C0045CA22 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE2A6FC62AA5CB1C0045CA22 /* ContentView.swift in Sources */, + CE2A6FC42AA5CB1C0045CA22 /* AppApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE2A6FCC2AA5CB1D0045CA22 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CE2A6FCD2AA5CB1D0045CA22 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CE2A6FCF2AA5CB1D0045CA22 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.App; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE2A6FD02AA5CB1D0045CA22 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.App; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE2A6FBB2AA5CB1C0045CA22 /* Build configuration list for PBXProject "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE2A6FCC2AA5CB1D0045CA22 /* Debug */, + CE2A6FCD2AA5CB1D0045CA22 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE2A6FCE2AA5CB1D0045CA22 /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE2A6FCF2AA5CB1D0045CA22 /* Debug */, + CE2A6FD02AA5CB1D0045CA22 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE2A6FB82AA5CB1C0045CA22 /* Project object */; +} diff --git a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceContainerTest/App/App/AppApp.swift b/TestAssets/WorkspaceContainerTest/App/App/AppApp.swift new file mode 100644 index 0000000..0e1902f --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App/AppApp.swift @@ -0,0 +1,17 @@ +// +// AppApp.swift +// App +// +// Created by Thomas Hedderwick on 04/09/2023. +// + +import SwiftUI + +@main +struct AppApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json b/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/Contents.json b/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceContainerTest/App/App/ContentView.swift b/TestAssets/WorkspaceContainerTest/App/App/ContentView.swift new file mode 100644 index 0000000..b21ce26 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App/ContentView.swift @@ -0,0 +1,30 @@ +// +// ContentView.swift +// App +// +// Created by Thomas Hedderwick on 04/09/2023. +// + +import SwiftUI +import FrameworkA +import FrameworkB + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundColor(.accentColor) + Text("Hello, world!") + Text(FrameworkA.name) + Text(FrameworkB.name) + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/TestAssets/WorkspaceContainerTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json b/TestAssets/WorkspaceContainerTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.swift b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.swift new file mode 100644 index 0000000..249fc72 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.swift @@ -0,0 +1,12 @@ +// +// FrameworkA.swift +// FrameworkA +// +// Created by Thomas Hedderwick on 04/09/2023. +// + +import Foundation + +public struct FrameworkA { + public static let name: String = "FrameworkA" +} diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3bfedf6 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj @@ -0,0 +1,350 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CEC8C14B2AA5CD560063BAAD /* FrameworkA.h in Headers */ = {isa = PBXBuildFile; fileRef = CEC8C14A2AA5CD560063BAAD /* FrameworkA.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CEC8C1522AA5CD5F0063BAAD /* FrameworkA.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC8C1512AA5CD5F0063BAAD /* FrameworkA.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CEC8C1472AA5CD560063BAAD /* FrameworkA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FrameworkA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CEC8C14A2AA5CD560063BAAD /* FrameworkA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameworkA.h; sourceTree = ""; }; + CEC8C1512AA5CD5F0063BAAD /* FrameworkA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameworkA.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CEC8C1442AA5CD560063BAAD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CEC8C13D2AA5CD560063BAAD = { + isa = PBXGroup; + children = ( + CEC8C1512AA5CD5F0063BAAD /* FrameworkA.swift */, + CEC8C1492AA5CD560063BAAD /* FrameworkA */, + CEC8C1482AA5CD560063BAAD /* Products */, + ); + sourceTree = ""; + }; + CEC8C1482AA5CD560063BAAD /* Products */ = { + isa = PBXGroup; + children = ( + CEC8C1472AA5CD560063BAAD /* FrameworkA.framework */, + ); + name = Products; + sourceTree = ""; + }; + CEC8C1492AA5CD560063BAAD /* FrameworkA */ = { + isa = PBXGroup; + children = ( + CEC8C14A2AA5CD560063BAAD /* FrameworkA.h */, + ); + path = FrameworkA; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CEC8C1422AA5CD560063BAAD /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CEC8C14B2AA5CD560063BAAD /* FrameworkA.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CEC8C1462AA5CD560063BAAD /* FrameworkA */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEC8C14E2AA5CD560063BAAD /* Build configuration list for PBXNativeTarget "FrameworkA" */; + buildPhases = ( + CEC8C1422AA5CD560063BAAD /* Headers */, + CEC8C1432AA5CD560063BAAD /* Sources */, + CEC8C1442AA5CD560063BAAD /* Frameworks */, + CEC8C1452AA5CD560063BAAD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FrameworkA; + productName = FrameworkA; + productReference = CEC8C1472AA5CD560063BAAD /* FrameworkA.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CEC8C13E2AA5CD560063BAAD /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1430; + TargetAttributes = { + CEC8C1462AA5CD560063BAAD = { + CreatedOnToolsVersion = 14.3; + LastSwiftMigration = 1430; + }; + }; + }; + buildConfigurationList = CEC8C1412AA5CD560063BAAD /* Build configuration list for PBXProject "FrameworkA" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CEC8C13D2AA5CD560063BAAD; + productRefGroup = CEC8C1482AA5CD560063BAAD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CEC8C1462AA5CD560063BAAD /* FrameworkA */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CEC8C1452AA5CD560063BAAD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CEC8C1432AA5CD560063BAAD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEC8C1522AA5CD5F0063BAAD /* FrameworkA.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CEC8C14C2AA5CD560063BAAD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CEC8C14D2AA5CD560063BAAD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CEC8C14F2AA5CD560063BAAD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEC8C1502AA5CD560063BAAD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CEC8C1412AA5CD560063BAAD /* Build configuration list for PBXProject "FrameworkA" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEC8C14C2AA5CD560063BAAD /* Debug */, + CEC8C14D2AA5CD560063BAAD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEC8C14E2AA5CD560063BAAD /* Build configuration list for PBXNativeTarget "FrameworkA" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEC8C14F2AA5CD560063BAAD /* Debug */, + CEC8C1502AA5CD560063BAAD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CEC8C13E2AA5CD560063BAAD /* Project object */; +} diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA/FrameworkA.h b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA/FrameworkA.h new file mode 100644 index 0000000..6bfbdc7 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA/FrameworkA.h @@ -0,0 +1,18 @@ +// +// FrameworkA.h +// FrameworkA +// +// Created by Thomas Hedderwick on 04/09/2023. +// + +#import + +//! Project version number for FrameworkA. +FOUNDATION_EXPORT double FrameworkAVersionNumber; + +//! Project version string for FrameworkA. +FOUNDATION_EXPORT const unsigned char FrameworkAVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2c9c860 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj @@ -0,0 +1,386 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CE2A6FF32AA5CB410045CA22 /* FrameworkB.h in Headers */ = {isa = PBXBuildFile; fileRef = CE2A6FF22AA5CB410045CA22 /* FrameworkB.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE2A700A2AA5CBB10045CA22 /* FrameworkB.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2A70092AA5CBB10045CA22 /* FrameworkB.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + CEC8C1572AA5CD7B0063BAAD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CEC8C1532AA5CD7B0063BAAD /* FrameworkA.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = CEC8C1472AA5CD560063BAAD; + remoteInfo = FrameworkA; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + CE2A6FEF2AA5CB410045CA22 /* FrameworkB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FrameworkB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE2A6FF22AA5CB410045CA22 /* FrameworkB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameworkB.h; sourceTree = ""; }; + CE2A70092AA5CBB10045CA22 /* FrameworkB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameworkB.swift; sourceTree = ""; }; + CEC8C1532AA5CD7B0063BAAD /* FrameworkA.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FrameworkA.xcodeproj; path = ../FrameworkA/FrameworkA.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE2A6FEC2AA5CB410045CA22 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE2A6FE52AA5CB410045CA22 = { + isa = PBXGroup; + children = ( + CEC8C1532AA5CD7B0063BAAD /* FrameworkA.xcodeproj */, + CE2A6FF12AA5CB410045CA22 /* FrameworkB */, + CE2A6FF02AA5CB410045CA22 /* Products */, + ); + sourceTree = ""; + }; + CE2A6FF02AA5CB410045CA22 /* Products */ = { + isa = PBXGroup; + children = ( + CE2A6FEF2AA5CB410045CA22 /* FrameworkB.framework */, + ); + name = Products; + sourceTree = ""; + }; + CE2A6FF12AA5CB410045CA22 /* FrameworkB */ = { + isa = PBXGroup; + children = ( + CE2A6FF22AA5CB410045CA22 /* FrameworkB.h */, + CE2A70092AA5CBB10045CA22 /* FrameworkB.swift */, + ); + path = FrameworkB; + sourceTree = ""; + }; + CEC8C1542AA5CD7B0063BAAD /* Products */ = { + isa = PBXGroup; + children = ( + CEC8C1582AA5CD7B0063BAAD /* FrameworkA.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CE2A6FEA2AA5CB410045CA22 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CE2A6FF32AA5CB410045CA22 /* FrameworkB.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CE2A6FEE2AA5CB410045CA22 /* FrameworkB */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE2A6FF62AA5CB410045CA22 /* Build configuration list for PBXNativeTarget "FrameworkB" */; + buildPhases = ( + CE2A6FEA2AA5CB410045CA22 /* Headers */, + CE2A6FEB2AA5CB410045CA22 /* Sources */, + CE2A6FEC2AA5CB410045CA22 /* Frameworks */, + CE2A6FED2AA5CB410045CA22 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FrameworkB; + productName = FrameworkB; + productReference = CE2A6FEF2AA5CB410045CA22 /* FrameworkB.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE2A6FE62AA5CB410045CA22 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1430; + TargetAttributes = { + CE2A6FEE2AA5CB410045CA22 = { + CreatedOnToolsVersion = 14.3; + LastSwiftMigration = 1430; + }; + }; + }; + buildConfigurationList = CE2A6FE92AA5CB410045CA22 /* Build configuration list for PBXProject "FrameworkB" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE2A6FE52AA5CB410045CA22; + productRefGroup = CE2A6FF02AA5CB410045CA22 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = CEC8C1542AA5CD7B0063BAAD /* Products */; + ProjectRef = CEC8C1532AA5CD7B0063BAAD /* FrameworkA.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + CE2A6FEE2AA5CB410045CA22 /* FrameworkB */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + CEC8C1582AA5CD7B0063BAAD /* FrameworkA.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FrameworkA.framework; + remoteRef = CEC8C1572AA5CD7B0063BAAD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + CE2A6FED2AA5CB410045CA22 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE2A6FEB2AA5CB410045CA22 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE2A700A2AA5CBB10045CA22 /* FrameworkB.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE2A6FF42AA5CB410045CA22 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CE2A6FF52AA5CB410045CA22 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CE2A6FF72AA5CB410045CA22 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkB; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE2A6FF82AA5CB410045CA22 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkB; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE2A6FE92AA5CB410045CA22 /* Build configuration list for PBXProject "FrameworkB" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE2A6FF42AA5CB410045CA22 /* Debug */, + CE2A6FF52AA5CB410045CA22 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE2A6FF62AA5CB410045CA22 /* Build configuration list for PBXNativeTarget "FrameworkB" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE2A6FF72AA5CB410045CA22 /* Debug */, + CE2A6FF82AA5CB410045CA22 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE2A6FE62AA5CB410045CA22 /* Project object */; +} diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.h b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.h new file mode 100644 index 0000000..a362bdc --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.h @@ -0,0 +1,18 @@ +// +// FrameworkB.h +// FrameworkB +// +// Created by Thomas Hedderwick on 04/09/2023. +// + +#import + +//! Project version number for FrameworkB. +FOUNDATION_EXPORT double FrameworkBVersionNumber; + +//! Project version string for FrameworkB. +FOUNDATION_EXPORT const unsigned char FrameworkBVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.swift b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.swift new file mode 100644 index 0000000..5170372 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB/FrameworkB.swift @@ -0,0 +1,12 @@ +// +// FrameworkB.swift +// FrameworkB +// +// Created by Thomas Hedderwick on 04/09/2023. +// + +import Foundation + +public struct FrameworkB { + public static let name: String = "FrameworkB" +} diff --git a/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..9e7f3e2 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/project.pbxproj b/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/project.pbxproj new file mode 100644 index 0000000..d7f79b9 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/project.pbxproj @@ -0,0 +1,338 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CEC8C13C2AA5CCD80063BAAD /* FrameworkA in Resources */ = {isa = PBXBuildFile; fileRef = CEC8C13B2AA5CCD80063BAAD /* FrameworkA */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CE2A6FDB2AA5CB340045CA22 /* FrameworkA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FrameworkA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CEC8C13B2AA5CCD80063BAAD /* FrameworkA */ = {isa = PBXFileReference; lastKnownFileType = folder; name = FrameworkA; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE2A6FD82AA5CB340045CA22 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE2A6FD12AA5CB340045CA22 = { + isa = PBXGroup; + children = ( + CEC8C13B2AA5CCD80063BAAD /* FrameworkA */, + CE2A6FDC2AA5CB340045CA22 /* Products */, + ); + sourceTree = ""; + }; + CE2A6FDC2AA5CB340045CA22 /* Products */ = { + isa = PBXGroup; + children = ( + CE2A6FDB2AA5CB340045CA22 /* FrameworkA.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CE2A6FD62AA5CB340045CA22 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CE2A6FDA2AA5CB340045CA22 /* FrameworkA */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE2A6FE22AA5CB340045CA22 /* Build configuration list for PBXNativeTarget "FrameworkA" */; + buildPhases = ( + CE2A6FD62AA5CB340045CA22 /* Headers */, + CE2A6FD72AA5CB340045CA22 /* Sources */, + CE2A6FD82AA5CB340045CA22 /* Frameworks */, + CE2A6FD92AA5CB340045CA22 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FrameworkA; + productName = FrameworkA; + productReference = CE2A6FDB2AA5CB340045CA22 /* FrameworkA.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE2A6FD22AA5CB340045CA22 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1430; + TargetAttributes = { + CE2A6FDA2AA5CB340045CA22 = { + CreatedOnToolsVersion = 14.3; + LastSwiftMigration = 1430; + }; + }; + }; + buildConfigurationList = CE2A6FD52AA5CB340045CA22 /* Build configuration list for PBXProject "FrameworkA" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE2A6FD12AA5CB340045CA22; + productRefGroup = CE2A6FDC2AA5CB340045CA22 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE2A6FDA2AA5CB340045CA22 /* FrameworkA */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE2A6FD92AA5CB340045CA22 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEC8C13C2AA5CCD80063BAAD /* FrameworkA in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE2A6FD72AA5CB340045CA22 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE2A6FE02AA5CB340045CA22 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CE2A6FE12AA5CB340045CA22 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CE2A6FE32AA5CB340045CA22 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE2A6FE42AA5CB340045CA22 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE2A6FD52AA5CB340045CA22 /* Build configuration list for PBXProject "FrameworkA" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE2A6FE02AA5CB340045CA22 /* Debug */, + CE2A6FE12AA5CB340045CA22 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE2A6FE22AA5CB340045CA22 /* Build configuration list for PBXNativeTarget "FrameworkA" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE2A6FE32AA5CB340045CA22 /* Debug */, + CE2A6FE42AA5CB340045CA22 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE2A6FD22AA5CB340045CA22 /* Project object */; +} diff --git a/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceContainerTest/WorkspaceContainerTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Tests/GenIRTests/WorkspaceContainerTests.swift b/Tests/GenIRTests/WorkspaceContainerTests.swift new file mode 100644 index 0000000..e910bf5 --- /dev/null +++ b/Tests/GenIRTests/WorkspaceContainerTests.swift @@ -0,0 +1,21 @@ +import XCTest +@testable import gen_ir +import PBXProjParser + +final class WorkspaceContainerTests: XCTestCase { + static private var testPath: URL = { + TestContext.testAssetPath + .appendingPathComponent("WorkspaceContainerTest") + .appendingPathComponent("WorkspaceContainerTest.xcworkspace") + }() + + func testWeirdGroupTagLocationParsing() throws { + let parser = try ProjectParser(path: Self.testPath, logLevel: .debug) + let targets = parser.targets + + XCTAssert(targets.count == 3) + XCTAssertNotNil(targets.first(where: { $0.name == "App" })) + XCTAssertNotNil(targets.first(where: { $0.name == "FrameworkA" })) + XCTAssertNotNil(targets.first(where: { $0.name == "FrameworkB" })) + } +} From 20c01303fa338449992449d162be581b4b493c78 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 4 Sep 2023 11:56:55 +0200 Subject: [PATCH 09/61] Add support for container tags in xcworkspaces --- .../Sources/PBXProjParser/XcodeWorkspace.swift | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift b/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift index 40cfbd8..424da25 100644 --- a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift +++ b/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift @@ -101,12 +101,20 @@ fileprivate class XCWorkspaceDataParser: NSObject, XMLParserDelegate { /// - Parameter attributeDict: the attribute dictionary to extract a location attribute from /// - Returns: the path of the location attribute value private func extractLocation(_ attributeDict: [String: String]) -> String? { - guard - let location = attributeDict["location"], - location.starts(with: "group:") - else { return nil } + guard let location = attributeDict["location"] else { return nil } + + if location.starts(with: "group:") { + return location.replacingOccurrences(of: "group:", with: "") + } else if location.starts(with: "container:") { + let location = location.replacingOccurrences(of: "container:", with: "") + + if !location.isEmpty { return location } + + // Sometimes, location could be empty, in this case _normally_ you'll have a name attribute + return attributeDict["name"] + } - return location.replacingOccurrences(of: "group:", with: "") + return nil } /// Handle a Group tag From 4dfefaf9ef95899a26118acf8c6c634dc21f1e00 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Thu, 19 Oct 2023 10:36:07 +0200 Subject: [PATCH 10/61] New workspace project discovery Rewrite workspace project discovery to break it up into more sensible & logical pieces. Add a test asset (test to come). The biggest change is that the group naming only follows the nearest group parent - not all of them --- .gitignore | 2 +- .../PBXProjParser/Workspace/Reference.swift | 87 +++ .../Workspace/WorkspaceParser.swift | 88 +++ .../PBXProjParser/XcodeWorkspace.swift | 119 +--- .../Foo/Foo.xcodeproj/project.pbxproj | 597 ++++++++++++++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Foo/Foo/Assets.xcassets/Contents.json | 6 + .../Infrastructure/Foo/Foo/ContentView.swift | 61 ++ .../Infrastructure/Foo/Foo/FooApp.swift | 32 + .../Infrastructure/Foo/Foo/Item.swift | 18 + .../Preview Assets.xcassets/Contents.json | 6 + .../Foo/FooTests/FooTests.swift | 36 ++ .../Foo/FooUITests/FooUITests.swift | 41 ++ .../FooUITests/FooUITestsLaunchTests.swift | 32 + .../contents.xcworkspacedata | 15 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + 17 files changed, 1058 insertions(+), 114 deletions(-) create mode 100644 PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift create mode 100644 PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo.xcodeproj/project.pbxproj create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/Contents.json create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/ContentView.swift create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/FooApp.swift create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Item.swift create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooTests/FooTests.swift create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITests.swift create mode 100644 TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITestsLaunchTests.swift create mode 100644 TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/.gitignore b/.gitignore index cc3876c..eaa8671 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ output/ *.bc *.dia _build/ -**/.build/* \ No newline at end of file +**/.build/* diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift new file mode 100644 index 0000000..581c8a6 --- /dev/null +++ b/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift @@ -0,0 +1,87 @@ +// +// Reference.swift +// +// +// Created by Thomas Hedderwick on 18/10/2023. +// +import Foundation + +protocol Reference { + var location: Location { get } + static var elementName: String { get } +} + +enum Location { + // TODO: Find where we can get a definitive list of these. Xcode must have them somewhere? + case container(String) + case group(String) + + enum Error: Swift.Error { + case invalidLocation(String) + } + + init(_ location: String) throws { + let split = location + .split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false) + .map(String.init) + + guard + let key = split.first, + let value = split.last + else { throw Error.invalidLocation("Couldn't extract key/value pair from split: \(split)") } + + switch key { + case "container": self = .container(value) + case "group": self = .group(value) + default: throw Error.invalidLocation("Key didn't match a supported location key: \(key)") + } + } + + var path: String { + switch self { + case .container(let path): return path + case .group(let path): return path + } + } +} + +class Group: Reference { + static let elementName: String = "Group" + + let location: Location + let name: String? + var references: [Reference] = [] + + init(location: String, name: String?) throws { + self.location = try .init(location) + self.name = name + } +} + +struct FileRef: Reference { + static let elementName: String = "FileRef" + + let location: Location + let enclosingGroup: Group? + + init(location: String, enclosingGroup: Group? = nil) throws { + self.location = try .init(location) + self.enclosingGroup = enclosingGroup + } + + var path: String { + guard + let enclosingGroup + else { return location.path } + + switch enclosingGroup.location { + case let .group(path), let .container(path): + if path.last == "/" { + return path + location.path + } + + return path + "/" + location.path + // return URL(fileURLWithPath: path).appendingPathComponent(location.path).path + } + } +} diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift new file mode 100644 index 0000000..4bca3fe --- /dev/null +++ b/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift @@ -0,0 +1,88 @@ +// +// WorkspaceParser.swift +// +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import Foundation + +struct Workspace { + private(set) var fileReferences: [FileRef] = [] + private(set) var groupReferences: [Group] = [] +} + +struct WorkspaceParser { + static func parse(_ path: URL) throws -> Workspace { + // Parse the `contents.xcworkspacedata` (XML) file and get the list of projects + let contentsPath = path.appendingPathComponent("contents.xcworkspacedata") + + let data = try Data(contentsOf: contentsPath) + let delegate = WorkspaceDataParserDelegate() + let parser = XMLParser(data: data) + parser.delegate = delegate + parser.parse() + + return .init( + fileReferences: delegate.fileReferences, + groupReferences: delegate.groupReferences + ) + } +} + +private class WorkspaceDataParserDelegate: NSObject, XMLParserDelegate { + private(set) var fileReferences: [FileRef] = [] + private(set) var groupReferences: [Group] = [] + + static let supportedElements = [Group.elementName, FileRef.elementName] + + private var groupPath: [Group] = [] + + func parser( + _ parser: XMLParser, + didStartElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?, + attributes attributeDict: [String: String] = [:] + ) { + guard Self.supportedElements.contains(elementName) else { + logger.debug("Skipping parsing of unsupported element: \(elementName)") + return + } + + guard + let location = attributeDict["location"] + else { + logger.debug("Location attribute for element \(elementName) is nil, this shouldn't be the case: \(attributeDict)") + return + } + + do { + switch elementName { + case Group.elementName: + let group = try Group(location: location, name: attributeDict["name"]) + groupPath.append(group) + groupReferences.append(group) + case FileRef.elementName: + let file = try FileRef(location: location, enclosingGroup: groupPath.last) + fileReferences.append(file) + groupPath.last?.references.append(file) + // Ignore any element that doesn't match the search space + default: + break + } + } catch { + logger.debug("Parsing element: \(elementName) failed. Reason: \(error)") + } + } + + func parser( + _ parser: XMLParser, + didEndElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String? + ) { + guard elementName == Group.elementName else { return } + groupPath.removeLast() + } +} diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift b/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift index 424da25..608dfea 100644 --- a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift +++ b/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift @@ -23,13 +23,14 @@ class XcodeWorkspace { self.path = path // Parse the `contents.xcworkspacedata` (XML) file and get the list of projects - let contentsPath = path.appendingPathComponent("contents.xcworkspacedata") - - let data = try Data(contentsOf: contentsPath) - let parser = XCWorkspaceDataParser(data: data) + let workspace = try WorkspaceParser.parse(path) + let paths = workspace + .fileReferences + .map { $0.path } + .filter { $0.hasSuffix("xcodeproj") } let baseFolder = path.deletingLastPathComponent() - projectPaths = parser.projects + projectPaths = paths .map { baseFolder.appendingPathComponent($0, isDirectory: true) } projects = try projectPaths.map(XcodeProject.init(path:)) @@ -58,111 +59,3 @@ class XcodeWorkspace { projects.flatMap { $0.packages } } } - -// swiftlint:disable private_over_fileprivate -/// A xcworkspace parser -fileprivate class XCWorkspaceDataParser: NSObject, XMLParserDelegate { - let parser: XMLParser - var projects = [String]() - - var isInGroup = false - var currentGroupPath: [String] = [] - - let groupTag = "Group" - let fileRefTag = "FileRef" - - init(data: Data) { - parser = .init(data: data) - - super.init() - - parser.delegate = self - parser.parse() - } - - func parser( - _ parser: XMLParser, - didStartElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?, - attributes attributeDict: [String: String] = [:] - ) { - switch elementName { - case groupTag: - handleGroupTag(attributeDict) - case fileRefTag: - handleFileRefTag(attributeDict) - default: - break - } - } - - /// Returns the location attribute value from the provided attributes, if one exists - /// - Parameter attributeDict: the attribute dictionary to extract a location attribute from - /// - Returns: the path of the location attribute value - private func extractLocation(_ attributeDict: [String: String]) -> String? { - guard let location = attributeDict["location"] else { return nil } - - if location.starts(with: "group:") { - return location.replacingOccurrences(of: "group:", with: "") - } else if location.starts(with: "container:") { - let location = location.replacingOccurrences(of: "container:", with: "") - - if !location.isEmpty { return location } - - // Sometimes, location could be empty, in this case _normally_ you'll have a name attribute - return attributeDict["name"] - } - - return nil - } - - /// Handle a Group tag - /// - /// Group tags require additional logic - since they can contain nested child paths via either additional group tags or file ref tags. - /// Set a flag in this function that's handled in `handleFileRefTag(_:)` - /// - Parameter attributeDict: the attributes attached to this tag - private func handleGroupTag(_ attributeDict: [String: String]) { - // For groups, we want to track the 'sub' path as we go deeper into the tree, - // this will allow us to create 'full' paths as we see file refs - guard let location = extractLocation(attributeDict) else { return } - currentGroupPath.append(location) - isInGroup = true - } - - /// Handle a FileRef tag - /// - /// Since Group tags can build out parts of paths, we also handle cases where this file ref is part of a group structure. - /// - Parameter attributeDict: the attributes attached to this tag - private func handleFileRefTag(_ attributeDict: [String: String]) { - // For file refs, we have two options - if we're not in a group we can just use the path as-is. - // If we're in a group, we will need to construct the current path from the depth we're currently in - guard - let location = extractLocation(attributeDict), - location.hasSuffix(".xcodeproj") - else { return } - - if isInGroup { - // Add a '/' in between group subpaths, then add the current location to the end - let fullLocation = currentGroupPath.reduce(into: "") { $0.append($1); $0.append("/") }.appending(location) - projects.append(fullLocation) - } else { - projects.append(location) - } - } - - func parser( - _ parser: XMLParser, - didEndElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String? - ) { - // If we're ending a group tag, we can pop the matching group off of the stack as we're done with it - guard elementName == groupTag else { return } - - _ = currentGroupPath.popLast() - - isInGroup = !currentGroupPath.isEmpty - } -} -// swiftlint:enable private_over_fileprivate diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo.xcodeproj/project.pbxproj b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..87f59e2 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo.xcodeproj/project.pbxproj @@ -0,0 +1,597 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CEBFD7A82ADFE8D20047795C /* FooApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFD7A72ADFE8D20047795C /* FooApp.swift */; }; + CEBFD7AA2ADFE8D20047795C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFD7A92ADFE8D20047795C /* ContentView.swift */; }; + CEBFD7AC2ADFE8D20047795C /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFD7AB2ADFE8D20047795C /* Item.swift */; }; + CEBFD7AE2ADFE8D30047795C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEBFD7AD2ADFE8D30047795C /* Assets.xcassets */; }; + CEBFD7B12ADFE8D30047795C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEBFD7B02ADFE8D30047795C /* Preview Assets.xcassets */; }; + CEBFD7BB2ADFE8D30047795C /* FooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFD7BA2ADFE8D30047795C /* FooTests.swift */; }; + CEBFD7C52ADFE8D30047795C /* FooUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFD7C42ADFE8D30047795C /* FooUITests.swift */; }; + CEBFD7C72ADFE8D30047795C /* FooUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFD7C62ADFE8D30047795C /* FooUITestsLaunchTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + CEBFD7B72ADFE8D30047795C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CEBFD79C2ADFE8D20047795C /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEBFD7A32ADFE8D20047795C; + remoteInfo = Foo; + }; + CEBFD7C12ADFE8D30047795C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CEBFD79C2ADFE8D20047795C /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEBFD7A32ADFE8D20047795C; + remoteInfo = Foo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + CEBFD7A42ADFE8D20047795C /* Foo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Foo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CEBFD7A72ADFE8D20047795C /* FooApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooApp.swift; sourceTree = ""; }; + CEBFD7A92ADFE8D20047795C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + CEBFD7AB2ADFE8D20047795C /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + CEBFD7AD2ADFE8D30047795C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CEBFD7B02ADFE8D30047795C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + CEBFD7B62ADFE8D30047795C /* FooTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FooTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + CEBFD7BA2ADFE8D30047795C /* FooTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooTests.swift; sourceTree = ""; }; + CEBFD7C02ADFE8D30047795C /* FooUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FooUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + CEBFD7C42ADFE8D30047795C /* FooUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooUITests.swift; sourceTree = ""; }; + CEBFD7C62ADFE8D30047795C /* FooUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooUITestsLaunchTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CEBFD7A12ADFE8D20047795C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBFD7B32ADFE8D30047795C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBFD7BD2ADFE8D30047795C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CEBFD79B2ADFE8D20047795C = { + isa = PBXGroup; + children = ( + CEBFD7A62ADFE8D20047795C /* Foo */, + CEBFD7B92ADFE8D30047795C /* FooTests */, + CEBFD7C32ADFE8D30047795C /* FooUITests */, + CEBFD7A52ADFE8D20047795C /* Products */, + ); + sourceTree = ""; + }; + CEBFD7A52ADFE8D20047795C /* Products */ = { + isa = PBXGroup; + children = ( + CEBFD7A42ADFE8D20047795C /* Foo.app */, + CEBFD7B62ADFE8D30047795C /* FooTests.xctest */, + CEBFD7C02ADFE8D30047795C /* FooUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + CEBFD7A62ADFE8D20047795C /* Foo */ = { + isa = PBXGroup; + children = ( + CEBFD7A72ADFE8D20047795C /* FooApp.swift */, + CEBFD7A92ADFE8D20047795C /* ContentView.swift */, + CEBFD7AB2ADFE8D20047795C /* Item.swift */, + CEBFD7AD2ADFE8D30047795C /* Assets.xcassets */, + CEBFD7AF2ADFE8D30047795C /* Preview Content */, + ); + path = Foo; + sourceTree = ""; + }; + CEBFD7AF2ADFE8D30047795C /* Preview Content */ = { + isa = PBXGroup; + children = ( + CEBFD7B02ADFE8D30047795C /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + CEBFD7B92ADFE8D30047795C /* FooTests */ = { + isa = PBXGroup; + children = ( + CEBFD7BA2ADFE8D30047795C /* FooTests.swift */, + ); + path = FooTests; + sourceTree = ""; + }; + CEBFD7C32ADFE8D30047795C /* FooUITests */ = { + isa = PBXGroup; + children = ( + CEBFD7C42ADFE8D30047795C /* FooUITests.swift */, + CEBFD7C62ADFE8D30047795C /* FooUITestsLaunchTests.swift */, + ); + path = FooUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CEBFD7A32ADFE8D20047795C /* Foo */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEBFD7CA2ADFE8D30047795C /* Build configuration list for PBXNativeTarget "Foo" */; + buildPhases = ( + CEBFD7A02ADFE8D20047795C /* Sources */, + CEBFD7A12ADFE8D20047795C /* Frameworks */, + CEBFD7A22ADFE8D20047795C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Foo; + productName = Foo; + productReference = CEBFD7A42ADFE8D20047795C /* Foo.app */; + productType = "com.apple.product-type.application"; + }; + CEBFD7B52ADFE8D30047795C /* FooTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEBFD7CD2ADFE8D30047795C /* Build configuration list for PBXNativeTarget "FooTests" */; + buildPhases = ( + CEBFD7B22ADFE8D30047795C /* Sources */, + CEBFD7B32ADFE8D30047795C /* Frameworks */, + CEBFD7B42ADFE8D30047795C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CEBFD7B82ADFE8D30047795C /* PBXTargetDependency */, + ); + name = FooTests; + productName = FooTests; + productReference = CEBFD7B62ADFE8D30047795C /* FooTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + CEBFD7BF2ADFE8D30047795C /* FooUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEBFD7D02ADFE8D30047795C /* Build configuration list for PBXNativeTarget "FooUITests" */; + buildPhases = ( + CEBFD7BC2ADFE8D30047795C /* Sources */, + CEBFD7BD2ADFE8D30047795C /* Frameworks */, + CEBFD7BE2ADFE8D30047795C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CEBFD7C22ADFE8D30047795C /* PBXTargetDependency */, + ); + name = FooUITests; + productName = FooUITests; + productReference = CEBFD7C02ADFE8D30047795C /* FooUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CEBFD79C2ADFE8D20047795C /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + CEBFD7A32ADFE8D20047795C = { + CreatedOnToolsVersion = 15.0; + }; + CEBFD7B52ADFE8D30047795C = { + CreatedOnToolsVersion = 15.0; + TestTargetID = CEBFD7A32ADFE8D20047795C; + }; + CEBFD7BF2ADFE8D30047795C = { + CreatedOnToolsVersion = 15.0; + TestTargetID = CEBFD7A32ADFE8D20047795C; + }; + }; + }; + buildConfigurationList = CEBFD79F2ADFE8D20047795C /* Build configuration list for PBXProject "Foo" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CEBFD79B2ADFE8D20047795C; + productRefGroup = CEBFD7A52ADFE8D20047795C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CEBFD7A32ADFE8D20047795C /* Foo */, + CEBFD7B52ADFE8D30047795C /* FooTests */, + CEBFD7BF2ADFE8D30047795C /* FooUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CEBFD7A22ADFE8D20047795C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEBFD7B12ADFE8D30047795C /* Preview Assets.xcassets in Resources */, + CEBFD7AE2ADFE8D30047795C /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBFD7B42ADFE8D30047795C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBFD7BE2ADFE8D30047795C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CEBFD7A02ADFE8D20047795C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEBFD7AA2ADFE8D20047795C /* ContentView.swift in Sources */, + CEBFD7AC2ADFE8D20047795C /* Item.swift in Sources */, + CEBFD7A82ADFE8D20047795C /* FooApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBFD7B22ADFE8D30047795C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEBFD7BB2ADFE8D30047795C /* FooTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBFD7BC2ADFE8D30047795C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEBFD7C52ADFE8D30047795C /* FooUITests.swift in Sources */, + CEBFD7C72ADFE8D30047795C /* FooUITestsLaunchTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + CEBFD7B82ADFE8D30047795C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEBFD7A32ADFE8D20047795C /* Foo */; + targetProxy = CEBFD7B72ADFE8D30047795C /* PBXContainerItemProxy */; + }; + CEBFD7C22ADFE8D30047795C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEBFD7A32ADFE8D20047795C /* Foo */; + targetProxy = CEBFD7C12ADFE8D30047795C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + CEBFD7C82ADFE8D30047795C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CEBFD7C92ADFE8D30047795C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CEBFD7CB2ADFE8D30047795C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Foo/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = kim.cracksby.Foo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEBFD7CC2ADFE8D30047795C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Foo/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = kim.cracksby.Foo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + CEBFD7CE2ADFE8D30047795C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = kim.cracksby.FooTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Foo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Foo"; + }; + name = Debug; + }; + CEBFD7CF2ADFE8D30047795C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = kim.cracksby.FooTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Foo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Foo"; + }; + name = Release; + }; + CEBFD7D12ADFE8D30047795C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = kim.cracksby.FooUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Foo; + }; + name = Debug; + }; + CEBFD7D22ADFE8D30047795C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = kim.cracksby.FooUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Foo; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CEBFD79F2ADFE8D20047795C /* Build configuration list for PBXProject "Foo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEBFD7C82ADFE8D30047795C /* Debug */, + CEBFD7C92ADFE8D30047795C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEBFD7CA2ADFE8D30047795C /* Build configuration list for PBXNativeTarget "Foo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEBFD7CB2ADFE8D30047795C /* Debug */, + CEBFD7CC2ADFE8D30047795C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEBFD7CD2ADFE8D30047795C /* Build configuration list for PBXNativeTarget "FooTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEBFD7CE2ADFE8D30047795C /* Debug */, + CEBFD7CF2ADFE8D30047795C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEBFD7D02ADFE8D30047795C /* Build configuration list for PBXNativeTarget "FooUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEBFD7D12ADFE8D30047795C /* Debug */, + CEBFD7D22ADFE8D30047795C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CEBFD79C2ADFE8D20047795C /* Project object */; +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AccentColor.colorset/Contents.json b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/Contents.json b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/ContentView.swift b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/ContentView.swift new file mode 100644 index 0000000..ef2e2eb --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/ContentView.swift @@ -0,0 +1,61 @@ +// +// ContentView.swift +// Foo +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import SwiftUI +import SwiftData + +struct ContentView: View { + @Environment(\.modelContext) private var modelContext + @Query private var items: [Item] + + var body: some View { + NavigationSplitView { + List { + ForEach(items) { item in + NavigationLink { + Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") + } label: { + Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) + } + } + .onDelete(perform: deleteItems) + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + EditButton() + } + ToolbarItem { + Button(action: addItem) { + Label("Add Item", systemImage: "plus") + } + } + } + } detail: { + Text("Select an item") + } + } + + private func addItem() { + withAnimation { + let newItem = Item(timestamp: Date()) + modelContext.insert(newItem) + } + } + + private func deleteItems(offsets: IndexSet) { + withAnimation { + for index in offsets { + modelContext.delete(items[index]) + } + } + } +} + +#Preview { + ContentView() + .modelContainer(for: Item.self, inMemory: true) +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/FooApp.swift b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/FooApp.swift new file mode 100644 index 0000000..f286f8b --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/FooApp.swift @@ -0,0 +1,32 @@ +// +// FooApp.swift +// Foo +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import SwiftUI +import SwiftData + +@main +struct FooApp: App { + var sharedModelContainer: ModelContainer = { + let schema = Schema([ + Item.self, + ]) + let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) + + do { + return try ModelContainer(for: schema, configurations: [modelConfiguration]) + } catch { + fatalError("Could not create ModelContainer: \(error)") + } + }() + + var body: some Scene { + WindowGroup { + ContentView() + } + .modelContainer(sharedModelContainer) + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Item.swift b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Item.swift new file mode 100644 index 0000000..3c8ac24 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Item.swift @@ -0,0 +1,18 @@ +// +// Item.swift +// Foo +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import Foundation +import SwiftData + +@Model +final class Item { + var timestamp: Date + + init(timestamp: Date) { + self.timestamp = timestamp + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Preview Content/Preview Assets.xcassets/Contents.json b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/Foo/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooTests/FooTests.swift b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooTests/FooTests.swift new file mode 100644 index 0000000..43b97f8 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooTests/FooTests.swift @@ -0,0 +1,36 @@ +// +// FooTests.swift +// FooTests +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import XCTest +@testable import Foo + +final class FooTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITests.swift b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITests.swift new file mode 100644 index 0000000..b90a9c4 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITests.swift @@ -0,0 +1,41 @@ +// +// FooUITests.swift +// FooUITests +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import XCTest + +final class FooUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITestsLaunchTests.swift b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITestsLaunchTests.swift new file mode 100644 index 0000000..57f906b --- /dev/null +++ b/TestAssets/WeirdContainerPaths/Infrastructure/Foo/FooUITests/FooUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// FooUITestsLaunchTests.swift +// FooUITests +// +// Created by Thomas Hedderwick on 18/10/2023. +// + +import XCTest + +final class FooUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/contents.xcworkspacedata b/TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..5247277 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WeirdContainerPaths/WeirdWorkspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + From 891f12130f62148610541a17656ba93aa5cdc8d5 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 1 Dec 2023 10:55:27 +0100 Subject: [PATCH 11/61] Attempt to fix workflow, remove dead code --- .github/workflows/periphery.yml | 1 + PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml index b645c49..c616c11 100644 --- a/.github/workflows/periphery.yml +++ b/.github/workflows/periphery.yml @@ -15,6 +15,7 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Install periphery run: | + brew update && brew upgrade brew install peripheryapp/periphery/periphery - name: Scan for unused code run: | diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift index 581c8a6..a205cdf 100644 --- a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift +++ b/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift @@ -81,7 +81,6 @@ struct FileRef: Reference { } return path + "/" + location.path - // return URL(fileURLWithPath: path).appendingPathComponent(location.path).path } } } From d0fd957ada63d8391dd68fe7bdb6005a9443a169 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 1 Dec 2023 11:07:28 +0100 Subject: [PATCH 12/61] Remove update, didn't help --- .github/workflows/periphery.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml index c616c11..b645c49 100644 --- a/.github/workflows/periphery.yml +++ b/.github/workflows/periphery.yml @@ -15,7 +15,6 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Install periphery run: | - brew update && brew upgrade brew install peripheryapp/periphery/periphery - name: Scan for unused code run: | From ec7b94090a13694029d5b359f3e271ef7b18ec29 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Wed, 23 Aug 2023 11:20:08 +0200 Subject: [PATCH 13/61] Adjust Xcode Project parsing This allows for determining the 'Link Binary with Libraries' build phase (which... for no reason... isn't listed as a dependency in the target?!) --- .../PBXProjParser/Models/PBXBuildFile.swift | 18 ++++++--- .../PBXProjParser/Models/PBXBuildPhase.swift | 14 ++++--- .../Models/PBXFileReference.swift | 10 +++-- .../PBXProjParser/Models/PBXTarget.swift | 9 +++-- .../Sources/PBXProjParser/XcodeProject.swift | 39 +++++++++++++++++++ 5 files changed, 72 insertions(+), 18 deletions(-) diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift index f40b5d9..25bc290 100644 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift +++ b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift @@ -8,33 +8,39 @@ import Foundation public class PBXBuildFile: PBXObject { -#if FULL_PBX_PARSING + public let productRef: String? public let fileRef: String? + + #if FULL_PBX_PARSING public let platformFilter: String? public let platformFilters: [String]? - public let productRef: String? public let settings: [String: Any]? + #endif private enum CodingKeys: String, CodingKey { + case productRef case fileRef + + #if FULL_PBX_PARSING case platformFilter case platformFilters - case productRef case settings + #endif } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - + productRef = try container.decodeIfPresent(String.self, forKey: .productRef) fileRef = try container.decodeIfPresent(String.self, forKey: .fileRef) + + #if FULL_PBX_PARSING platformFilter = try container.decodeIfPresent(String.self, forKey: .platformFilter) platformFilters = try container.decodeIfPresent([String].self, forKey: .platformFilters) - productRef = try container.decodeIfPresent(String.self, forKey: .productRef) settings = try container.decodeIfPresent([String: Any].self, forKey: .settings) + #endif try super.init(from: decoder) } -#endif } public class PBXBuildRule: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift index ffcc88b..cbc5d70 100644 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift +++ b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift @@ -8,35 +8,39 @@ import Foundation public class PBXBuildPhase: PBXObject { + public let files: [String] #if FULL_PBX_PARSING public let alwaysOutOfDate: String? public let buildActionMask: UInt32 - public let files: [String] public let runOnlyForDeploymentPostprocessing: Int +#endif private enum CodingKeys: String, CodingKey { + case files + #if FULL_PBX_PARSING case alwaysOutOfDate case buildActionMask - case files case runOnlyForDeploymentPostprocessing + #endif } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) + #if FULL_PBX_PARSING alwaysOutOfDate = try container.decodeIfPresent(String.self, forKey: .alwaysOutOfDate) let mask = try container.decode(String.self, forKey: .buildActionMask) buildActionMask = UInt32(mask) ?? 0 - files = try container.decode([String].self, forKey: .files) - let flag = try container.decode(String.self, forKey: .runOnlyForDeploymentPostprocessing) runOnlyForDeploymentPostprocessing = Int(flag) ?? 0 + #endif + + files = try container.decodeIfPresent([String].self, forKey: .files) ?? [] try super.init(from: decoder) } -#endif } public class PBXCopyFilesBuildPhase: PBXBuildPhase { diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift index 1f3385c..9dea7fd 100644 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift +++ b/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift @@ -10,37 +10,39 @@ import Foundation public class PBXFileReference: PBXObject { #if FULL_PBX_PARSING public let fileEncoding: String? - public let explicitFileType: String? public let includeInIndex: String? public let lastKnownFileType: String? public let name: String? public let sourceTree: String #endif + public let explicitFileType: String? public let path: String private enum CodingKeys: String, CodingKey { #if FULL_PBX_PARSING case fileEncoding - case explicitFileType case includeInIndex case lastKnownFileType case name case sourceTree #endif + case explicitFileType case path } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) + + explicitFileType = try container.decodeIfPresent(String.self, forKey: .explicitFileType) + path = try container.decode(String.self, forKey: .path) + #if FULL_PBX_PARSING fileEncoding = try container.decodeIfPresent(String.self, forKey: .fileEncoding) - explicitFileType = try container.decodeIfPresent(String.self, forKey: .explicitFileType) includeInIndex = try container.decodeIfPresent(String.self, forKey: .includeInIndex) lastKnownFileType = try container.decodeIfPresent(String.self, forKey: .lastKnownFileType) name = try container.decodeIfPresent(String.self, forKey: .name) sourceTree = try container.decode(String.self, forKey: .sourceTree) #endif - path = try container.decode(String.self, forKey: .path) try super.init(from: decoder) } diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift index c203bd1..26b458c 100644 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift +++ b/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift @@ -62,9 +62,9 @@ public class PBXLegacyTarget: PBXTarget {} public class PBXNativeTarget: PBXTarget { #if FULL_PBX_PARSING - public let buildPhases: [String] public let productInstallPath: String? #endif + public let buildPhases: [String] public let productType: String? public let productReference: String? public let packageProductDependencies: [String] @@ -74,6 +74,7 @@ public class PBXNativeTarget: PBXTarget { public enum TargetDependency { case native(PBXNativeTarget) case package(XCSwiftPackageProductDependency) + case externalProjectFramework(String) public var name: String { switch self { @@ -81,15 +82,17 @@ public class PBXNativeTarget: PBXTarget { return target.name case .package(let package): return package.productName + case .externalProjectFramework(let filename): + return (filename as NSString).deletingPathExtension } } } private enum CodingKeys: String, CodingKey { #if FULL_PBX_PARSING - case buildPhases case productInstallPath #endif + case buildPhases case productType case productReference case packageProductDependencies @@ -99,9 +102,9 @@ public class PBXNativeTarget: PBXTarget { let container = try decoder.container(keyedBy: CodingKeys.self) #if FULL_PBX_PARSING - buildPhases = try container.decode([String].self, forKey: .buildPhases) productInstallPath = try container.decodeIfPresent(String.self, forKey: .productInstallPath) #endif + buildPhases = try container.decodeIfPresent([String].self, forKey: .buildPhases) ?? [] productType = try container.decodeIfPresent(String.self, forKey: .productType) productReference = try container.decodeIfPresent(String.self, forKey: .productReference) packageProductDependencies = try container.decodeIfPresent([String].self, forKey: .packageProductDependencies) ?? [] diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift index 34f7219..d5eb2c7 100644 --- a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift +++ b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift @@ -104,6 +104,30 @@ public struct XcodeProject { target.packageProductDependencies .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } .forEach { target.add(dependency: .package($0)) } + + // Calculate the dependencies from "Link Binary with Library" build phase + let buildFiles = determineBuildPhaseFrameworkDependencies(target, with: model) + + // Now, we have two potential targets - file & package dependencies. + // File dependencies will likely have a reference in another Xcode Project. We might not have seen said project yet, so we need to offload discovery until after we've parsed all projects... + // Package dependencies will be a swift package - those we can handle easily :) + + // ONE: package dependencies - they are the easiest + buildFiles + .compactMap { $0.productRef } + .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } + .forEach { target.add(dependency: .package($0)) } + + // TWO: Resolve dependencies to... a thing that refers to something in the other project + let fileReferences = buildFiles + .compactMap { $0.fileRef } + .compactMap { model.object(forKey: $0, as: PBXFileReference.self) } + + fileReferences + .filter { $0.explicitFileType == "wrapper.framework" } + .compactMap { $0.path } // TODO: do we want to last path component the path here? Need to figure out matching... + .filter { !$0.contains("System/Library/Frameworks/")} // System frameworks will contain this path + .forEach { target.add(dependency: .externalProjectFramework($0)) } } /// Determines transitive dependencies by looping through direct dependencies and finding the items they depend on @@ -165,3 +189,18 @@ public struct XcodeProject { return path } } + +private func determineBuildPhaseFrameworkDependencies(_ target: PBXNativeTarget, with model: PBXProj) -> [PBXBuildFile] { + // Find the 'Link Binary with Libraries' build phase + let buildPhase = target.buildPhases + .compactMap { model.object(forKey: $0, as: PBXFrameworksBuildPhase.self) } + .first + + guard let buildPhase else { + logger.debug("No PBXFrameworkBuild phase for target: \(target) found, continuing.") + return [] + } + + return buildPhase.files + .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } +} From de3fcee487f64e733caaf21457dc00117625d0a4 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Wed, 23 Aug 2023 11:38:30 +0200 Subject: [PATCH 14/61] Add dry run to BuildCacheManipulator --- Sources/GenIR/BuildCacheManipulator.swift | 11 ++++++++--- Sources/GenIR/GenIR.swift | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index 35def63..00ea025 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -8,6 +8,8 @@ struct BuildCacheManipulator { /// Build settings used as part of the build private let buildSettings: [String: String] + private let dryRun: Bool + /// Should we run the SKIP_INSTALL hack? private let shouldDeploySkipInstallHack: Bool @@ -19,14 +21,17 @@ struct BuildCacheManipulator { case tooManyDirectories(String) } - init(buildCachePath: URL, buildSettings: [String: String], archive: URL) throws { + init(buildCachePath: URL, buildSettings: [String: String], archive: URL, dryRun: Bool) throws { self.buildCachePath = buildCachePath self.buildSettings = buildSettings + self.dryRun = dryRun buildProductsPath = archive shouldDeploySkipInstallHack = self.buildSettings["SKIP_INSTALL"] == "NO" - guard FileManager.default.directoryExists(at: buildCachePath) else { - throw Error.directoryNotFound("Build cache path doesn't exist at expected path: \(buildCachePath)") + if !dryRun { + guard FileManager.default.directoryExists(at: buildCachePath) else { + throw Error.directoryNotFound("Build cache path doesn't exist at expected path: \(buildCachePath)") + } } } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 910c4c7..7875d8a 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -119,7 +119,8 @@ struct IREmitterCommand: ParsableCommand { let buildCacheManipulator = try BuildCacheManipulator( buildCachePath: log.buildCachePath, buildSettings: log.settings, - archive: archive + archive: archive, + dryRun: dryRun ) let runner = CompilerCommandRunner( From 8d99b85342e51a01664c40a110247e0251ac0d80 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Wed, 23 Aug 2023 11:39:25 +0200 Subject: [PATCH 15/61] Link targets & IR folders --- .gitignore | 2 +- Sources/GenIR/OutputPostprocessor.swift | 29 ++++++++++++++----------- Sources/GenIR/Targets/Target.swift | 3 +++ Sources/GenIR/Targets/Targets.swift | 18 ++++++++------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index eaa8671..40915b2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ output/ *.bc *.dia _build/ -**/.build/* +**/.build/ diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index e5484a5..4e9fc0d 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -28,8 +28,8 @@ struct OutputPostprocessor { /// Starts the OutputPostprocessor /// - Parameter targets: the targets to operate on func process(targets: inout Targets) throws { - let targetsToPaths = try FileManager.default.directories(at: output, recursive: false) - .reduce(into: [Target: URL]()) { partialResult, path in + try FileManager.default.directories(at: output, recursive: false) + .forEach { path in let product = path.lastPathComponent.deletingPathExtension() guard let target = targets.target(for: product) else { @@ -37,18 +37,21 @@ struct OutputPostprocessor { return } - partialResult[target] = path + target.irFolderPath = path } // TODO: remove 'static' deps so we don't duplicate them in the submission? - _ = try targets.flatMap { target in - guard let path = targetsToPaths[target] else { - logger.error("Couldn't find path for target: \(target)") - return Set() - } + _ = try FileManager.default.directories(at: output, recursive: false) + .flatMap { path in + let product = path.lastPathComponent.deletingPathExtension() - return try process(target: target, in: targets, at: path, with: targetsToPaths) - } + guard let target = targets.target(for: product) else { + logger.error("Failed to look up target for product: \(product)") + return Set() + } + + return try process(target: target, in: targets, at: path/*, with: targetsToPaths*/) + } } /// Processes an individual target @@ -61,9 +64,9 @@ struct OutputPostprocessor { private func process( target: Target, in targets: Targets, - at path: URL, - with targetsToPaths: [Target: URL] + at path: URL ) throws -> Set { + logger.debug("Processing -- \(target.name)") let dependencies = targets.calculateDependencies(for: target) let staticDependencies = dependencies @@ -76,7 +79,7 @@ struct OutputPostprocessor { return nil } - guard let dependencyPath = targetsToPaths[dependencyTarget] else { + guard let dependencyPath = dependencyTarget.irFolderPath else { logger.debug("Failed to lookup path for target: \(dependencyTarget.name)") return nil } diff --git a/Sources/GenIR/Targets/Target.swift b/Sources/GenIR/Targets/Target.swift index ef757da..6d4835d 100644 --- a/Sources/GenIR/Targets/Target.swift +++ b/Sources/GenIR/Targets/Target.swift @@ -67,6 +67,9 @@ class Target { } }() + /// The path to the IR folder on disk + var irFolderPath: URL? + init( name: String, backingTarget: BackingTarget? = nil, diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift index 32862d9..b3aebe7 100644 --- a/Sources/GenIR/Targets/Targets.swift +++ b/Sources/GenIR/Targets/Targets.swift @@ -69,16 +69,18 @@ struct Targets { // TODO: maybe specialize a product vs name lookup for those sweet sweet milliseconds func target(for key: String) -> Target? { - if let result = targets.filter({ $0.name == key }).first { - return result + for target in targets { + if target.name == key { + return target + } else if target.productName == key { + return target + } else if target.path == key { + return target + } } - if let result = targets.filter({ $0.productName == key }).first { - return result - } - - if let result = targets.filter({ $0.path == key }).first { - return result + for target in targets where target.nameForOutput.deletingPathExtension() == key { + return target } return nil From a38e8dde9518d7fdf53d5154406b466a2f7dc529 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Wed, 23 Aug 2023 14:13:50 +0200 Subject: [PATCH 16/61] Add some logging for later debugging :) --- Sources/GenIR/Targets/Targets.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift index b3aebe7..0a0568d 100644 --- a/Sources/GenIR/Targets/Targets.swift +++ b/Sources/GenIR/Targets/Targets.swift @@ -89,13 +89,15 @@ struct Targets { // TODO: once we stabilize Targets, this should return a Set not [String] func calculateDependencies(for target: Target) -> [String] { // TODO: eventually we'd like to move some of the project dependencies calculations here - let dependencies = project.dependencies(for: target.name) + var dependencies = project.dependencies(for: target.name) if dependencies.count == 0, let productName = target.productName { // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... - return project.dependencies(for: productName) + dependencies = project.dependencies(for: productName) } + logger.debug("Calculated dependencies for target: \(target.name). Dependencies: \(dependencies)") + return dependencies } } From 733c424ab79ac4f368256adbbb7c7a7e6b5fe02e Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 4 Sep 2023 10:20:46 +0200 Subject: [PATCH 17/61] WIP --- .../Dependency Graph/DependencyGraph.swift | 112 +++++ .../DependencyGraphBuilder.swift | 51 +++ Sources/GenIR/Dependency Graph/Edge.swift | 24 + Sources/GenIR/Dependency Graph/Node.swift | 51 +++ Sources/GenIR/GenIR.swift | 2 +- Sources/GenIR/OutputPostprocessor.swift | 20 +- Sources/GenIR/Targets/Targets.swift | 8 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../App/App.xcodeproj/project.pbxproj | 369 ++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + TestAssets/WorkspaceTest/App/App/AppApp.swift | 17 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../App/App/Assets.xcassets/Contents.json | 6 + .../WorkspaceTest/App/App/ContentView.swift | 27 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../Common/Common.xcodeproj/project.pbxproj | 361 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../WorkspaceTest/Common/Common/Common.h | 18 + .../WorkspaceTest/Common/Common/Model.swift | 17 + .../Framework.xcodeproj/project.pbxproj | 416 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../Framework/Framework/Framework.h | 18 + .../Framework/Framework/Framework.swift | 21 + .../contents.xcworkspacedata | 13 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 14 + Tests/GenIRTests/TestContext.swift | 18 +- Tests/GenIRTests/WorkspaceTests.swift | 33 ++ 33 files changed, 1705 insertions(+), 10 deletions(-) create mode 100644 Sources/GenIR/Dependency Graph/DependencyGraph.swift create mode 100644 Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift create mode 100644 Sources/GenIR/Dependency Graph/Edge.swift create mode 100644 Sources/GenIR/Dependency Graph/Node.swift create mode 100644 TestAssets/CocoapodsFrameworkTest/CocoapodsFrameworkTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/ProjectInProject/ProjectInProject.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceTest/App/App.xcodeproj/project.pbxproj create mode 100644 TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceTest/App/App/AppApp.swift create mode 100644 TestAssets/WorkspaceTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 TestAssets/WorkspaceTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TestAssets/WorkspaceTest/App/App/Assets.xcassets/Contents.json create mode 100644 TestAssets/WorkspaceTest/App/App/ContentView.swift create mode 100644 TestAssets/WorkspaceTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.pbxproj create mode 100644 TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceTest/Common/Common/Common.h create mode 100644 TestAssets/WorkspaceTest/Common/Common/Model.swift create mode 100644 TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.pbxproj create mode 100644 TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceTest/Framework/Framework/Framework.h create mode 100644 TestAssets/WorkspaceTest/Framework/Framework/Framework.swift create mode 100644 TestAssets/WorkspaceTest/Workspace.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Tests/GenIRTests/WorkspaceTests.swift diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift new file mode 100644 index 0000000..5651757 --- /dev/null +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -0,0 +1,112 @@ +// +// DependencyGraph.swift +// +// +// Created by Thomas Hedderwick on 28/08/2023. +// + +class DependencyGraph { + private(set) var nodes: [Node] = [] + + func addNode(target: Target) -> Node? { + // Don't add nodes we've already added + if findNode(for: target) != nil { + return nil + } + + let node = Node(target) + nodes.append(node) + return node + } + + func addEdge(from source: Node, to destination: Node) { + source.add(neighbor: .init(neighbor: destination)) + } + + func findNode(for target: Target) -> Node? { + nodes.first(where: { $0.target == target }) + } + + func search(_ target: Target) -> [Node] { + guard let targetNode = findNode(for: target) else { + logger.debug("Couldn't find node for target: \(target.name)") + return [] + } + + return depthFirstSearch(startingAt: targetNode) + } + + func depthFirstSearch(startingAt node: Node) -> [Node] { + logger.info("----\nSearching for: \(node.target.name)") + var visited = Set() + var chain = [Node]() + + func depthFirst(node: Node) { + logger.info("inserting node: \(node.target.name)") + visited.insert(node) + logger.info("visited: \(visited)") + + for edge in node.neighbors { + logger.info("edge to: \(edge.neighbor)") + if visited.insert(edge.neighbor).inserted { + logger.info("inserted, recursing") + depthFirst(node: edge.neighbor) + } else { + logger.info("edge already in visited: \(visited)") + } + } + + logger.info("appending to chain: \(node.target.name)") + chain.append(node) + } + + depthFirst(node: node) + return chain + } + + + + /// Builds a dependency chain in the order of which dependencies should be operated on + /// - Parameter target: the target to build a chain for + /// - Returns: an array of nodes, ordered in the way they should be operated on + func buildChain(for target: Target) -> [Node]? { + guard let targetNode = findNode(for: target) else { + logger.debug("Couldn't find node for target: \(target.name)") + return nil + } + + func depthFirst(startingAt node: Node, visited: inout Set) -> [Node] { + logger.debug("search ------\nstart: \(node). Visited: \(visited)") + var chain = [node] + visited.insert(node) + + for edge in node.neighbors where visited.insert(edge.neighbor).inserted { + logger.debug("found edge: \(edge.neighbor).") + chain += depthFirst(startingAt: edge.neighbor, visited: &visited) + } + + return chain + } + + /// do a depth first search + var visited = Set() + let chain = depthFirst(startingAt: targetNode, visited: &visited) + + print("chain: \(chain)") + + return chain + } +} + +extension DependencyGraph: CustomStringConvertible { + var description: String { + var description = "" + + nodes + .forEach { + description += "[\($0)]" + } + + return description + } +} diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift new file mode 100644 index 0000000..a177e53 --- /dev/null +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -0,0 +1,51 @@ +// +// DependencyGraphBuilder.swift +// +// +// Created by Thomas Hedderwick on 28/08/2023. +// + +class DependencyGraphBuilder { + let targets: Targets + + init(targets: Targets) { + self.targets = targets + } + + func build() -> DependencyGraph { + let graph = DependencyGraph() + + for target in targets { + addToGraph(graph, target: target) + } + + return graph + } + + func addToGraph(_ graph: DependencyGraph, target: Target) { + logger.debug("Adding target: \(target.name) to graph") + + guard let node = graph.addNode(target: target) else { + logger.debug("Already inserted node: \(target.name). Skipping.") + return + } + + let dependencies = targets.calculateDependencies(for: target) + + for dependency in dependencies { + guard let dependencyTarget = targets.target(for: dependency) else { + logger.debug("Couldn't lookup dependency in targets: \(dependency)") + continue + } + + addToGraph(graph, target: dependencyTarget) + + guard let dependencyNode = graph.findNode(for: dependencyTarget) else { + logger.debug("Couldn't find node for target (\(dependencyTarget.name)) even though it was just inserted?") + continue + } + + node.add(neighbor: .init(neighbor: dependencyNode)) + } + } +} diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift new file mode 100644 index 0000000..dcba712 --- /dev/null +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -0,0 +1,24 @@ +// +// Edge.swift +// +// +// Created by Thomas Hedderwick on 28/08/2023. +// + +class Edge { + let neighbor: Node + + init(neighbor: Node) { + self.neighbor = neighbor + } +} + +extension Edge: Equatable { + static func == (_ lhs: Edge, rhs: Edge) -> Bool { + lhs.neighbor == rhs.neighbor + } +} + +extension Edge: CustomStringConvertible { + var description: String { "[Edge: \(neighbor)]"} +} diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift new file mode 100644 index 0000000..7436cc4 --- /dev/null +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -0,0 +1,51 @@ +// +// Node.swift +// +// +// Created by Thomas Hedderwick on 28/08/2023. +// + +import Foundation + +class Node { + private(set) var neighbors: [Edge] = [] + let target: Target + let name: String + let uuid: UUID + + init(_ target: Target) { + self.target = target + self.name = target.name + self.uuid = UUID() + } + + func add(neighbor: Edge) { + neighbors.append(neighbor) + } +} + +extension Node: Equatable { + static func == (_ lhs: Node, rhs: Node) -> Bool { + lhs.target == rhs.target && lhs.neighbors == rhs.neighbors + } +} + +extension Node: CustomStringConvertible { + var description: String { + var description = "" + + if !neighbors.isEmpty { + description += "[Node: \(target.name), edges: \(neighbors.map { $0.neighbor.target.name})] " + } else { + description += "[Node: \(target.name)] " + } + + return description + } +} + +extension Node: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(uuid) + } +} diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 7875d8a..f632274 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -130,7 +130,7 @@ struct IREmitterCommand: ParsableCommand { ) try runner.run(targets: targets) - let postprocessor = try OutputPostprocessor(archive: archive, output: output) + let postprocessor = try OutputPostprocessor(archive: archive, output: output, targets: targets) try postprocessor.process(targets: &targets) } // swiftlint:enable function_parameter_count diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 4e9fc0d..144412a 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -19,10 +19,14 @@ struct OutputPostprocessor { /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk private let dynamicDependencyToPath: [String: URL] - init(archive: URL, output: URL) throws { + private let graph: DependencyGraph + + init(archive: URL, output: URL, targets: Targets) throws { self.output = output dynamicDependencyToPath = dynamicDependencies(in: archive) + + graph = DependencyGraphBuilder(targets: targets).build() } /// Starts the OutputPostprocessor @@ -66,7 +70,19 @@ struct OutputPostprocessor { in targets: Targets, at path: URL ) throws -> Set { - logger.debug("Processing -- \(target.name)") + + // guard let chain = graph.buildChain(for: target) else { + // logger.debug("Uh oh...") + // return [] + // } + let chain = graph.search(target) + + logger.info("Chain for target: \(target.name):\n\(chain)") + + + + + logger.debug("Processing -- \(target.name)") let dependencies = targets.calculateDependencies(for: target) let staticDependencies = dependencies diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift index 0a0568d..7d9e582 100644 --- a/Sources/GenIR/Targets/Targets.swift +++ b/Sources/GenIR/Targets/Targets.swift @@ -9,7 +9,7 @@ import Foundation import PBXProjParser /// Represents a collection of `Target`s -struct Targets { +class Targets { /// The underlying storage of `Target`s private(set) var targets: Set = [] @@ -34,7 +34,7 @@ struct Targets { /// - Parameter target: the element to insert /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. @discardableResult - mutating func insert(native target: PBXNativeTarget) -> (inserted: Bool, memberAfterInsert: Element) { + func insert(native target: PBXNativeTarget) -> (inserted: Bool, memberAfterInsert: Element) { let newTarget = Target( name: target.name, backingTarget: .native(target), @@ -48,7 +48,7 @@ struct Targets { /// - Parameter package: the element to insert /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. @discardableResult - mutating func insert(package target: XCSwiftPackageProductDependency) -> (inserted: Bool, memberAfterInsert: Element) { + func insert(package target: XCSwiftPackageProductDependency) -> (inserted: Bool, memberAfterInsert: Element) { // TODO: when we can handle SPM transitive deps, should we look up the name here? Can we even do that? let newTarget = Target( name: target.productName, @@ -63,7 +63,7 @@ struct Targets { /// - Parameter target: the element to insert /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. @discardableResult - mutating func insert(target: Target) -> (inserted: Bool, memberAfterInsert: Element) { + func insert(target: Target) -> (inserted: Bool, memberAfterInsert: Element) { targets.insert(target) } diff --git a/TestAssets/CocoapodsFrameworkTest/CocoapodsFrameworkTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/CocoapodsFrameworkTest/CocoapodsFrameworkTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/CocoapodsFrameworkTest/CocoapodsFrameworkTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/ProjectInProject/ProjectInProject.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/ProjectInProject/ProjectInProject.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/ProjectInProject/ProjectInProject.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceTest/App/App.xcodeproj/project.pbxproj b/TestAssets/WorkspaceTest/App/App.xcodeproj/project.pbxproj new file mode 100644 index 0000000..55652ce --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App.xcodeproj/project.pbxproj @@ -0,0 +1,369 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CE1570522A978AB700520802 /* AppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1570512A978AB700520802 /* AppApp.swift */; }; + CE1570542A978AB700520802 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1570532A978AB700520802 /* ContentView.swift */; }; + CE1570562A978ABA00520802 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE1570552A978ABA00520802 /* Assets.xcassets */; }; + CE1570592A978ABA00520802 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE1570582A978ABA00520802 /* Preview Assets.xcassets */; }; + CE1570922A9794A500520802 /* Framework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1570912A9794A500520802 /* Framework.framework */; }; + CE1570942A9794BC00520802 /* Common.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1570932A9794BC00520802 /* Common.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CE15704E2A978AB700520802 /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CE1570512A978AB700520802 /* AppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppApp.swift; sourceTree = ""; }; + CE1570532A978AB700520802 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + CE1570552A978ABA00520802 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CE1570582A978ABA00520802 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + CE1570912A9794A500520802 /* Framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Framework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE1570932A9794BC00520802 /* Common.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Common.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE15704B2A978AB700520802 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1570942A9794BC00520802 /* Common.framework in Frameworks */, + CE1570922A9794A500520802 /* Framework.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE1570452A978AB700520802 = { + isa = PBXGroup; + children = ( + CE1570502A978AB700520802 /* App */, + CE15704F2A978AB700520802 /* Products */, + CE1570902A9794A500520802 /* Frameworks */, + ); + sourceTree = ""; + }; + CE15704F2A978AB700520802 /* Products */ = { + isa = PBXGroup; + children = ( + CE15704E2A978AB700520802 /* App.app */, + ); + name = Products; + sourceTree = ""; + }; + CE1570502A978AB700520802 /* App */ = { + isa = PBXGroup; + children = ( + CE1570512A978AB700520802 /* AppApp.swift */, + CE1570532A978AB700520802 /* ContentView.swift */, + CE1570552A978ABA00520802 /* Assets.xcassets */, + CE1570572A978ABA00520802 /* Preview Content */, + ); + path = App; + sourceTree = ""; + }; + CE1570572A978ABA00520802 /* Preview Content */ = { + isa = PBXGroup; + children = ( + CE1570582A978ABA00520802 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + CE1570902A9794A500520802 /* Frameworks */ = { + isa = PBXGroup; + children = ( + CE1570932A9794BC00520802 /* Common.framework */, + CE1570912A9794A500520802 /* Framework.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CE15704D2A978AB700520802 /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE15705C2A978ABA00520802 /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + CE15704A2A978AB700520802 /* Sources */, + CE15704B2A978AB700520802 /* Frameworks */, + CE15704C2A978AB700520802 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = App; + productName = App; + productReference = CE15704E2A978AB700520802 /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE1570462A978AB700520802 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + CE15704D2A978AB700520802 = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = CE1570492A978AB700520802 /* Build configuration list for PBXProject "App" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE1570452A978AB700520802; + productRefGroup = CE15704F2A978AB700520802 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE15704D2A978AB700520802 /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE15704C2A978AB700520802 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1570592A978ABA00520802 /* Preview Assets.xcassets in Resources */, + CE1570562A978ABA00520802 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE15704A2A978AB700520802 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1570542A978AB700520802 /* ContentView.swift in Sources */, + CE1570522A978AB700520802 /* AppApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE15705A2A978ABA00520802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CE15705B2A978ABA00520802 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CE15705D2A978ABA00520802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.App; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE15705E2A978ABA00520802 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.App; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE1570492A978AB700520802 /* Build configuration list for PBXProject "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE15705A2A978ABA00520802 /* Debug */, + CE15705B2A978ABA00520802 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE15705C2A978ABA00520802 /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE15705D2A978ABA00520802 /* Debug */, + CE15705E2A978ABA00520802 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE1570462A978AB700520802 /* Project object */; +} diff --git a/TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceTest/App/App/AppApp.swift b/TestAssets/WorkspaceTest/App/App/AppApp.swift new file mode 100644 index 0000000..6ec6300 --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App/AppApp.swift @@ -0,0 +1,17 @@ +// +// AppApp.swift +// App +// +// Created by Thomas Hedderwick on 24/08/2023. +// + +import SwiftUI + +@main +struct AppApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/TestAssets/WorkspaceTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json b/TestAssets/WorkspaceTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestAssets/WorkspaceTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceTest/App/App/Assets.xcassets/Contents.json b/TestAssets/WorkspaceTest/App/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceTest/App/App/ContentView.swift b/TestAssets/WorkspaceTest/App/App/ContentView.swift new file mode 100644 index 0000000..81712eb --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App/ContentView.swift @@ -0,0 +1,27 @@ +// +// ContentView.swift +// App +// +// Created by Thomas Hedderwick on 24/08/2023. +// + +import SwiftUI +import Framework + +struct ContentView: View { + let framework: Framework = .init(model: .init(name: "ContentView")) + + var body: some View { + VStack { + framework.icon + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, \(framework.model.name)!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/TestAssets/WorkspaceTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json b/TestAssets/WorkspaceTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/WorkspaceTest/App/App/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.pbxproj b/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.pbxproj new file mode 100644 index 0000000..877d60b --- /dev/null +++ b/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.pbxproj @@ -0,0 +1,361 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CE1570832A978E1E00520802 /* Common.h in Headers */ = {isa = PBXBuildFile; fileRef = CE1570822A978E1E00520802 /* Common.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE15708A2A978E3D00520802 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1570892A978E3D00520802 /* Model.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CE15707F2A978E1E00520802 /* Common.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Common.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE1570822A978E1E00520802 /* Common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; }; + CE1570892A978E3D00520802 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE15707C2A978E1E00520802 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE1570752A978E1E00520802 = { + isa = PBXGroup; + children = ( + CE1570812A978E1E00520802 /* Common */, + CE1570802A978E1E00520802 /* Products */, + ); + sourceTree = ""; + }; + CE1570802A978E1E00520802 /* Products */ = { + isa = PBXGroup; + children = ( + CE15707F2A978E1E00520802 /* Common.framework */, + ); + name = Products; + sourceTree = ""; + }; + CE1570812A978E1E00520802 /* Common */ = { + isa = PBXGroup; + children = ( + CE1570822A978E1E00520802 /* Common.h */, + CE1570892A978E3D00520802 /* Model.swift */, + ); + path = Common; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CE15707A2A978E1E00520802 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1570832A978E1E00520802 /* Common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CE15707E2A978E1E00520802 /* Common */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE1570862A978E1E00520802 /* Build configuration list for PBXNativeTarget "Common" */; + buildPhases = ( + CE15707A2A978E1E00520802 /* Headers */, + CE15707B2A978E1E00520802 /* Sources */, + CE15707C2A978E1E00520802 /* Frameworks */, + CE15707D2A978E1E00520802 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Common; + productName = Common; + productReference = CE15707F2A978E1E00520802 /* Common.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE1570762A978E1E00520802 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1500; + TargetAttributes = { + CE15707E2A978E1E00520802 = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1500; + }; + }; + }; + buildConfigurationList = CE1570792A978E1E00520802 /* Build configuration list for PBXProject "Common" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE1570752A978E1E00520802; + productRefGroup = CE1570802A978E1E00520802 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE15707E2A978E1E00520802 /* Common */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE15707D2A978E1E00520802 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE15707B2A978E1E00520802 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE15708A2A978E3D00520802 /* Model.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE1570842A978E1E00520802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CE1570852A978E1E00520802 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CE1570872A978E1E00520802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.Common; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE1570882A978E1E00520802 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.Common; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE1570792A978E1E00520802 /* Build configuration list for PBXProject "Common" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE1570842A978E1E00520802 /* Debug */, + CE1570852A978E1E00520802 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE1570862A978E1E00520802 /* Build configuration list for PBXNativeTarget "Common" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE1570872A978E1E00520802 /* Debug */, + CE1570882A978E1E00520802 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE1570762A978E1E00520802 /* Project object */; +} diff --git a/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceTest/Common/Common.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceTest/Common/Common/Common.h b/TestAssets/WorkspaceTest/Common/Common/Common.h new file mode 100644 index 0000000..0fac0b3 --- /dev/null +++ b/TestAssets/WorkspaceTest/Common/Common/Common.h @@ -0,0 +1,18 @@ +// +// Common.h +// Common +// +// Created by Thomas Hedderwick on 24/08/2023. +// + +#import + +//! Project version number for Common. +FOUNDATION_EXPORT double CommonVersionNumber; + +//! Project version string for Common. +FOUNDATION_EXPORT const unsigned char CommonVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/TestAssets/WorkspaceTest/Common/Common/Model.swift b/TestAssets/WorkspaceTest/Common/Common/Model.swift new file mode 100644 index 0000000..fd1d3ce --- /dev/null +++ b/TestAssets/WorkspaceTest/Common/Common/Model.swift @@ -0,0 +1,17 @@ +// +// Model.swift +// Common +// +// Created by Thomas Hedderwick on 24/08/2023. +// + +import Foundation + +public struct Model { + public let uuid: UUID = .init() + public let name: String + + public init(name: String) { + self.name = name + } +} diff --git a/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.pbxproj b/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fbcc3c8 --- /dev/null +++ b/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.pbxproj @@ -0,0 +1,416 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CE15706D2A978B4700520802 /* Framework.h in Headers */ = {isa = PBXBuildFile; fileRef = CE15706C2A978B4700520802 /* Framework.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE1570742A978BD300520802 /* Framework.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1570732A978BD300520802 /* Framework.swift */; }; + CE15708D2A97931E00520802 /* Common.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15708C2A97931E00520802 /* Common.framework */; }; + CE15708E2A97931E00520802 /* Common.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CE15708C2A97931E00520802 /* Common.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CE1570972A979D4400520802 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = CE1570962A979D4400520802 /* SFSafeSymbols */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + CE15708F2A97931E00520802 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CE15708E2A97931E00520802 /* Common.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + CE1570692A978B4700520802 /* Framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE15706C2A978B4700520802 /* Framework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Framework.h; sourceTree = ""; }; + CE1570732A978BD300520802 /* Framework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Framework.swift; sourceTree = ""; }; + CE15708C2A97931E00520802 /* Common.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Common.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE1570662A978B4700520802 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1570972A979D4400520802 /* SFSafeSymbols in Frameworks */, + CE15708D2A97931E00520802 /* Common.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE15705F2A978B4700520802 = { + isa = PBXGroup; + children = ( + CE15706B2A978B4700520802 /* Framework */, + CE15706A2A978B4700520802 /* Products */, + CE15708B2A97931E00520802 /* Frameworks */, + ); + sourceTree = ""; + }; + CE15706A2A978B4700520802 /* Products */ = { + isa = PBXGroup; + children = ( + CE1570692A978B4700520802 /* Framework.framework */, + ); + name = Products; + sourceTree = ""; + }; + CE15706B2A978B4700520802 /* Framework */ = { + isa = PBXGroup; + children = ( + CE15706C2A978B4700520802 /* Framework.h */, + CE1570732A978BD300520802 /* Framework.swift */, + ); + path = Framework; + sourceTree = ""; + }; + CE15708B2A97931E00520802 /* Frameworks */ = { + isa = PBXGroup; + children = ( + CE15708C2A97931E00520802 /* Common.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CE1570642A978B4700520802 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CE15706D2A978B4700520802 /* Framework.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CE1570682A978B4700520802 /* Framework */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE1570702A978B4700520802 /* Build configuration list for PBXNativeTarget "Framework" */; + buildPhases = ( + CE1570642A978B4700520802 /* Headers */, + CE1570652A978B4700520802 /* Sources */, + CE1570662A978B4700520802 /* Frameworks */, + CE1570672A978B4700520802 /* Resources */, + CE15708F2A97931E00520802 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Framework; + packageProductDependencies = ( + CE1570962A979D4400520802 /* SFSafeSymbols */, + ); + productName = Framework; + productReference = CE1570692A978B4700520802 /* Framework.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE1570602A978B4700520802 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1500; + TargetAttributes = { + CE1570682A978B4700520802 = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1500; + }; + }; + }; + buildConfigurationList = CE1570632A978B4700520802 /* Build configuration list for PBXProject "Framework" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE15705F2A978B4700520802; + packageReferences = ( + CE1570952A979D4400520802 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */, + ); + productRefGroup = CE15706A2A978B4700520802 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE1570682A978B4700520802 /* Framework */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE1570672A978B4700520802 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE1570652A978B4700520802 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1570742A978BD300520802 /* Framework.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE15706E2A978B4700520802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CE15706F2A978B4700520802 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CE1570712A978B4700520802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.Framework; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE1570722A978B4700520802 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.Framework; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE1570632A978B4700520802 /* Build configuration list for PBXProject "Framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE15706E2A978B4700520802 /* Debug */, + CE15706F2A978B4700520802 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE1570702A978B4700520802 /* Build configuration list for PBXNativeTarget "Framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE1570712A978B4700520802 /* Debug */, + CE1570722A978B4700520802 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CE1570952A979D4400520802 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.1.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CE1570962A979D4400520802 /* SFSafeSymbols */ = { + isa = XCSwiftPackageProductDependency; + package = CE1570952A979D4400520802 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */; + productName = SFSafeSymbols; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = CE1570602A978B4700520802 /* Project object */; +} diff --git a/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceTest/Framework/Framework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceTest/Framework/Framework/Framework.h b/TestAssets/WorkspaceTest/Framework/Framework/Framework.h new file mode 100644 index 0000000..c565134 --- /dev/null +++ b/TestAssets/WorkspaceTest/Framework/Framework/Framework.h @@ -0,0 +1,18 @@ +// +// Framework.h +// Framework +// +// Created by Thomas Hedderwick on 24/08/2023. +// + +#import + +//! Project version number for Framework. +FOUNDATION_EXPORT double FrameworkVersionNumber; + +//! Project version string for Framework. +FOUNDATION_EXPORT const unsigned char FrameworkVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/TestAssets/WorkspaceTest/Framework/Framework/Framework.swift b/TestAssets/WorkspaceTest/Framework/Framework/Framework.swift new file mode 100644 index 0000000..6298511 --- /dev/null +++ b/TestAssets/WorkspaceTest/Framework/Framework/Framework.swift @@ -0,0 +1,21 @@ +// +// Framework.swift +// Framework +// +// Created by Thomas Hedderwick on 24/08/2023. +// + +import SwiftUI +import Common +import SFSafeSymbols + +public struct Framework { + public let uuid: UUID = UUID() + public let model: Model + public let icon: Image + + public init(model: Model) { + self.model = model + self.icon = .init(systemSymbol: .globe) + } +} diff --git a/TestAssets/WorkspaceTest/Workspace.xcworkspace/contents.xcworkspacedata b/TestAssets/WorkspaceTest/Workspace.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..6fb05b9 --- /dev/null +++ b/TestAssets/WorkspaceTest/Workspace.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..e8341d6 --- /dev/null +++ b/TestAssets/WorkspaceTest/Workspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "sfsafesymbols", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SFSafeSymbols/SFSafeSymbols", + "state" : { + "revision" : "7cca2d60925876b5953a2cf7341cd80fbeac983c", + "version" : "4.1.1" + } + } + ], + "version" : 2 +} diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 2049559..661ebfb 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -4,6 +4,7 @@ import Foundation class TestContext { enum Error: Swift.Error { case commandFailed(Process.ReturnValue) + case invalidArgument(String) } static let baseTestingPath: URL = { @@ -15,10 +16,21 @@ class TestContext { baseTestingPath.appendingPathComponent("TestAssets") }() - func clean(test path: URL) throws -> Process.ReturnValue { + func clean(test path: URL, scheme: String) throws -> Process.ReturnValue { + var arguments = ["clean"] + + switch path.pathExtension { + case "xcodeproj": + arguments.append(contentsOf: ["-project", path.filePath]) + case "xcworkspace": + arguments.append(contentsOf: ["-workspace", path.filePath, "-scheme", scheme]) + default: + throw Error.invalidArgument("path passed to clean(test:scheme:) was not xcodeproj or xcworkspace") + } + return try Process.runShell( "/usr/bin/xcodebuild", - arguments: ["clean"], + arguments: arguments, runInDirectory: path.deletingLastPathComponent(), joinPipes: true ) @@ -29,7 +41,7 @@ class TestContext { scheme: String, additionalArguments: [String] = [] ) throws -> Process.ReturnValue { - let clean = try clean(test: path) + let clean = try clean(test: path, scheme: scheme) guard clean.code == 0 else { throw Error.commandFailed(clean) diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift new file mode 100644 index 0000000..2c0cf97 --- /dev/null +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -0,0 +1,33 @@ +import XCTest +@testable import gen_ir +import PBXProjParser + +final class WorkspaceTests: XCTestCase { + static private var testPath: URL = { + TestContext.testAssetPath + .appendingPathComponent("WorkspaceTest") + .appendingPathComponent("Workspace.xcworkspace") + }() + + static private var scheme = "App" + + func testWorkspace() throws { + let context = try TestContext() + let process = try context.build(test: Self.testPath, scheme: Self.scheme) + XCTAssertEqual(process.code, 0, "Failed to build test case") + + let output = context.archive.appendingPathComponent("IR") + var genIR = gen_ir.IREmitterCommand() + + try genIR.run( + project: Self.testPath, + log: context.buildLog.filePath, + archive: context.archive, + output: output, + level: .debug, + dryRun: false + ) + + print("ran Gen IR") + } +} From b274fac5fed57105337c5bac5f1004f320899167 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 11 Sep 2023 15:24:03 +0200 Subject: [PATCH 18/61] Small cleanup of linter warnings, etc --- .swiftlint.yml | 6 ++---- Sources/GenIR/BuildCacheManipulator.swift | 17 ++++++++++++----- .../Extensions/FileManager+Extension.swift | 6 ++++-- Tests/GenIRTests/MultipleAppTests.swift | 1 + Tests/GenIRTests/UmbrellaTests.swift | 8 +++++--- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 44b675c..1b816f3 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -7,8 +7,6 @@ excluded: - TestAssets/ line_length: - warning: 150 + warning: 200 ignores_comments: true - -disabled_rules: - - todo \ No newline at end of file +todo: false diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index 00ea025..b9a2e7a 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -28,7 +28,7 @@ struct BuildCacheManipulator { buildProductsPath = archive shouldDeploySkipInstallHack = self.buildSettings["SKIP_INSTALL"] == "NO" - if !dryRun { + if !self.dryRun { guard FileManager.default.directoryExists(at: buildCachePath) else { throw Error.directoryNotFound("Build cache path doesn't exist at expected path: \(buildCachePath)") } @@ -46,10 +46,13 @@ struct BuildCacheManipulator { do { intermediateFolders = try FileManager.default.directories(at: intermediatesPath, recursive: false) } catch { - throw Error.directoryNotFound("No directories found at \(intermediatesPath), expected exactly one. Ensure you did an archive build.") + throw Error.directoryNotFound( + "No directories found at \(intermediatesPath), expected exactly one. Ensure you did an archive build." + ) } - // TODO: Can we determine the main target being built here (via scheme or something similar?). That way we don't require a cleaned derived data + // TODO: Can we determine the main target being built here (via scheme or something similar?). + // That way we don't require a cleaned derived data guard intermediateFolders.count == 1 else { throw Error.tooManyDirectories( """ @@ -63,8 +66,12 @@ struct BuildCacheManipulator { .appendingPathComponent(intermediateFolders.first!.lastPathComponent) .appendingPathComponent("BuildProductsPath") - guard let archivePath = Self.findConfigurationDirectory(intermediatesBuildPath) else { - throw Error.directoryNotFound("Couldn't find or determine a build configuration directory (expected inside of: \(intermediatesBuildPath))") + guard + let archivePath = Self.findConfigurationDirectory(intermediatesBuildPath) + else { + throw Error.directoryNotFound( + "Couldn't find or determine a build configuration directory (expected inside of: \(intermediatesBuildPath))" + ) } try skipInstallHack(archivePath) diff --git a/Sources/GenIR/Extensions/FileManager+Extension.swift b/Sources/GenIR/Extensions/FileManager+Extension.swift index 964df3f..76a544c 100644 --- a/Sources/GenIR/Extensions/FileManager+Extension.swift +++ b/Sources/GenIR/Extensions/FileManager+Extension.swift @@ -60,7 +60,7 @@ extension FileManager { /// - path: The path of the directory to search in /// - suffix: The suffix to match against file names /// - recursive: A Boolean value to indicate whether a recursive search should be performed - /// - Returns: An array of URL file paths matching the suffix found in the specifed path + /// - Returns: An array of URL file paths matching the suffix found in the specified path func files(at path: URL, withSuffix suffix: String, recursive: Bool = true) throws -> [URL] { try filteredContents(of: path, recursive: recursive) { url in let attributes = try url.resourceValues(forKeys: [.isRegularFileKey]) @@ -156,7 +156,9 @@ extension FileManager { if let type = attributes[.type] as? FileAttributeType, type == .typeSymbolicLink { let destination = try destinationOfSymbolicLink(atPath: path.filePath) // swiftlint:disable identifier_name - let actualDestinationCausePathingSucksInFoundation = path.deletingLastPathComponent().appendingPathComponent(destination) + let actualDestinationCausePathingSucksInFoundation = path + .deletingLastPathComponent() + .appendingPathComponent(destination) // swiftlint:enable identifier_name return fileExists(atPath: actualDestinationCausePathingSucksInFoundation.filePath) } diff --git a/Tests/GenIRTests/MultipleAppTests.swift b/Tests/GenIRTests/MultipleAppTests.swift index 87ddbaf..5d946c3 100644 --- a/Tests/GenIRTests/MultipleAppTests.swift +++ b/Tests/GenIRTests/MultipleAppTests.swift @@ -12,6 +12,7 @@ final class MultipleAppTests: XCTestCase { func testExpectedTargetLookup() throws { let context = try TestContext() let result = try context.build(test: Self.testPath, scheme: "MultipleApp") + XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") let project: ProjectParser = try ProjectParser(path: Self.testPath, logLevel: .debug) var targets = Targets(for: project) diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index fe9df60..2275bd6 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -21,7 +21,7 @@ final class UmbrellaTests: XCTestCase { func testUmbrellaTargets() throws { let context = try TestContext() let process = try context.build(test: Self.testPath, scheme: Self.scheme) - XCTAssertEqual(process.code, 0, "Failed to build test case") + XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") let projectParser = try ProjectParser(path: Self.testPath, logLevel: .info) let targets = Targets(for: projectParser) @@ -40,7 +40,8 @@ final class UmbrellaTests: XCTestCase { func testSkipInstallNo() throws { let context = try TestContext() defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } - _ = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO"]) + let result = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO"]) + XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") let output = context.archive.appendingPathComponent("IR") @@ -71,7 +72,8 @@ final class UmbrellaTests: XCTestCase { func testCustomDerivedDataAndSkipInstallNo() throws { let context = try TestContext() defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } - _ = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) + let result = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) + XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") let output = context.archive.appendingPathComponent("IR") From 977deec02e016a8f800ad9c645e40bc996492946 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 11 Sep 2023 15:24:43 +0200 Subject: [PATCH 19/61] Adjust graph, search, relationship modelling Include tests --- .../Dependency Graph/DependencyGraph.swift | 79 +++++++------------ .../DependencyGraphBuilder.swift | 25 +++--- Sources/GenIR/Dependency Graph/Edge.swift | 28 +++++-- Sources/GenIR/Dependency Graph/Node.swift | 22 +++--- Sources/GenIR/OutputPostprocessor.swift | 63 +++++++-------- Tests/GenIRTests/DependencyGraphTests.swift | 50 ++++++++++++ Tests/GenIRTests/WorkspaceTests.swift | 77 +++++++++++++++++- 7 files changed, 236 insertions(+), 108 deletions(-) create mode 100644 Tests/GenIRTests/DependencyGraphTests.swift diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 5651757..6f6f49c 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -5,11 +5,18 @@ // Created by Thomas Hedderwick on 28/08/2023. // +import Logging + + +/// A directed graph that maps dependencies between targets (nodes) via edges (directions between nodes) class DependencyGraph { + /// All the nodes in the graph private(set) var nodes: [Node] = [] + /// Adds a target to the graph + /// - Parameter target: the target to add + /// - Returns: the node added, iff a node for this target didn't already exist in the graph func addNode(target: Target) -> Node? { - // Don't add nodes we've already added if findNode(for: target) != nil { return nil } @@ -19,15 +26,17 @@ class DependencyGraph { return node } - func addEdge(from source: Node, to destination: Node) { - source.add(neighbor: .init(neighbor: destination)) - } - + /// Finds a target's node in the graph + /// - Parameter target: the target to look for + /// - Returns: the node for the given target, if found func findNode(for target: Target) -> Node? { nodes.first(where: { $0.target == target }) } - func search(_ target: Target) -> [Node] { + /// Builds a dependency 'chain' for a target using a depth-first search + /// - Parameter target: the target to get a chain for + /// - Returns: the chain of nodes, starting + func chain(for target: Target) -> [Node] { guard let targetNode = findNode(for: target) else { logger.debug("Couldn't find node for target: \(target.name)") return [] @@ -36,66 +45,36 @@ class DependencyGraph { return depthFirstSearch(startingAt: targetNode) } - func depthFirstSearch(startingAt node: Node) -> [Node] { - logger.info("----\nSearching for: \(node.target.name)") + /// Perform a depth-first search starting at the provided node + /// - Parameter node: the node whose children to search through + /// - Returns: an array of nodes ordered by a depth-first search approach + private func depthFirstSearch(startingAt node: Node) -> [Node] { + logger.debug("----\nSearching for: \(node.target.name)") var visited = Set() var chain = [Node]() func depthFirst(node: Node) { - logger.info("inserting node: \(node.target.name)") + logger.debug("inserting node: \(node.target.name)") visited.insert(node) - logger.info("visited: \(visited)") + logger.debug("visited: \(visited)") - for edge in node.neighbors { - logger.info("edge to: \(edge.neighbor)") - if visited.insert(edge.neighbor).inserted { - logger.info("inserted, recursing") - depthFirst(node: edge.neighbor) + for edge in node.edges { + logger.debug("edge to: \(edge.to)") + if visited.insert(edge.to).inserted { + logger.debug("inserted, recursing") + depthFirst(node: edge.to) } else { - logger.info("edge already in visited: \(visited)") + logger.debug("edge already in visited: \(visited)") } } - logger.info("appending to chain: \(node.target.name)") + logger.debug("appending to chain: \(node.target.name)") chain.append(node) } depthFirst(node: node) return chain } - - - - /// Builds a dependency chain in the order of which dependencies should be operated on - /// - Parameter target: the target to build a chain for - /// - Returns: an array of nodes, ordered in the way they should be operated on - func buildChain(for target: Target) -> [Node]? { - guard let targetNode = findNode(for: target) else { - logger.debug("Couldn't find node for target: \(target.name)") - return nil - } - - func depthFirst(startingAt node: Node, visited: inout Set) -> [Node] { - logger.debug("search ------\nstart: \(node). Visited: \(visited)") - var chain = [node] - visited.insert(node) - - for edge in node.neighbors where visited.insert(edge.neighbor).inserted { - logger.debug("found edge: \(edge.neighbor).") - chain += depthFirst(startingAt: edge.neighbor, visited: &visited) - } - - return chain - } - - /// do a depth first search - var visited = Set() - let chain = depthFirst(startingAt: targetNode, visited: &visited) - - print("chain: \(chain)") - - return chain - } } extension DependencyGraph: CustomStringConvertible { diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index a177e53..ab2983f 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -6,23 +6,25 @@ // class DependencyGraphBuilder { - let targets: Targets - - init(targets: Targets) { - self.targets = targets - } - - func build() -> DependencyGraph { + /// Builds a dependency graph for the given collection of targets + /// - Parameter targets: the targets to build a graph for + /// - Returns: the dependency graph + static func build(targets: Targets) -> DependencyGraph { let graph = DependencyGraph() for target in targets { - addToGraph(graph, target: target) + add(target: target, in: targets, to: graph) } return graph } - func addToGraph(_ graph: DependencyGraph, target: Target) { + /// Adds a target (and it's dependencies) to the graph + /// - Parameters: + /// - graph: the graph to add a target to + /// - target: the target to add + /// - targets: the targets containing the target and it's dependencies + static func add(target: Target, in targets: Targets, to graph: DependencyGraph) { logger.debug("Adding target: \(target.name) to graph") guard let node = graph.addNode(target: target) else { @@ -38,14 +40,15 @@ class DependencyGraphBuilder { continue } - addToGraph(graph, target: dependencyTarget) + add(target: dependencyTarget, in: targets, to: graph) guard let dependencyNode = graph.findNode(for: dependencyTarget) else { logger.debug("Couldn't find node for target (\(dependencyTarget.name)) even though it was just inserted?") continue } - node.add(neighbor: .init(neighbor: dependencyNode)) + node.add(edge: .init(to: dependencyNode, from: node, relationship: .dependency)) + dependencyNode.add(edge: .init(to: node, from: dependencyNode, relationship: .depender)) } } } diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift index dcba712..d468fa9 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -5,20 +5,38 @@ // Created by Thomas Hedderwick on 28/08/2023. // +// swiftlint:disable identifier_name +/// An edge describes the relationship between two Nodes in a graph class Edge { - let neighbor: Node + /// The source node + let to: Node + /// The destination node + let from: Node + /// The relationship between the two nodes + let relationship: Relationship - init(neighbor: Node) { - self.neighbor = neighbor + /// Description of the relationships between two nodes + enum Relationship { + /// From depends on To + case dependency + /// From is a depender of To + case depender + } + + init(to: Node, from: Node, relationship: Relationship) { + self.to = to + self.from = from + self.relationship = relationship } } extension Edge: Equatable { static func == (_ lhs: Edge, rhs: Edge) -> Bool { - lhs.neighbor == rhs.neighbor + lhs.to == rhs.to && lhs.from == rhs.from } } extension Edge: CustomStringConvertible { - var description: String { "[Edge: \(neighbor)]"} + var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} } +// swiftlint:enable identifier_name diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index 7436cc4..799276c 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -8,25 +8,28 @@ import Foundation class Node { - private(set) var neighbors: [Edge] = [] + /// The edges from and to this node + private(set) var edges = [Edge]() + /// The target this node represents let target: Target + /// The name of this node, mostly used for debugging and printing let name: String - let uuid: UUID init(_ target: Target) { self.target = target self.name = target.name - self.uuid = UUID() } - func add(neighbor: Edge) { - neighbors.append(neighbor) + /// Adds an edge to this node + /// - Parameter edge: the edge to add + func add(edge: Edge) { + edges.append(edge) } } extension Node: Equatable { static func == (_ lhs: Node, rhs: Node) -> Bool { - lhs.target == rhs.target && lhs.neighbors == rhs.neighbors + lhs.target == rhs.target && lhs.edges == rhs.edges } } @@ -34,8 +37,8 @@ extension Node: CustomStringConvertible { var description: String { var description = "" - if !neighbors.isEmpty { - description += "[Node: \(target.name), edges: \(neighbors.map { $0.neighbor.target.name})] " + if !edges.isEmpty { + description += "[Node: \(target.name), edges: \(edges.map { $0.to.target.name})] " } else { description += "[Node: \(target.name)] " } @@ -46,6 +49,7 @@ extension Node: CustomStringConvertible { extension Node: Hashable { func hash(into hasher: inout Hasher) { - hasher.combine(uuid) + hasher.combine(name) + hasher.combine(target) } } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 144412a..4c3b0ec 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -24,9 +24,9 @@ struct OutputPostprocessor { init(archive: URL, output: URL, targets: Targets) throws { self.output = output - dynamicDependencyToPath = dynamicDependencies(in: archive) + dynamicDependencyToPath = dynamicDependencies(in: self.archive) - graph = DependencyGraphBuilder(targets: targets).build() + graph = DependencyGraphBuilder.build(targets: targets) } /// Starts the OutputPostprocessor @@ -54,57 +54,58 @@ struct OutputPostprocessor { return Set() } - return try process(target: target, in: targets, at: path/*, with: targetsToPaths*/) + return try process(target: target) } } /// Processes an individual target /// - Parameters: /// - target: the target to process - /// - targets: a list of all targets /// - path: the output path - /// - targetsToPaths: a map of targets to their IR folder paths /// - Returns: private func process( - target: Target, - in targets: Targets, - at path: URL + target: Target ) throws -> Set { - - // guard let chain = graph.buildChain(for: target) else { - // logger.debug("Uh oh...") - // return [] - // } - let chain = graph.search(target) + let chain = graph.chain(for: target) logger.info("Chain for target: \(target.name):\n\(chain)") + // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent + var processed = Set() + for node in chain { + logger.debug("Processing Node: \(node.name)") + // Ensure node is not a dynamic dependency + guard dynamicDependencyToPath[node.target.nameForOutput] == nil else { continue } + // Only care about moving dependencies into dependers - check this node's edges to dependent relationships + let dependers = node.edges + .filter { $0.relationship == .depender } + .map { $0.to } - logger.debug("Processing -- \(target.name)") - let dependencies = targets.calculateDependencies(for: target) - - let staticDependencies = dependencies - .filter { dynamicDependencyToPath[$0] == nil } + // Move node's IR into depender's IR folder + guard let nodeFolderPath = node.target.irFolderPath else { + logger.debug("IR folder for node: \(node) is nil") + continue + } - let processedPaths = try staticDependencies - .compactMap { product -> URL? in - guard let dependencyTarget = targets.target(for: product) else { - logger.debug("Failed to lookup target for product: \(product)") - return nil + for depender in dependers { + guard let dependerFolderPath = depender.target.irFolderPath else { + logger.debug("IR folder for depender node \(depender) is nil") + continue } - guard let dependencyPath = dependencyTarget.irFolderPath else { - logger.debug("Failed to lookup path for target: \(dependencyTarget.name)") - return nil + // Move the dependency IR (the node) to the depender (the thing depending on this node) + do { + try FileManager.default.copyItemMerging(at: nodeFolderPath, to: dependerFolderPath, replacing: true) + } catch { + logger.debug("Copy error: \(error)") } - - try FileManager.default.copyItemMerging(at: dependencyPath, to: path) - return dependencyPath + processed.insert(nodeFolderPath) } + } - return Set(processedPaths) + return processed } } diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift new file mode 100644 index 0000000..5283e31 --- /dev/null +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -0,0 +1,50 @@ +import XCTest +@testable import gen_ir +import PBXProjParser + +final class DependencyGraphTests: XCTestCase { + static private var testPath: URL = { + TestContext.testAssetPath + .appendingPathComponent("WorkspaceTest") + .appendingPathComponent("Workspace.xcworkspace") + }() + + static private var scheme = "App" + + func testChains() throws { + // Test Setup + let context = try TestContext() + let process = try context.build(test: Self.testPath, scheme: Self.scheme) + XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") + + let project = try ProjectParser(path: Self.testPath, logLevel: .debug) + var targets = Targets(for: project) + + let buildLog = try String(contentsOf: context.buildLog).components(separatedBy: .newlines) + let logParser = XcodeLogParser(log: buildLog) + + try logParser.parse(&targets) + + let graph = DependencyGraphBuilder.build(targets: targets) + let appTarget = try XCTUnwrap(targets.target(for: "App"), "Failed to get App target from targets") + let app = try XCTUnwrap(graph.findNode(for: appTarget), "Failed to find App node in graph") + + // App should have two nodes - Framework & Common + XCTAssertTrue(app.edges.count == 2, "App's edges is not equal to 2") + _ = try XCTUnwrap(app.edges.first(where: { $0.to.name == "Framework" }), "Failed to get Framework edge from App") + let commonEdge = try XCTUnwrap(app.edges.first(where: { $0.to.name == "Common" }), "Failed to get Common edge from App") + + let frameworkTarget = try XCTUnwrap(targets.target(for: "Framework"), "Failed to get Framework from targets") + let framework = try XCTUnwrap(graph.findNode(for: frameworkTarget), "Failed to find Framework node in graph") + + // Framework should have two dependency edges - Common & SFSafeSymbols and one depender edge - App + XCTAssertTrue(framework.edges.count == 3, "Framework's edges is not equal to 3") + let symbolsEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "SFSafeSymbols" }), "Failed to get SFSafeSymbols edge from Framework") + let frameworkCommonEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "Common" }), "Failed to get SFSafeSymbols edge from Framework") + let frameworkAppEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "App" }), "Failed to get App edge from Framework") + XCTAssertNotEqual(commonEdge, frameworkCommonEdge, "App's Common edge is equal to Framework's Common edge - they should have different from values") + XCTAssertEqual(symbolsEdge.relationship, .dependency) + XCTAssertEqual(frameworkCommonEdge.relationship, .dependency) + XCTAssertEqual(frameworkAppEdge.relationship, .depender) + } +} \ No newline at end of file diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 2c0cf97..71b8ea1 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -11,10 +11,48 @@ final class WorkspaceTests: XCTestCase { static private var scheme = "App" + static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] + static let commonIRFiles: Set = ["Common_vers.bc", "Model.bc"] + static let frameworkIRFiles: Set = ["Framework_vers.bc", "Framework.bc"] + static let sfSafeSymbolsIRFiles: Set = [ + "NSImageExtension.bc", + "SFSymbol+1.0.bc", + "SFSymbol+1.1.bc", + "SFSymbol+2.0.bc", + "SFSymbol+2.1.bc", + "SFSymbol+2.2.bc", + "SFSymbol+3.0.bc", + "SFSymbol+3.1.bc", + "SFSymbol+3.2.bc", + "SFSymbol+3.3.bc", + "SFSymbol+4.0.bc", + "SFSymbol+4.1.bc", + "SFSymbol+AllSymbols+1.0.bc", + "SFSymbol+AllSymbols+1.1.bc", + "SFSymbol+AllSymbols+2.0.bc", + "SFSymbol+AllSymbols+2.1.bc", + "SFSymbol+AllSymbols+2.2.bc", + "SFSymbol+AllSymbols+3.0.bc", + "SFSymbol+AllSymbols+3.1.bc", + "SFSymbol+AllSymbols+3.2.bc", + "SFSymbol+AllSymbols+3.3.bc", + "SFSymbol+AllSymbols+4.0.bc", + "SFSymbol+AllSymbols+4.1.bc", + "SFSymbol+AllSymbols.bc", + "SFSymbol.bc", + "SwiftUIImageExtension.bc", + "SwiftUILabelExtension.bc", + "SymbolLocalizations.bc", + "SymbolWithLocalizations.bc", + "UIApplicationShortcutIconExtension.bc", + "UIButtonExtension.bc", + "UIImageExtension.bc" + ] + func testWorkspace() throws { let context = try TestContext() let process = try context.build(test: Self.testPath, scheme: Self.scheme) - XCTAssertEqual(process.code, 0, "Failed to build test case") + XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") let output = context.archive.appendingPathComponent("IR") var genIR = gen_ir.IREmitterCommand() @@ -28,6 +66,41 @@ final class WorkspaceTests: XCTestCase { dryRun: false ) - print("ran Gen IR") + // Check dependencies made it to the right place + let appIRPath = context.archive.appending(path: "IR/App.app/") + let commonIRPath = context.archive.appending(path: "IR/Common.framework/") + let frameworkIRPath = context.archive.appending(path: "IR/Framework.framework/") + let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols/") + + let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) + .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) + let commonIRPathContents = try FileManager.default.contentsOfDirectory(at: commonIRPath, includingPropertiesForKeys: nil) + .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) + let frameworkIRPathContents = try FileManager.default.contentsOfDirectory(at: frameworkIRPath, includingPropertiesForKeys: nil) + .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) + let sfSafeSymbolsIRPathContents = try FileManager.default.contentsOfDirectory(at: sfSafeSymbolsIRPath, includingPropertiesForKeys: nil) + .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) + + let expectedAppIRFiles = Self.appIRFiles + .union(Self.commonIRFiles) + .union(Self.frameworkIRFiles) + .union(Self.sfSafeSymbolsIRFiles) + .reduce(into: Set(), { $0.insert($1) }) + + let expectedFrameworkIRFiles = Self.frameworkIRFiles + .union(Self.commonIRFiles) + .union(Self.sfSafeSymbolsIRFiles) + .reduce(into: Set(), { $0.insert($1) }) + + let expectedCommonIRFiles = Self.commonIRFiles + .reduce(into: Set(), { $0.insert($1) }) + + let expectedSFSafeSymbolsIRFiles = Self.sfSafeSymbolsIRFiles + .reduce(into: Set(), { $0.insert($1) }) + + XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual") + XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual") + XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual") + XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual") } } From 41b64dd429ee61cb175d42c852daf78e086d8dbf Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 12 Sep 2023 14:01:44 +0200 Subject: [PATCH 20/61] Remove depender nodes from description printing --- Sources/GenIR/Dependency Graph/Node.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index 799276c..f0ff937 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -38,7 +38,7 @@ extension Node: CustomStringConvertible { var description = "" if !edges.isEmpty { - description += "[Node: \(target.name), edges: \(edges.map { $0.to.target.name})] " + description += "[Node: \(target.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.target.name})] " } else { description += "[Node: \(target.name)] " } From 1f5bd3ca8c41518d4b6c498351fd53d3e2608d39 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Thu, 21 Sep 2023 18:20:27 +0200 Subject: [PATCH 21/61] Remove some if lets for switches - makes us handle future cases --- .swiftlint.yml | 4 +++- .../Sources/PBXProjParser/ProjectParser.swift | 13 +++++++++---- .../Sources/PBXProjParser/XcodeProject.swift | 14 +++++++++----- .../GenIR/Dependency Graph/DependencyGraph.swift | 3 --- .../Dependency Graph/DependencyGraphBuilder.swift | 6 ++++-- Sources/GenIR/Dependency Graph/Edge.swift | 2 +- Sources/GenIR/OutputPostprocessor.swift | 3 ++- .../Networking/Networking/Networking.swift | 1 - Tests/GenIRTests/DependencyGraphTests.swift | 2 +- 9 files changed, 29 insertions(+), 19 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 1b816f3..5d6a87b 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -6,7 +6,9 @@ excluded: - GenIRLogging/.build/ - TestAssets/ +disabled_rules: + - todo + line_length: warning: 200 ignores_comments: true -todo: false diff --git a/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift b/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift index bd24573..8e4fbc6 100644 --- a/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift +++ b/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift @@ -87,11 +87,16 @@ public struct ProjectParser { return target.targetDependencies .values .map { dependency in - if case .native(let native) = dependency, let path = project.path(for: native) { - return path + switch dependency { + case .native(let target): + if let path = project.path(for: target) { + return path + } + + fallthrough + default: + return dependency.name } - - return dependency.name } } diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift index d5eb2c7..767d613 100644 --- a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift +++ b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift @@ -149,14 +149,18 @@ public struct XcodeProject { seen.insert(dependency.name) - if case .native(let native) = dependency { - logger.debug("Adding native dependency: \(dependency.name), deps: \(native.targetDependencies.map { $0.0 })") - targetDependencies.append(contentsOf: native.targetDependencies.map { $0.1 }) - native.targetDependencies.forEach { target.add(dependency: $0.1) } - } else { + switch dependency { + case .native(let nativeTarget): + logger.debug("Adding native dependency: \(dependency.name), deps: \(nativeTarget.targetDependencies.map { $0.0 })") + targetDependencies.append(contentsOf: nativeTarget.targetDependencies.map { $0.1 }) + nativeTarget.targetDependencies.forEach { target.add(dependency: $0.1) } + case .package: // Packages don't have a transitive dependency field like native targets do, so we can't find dependency of a dependency from the project file logger.debug("Adding package dependency: \(dependency.name)") target.add(dependency: dependency) + case .externalProjectFramework: + // Can't move IR dependencies for prebuilt frameworks + continue } } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 6f6f49c..a6d1ca5 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -5,9 +5,6 @@ // Created by Thomas Hedderwick on 28/08/2023. // -import Logging - - /// A directed graph that maps dependencies between targets (nodes) via edges (directions between nodes) class DependencyGraph { /// All the nodes in the graph diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index ab2983f..e2985d0 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -12,8 +12,8 @@ class DependencyGraphBuilder { static func build(targets: Targets) -> DependencyGraph { let graph = DependencyGraph() - for target in targets { - add(target: target, in: targets, to: graph) + targets.forEach { + add(target: $0, in: targets, to: graph) } return graph @@ -47,6 +47,8 @@ class DependencyGraphBuilder { continue } + // We add both an edge from and to the node and dependency - this way we can do bidirectional + // searches for a given node and see what it's dependencies are and who depends on it node.add(edge: .init(to: dependencyNode, from: node, relationship: .dependency)) dependencyNode.add(edge: .init(to: node, from: dependencyNode, relationship: .depender)) } diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift index d468fa9..4e62019 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -32,7 +32,7 @@ class Edge { extension Edge: Equatable { static func == (_ lhs: Edge, rhs: Edge) -> Bool { - lhs.to == rhs.to && lhs.from == rhs.from + lhs.to == rhs.to && lhs.from == rhs.from && lhs.relationship == rhs.relationship } } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 4c3b0ec..36b0a4b 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -68,7 +68,8 @@ struct OutputPostprocessor { ) throws -> Set { let chain = graph.chain(for: target) - logger.info("Chain for target: \(target.name):\n\(chain)") + logger.info("Chain for target: \(target.nameForOutput):\n") + chain.forEach { logger.info("\($0)") } // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent var processed = Set() diff --git a/TestAssets/Umbrella/Networking/Networking/Networking.swift b/TestAssets/Umbrella/Networking/Networking/Networking.swift index 2de21f0..dc3ff71 100644 --- a/TestAssets/Umbrella/Networking/Networking/Networking.swift +++ b/TestAssets/Umbrella/Networking/Networking/Networking.swift @@ -7,7 +7,6 @@ import Foundation - public struct Networking { static public func get(_ url: URL) async throws -> Data { let request = URLRequest(url: url) diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 5283e31..300b933 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -47,4 +47,4 @@ final class DependencyGraphTests: XCTestCase { XCTAssertEqual(frameworkCommonEdge.relationship, .dependency) XCTAssertEqual(frameworkAppEdge.relationship, .depender) } -} \ No newline at end of file +} From 6601453605a470ec8fa9577e1a0fb4371841e62c Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Thu, 21 Sep 2023 18:21:17 +0200 Subject: [PATCH 22/61] Rework file copying to match file sizes to stop copying of the same file --- Sources/GenIR/OutputPostprocessor.swift | 73 ++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 36b0a4b..ed3235a 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -12,7 +12,7 @@ import PBXProjParser /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. /// The `OutputPostprocessor` attempts to remedy this by using the `ProjectParser` to detect dependencies between products AND /// parsing the `xcarchive` to determine if something was statically linked, and if so, copies the IR for that product into the linker's IR folder. -struct OutputPostprocessor { +class OutputPostprocessor { /// The to the output IR folders that will be processed let output: URL @@ -21,6 +21,8 @@ struct OutputPostprocessor { private let graph: DependencyGraph + private var seenConflictingFiles: [URL: [(String, Int)]] = [:] + init(archive: URL, output: URL, targets: Targets) throws { self.output = output @@ -98,7 +100,8 @@ struct OutputPostprocessor { // Move the dependency IR (the node) to the depender (the thing depending on this node) do { - try FileManager.default.copyItemMerging(at: nodeFolderPath, to: dependerFolderPath, replacing: true) + try copyDirectoryMergingDifferingFiles(at: nodeFolderPath, to: dependerFolderPath) + // try FileManager.default.copyItemMerging(at: nodeFolderPath, to: dependerFolderPath, replacing: true) } catch { logger.debug("Copy error: \(error)") } @@ -108,6 +111,72 @@ struct OutputPostprocessor { return processed } + + // TODO: rework this file to not have side effects and make it more readable... + func copyDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { + let manager = FileManager.default + let files = try manager.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) + + func size(for path: URL) throws -> Int? { + do { + return try manager.attributesOfItem(atPath: path.filePath)[.size] as? Int + } catch { + logger.debug("Couldn't get size attribute for path: \(path.filePath)") + throw error + } + } + + let (existing, nonexisting) = files + .map { + (source: source.appendingPathComponent($0.lastPathComponent), destination: destination.appendingPathComponent($0.lastPathComponent)) + } + .reduce(into: (existing: [(source: URL, destination: URL)](), nonexisting: [(source: URL, destination: URL)]())) { (partialResult, sourceAndDestination) in + if manager.fileExists(atPath: sourceAndDestination.destination.filePath) { + partialResult.existing.append(sourceAndDestination) + } else { + partialResult.nonexisting.append(sourceAndDestination) + } + } + + // Files that don't already exist at the destination can be moved over easily + try nonexisting + .forEach { (source, destination) in + try manager.copyItem(at: source, to: destination) + } + + // Files that do exist require some additional checks, and potentially renaming + for (sourceURL, destinationURL) in existing { + guard + let destinationSize = try size(for: destinationURL), + let sourceSize = try size(for: sourceURL) + else { + continue + } + + if sourceSize == destinationSize { + // Ignore the file + logger.debug("Ignoring copy of: \(destinationURL.lastPathComponent) as the sizes are the same") + continue + } + + // Unique the file name + let uniqueDestinationURL = manager.uniqueFilename(directory: destination, filename: sourceURL.lastPathComponent) + + // TODO: Should we use all file attributes here? What about created date etc? + if seenConflictingFiles[sourceURL] == nil { + seenConflictingFiles[sourceURL] = [(sourceURL.lastPathComponent, sourceSize)] + } + + for (_, size) in seenConflictingFiles[sourceURL]! where size == destinationSize { + logger.debug("Ignoring copy of: \(destinationURL.lastPathComponent) as the sizes where the same") + continue + } + + seenConflictingFiles[sourceURL]?.append((uniqueDestinationURL.lastPathComponent, destinationSize)) + + try manager.copyItem(at: sourceURL, to: uniqueDestinationURL) + } + } } // swiftlint:disable private_over_fileprivate From 94aaeea85c8cc8980a473c0501f5272613eb7db0 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 25 Sep 2023 10:46:53 +0200 Subject: [PATCH 23/61] Simplify the IREmitterCommand inits to remove redundant arguments --- Sources/GenIR/GenIR.swift | 20 +- Sources/GenIR/OutputPostprocessor.swift | 110 +++--- .../ImageFramework/ImageFramework.h | 18 + .../ImageLibrary/Image.swift | 17 + .../project.pbxproj | 360 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 63 +++ .../Assets.xcassets/Contents.json | 6 + .../ContentView.swift | 24 ++ .../Image.swift | 8 + ...utPostprocessorFileMoverTests.entitlements | 10 + ...OutputPostprocessorFileMoverTestsApp.swift | 17 + .../Preview Assets.xcassets/Contents.json | 6 + .../OutputPostprocessorFileMoverTests.swift | 11 + Tests/GenIRTests/UmbrellaTests.swift | 2 - Tests/GenIRTests/WorkspaceTests.swift | 1 - Tests/GenIRTests/gen_irTests.swift | 5 - 19 files changed, 638 insertions(+), 66 deletions(-) create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/ImageFramework.h create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/Contents.json create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTestsApp.swift create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index f632274..be1d866 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -59,9 +59,6 @@ struct IREmitterCommand: ParsableCommand { @Flag(help: "Runs the tool without outputting IR to disk (i.e. leaving out the compiler command runner stage)") var dryRun = false - /// Path to write IR to - private lazy var outputPath: URL = xcarchivePath.appendingPathComponent("IR") - mutating func validate() throws { if debug { logger.logLevel = .debug @@ -76,7 +73,7 @@ struct IREmitterCommand: ParsableCommand { throw ValidationError("Project doesn't exist at path: \(projectPath.filePath)") } - // Version 0.2.x and below allowed the output folder to be any artibrary folder. + // Version 0.2.x and below allowed the output folder to be any arbitrary folder. // Docs said to use 'IR' inside an xcarchive. For backwards compatibility, if we have an xcarchive path with an IR // folder, remove the IR portion if xcarchivePath.filePath.hasSuffix("IR") { @@ -87,13 +84,8 @@ struct IREmitterCommand: ParsableCommand { throw ValidationError("xcarchive path must have an .xcarchive extension. Found \(xcarchivePath.lastPathComponent)") } - if !FileManager.default.directoryExists(at: outputPath) { - logger.debug("Output path doesn't exist, creating \(outputPath)") - do { - try FileManager.default.createDirectory(at: outputPath, withIntermediateDirectories: true) - } catch { - throw ValidationError("Failed to create output directory with error: \(error)") - } + if !FileManager.default.directoryExists(at: xcarchivePath) { + throw ValidationError("Archive path doesn't exist: \(xcarchivePath.filePath)") } } @@ -102,14 +94,13 @@ struct IREmitterCommand: ParsableCommand { project: projectPath, log: logPath, archive: xcarchivePath, - output: outputPath, level: logger.logLevel, dryRun: dryRun ) } - // swiftlint:disable function_parameter_count - mutating func run(project: URL, log: String, archive: URL, output: URL, level: Logger.Level, dryRun: Bool) throws { + mutating func run(project: URL, log: String, archive: URL, level: Logger.Level, dryRun: Bool) throws { + let output = archive.appendingPathComponent("IR") let project = try ProjectParser(path: project, logLevel: level) var targets = Targets(for: project) @@ -133,7 +124,6 @@ struct IREmitterCommand: ParsableCommand { let postprocessor = try OutputPostprocessor(archive: archive, output: output, targets: targets) try postprocessor.process(targets: &targets) } - // swiftlint:enable function_parameter_count /// Gets an `XcodeLogParser` for a path /// - Parameter path: The path to a file on disk containing an Xcode build log, or `-` if stdin should be read diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index ed3235a..f3711fc 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -8,6 +8,8 @@ import Foundation import PBXProjParser +private typealias FilenameAndSize = (String, Int) + /// The `OutputPostprocessor` is responsible for trying to match the IR output of the `CompilerCommandRunner` with the products in the `xcarchive`. /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. /// The `OutputPostprocessor` attempts to remedy this by using the `ProjectParser` to detect dependencies between products AND @@ -21,7 +23,9 @@ class OutputPostprocessor { private let graph: DependencyGraph - private var seenConflictingFiles: [URL: [(String, Int)]] = [:] + private var seenConflictingFiles: [URL: [FilenameAndSize]] = [:] + + private let manager: FileManager = .default init(archive: URL, output: URL, targets: Targets) throws { self.output = output @@ -34,7 +38,7 @@ class OutputPostprocessor { /// Starts the OutputPostprocessor /// - Parameter targets: the targets to operate on func process(targets: inout Targets) throws { - try FileManager.default.directories(at: output, recursive: false) + try manager.directories(at: output, recursive: false) .forEach { path in let product = path.lastPathComponent.deletingPathExtension() @@ -47,7 +51,7 @@ class OutputPostprocessor { } // TODO: remove 'static' deps so we don't duplicate them in the submission? - _ = try FileManager.default.directories(at: output, recursive: false) + _ = try manager.directories(at: output, recursive: false) .flatMap { path in let product = path.lastPathComponent.deletingPathExtension() @@ -100,8 +104,7 @@ class OutputPostprocessor { // Move the dependency IR (the node) to the depender (the thing depending on this node) do { - try copyDirectoryMergingDifferingFiles(at: nodeFolderPath, to: dependerFolderPath) - // try FileManager.default.copyItemMerging(at: nodeFolderPath, to: dependerFolderPath, replacing: true) + try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: dependerFolderPath) } catch { logger.debug("Copy error: \(error)") } @@ -112,25 +115,26 @@ class OutputPostprocessor { return processed } - // TODO: rework this file to not have side effects and make it more readable... - func copyDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { - let manager = FileManager.default + // TODO: Write tests for this. + /// Copies the contents of a directory from source to destination, merging files that share the same name but differ in attributes + /// - Parameters: + /// - source: the source directory to copy the contents of + /// - destination: the destination directory for the contents + func copyContentsOfDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { let files = try manager.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) - func size(for path: URL) throws -> Int? { - do { - return try manager.attributesOfItem(atPath: path.filePath)[.size] as? Int - } catch { - logger.debug("Couldn't get size attribute for path: \(path.filePath)") - throw error - } - } - + // Get two arrays of file paths of the source and destination file where: + // 1) destination already exists + // 2) destination doesn't already exist + typealias SourceAndDestination = (source: URL, destination: URL) let (existing, nonexisting) = files .map { - (source: source.appendingPathComponent($0.lastPathComponent), destination: destination.appendingPathComponent($0.lastPathComponent)) + ( + source: source.appendingPathComponent($0.lastPathComponent), + destination: destination.appendingPathComponent($0.lastPathComponent) + ) } - .reduce(into: (existing: [(source: URL, destination: URL)](), nonexisting: [(source: URL, destination: URL)]())) { (partialResult, sourceAndDestination) in + .reduce(into: (existing: [SourceAndDestination](), nonexisting: [SourceAndDestination]())) { (partialResult, sourceAndDestination) in if manager.fileExists(atPath: sourceAndDestination.destination.filePath) { partialResult.existing.append(sourceAndDestination) } else { @@ -138,44 +142,64 @@ class OutputPostprocessor { } } - // Files that don't already exist at the destination can be moved over easily + // Nonexisting files are easy - just move them try nonexisting .forEach { (source, destination) in try manager.copyItem(at: source, to: destination) } - // Files that do exist require some additional checks, and potentially renaming - for (sourceURL, destinationURL) in existing { - guard - let destinationSize = try size(for: destinationURL), - let sourceSize = try size(for: sourceURL) - else { - continue + // Existing files require some additional checks and renaming + try existing + .forEach { + try copyFileUniquingConflictingFiles(source: $0.source, destination: $0.destination) } + } - if sourceSize == destinationSize { - // Ignore the file - logger.debug("Ignoring copy of: \(destinationURL.lastPathComponent) as the sizes are the same") - continue + /// Copies a file, uniquing the path if it conflicts, _if_ the files they conflict with aren't the same size + /// - Parameters: + /// - source: source file path + /// - destination: destination file path + private func copyFileUniquingConflictingFiles(source: URL, destination: URL) throws { + /// Returns the size of the path. Throws is attributes cannot be found for this file path. + /// - Parameter path: the path to get the file size of + /// - Returns: the size of the file at path, if it was able to be converted to an integer + func size(for path: URL) throws -> Int? { + do { + return try manager.attributesOfItem(atPath: path.filePath)[.size] as? Int + } catch { + logger.debug("Couldn't get size attribute for path: \(path.filePath)") + throw error } + } - // Unique the file name - let uniqueDestinationURL = manager.uniqueFilename(directory: destination, filename: sourceURL.lastPathComponent) + guard + let destinationSize = try size(for: destination), + let sourceSize = try size(for: source) + else { + return + } - // TODO: Should we use all file attributes here? What about created date etc? - if seenConflictingFiles[sourceURL] == nil { - seenConflictingFiles[sourceURL] = [(sourceURL.lastPathComponent, sourceSize)] - } + // if sourceSize == destinationSize { + // // Ignore the file + // logger.debug("Ignoring copy of: \(destination.lastPathComponent) as the sizes are the same") + // return + // } - for (_, size) in seenConflictingFiles[sourceURL]! where size == destinationSize { - logger.debug("Ignoring copy of: \(destinationURL.lastPathComponent) as the sizes where the same") - continue - } + let uniqueDestinationURL = manager.uniqueFilename(directory: destination, filename: source.lastPathComponent) - seenConflictingFiles[sourceURL]?.append((uniqueDestinationURL.lastPathComponent, destinationSize)) + // TODO: Should we use all file attributes here? What about created date etc? + if seenConflictingFiles[source] == nil { + seenConflictingFiles[source] = [(source.lastPathComponent, sourceSize)] + } - try manager.copyItem(at: sourceURL, to: uniqueDestinationURL) + for (_, size) in seenConflictingFiles[source]! where size == destinationSize { + logger.debug("Ignoring copy of: \(destination.lastPathComponent) as the sizes where the same") + continue } + + seenConflictingFiles[source]?.append((uniqueDestinationURL.lastPathComponent, destinationSize)) + + try manager.copyItem(at: source, to: uniqueDestinationURL) } } diff --git a/TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/ImageFramework.h b/TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/ImageFramework.h new file mode 100644 index 0000000..9ab3b04 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/ImageFramework.h @@ -0,0 +1,18 @@ +// +// ImageFramework.h +// ImageFramework +// +// Created by Thomas Hedderwick on 22/09/2023. +// + +#import + +//! Project version number for ImageFramework. +FOUNDATION_EXPORT double ImageFrameworkVersionNumber; + +//! Project version string for ImageFramework. +FOUNDATION_EXPORT const unsigned char ImageFrameworkVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift b/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift new file mode 100644 index 0000000..feb2a75 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift @@ -0,0 +1,17 @@ +// +// Image.swift +// ImageFramework +// +// Created by Thomas Hedderwick on 22/09/2023. +// + +import SwiftUI + +public struct Image: View { + @inline(never) + public var body: some View { + Text("Pretend I'm an Image - Framework") + } + + public init () {} +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2177366 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj @@ -0,0 +1,360 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + CE1C4FB82ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1C4FB72ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift */; }; + CE1C4FBA2ABD9581006B8A35 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1C4FB92ABD9581006B8A35 /* ContentView.swift */; }; + CE1C4FBC2ABD9583006B8A35 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE1C4FBB2ABD9583006B8A35 /* Assets.xcassets */; }; + CE1C4FC02ABD9583006B8A35 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE1C4FBF2ABD9583006B8A35 /* Preview Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CE1C4FB42ABD9581006B8A35 /* OutputPostprocessorFileMoverTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OutputPostprocessorFileMoverTests.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CE1C4FB72ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputPostprocessorFileMoverTestsApp.swift; sourceTree = ""; }; + CE1C4FB92ABD9581006B8A35 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + CE1C4FBB2ABD9583006B8A35 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CE1C4FBD2ABD9583006B8A35 /* OutputPostprocessorFileMoverTests.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OutputPostprocessorFileMoverTests.entitlements; sourceTree = ""; }; + CE1C4FBF2ABD9583006B8A35 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE1C4FB12ABD9581006B8A35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE1C4FAB2ABD9581006B8A35 = { + isa = PBXGroup; + children = ( + CE1C4FB62ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */, + CE1C4FB52ABD9581006B8A35 /* Products */, + ); + sourceTree = ""; + }; + CE1C4FB52ABD9581006B8A35 /* Products */ = { + isa = PBXGroup; + children = ( + CE1C4FB42ABD9581006B8A35 /* OutputPostprocessorFileMoverTests.app */, + ); + name = Products; + sourceTree = ""; + }; + CE1C4FB62ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */ = { + isa = PBXGroup; + children = ( + CE1C4FB72ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift */, + CE1C4FB92ABD9581006B8A35 /* ContentView.swift */, + CE1C4FBB2ABD9583006B8A35 /* Assets.xcassets */, + CE1C4FBD2ABD9583006B8A35 /* OutputPostprocessorFileMoverTests.entitlements */, + CE1C4FBE2ABD9583006B8A35 /* Preview Content */, + ); + path = OutputPostprocessorFileMoverTests; + sourceTree = ""; + }; + CE1C4FBE2ABD9583006B8A35 /* Preview Content */ = { + isa = PBXGroup; + children = ( + CE1C4FBF2ABD9583006B8A35 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CE1C4FB32ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE1C4FC32ABD9583006B8A35 /* Build configuration list for PBXNativeTarget "OutputPostprocessorFileMoverTests" */; + buildPhases = ( + CE1C4FB02ABD9581006B8A35 /* Sources */, + CE1C4FB12ABD9581006B8A35 /* Frameworks */, + CE1C4FB22ABD9581006B8A35 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OutputPostprocessorFileMoverTests; + productName = OutputPostprocessorFileMoverTests; + productReference = CE1C4FB42ABD9581006B8A35 /* OutputPostprocessorFileMoverTests.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE1C4FAC2ABD9581006B8A35 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + CE1C4FB32ABD9581006B8A35 = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = CE1C4FAF2ABD9581006B8A35 /* Build configuration list for PBXProject "OutputPostprocessorFileMoverTests" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE1C4FAB2ABD9581006B8A35; + productRefGroup = CE1C4FB52ABD9581006B8A35 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE1C4FB32ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE1C4FB22ABD9581006B8A35 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1C4FC02ABD9583006B8A35 /* Preview Assets.xcassets in Resources */, + CE1C4FBC2ABD9583006B8A35 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE1C4FB02ABD9581006B8A35 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1C4FBA2ABD9581006B8A35 /* ContentView.swift in Sources */, + CE1C4FB82ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE1C4FC12ABD9583006B8A35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CE1C4FC22ABD9583006B8A35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + CE1C4FC42ABD9583006B8A35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"OutputPostprocessorFileMoverTests/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.6; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.OutputPostprocessorFileMoverTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE1C4FC52ABD9583006B8A35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"OutputPostprocessorFileMoverTests/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.6; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.OutputPostprocessorFileMoverTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE1C4FAF2ABD9581006B8A35 /* Build configuration list for PBXProject "OutputPostprocessorFileMoverTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE1C4FC12ABD9583006B8A35 /* Debug */, + CE1C4FC22ABD9583006B8A35 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE1C4FC32ABD9583006B8A35 /* Build configuration list for PBXNativeTarget "OutputPostprocessorFileMoverTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE1C4FC42ABD9583006B8A35 /* Debug */, + CE1C4FC52ABD9583006B8A35 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE1C4FAC2ABD9581006B8A35 /* Project object */; +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AccentColor.colorset/Contents.json b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..532cd72 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/Contents.json b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift new file mode 100644 index 0000000..acc46f7 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift @@ -0,0 +1,24 @@ +// +// ContentView.swift +// OutputPostprocessorFileMoverTests +// +// Created by Thomas Hedderwick on 22/09/2023. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift new file mode 100644 index 0000000..366c5dc --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift @@ -0,0 +1,8 @@ +// +// Image.swift +// OutputPostprocessorFileMoverTests +// +// Created by Thomas Hedderwick on 22/09/2023. +// + +import Foundation diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements new file mode 100644 index 0000000..f2ef3ae --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTestsApp.swift b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTestsApp.swift new file mode 100644 index 0000000..9dc2263 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTestsApp.swift @@ -0,0 +1,17 @@ +// +// OutputPostprocessorFileMoverTestsApp.swift +// OutputPostprocessorFileMoverTests +// +// Created by Thomas Hedderwick on 22/09/2023. +// + +import SwiftUI + +@main +struct OutputPostprocessorFileMoverTestsApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Preview Content/Preview Assets.xcassets/Contents.json b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift new file mode 100644 index 0000000..8272418 --- /dev/null +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -0,0 +1,11 @@ +import XCTest +@testable import gen_ir +import PBXProjParser + +final class OutputPostprocessorFileMoverTests: XCTestCase { + static private var testPath: URL = { + TestContext.testAssetPath + .appendingPathComponent("OutputPostprocessorFileMoverTests") + .appendingPathComponent("OutputPostprocessorFileMoverTests.xcodeproj") + }() +} diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index 2275bd6..ed8064b 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -50,7 +50,6 @@ final class UmbrellaTests: XCTestCase { project: Self.testPath, log: context.buildLog.filePath, archive: context.archive, - output: output, level: .debug, dryRun: false ) @@ -82,7 +81,6 @@ final class UmbrellaTests: XCTestCase { project: Self.testPath, log: context.buildLog.filePath, archive: context.archive, - output: output, level: .debug, dryRun: false ) diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 71b8ea1..9979666 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -61,7 +61,6 @@ final class WorkspaceTests: XCTestCase { project: Self.testPath, log: context.buildLog.filePath, archive: context.archive, - output: output, level: .debug, dryRun: false ) diff --git a/Tests/GenIRTests/gen_irTests.swift b/Tests/GenIRTests/gen_irTests.swift index a8710fa..ffdfd9f 100644 --- a/Tests/GenIRTests/gen_irTests.swift +++ b/Tests/GenIRTests/gen_irTests.swift @@ -13,11 +13,6 @@ final class GenIRTests: XCTestCase { XCTAssert(targets.count == 3, "Targets count expected to be 3, was \(targets.count)") - guard let app = targets.target(for: "ManyTargetTest") else { - XCTAssert(false, "Failed to get target 'ManyTargetTest' from targets") - return - } - XCTAssertNotNil(targets.filter({ $0.name == "ManyTargetTest"}).first, "ManyTargetTest target not found") XCTAssertNotNil(targets.filter({ $0.name == "ManyFramework"}).first, "ManyTargetTest target not found") XCTAssertNotNil(targets.filter({ $0.name == "ManyTargetTest"}).first, "ManyTargetTest target not found") From 359e2dd93118f8e9e476c6b78f20c22fe1d85201 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 25 Sep 2023 10:48:09 +0200 Subject: [PATCH 24/61] Add test for new file de-duplication methods --- Sources/GenIR/OutputPostprocessor.swift | 12 +- .../ImageFramework/Image.swift | 17 + .../ImageLibrary/Image.swift | 2 +- .../project.pbxproj | 334 +++++++++++++++++- .../ContentView.swift | 25 +- .../Image.swift | 9 +- .../OutputPostprocessorFileMoverTests.swift | 27 ++ 7 files changed, 403 insertions(+), 23 deletions(-) create mode 100644 TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/Image.swift diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index f3711fc..19f6824 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -179,13 +179,7 @@ class OutputPostprocessor { return } - // if sourceSize == destinationSize { - // // Ignore the file - // logger.debug("Ignoring copy of: \(destination.lastPathComponent) as the sizes are the same") - // return - // } - - let uniqueDestinationURL = manager.uniqueFilename(directory: destination, filename: source.lastPathComponent) + let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) // TODO: Should we use all file attributes here? What about created date etc? if seenConflictingFiles[source] == nil { @@ -194,10 +188,10 @@ class OutputPostprocessor { for (_, size) in seenConflictingFiles[source]! where size == destinationSize { logger.debug("Ignoring copy of: \(destination.lastPathComponent) as the sizes where the same") - continue + return } - seenConflictingFiles[source]?.append((uniqueDestinationURL.lastPathComponent, destinationSize)) + seenConflictingFiles[source]!.append((uniqueDestinationURL.lastPathComponent, destinationSize)) try manager.copyItem(at: source, to: uniqueDestinationURL) } diff --git a/TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/Image.swift b/TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/Image.swift new file mode 100644 index 0000000..feb2a75 --- /dev/null +++ b/TestAssets/OutputPostprocessorFileMoverTests/ImageFramework/Image.swift @@ -0,0 +1,17 @@ +// +// Image.swift +// ImageFramework +// +// Created by Thomas Hedderwick on 22/09/2023. +// + +import SwiftUI + +public struct Image: View { + @inline(never) + public var body: some View { + Text("Pretend I'm an Image - Framework") + } + + public init () {} +} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift b/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift index feb2a75..27410ca 100644 --- a/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift +++ b/TestAssets/OutputPostprocessorFileMoverTests/ImageLibrary/Image.swift @@ -10,7 +10,7 @@ import SwiftUI public struct Image: View { @inline(never) public var body: some View { - Text("Pretend I'm an Image - Framework") + Text("Pretend I'm an Image - Library") } public init () {} diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj index 2177366..1da1208 100644 --- a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.xcodeproj/project.pbxproj @@ -11,8 +11,55 @@ CE1C4FBA2ABD9581006B8A35 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1C4FB92ABD9581006B8A35 /* ContentView.swift */; }; CE1C4FBC2ABD9583006B8A35 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE1C4FBB2ABD9583006B8A35 /* Assets.xcassets */; }; CE1C4FC02ABD9583006B8A35 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE1C4FBF2ABD9583006B8A35 /* Preview Assets.xcassets */; }; + CE1C4FC72ABD959A006B8A35 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1C4FC62ABD959A006B8A35 /* Image.swift */; }; + CE1C4FD02ABD95AC006B8A35 /* ImageFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = CE1C4FCF2ABD95AC006B8A35 /* ImageFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE1C4FD32ABD95AC006B8A35 /* ImageFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1C4FCD2ABD95AC006B8A35 /* ImageFramework.framework */; }; + CE1C4FD42ABD95AC006B8A35 /* ImageFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CE1C4FCD2ABD95AC006B8A35 /* ImageFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CEBF0D152AC17B4B00140C74 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1C4FD92ABD95B4006B8A35 /* Image.swift */; }; + CEBF0D172AC17BA500140C74 /* libImageLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBF0D0E2AC17B4600140C74 /* libImageLibrary.a */; platformFilter = ios; }; + CEBF0D1B2AC17BFC00140C74 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBF0D1A2AC17BFC00140C74 /* Image.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + CE1C4FD12ABD95AC006B8A35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CE1C4FAC2ABD9581006B8A35 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CE1C4FCC2ABD95AC006B8A35; + remoteInfo = ImageFramework; + }; + CEBF0D182AC17BA500140C74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CE1C4FAC2ABD9581006B8A35 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEBF0D0D2AC17B4600140C74; + remoteInfo = ImageLibrary; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + CE1C4FD82ABD95AC006B8A35 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CE1C4FD42ABD95AC006B8A35 /* ImageFramework.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + CEBF0D0C2AC17B4600140C74 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ CE1C4FB42ABD9581006B8A35 /* OutputPostprocessorFileMoverTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OutputPostprocessorFileMoverTests.app; sourceTree = BUILT_PRODUCTS_DIR; }; CE1C4FB72ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputPostprocessorFileMoverTestsApp.swift; sourceTree = ""; }; @@ -20,10 +67,32 @@ CE1C4FBB2ABD9583006B8A35 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; CE1C4FBD2ABD9583006B8A35 /* OutputPostprocessorFileMoverTests.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OutputPostprocessorFileMoverTests.entitlements; sourceTree = ""; }; CE1C4FBF2ABD9583006B8A35 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + CE1C4FC62ABD959A006B8A35 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; + CE1C4FCD2ABD95AC006B8A35 /* ImageFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ImageFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE1C4FCF2ABD95AC006B8A35 /* ImageFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageFramework.h; sourceTree = ""; }; + CE1C4FD92ABD95B4006B8A35 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; + CEBF0D0E2AC17B4600140C74 /* libImageLibrary.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libImageLibrary.a; sourceTree = BUILT_PRODUCTS_DIR; }; + CEBF0D1A2AC17BFC00140C74 /* Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ CE1C4FB12ABD9581006B8A35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1C4FD32ABD95AC006B8A35 /* ImageFramework.framework in Frameworks */, + CEBF0D172AC17BA500140C74 /* libImageLibrary.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CE1C4FCA2ABD95AC006B8A35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBF0D0B2AC17B4600140C74 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -37,7 +106,10 @@ isa = PBXGroup; children = ( CE1C4FB62ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */, + CE1C4FCE2ABD95AC006B8A35 /* ImageFramework */, + CEBF0D0F2AC17B4600140C74 /* ImageLibrary */, CE1C4FB52ABD9581006B8A35 /* Products */, + CEBF0D162AC17BA500140C74 /* Frameworks */, ); sourceTree = ""; }; @@ -45,6 +117,8 @@ isa = PBXGroup; children = ( CE1C4FB42ABD9581006B8A35 /* OutputPostprocessorFileMoverTests.app */, + CE1C4FCD2ABD95AC006B8A35 /* ImageFramework.framework */, + CEBF0D0E2AC17B4600140C74 /* libImageLibrary.a */, ); name = Products; sourceTree = ""; @@ -54,6 +128,7 @@ children = ( CE1C4FB72ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift */, CE1C4FB92ABD9581006B8A35 /* ContentView.swift */, + CE1C4FC62ABD959A006B8A35 /* Image.swift */, CE1C4FBB2ABD9583006B8A35 /* Assets.xcassets */, CE1C4FBD2ABD9583006B8A35 /* OutputPostprocessorFileMoverTests.entitlements */, CE1C4FBE2ABD9583006B8A35 /* Preview Content */, @@ -69,8 +144,43 @@ path = "Preview Content"; sourceTree = ""; }; + CE1C4FCE2ABD95AC006B8A35 /* ImageFramework */ = { + isa = PBXGroup; + children = ( + CE1C4FCF2ABD95AC006B8A35 /* ImageFramework.h */, + CEBF0D1A2AC17BFC00140C74 /* Image.swift */, + ); + path = ImageFramework; + sourceTree = ""; + }; + CEBF0D0F2AC17B4600140C74 /* ImageLibrary */ = { + isa = PBXGroup; + children = ( + CE1C4FD92ABD95B4006B8A35 /* Image.swift */, + ); + path = ImageLibrary; + sourceTree = ""; + }; + CEBF0D162AC17BA500140C74 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + CE1C4FC82ABD95AC006B8A35 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CE1C4FD02ABD95AC006B8A35 /* ImageFramework.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ CE1C4FB32ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */ = { isa = PBXNativeTarget; @@ -79,16 +189,54 @@ CE1C4FB02ABD9581006B8A35 /* Sources */, CE1C4FB12ABD9581006B8A35 /* Frameworks */, CE1C4FB22ABD9581006B8A35 /* Resources */, + CE1C4FD82ABD95AC006B8A35 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + CE1C4FD22ABD95AC006B8A35 /* PBXTargetDependency */, + CEBF0D192AC17BA500140C74 /* PBXTargetDependency */, ); name = OutputPostprocessorFileMoverTests; productName = OutputPostprocessorFileMoverTests; productReference = CE1C4FB42ABD9581006B8A35 /* OutputPostprocessorFileMoverTests.app */; productType = "com.apple.product-type.application"; }; + CE1C4FCC2ABD95AC006B8A35 /* ImageFramework */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE1C4FD52ABD95AC006B8A35 /* Build configuration list for PBXNativeTarget "ImageFramework" */; + buildPhases = ( + CE1C4FC82ABD95AC006B8A35 /* Headers */, + CE1C4FC92ABD95AC006B8A35 /* Sources */, + CE1C4FCA2ABD95AC006B8A35 /* Frameworks */, + CE1C4FCB2ABD95AC006B8A35 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ImageFramework; + productName = ImageFramework; + productReference = CE1C4FCD2ABD95AC006B8A35 /* ImageFramework.framework */; + productType = "com.apple.product-type.framework"; + }; + CEBF0D0D2AC17B4600140C74 /* ImageLibrary */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEBF0D122AC17B4600140C74 /* Build configuration list for PBXNativeTarget "ImageLibrary" */; + buildPhases = ( + CEBF0D0A2AC17B4600140C74 /* Sources */, + CEBF0D0B2AC17B4600140C74 /* Frameworks */, + CEBF0D0C2AC17B4600140C74 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ImageLibrary; + productName = ImageLibrary; + productReference = CEBF0D0E2AC17B4600140C74 /* libImageLibrary.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -102,6 +250,13 @@ CE1C4FB32ABD9581006B8A35 = { CreatedOnToolsVersion = 15.0; }; + CE1C4FCC2ABD95AC006B8A35 = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1500; + }; + CEBF0D0D2AC17B4600140C74 = { + CreatedOnToolsVersion = 15.0; + }; }; }; buildConfigurationList = CE1C4FAF2ABD9581006B8A35 /* Build configuration list for PBXProject "OutputPostprocessorFileMoverTests" */; @@ -118,6 +273,8 @@ projectRoot = ""; targets = ( CE1C4FB32ABD9581006B8A35 /* OutputPostprocessorFileMoverTests */, + CE1C4FCC2ABD95AC006B8A35 /* ImageFramework */, + CEBF0D0D2AC17B4600140C74 /* ImageLibrary */, ); }; /* End PBXProject section */ @@ -132,6 +289,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CE1C4FCB2ABD95AC006B8A35 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -140,12 +304,43 @@ buildActionMask = 2147483647; files = ( CE1C4FBA2ABD9581006B8A35 /* ContentView.swift in Sources */, + CE1C4FC72ABD959A006B8A35 /* Image.swift in Sources */, CE1C4FB82ABD9581006B8A35 /* OutputPostprocessorFileMoverTestsApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + CE1C4FC92ABD95AC006B8A35 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEBF0D1B2AC17BFC00140C74 /* Image.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEBF0D0A2AC17B4600140C74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEBF0D152AC17B4B00140C74 /* Image.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + CE1C4FD22ABD95AC006B8A35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CE1C4FCC2ABD95AC006B8A35 /* ImageFramework */; + targetProxy = CE1C4FD12ABD95AC006B8A35 /* PBXContainerItemProxy */; + }; + CEBF0D192AC17BA500140C74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + platformFilter = ios; + target = CEBF0D0D2AC17B4600140C74 /* ImageLibrary */; + targetProxy = CEBF0D182AC17BA500140C74 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ CE1C4FC12ABD9583006B8A35 /* Debug */ = { isa = XCBuildConfiguration; @@ -264,12 +459,15 @@ CE1C4FC42ABD9583006B8A35 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"OutputPostprocessorFileMoverTests/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; @@ -289,6 +487,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.test.OutputPostprocessorFileMoverTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -300,12 +499,15 @@ CE1C4FC52ABD9583006B8A35 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests.entitlements; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"OutputPostprocessorFileMoverTests/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; @@ -325,6 +527,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.test.OutputPostprocessorFileMoverTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -333,6 +536,115 @@ }; name = Release; }; + CE1C4FD62ABD95AC006B8A35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.ImageFramework; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CE1C4FD72ABD95AC006B8A35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.test.ImageFramework; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CEBF0D132AC17B4600140C74 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEBF0D142AC17B4600140C74 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -354,6 +666,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + CE1C4FD52ABD95AC006B8A35 /* Build configuration list for PBXNativeTarget "ImageFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE1C4FD62ABD95AC006B8A35 /* Debug */, + CE1C4FD72ABD95AC006B8A35 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEBF0D122AC17B4600140C74 /* Build configuration list for PBXNativeTarget "ImageLibrary" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEBF0D132AC17B4600140C74 /* Debug */, + CEBF0D142AC17B4600140C74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = CE1C4FAC2ABD9581006B8A35 /* Project object */; diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift index acc46f7..29051fc 100644 --- a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/ContentView.swift @@ -6,19 +6,24 @@ // import SwiftUI +import ImageFramework +import ImageLibrary struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") - } - .padding() - } + var body: some View { + VStack { + SwiftUI.Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + ImageFramework.Image() + Image() + ImageLibrary.Image() + } + .padding() + } } #Preview { - ContentView() + ContentView() } diff --git a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift index 366c5dc..abb11c0 100644 --- a/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift +++ b/TestAssets/OutputPostprocessorFileMoverTests/OutputPostprocessorFileMoverTests/Image.swift @@ -5,4 +5,11 @@ // Created by Thomas Hedderwick on 22/09/2023. // -import Foundation +import SwiftUI + +struct Image: View { + @inline(never) + var body: some View { + Text("Pretend I'm an image - app") + } +} diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index 8272418..7e927be 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -8,4 +8,31 @@ final class OutputPostprocessorFileMoverTests: XCTestCase { .appendingPathComponent("OutputPostprocessorFileMoverTests") .appendingPathComponent("OutputPostprocessorFileMoverTests.xcodeproj") }() + + func testFileMoving() throws { + let context = try TestContext() + let result = try context.build(test: Self.testPath, scheme: "OutputPostprocessorFileMoverTests") + XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + + var runner = IREmitterCommand() + try runner.run( + project: Self.testPath, + log: context.buildLog.filePath, + archive: context.archive, + level: .debug, + dryRun: false + ) + + // Check the output path for unique Image files + print("archivePAth: \(context.archive)") + let appIRPath = context.archive + .appendingPathComponent("IR") + .appendingPathComponent("OutputPostprocessorFileMoverTests.app") + let files = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) + let imageFilesCount = files.filter { $0.lastPathComponent.starts(with: "Image") }.count + print("imageFilesCount: \(imageFilesCount)") + + // Only expecting two - one of the dependencies is dynamic and won't be moved. + XCTAssertEqual(imageFilesCount, 2, "2 Image*.bc files expected, \(imageFilesCount) were found.") + } } From 89d3231c67e5b85e703f7853f65348da767fcd00 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 6 Oct 2023 10:51:15 +0200 Subject: [PATCH 25/61] Clean up, allow for graph dumping, fix the graph This change adds: * the ability to write a DOT file representing the dependency graph of the project with new CLI option * fixes the graph by correcting a mistake where edges could be linked multiple times, and removes original transitive depependency finding since that's now in the graph * adds support for 'embed frameworks' copy files build phase --- .../Sources/PBXProjParser/XcodeProject.swift | 33 +++++++++--- .../Dependency Graph/DependencyGraph.swift | 15 ++++++ Sources/GenIR/Dependency Graph/Node.swift | 4 +- Sources/GenIR/GenIR.swift | 10 +++- Sources/GenIR/OutputPostprocessor.swift | 53 +++++++++---------- 5 files changed, 79 insertions(+), 36 deletions(-) diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift index 767d613..90e9a77 100644 --- a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift +++ b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift @@ -42,12 +42,9 @@ public struct XcodeProject { packages = model.objects(of: .swiftPackageProductDependency, as: XCSwiftPackageProductDependency.self) - // First pass - get all the direct dependencies + // get all the direct dependencies targets.forEach { determineDirectDependencies($0) } - // Second pass - get all the transitive dependencies - targets.forEach { determineTransitiveDependencies($0) } - targets.forEach { target in logger.debug("target: \(target.name). Dependencies: \(target.targetDependencies.map { $0.1.name })") } @@ -105,8 +102,13 @@ public struct XcodeProject { .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } .forEach { target.add(dependency: .package($0)) } + // Calculate dependencies from "Embed Frameworks" copy files build phase + let embeddedFrameworks = determineEmbeddedFrameworksDependencies(target, with: model) + // Calculate the dependencies from "Link Binary with Library" build phase - let buildFiles = determineBuildPhaseFrameworkDependencies(target, with: model) + let linkLibraries = determineBuildPhaseFrameworkDependencies(target, with: model) + + let buildFiles = embeddedFrameworks + linkLibraries // Now, we have two potential targets - file & package dependencies. // File dependencies will likely have a reference in another Xcode Project. We might not have seen said project yet, so we need to offload discovery until after we've parsed all projects... @@ -205,6 +207,25 @@ private func determineBuildPhaseFrameworkDependencies(_ target: PBXNativeTarget, return [] } - return buildPhase.files + return buildPhase + .files .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } } + +private func determineEmbeddedFrameworksDependencies(_ target: PBXNativeTarget, with model: PBXProj) -> [PBXBuildFile] { + // Find the "Embed Frameworks" build phase (copy files build phase) + let buildPhases = target + .buildPhases + .compactMap { model.object(forKey: $0, as: PBXCopyFilesBuildPhase.self) } + + return buildPhases + .flatMap { $0.files } + .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } + .filter { file in + guard let ref = file.fileRef else { return false } + guard let object = model.object(forKey: ref, as: PBXFileReference.self) else { return false } + guard object.explicitFileType == "wrapper.framework" else { return false } + + return true + } +} diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index a6d1ca5..b2d4098 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -5,6 +5,8 @@ // Created by Thomas Hedderwick on 28/08/2023. // +import Foundation + /// A directed graph that maps dependencies between targets (nodes) via edges (directions between nodes) class DependencyGraph { /// All the nodes in the graph @@ -42,6 +44,19 @@ class DependencyGraph { return depthFirstSearch(startingAt: targetNode) } + func toDot(_ path: String) throws { + var contents = "digraph DependencyGraph {\n" + + for node in nodes { + for edge in node.edges.filter({ $0.relationship == .dependency }) { + contents.append("\(node.name.replacingOccurrences(of: "-", with: "_")) -> \(edge.to.name.replacingOccurrences(of: "-", with: "_"))\n") + } + } + + contents.append("}") + try contents.write(toFile: path, atomically: true, encoding: .utf8) + } + /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index f0ff937..dd9a8c1 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -23,7 +23,9 @@ class Node { /// Adds an edge to this node /// - Parameter edge: the edge to add func add(edge: Edge) { - edges.append(edge) + if edges.filter({ $0.to.name == edge.to.name }).count == 0 { + edges.append(edge) + } } } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index be1d866..605bfe5 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -59,6 +59,9 @@ struct IREmitterCommand: ParsableCommand { @Flag(help: "Runs the tool without outputting IR to disk (i.e. leaving out the compiler command runner stage)") var dryRun = false + @Flag(help: "Output the dependency graph as .dot files to the output directory - debug only") + var dumpDependencyGraph = false + mutating func validate() throws { if debug { logger.logLevel = .debug @@ -121,7 +124,12 @@ struct IREmitterCommand: ParsableCommand { ) try runner.run(targets: targets) - let postprocessor = try OutputPostprocessor(archive: archive, output: output, targets: targets) + let postprocessor = try OutputPostprocessor( + archive: archive, + output: output, + targets: targets, + dumpGraph: dumpDependencyGraph + ) try postprocessor.process(targets: &targets) } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 19f6824..0ddcf6c 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -8,7 +8,7 @@ import Foundation import PBXProjParser -private typealias FilenameAndSize = (String, Int) +private typealias SizeAndCreation = (Int, Date) /// The `OutputPostprocessor` is responsible for trying to match the IR output of the `CompilerCommandRunner` with the products in the `xcarchive`. /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. @@ -23,16 +23,23 @@ class OutputPostprocessor { private let graph: DependencyGraph - private var seenConflictingFiles: [URL: [FilenameAndSize]] = [:] + private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] private let manager: FileManager = .default - init(archive: URL, output: URL, targets: Targets) throws { + init(archive: URL, output: URL, targets: Targets, dumpGraph: Bool) throws { self.output = output dynamicDependencyToPath = dynamicDependencies(in: self.archive) graph = DependencyGraphBuilder.build(targets: targets) + if dumpGraph { + do { + try graph.toDot(output.appendingPathComponent("graph.dot").filePath) + } catch { + logger.error("toDot error: \(error)") + } + } } /// Starts the OutputPostprocessor @@ -74,8 +81,8 @@ class OutputPostprocessor { ) throws -> Set { let chain = graph.chain(for: target) - logger.info("Chain for target: \(target.nameForOutput):\n") - chain.forEach { logger.info("\($0)") } + logger.debug("Chain for target: \(target.nameForOutput):\n") + chain.forEach { logger.debug("\($0)") } // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent var processed = Set() @@ -160,48 +167,39 @@ class OutputPostprocessor { /// - source: source file path /// - destination: destination file path private func copyFileUniquingConflictingFiles(source: URL, destination: URL) throws { - /// Returns the size of the path. Throws is attributes cannot be found for this file path. - /// - Parameter path: the path to get the file size of - /// - Returns: the size of the file at path, if it was able to be converted to an integer - func size(for path: URL) throws -> Int? { - do { - return try manager.attributesOfItem(atPath: path.filePath)[.size] as? Int - } catch { - logger.debug("Couldn't get size attribute for path: \(path.filePath)") - throw error - } - } + let destinationAttributes = try manager.attributesOfItem(atPath: destination.filePath) + let sourceAttributes = try manager.attributesOfItem(atPath: source.filePath) guard - let destinationSize = try size(for: destination), - let sourceSize = try size(for: source) + let destinationSize = destinationAttributes[.size] as? Int, + let sourceSize = sourceAttributes[.size] as? Int, + let destinationCreatedDate = destinationAttributes[.creationDate] as? Date, + let sourceCreatedDate = sourceAttributes[.creationDate] as? Date else { + logger.debug("Failed to get attributes for source: \(source) & destination: \(destination)") return } let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - // TODO: Should we use all file attributes here? What about created date etc? if seenConflictingFiles[source] == nil { - seenConflictingFiles[source] = [(source.lastPathComponent, sourceSize)] + seenConflictingFiles[source] = [(sourceSize, sourceCreatedDate)] } - for (_, size) in seenConflictingFiles[source]! where size == destinationSize { - logger.debug("Ignoring copy of: \(destination.lastPathComponent) as the sizes where the same") + for (size, date) in seenConflictingFiles[source]! where size == destinationSize && date == destinationCreatedDate { return } - seenConflictingFiles[source]!.append((uniqueDestinationURL.lastPathComponent, destinationSize)) - + seenConflictingFiles[source]!.append((destinationSize, destinationCreatedDate)) + logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") try manager.copyItem(at: source, to: uniqueDestinationURL) } } -// swiftlint:disable private_over_fileprivate /// Returns a map of dynamic objects in the provided path /// - Parameter xcarchive: the path to search through /// - Returns: a mapping of filename to filepath for dynamic objects in the provided path -fileprivate func dynamicDependencies(in xcarchive: URL) -> [String: URL] { +private func dynamicDependencies(in xcarchive: URL) -> [String: URL] { let searchPath = baseSearchPath(startingAt: xcarchive) logger.debug("Using search path for dynamic dependencies: \(searchPath)") @@ -220,7 +218,7 @@ fileprivate func dynamicDependencies(in xcarchive: URL) -> [String: URL] { /// Returns the base URL to start searching inside an xcarchive /// - Parameter path: the original path, should be an xcarchive /// - Returns: the path to start a dependency search from -fileprivate func baseSearchPath(startingAt path: URL) -> URL { +private func baseSearchPath(startingAt path: URL) -> URL { let productsPath = path.appendingPathComponent("Products") let applicationsPath = productsPath.appendingPathComponent("Applications") let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") @@ -250,4 +248,3 @@ fileprivate func baseSearchPath(startingAt path: URL) -> URL { logger.debug("Couldn't determine the base search path for the xcarchive, using: \(productsPath)") return productsPath } -// swiftlint:enable private_over_fileprivate From de5d42d0e42c05f69f708fa8e5824129af01d202 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 30 Oct 2023 10:42:38 +0100 Subject: [PATCH 26/61] Add support in the API for the new arguments --- Sources/GenIR/GenIR.swift | 12 ++++++++++-- .../OutputPostprocessorFileMoverTests.swift | 3 ++- Tests/GenIRTests/UmbrellaTests.swift | 6 ++++-- Tests/GenIRTests/WorkspaceTests.swift | 4 ++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 605bfe5..58694a0 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -98,11 +98,19 @@ struct IREmitterCommand: ParsableCommand { log: logPath, archive: xcarchivePath, level: logger.logLevel, - dryRun: dryRun + dryRun: dryRun, + dumpDependencyGraph: dumpDependencyGraph ) } - mutating func run(project: URL, log: String, archive: URL, level: Logger.Level, dryRun: Bool) throws { + mutating func run( + project: URL, + log: String, + archive: URL, + level: Logger.Level, + dryRun: Bool, + dumpDependencyGraph: Bool + ) throws { let output = archive.appendingPathComponent("IR") let project = try ProjectParser(path: project, logLevel: level) var targets = Targets(for: project) diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index 7e927be..b4ca4e8 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -20,7 +20,8 @@ final class OutputPostprocessorFileMoverTests: XCTestCase { log: context.buildLog.filePath, archive: context.archive, level: .debug, - dryRun: false + dryRun: false, + dumpDependencyGraph: false ) // Check the output path for unique Image files diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index ed8064b..167b05f 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -51,7 +51,8 @@ final class UmbrellaTests: XCTestCase { log: context.buildLog.filePath, archive: context.archive, level: .debug, - dryRun: false + dryRun: false, + dumpDependencyGraph: false ) let directories = try FileManager.default.directories(at: output, recursive: false) @@ -82,7 +83,8 @@ final class UmbrellaTests: XCTestCase { log: context.buildLog.filePath, archive: context.archive, level: .debug, - dryRun: false + dryRun: false, + dumpDependencyGraph: false ) let directories = try FileManager.default.directories(at: output, recursive: false) diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 9979666..7c2851a 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -54,7 +54,6 @@ final class WorkspaceTests: XCTestCase { let process = try context.build(test: Self.testPath, scheme: Self.scheme) XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") - let output = context.archive.appendingPathComponent("IR") var genIR = gen_ir.IREmitterCommand() try genIR.run( @@ -62,7 +61,8 @@ final class WorkspaceTests: XCTestCase { log: context.buildLog.filePath, archive: context.archive, level: .debug, - dryRun: false + dryRun: false, + dumpDependencyGraph: false ) // Check dependencies made it to the right place From 50de129d29cb7921be067ba35acdfab83435842a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 1 Dec 2023 11:30:49 +0100 Subject: [PATCH 27/61] Fix compliation error --- Sources/GenIR/OutputPostprocessor.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 0ddcf6c..643f8c2 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -18,6 +18,9 @@ class OutputPostprocessor { /// The to the output IR folders that will be processed let output: URL + /// The archive path, this should be the parent path of `output` + let archive: URL + /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk private let dynamicDependencyToPath: [String: URL] @@ -29,6 +32,7 @@ class OutputPostprocessor { init(archive: URL, output: URL, targets: Targets, dumpGraph: Bool) throws { self.output = output + self.archive = archive dynamicDependencyToPath = dynamicDependencies(in: self.archive) From 42c2eb9cedd9a56126d3c01c4cf626ea91a1a98b Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 1 Dec 2023 11:51:42 +0100 Subject: [PATCH 28/61] Remove periphery. Broken on runners --- .github/workflows/periphery.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/periphery.yml diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml deleted file mode 100644 index b645c49..0000000 --- a/.github/workflows/periphery.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: periphery - -on: - pull_request: - paths: - - '**/*.swift' - -jobs: - periphery-scan: - runs-on: macos-13 - steps: - - uses: actions/checkout@v1 - - name: Set up Homebrew - id: set-up-homebrew - uses: Homebrew/actions/setup-homebrew@master - - name: Install periphery - run: | - brew install peripheryapp/periphery/periphery - - name: Scan for unused code - run: | - periphery scan From 0536a2aa134ca1f6c981e21480b4466b563b9902 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 1 Dec 2023 11:51:56 +0100 Subject: [PATCH 29/61] Fix missing image in branching model --- Documentation/branching_model.md | 2 +- Documentation/images/branching_model.svg | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Documentation/images/branching_model.svg diff --git a/Documentation/branching_model.md b/Documentation/branching_model.md index 21484dd..024f996 100644 --- a/Documentation/branching_model.md +++ b/Documentation/branching_model.md @@ -1,6 +1,6 @@ # Gen IR Branching Model -![Branching Model](images/branching_model.png) +![Branching Model](images/branching_model.svg) Gen IR uses the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. This model is tried, tested, and has remained so over 10+ years for a project of this type. diff --git a/Documentation/images/branching_model.svg b/Documentation/images/branching_model.svg new file mode 100644 index 0000000..446ec5f --- /dev/null +++ b/Documentation/images/branching_model.svg @@ -0,0 +1,4 @@ + + + +
0.4.3
0.4...
main
main
0.5.0
0.5...
develop
develop
0.4.4
0.4...
feature branch 1
feature branch 1
feature branch 2
feature branch 2
0.4.4-alpha
0.4...
0.4.4-alpha.1
0.4...
0.5.0-alpha
0.5...
Text is not SVG - cannot display
\ No newline at end of file From 2e833907631d0bee09ae7e09088edbb6df108a26 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 1 Dec 2023 15:09:20 +0100 Subject: [PATCH 30/61] Improve brew release docs slightly --- Documentation/releasing_an_update.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/releasing_an_update.md b/Documentation/releasing_an_update.md index b08ddab..e8d1be8 100644 --- a/Documentation/releasing_an_update.md +++ b/Documentation/releasing_an_update.md @@ -90,6 +90,9 @@ So, if you have released a new major or minor version you should: Using the history of the `homebrew-tap` find the version of the Gen IR formula you're looking for, then copy the file to the `Formula` folder renaming it like so: `gen-ir@` -Edit the file to add the `keg_only :versioned_formula` tag after the `bottle`. +Edit the file to: + +- change the name of the class to add the version, for example version 0.3.11 becomes: `GenIrAT0311`. +- add the `keg_only :versioned_formula` tag after the `bottle`. > Note: it is a good idea to run `brew style Formulae/gen-ir@.rb` before you push the commit! Brew is _very_ particular about the layout of a formula and the test-bot will fail if your key isn't in the right spot. From 351abe7d6aebe15264bc71d171a0f451b591beda Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 7 May 2024 10:56:47 +0200 Subject: [PATCH 31/61] Add PIFSupport Library This library derives it's behaviour from SPM's PIF decoding, but adjusts it to work with Xcode's internal json file format. --- .swiftlint.yml | 1 + PIF/.gitignore | 8 + PIF/NOTICE.txt | 11 + PIF/Package.swift | 35 + PIF/Sources/PIFSupport/PIF.swift | 909 ++++++++++++++++++++++++ PIF/Sources/PIFSupport/PIFSupport.swift | 49 ++ PIF/Sources/pif-parser/pif-parser.swift | 27 + PIF/Tests/PIFTests/PIFTests.swift | 12 + 8 files changed, 1052 insertions(+) create mode 100644 PIF/.gitignore create mode 100644 PIF/NOTICE.txt create mode 100644 PIF/Package.swift create mode 100644 PIF/Sources/PIFSupport/PIF.swift create mode 100644 PIF/Sources/PIFSupport/PIFSupport.swift create mode 100644 PIF/Sources/pif-parser/pif-parser.swift create mode 100644 PIF/Tests/PIFTests/PIFTests.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 5d6a87b..8b2d54e 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -8,6 +8,7 @@ excluded: disabled_rules: - todo + - nesting line_length: warning: 200 diff --git a/PIF/.gitignore b/PIF/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/PIF/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/PIF/NOTICE.txt b/PIF/NOTICE.txt new file mode 100644 index 0000000..26b34a9 --- /dev/null +++ b/PIF/NOTICE.txt @@ -0,0 +1,11 @@ + + PIF Support + =========== + +This product contains a derivation of Swift Package Manager's XCBuildSupport library, specifically `PIF.swift` + + * LICENSE (Apache License 2.0): + * https://raw.githubusercontent.com/apple/swift-package-manager/main/LICENSE.txt + * HOMEPAGE: + * https://github.com/apple/swift-package-manager/ + * https://swift.org diff --git a/PIF/Package.swift b/PIF/Package.swift new file mode 100644 index 0000000..9391feb --- /dev/null +++ b/PIF/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "PIF", + platforms: [.macOS(.v12)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "PIFSupport", + targets: ["PIFSupport"] + ) + ], + dependencies: [], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "PIFSupport", + dependencies: [] + ), + .testTarget( + name: "PIFSupportTests", + dependencies: ["PIFSupport"] + ), + .executableTarget( + name: "pif-parser", + dependencies: [ + "PIFSupport" + ] + ) + ] +) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift new file mode 100644 index 0000000..ef342cb --- /dev/null +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -0,0 +1,909 @@ +// ===----------------------------------------------------------------------=== // +// +// This source file contains derivative work from the Swift Open Source Project +// +// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +// ===----------------------------------------------------------------------=== // + +/* Changes: Thomas Hedderwick: + - adjust structures to allow for only decoding Xcode's PIF files found in DerivedData + - adjust types to remove external dependencies on SPM +*/ + +// swiftlint:disable file_length type_body_length +import Foundation + +/// The Project Interchange Format (PIF) is a structured representation of the +/// project model created by clients (Xcode/SwiftPM) to send to XCBuild. +/// +/// The PIF is a representation of the project model describing the static +/// objects which contribute to building products from the project, independent +/// of "how" the user has chosen to build those products in any particular +/// build. This information can be cached by XCBuild between builds (even +/// between builds which use different schemes or configurations), and can be +/// incrementally updated by clients when something changes. +public enum PIF { + /// This is used as part of the signature for the high-level PIF objects, to ensure that changes to the PIF schema + /// are represented by the objects which do not use a content-based signature scheme (workspaces and projects, + /// currently). + static let schemaVersion = 11 + + /// The file extension for files in the PIF cache + static let cacheFileExtension = "-json" + + /// The type used for identifying PIF objects. + public typealias GUID = String + + public enum Error: Swift.Error { + case decodingError(String) + case userInfoError(String) + case dataReadingFailure(String) + } + + /// The top-level PIF object. + public struct TopLevelObject: Decodable { + public let workspace: PIF.Workspace + + public init(workspace: PIF.Workspace) { + self.workspace = workspace + } + } + + public class TypedObject: Decodable { + class var type: String { + fatalError("\(self) missing implementation") + } + + let type: String? + + fileprivate init() { + type = Swift.type(of: self).type + } + + private enum CodingKeys: CodingKey { + case type + } + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decode(String.self, forKey: .type) + } + } + + public final class Workspace: Decodable { + public let guid: GUID + public let name: String + public let path: URL + public let projects: [Project] + + private enum CodingKeys: CodingKey { + case guid, name, path, projects + } + + public required init(from decoder: Decoder) throws { + guard let cachePath = decoder.userInfo[.pifCachePath] as? URL else { + throw Error.userInfoError("decoder's userInfo doesn't container required key .cachePath, or that value isn't a URL") + } + + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + name = try container.decode(String.self, forKey: .name) + path = try container.decode(URL.self, forKey: .path) + + let projectPaths = try container.decode([String].self, forKey: .projects) + .map { + cachePath + .appendingPathComponent("project") + .appendingPathComponent("\($0)\(PIF.cacheFileExtension)") + } + + let projectContents = try projectPaths + .map { + do { + return try Data(contentsOf: $0) + } catch { + throw Error.dataReadingFailure(error.localizedDescription) + } + } + + projects = try projectContents + .map { + // do { + return try PIFDecoder(cache: cachePath).decode(PIF.Project.self, from: $0) + // } catch { + // fatalError(String(data: $0, encoding: .utf8)!) + // } + } + } + } + + /// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional + /// information. + public final class Project: Decodable { + public let guid: GUID + public let projectName: String? + public let path: URL + public let projectDirectory: URL + public let developmentRegion: String? + public let buildConfigurations: [BuildConfiguration] + public let targets: [BaseTarget] + public let groupTree: Group + + private enum CodingKeys: CodingKey { + case guid, projectName, projectIsPackage, path, projectDirectory, developmentRegion, defaultConfigurationName, buildConfigurations, targets, groupTree + } + + public required init(from decoder: Decoder) throws { + guard let cachePath = decoder.userInfo[.pifCachePath] as? URL else { + throw Error.userInfoError("decoder's userInfo doesn't container required key .cachePath, or that value isn't a URL") + } + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + projectName = try container.decodeIfPresent(String.self, forKey: .projectName) + path = try container.decode(URL.self, forKey: .path) + projectDirectory = try container.decode(URL.self, forKey: .projectDirectory) + developmentRegion = try container.decodeIfPresent(String.self, forKey: .developmentRegion) + buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + + let targetContents = try container.decode([String].self, forKey: .targets) + .map { + cachePath + .appendingPathComponent("target") + .appendingPathComponent("\($0)\(PIF.cacheFileExtension)") + } + .map { + do { + return try Data(contentsOf: $0) + } catch { + throw Error.dataReadingFailure(error.localizedDescription) + } + } + + targets = try targetContents + .map { targetData -> BaseTarget in + let pifDecoder = PIFDecoder(cache: cachePath) + let untypedTarget = try pifDecoder.decode(PIF.TypedObject.self, from: targetData) + switch untypedTarget.type { + case "aggregate": + return try pifDecoder.decode(PIF.AggregateTarget.self, from: targetData) + case "standard", "packageProduct": + return try pifDecoder.decode(PIF.Target.self, from: targetData) + default: + throw Error.decodingError("Target type unknown: \(untypedTarget)") + } + } + + self.groupTree = try container.decode(Group.self, forKey: .groupTree) + } + } + + /// Abstract base class for all items in the group hierarchy. + public class Reference: TypedObject { + /// Determines the base path for a reference's relative path. + public enum SourceTree: String, Decodable { + /// Indicates that the path is relative to the source root (i.e. the "project directory"). + case sourceRoot = "SOURCE_ROOT" + + /// Indicates that the path is relative to the path of the parent group. + case group = "" + + /// Indicates that the path is relative to the effective build directory (which varies depending on active + /// scheme, active run destination, or even an overridden build setting. + case builtProductsDir = "BUILT_PRODUCTS_DIR" + + /// Indicates that the path is an absolute path. + case absolute = "" + + /// Indicates that the path is relative to the SDKROOT + case sdkRoot = "SDKROOT" + } + + public let guid: GUID + + /// Relative path of the reference. It is usually a literal, but may in fact contain build settings. + public let path: String + + /// Determines the base path for the reference's relative path. + public let sourceTree: SourceTree + + /// Name of the reference, if different from the last path component (if not set, the last path component will + /// be used as the name). + public let name: String? + + private enum CodingKeys: CodingKey { + case guid, sourceTree, path, name, type + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(String.self, forKey: .guid) + sourceTree = try container.decode(SourceTree.self, forKey: .sourceTree) + path = try container.decode(String.self, forKey: .path) + name = try container.decodeIfPresent(String.self, forKey: .name) + + try super.init(from: decoder) + } + } + + /// A reference to a file system entity (a file, folder, etc). + public final class FileReference: Reference { + override class var type: String { "file" } + + public var fileType: String + + private enum CodingKeys: CodingKey { + case fileType + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + fileType = try container.decode(String.self, forKey: .fileType) + + try super.init(from: decoder) + } + } + + /// A group that can contain References (FileReferences and other Groups). The resolved path of a group is used as + /// the base path for any child references whose source tree type is GroupRelative. + public final class VariantGroup: Group { + override class var type: String { "variantGroup" } + + private enum CodingKeys: CodingKey { + case children, type + } + + public required init(from decoder: Decoder) throws { + try super.init(from: decoder) + } + } + + public final class VersionGroup: Group { + override class var type: String { "versionGroup" } + + private enum CodingKeys: CodingKey { + case children, type + } + + public required init(from decoder: Decoder) throws { + try super.init(from: decoder) + } + } + + /// A group that can contain References (FileReferences and other Groups). The resolved path of a group is used as + /// the base path for any child references whose source tree type is GroupRelative. + public class Group: Reference { + override class var type: String { "group" } + + public let children: [Reference] + + private enum CodingKeys: CodingKey { + case children, type + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let untypedChildren = try container.decodeIfPresent([TypedObject].self, forKey: .children) ?? [] + if !untypedChildren.isEmpty { + var childrenContainer = try container.nestedUnkeyedContainer(forKey: .children) + + children = try untypedChildren.compactMap { child in + switch child.type { + case Group.type: + return try childrenContainer.decode(Group.self) + case VariantGroup.type: + return try childrenContainer.decode(VariantGroup.self) + case VersionGroup.type: + return try childrenContainer.decode(VersionGroup.self) + case FileReference.type: + return try childrenContainer.decode(FileReference.self) + default: + // TODO: use logger here + print("unknown reference type: \(child.type ?? "")") + // TODO: support variantGroup etc + return nil + // throw Error.decodingError("unknown reference type \(child.type ?? "")") + } + } + } else { + children = [] + } + + try super.init(from: decoder) + } + } + + /// Represents a dependency on another target (identified by its PIF GUID). + public struct TargetDependency: Decodable { + /// Identifier of depended-upon target. + public let targetGUID: String + + /// The platform filters for this target dependency. + public let platformFilters: [PlatformFilter] + + private enum CodingKeys: CodingKey { + case guid, platformFilters + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + targetGUID = try container.decode(String.self, forKey: .guid) + platformFilters = try container.decodeIfPresent([PlatformFilter].self, forKey: .platformFilters) ?? [] + } + } + + public class BaseTarget: TypedObject { + class override var type: String { "target" } + + public let guid: GUID + public let name: String + public let buildConfigurations: [BuildConfiguration] + public let buildPhases: [BuildPhase] + public let dependencies: [TargetDependency] + public let impartedBuildProperties: ImpartedBuildProperties? + + fileprivate init( + guid: GUID, + name: String, + buildConfigurations: [BuildConfiguration], + buildPhases: [BuildPhase], + dependencies: [TargetDependency], + impartedBuildSettings: PIF.BuildSettings? + ) { + self.guid = guid + self.name = name + self.buildConfigurations = buildConfigurations + self.buildPhases = buildPhases + self.dependencies = dependencies + self.impartedBuildProperties = ImpartedBuildProperties(buildSettings: impartedBuildSettings ?? .init()) + super.init() + } + + public required init(from decoder: Decoder) throws { + throw Error.decodingError("init(from:) has not been implemented") + } + } + + public final class AggregateTarget: BaseTarget { + private enum CodingKeys: CodingKey { + case type, guid, name, buildConfigurations, buildPhases, dependencies, impartedBuildProperties + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let guid = try container.decode(GUID.self, forKey: .guid) + let name = try container.decode(String.self, forKey: .name) + let buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + + let untypedBuildPhases = try container.decode([TypedObject].self, forKey: .buildPhases) + var buildPhasesContainer = try container.nestedUnkeyedContainer(forKey: .buildPhases) + + let buildPhases: [BuildPhase] = try untypedBuildPhases.compactMap { + guard let type = $0.type else { + throw Error.decodingError("Expected type in build phase \($0)") + } + return try BuildPhase.decode(container: &buildPhasesContainer, type: type) + } + + let dependencies = try container.decode([TargetDependency].self, forKey: .dependencies) + let impartedBuildProperties = try container.decodeIfPresent(BuildSettings.self, forKey: .impartedBuildProperties) + + super.init( + guid: guid, + name: name, + buildConfigurations: buildConfigurations, + buildPhases: buildPhases, + dependencies: dependencies, + impartedBuildSettings: impartedBuildProperties + ) + } + } + + /// An Xcode target, representing a single entity to build. + public final class Target: BaseTarget { + public enum ProductType: String, Decodable { + case appExtension = "com.apple.product-type.app-extension" + case appExtensionMessages = "com.apple.product-type.app-extension.messages" + case stickerPackExtension = "com.apple.product-type.app-extension.messages-sticker-pack" + case application = "com.apple.product-type.application" + case applicationMessages = "com.apple.product-type.application.messages" + case appClip = "com.apple.product-type.application.on-demand-install-capable" + case bundle = "com.apple.product-type.bundle" + case externalTest = "com.apple.product-type.bundle.external-test" + case ocUnitTest = "com.apple.product-type.bundle.ocunit-test" + case uiTesting = "com.apple.product-type.bundle.ui-testing" + case unitTest = "com.apple.product-type.bundle.unit-test" + case extensionKitExtension = "com.apple.product-type.extensionkit-extension" + case framework = "com.apple.product-type.framework" + case staticFramework = "com.apple.product-type.framework.static" + case instrumentsPackage = "com.apple.product-type.instruments-package" + case kernelExtension = "com.apple.product-type.kernel-extension" + case ioKitKernelExtension = "com.apple.product-type.kernel-extension.iokit" + case dynamicLibrary = "com.apple.product-type.library.dynamic" + case staticLibrary = "com.apple.product-type.library.static" + case objectFile = "com.apple.product-type.objfile" + case pluginKitPlugin = "com.apple.product-type.pluginkit-plugin" + case packageProduct = "packageProduct" + case systemExtension = "com.apple.product-type.system-extension" + case tool = "com.apple.product-type.tool" + case hostBuild = "com.apple.product-type.tool.host-build" + case xpcService = "com.apple.product-type.xpc-service" + } + + public let productName: String + public let productType: ProductType + + private enum CodingKeys: CodingKey { + case guid, name, dependencies, buildConfigurations, type, frameworksBuildPhase, productTypeIdentifier, productReference, buildRules, buildPhases, impartedBuildProperties + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let guid = try container.decode(GUID.self, forKey: .guid) + let name = try container.decode(String.self, forKey: .name) + let buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + let dependencies = try container.decode([TargetDependency].self, forKey: .dependencies) + let type = try container.decode(String.self, forKey: .type) + + let buildPhases: [BuildPhase] + let impartedBuildProperties: ImpartedBuildProperties + + if type == "packageProduct" { + self.productType = .packageProduct + self.productName = "" + let fwkBuildPhase = try container.decodeIfPresent(FrameworksBuildPhase.self, forKey: .frameworksBuildPhase) + buildPhases = fwkBuildPhase.map { [$0] } ?? [] + impartedBuildProperties = ImpartedBuildProperties(buildSettings: BuildSettings()) + } else if type == "standard" { + self.productType = try container.decode(ProductType.self, forKey: .productTypeIdentifier) + + let productReference = try container.decode([String: String].self, forKey: .productReference) + self.productName = productReference["name"]! + + let untypedBuildPhases = try container.decodeIfPresent([TypedObject].self, forKey: .buildPhases) ?? [] + var buildPhasesContainer = try container.nestedUnkeyedContainer(forKey: .buildPhases) + + buildPhases = try untypedBuildPhases.compactMap { + guard let type = $0.type else { + throw Error.decodingError("Expected type in build phase \($0)") + } + return try BuildPhase.decode(container: &buildPhasesContainer, type: type) + } + + impartedBuildProperties = try container.decodeIfPresent(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) ?? .init(buildSettings: .init()) + } else { + throw Error.decodingError("Unhandled target type \(type)") + } + + super.init( + guid: guid, + name: name, + buildConfigurations: buildConfigurations, + buildPhases: buildPhases, + dependencies: dependencies, + impartedBuildSettings: impartedBuildProperties.buildSettings + ) + } + } + + /// Abstract base class for all build phases in a target. + public class BuildPhase: TypedObject { + static func decode(container: inout UnkeyedDecodingContainer, type: String) throws -> BuildPhase? { + switch type { + case HeadersBuildPhase.type: + return try container.decode(HeadersBuildPhase.self) + case SourcesBuildPhase.type: + return try container.decode(SourcesBuildPhase.self) + case FrameworksBuildPhase.type: + return try container.decode(FrameworksBuildPhase.self) + case ResourcesBuildPhase.type: + return try container.decode(ResourcesBuildPhase.self) + default: + // TODO: replace with logger + print("unknown build phase: \(type)") + return nil + // TODO: we should probably handle these: + /* + case copyFiles = "com.apple.buildphase.copy-files" + case frameworks = "com.apple.buildphase.frameworks" + case headers = "com.apple.buildphase.headers" + case resources = "com.apple.buildphase.resources" + case shellScript = "com.apple.buildphase.shell-script" + case sources = "com.apple.buildphase.sources"*/ + // throw Error.decodingError("unknown build phase \(type)") + } + } + + public let guid: GUID + public let buildFiles: [BuildFile] + + private enum CodingKeys: CodingKey { + case guid, buildFiles + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + buildFiles = try container.decode([BuildFile].self, forKey: .buildFiles) + + try super.init(from: decoder) + } + } + + /// A "headers" build phase, i.e. one that copies headers into a directory of the product, after suitable + /// processing. + public final class HeadersBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.headers" } + } + + /// A "sources" build phase, i.e. one that compiles sources and provides them to be linked into the executable code + /// of the product. + public final class SourcesBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.sources" } + } + + /// A "frameworks" build phase, i.e. one that links compiled code and libraries into the executable of the product. + public final class FrameworksBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.frameworks" } + } + + public final class ResourcesBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.resources" } + } + + /// A build file, representing the membership of either a file or target product reference in a build phase. + public struct BuildFile: Decodable { + public enum Reference { + case file(guid: PIF.GUID) + case target(guid: PIF.GUID) + } + + public enum HeaderVisibility: String, Decodable { + case `public` = "public" + case `private` = "private" + } + + public let guid: GUID + public let reference: Reference + public let headerVisibility: HeaderVisibility? + public let platformFilters: [PlatformFilter] + + private enum CodingKeys: CodingKey { + case guid, platformFilters, fileReference, targetReference, headerVisibility + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + platformFilters = try container.decodeIfPresent([PlatformFilter].self, forKey: .platformFilters) ?? [] + headerVisibility = try container.decodeIfPresent(HeaderVisibility.self, forKey: .headerVisibility) ?? nil + + if container.allKeys.contains(.fileReference) { + reference = try .file(guid: container.decode(GUID.self, forKey: .fileReference)) + } else if container.allKeys.contains(.targetReference) { + reference = .target(guid: try container.decode(GUID.self, forKey: .targetReference)) + } else { + throw Error.decodingError("Expected \(CodingKeys.fileReference) or \(CodingKeys.targetReference) in the keys") + } + } + } + + /// Represents a generic platform filter. + public struct PlatformFilter: Decodable, Equatable { + /// The name of the platform (`LC_BUILD_VERSION`). + /// + /// Example: macos, ios, watchos, tvos. + public let platform: String + + /// The name of the environment (`LC_BUILD_VERSION`) + /// + /// Example: simulator, maccatalyst. + public let environment: String? + } + + /// A build configuration, which is a named collection of build settings. + public struct BuildConfiguration: Decodable { + public let guid: GUID + public let name: String + public let buildSettings: BuildSettings + public let impartedBuildProperties: ImpartedBuildProperties + + private enum CodingKeys: CodingKey { + case guid, name, buildSettings, impartedBuildProperties + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + name = try container.decode(String.self, forKey: .name) + buildSettings = try container.decode(BuildSettings.self, forKey: .buildSettings) + impartedBuildProperties = try container.decodeIfPresent(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) ?? .init(buildSettings: .init()) + } + } + + public struct ImpartedBuildProperties: Decodable { + public let buildSettings: BuildSettings + } + + // swiftlint:disable identifier_name inclusive_language + /// A set of build settings, which is represented as a struct of optional build settings. This is not optimally + /// efficient, but it is great for code completion and type-checking. + public struct BuildSettings: Decodable { + public enum SingleValueSetting: String, Decodable { + case APPLICATION_EXTENSION_API_ONLY + case BUILT_PRODUCTS_DIR + case CLANG_CXX_LANGUAGE_STANDARD + case CLANG_ENABLE_MODULES + case CLANG_ENABLE_OBJC_ARC + case CODE_SIGNING_REQUIRED + case CODE_SIGN_IDENTITY + case COMBINE_HIDPI_IMAGES + case COPY_PHASE_STRIP + case DEBUG_INFORMATION_FORMAT + case DEFINES_MODULE + case DRIVERKIT_DEPLOYMENT_TARGET + case DYLIB_INSTALL_NAME_BASE + case EMBEDDED_CONTENT_CONTAINS_SWIFT + case ENABLE_NS_ASSERTIONS + case ENABLE_TESTABILITY + case ENABLE_TESTING_SEARCH_PATHS + case ENTITLEMENTS_REQUIRED + case EXECUTABLE_NAME + case GENERATE_INFOPLIST_FILE + case GCC_C_LANGUAGE_STANDARD + case GCC_OPTIMIZATION_LEVEL + case GENERATE_MASTER_OBJECT_FILE + case INFOPLIST_FILE + case IPHONEOS_DEPLOYMENT_TARGET + case KEEP_PRIVATE_EXTERNS + case CLANG_COVERAGE_MAPPING_LINKER_ARGS + case MACH_O_TYPE + case MACOSX_DEPLOYMENT_TARGET + case MODULEMAP_FILE + case MODULEMAP_FILE_CONTENTS + case MODULEMAP_PATH + case MODULE_CACHE_DIR + case ONLY_ACTIVE_ARCH + case PACKAGE_RESOURCE_BUNDLE_NAME + case PACKAGE_RESOURCE_TARGET_KIND + case PRODUCT_BUNDLE_IDENTIFIER + case PRODUCT_MODULE_NAME + case PRODUCT_NAME + case PROJECT_NAME + case SDKROOT + case SDK_VARIANT + case SKIP_INSTALL + case INSTALL_PATH + case SUPPORTS_MACCATALYST + case SWIFT_SERIALIZE_DEBUGGING_OPTIONS + case SWIFT_FORCE_STATIC_LINK_STDLIB + case SWIFT_FORCE_DYNAMIC_LINK_STDLIB + case SWIFT_INSTALL_OBJC_HEADER + case SWIFT_OBJC_INTERFACE_HEADER_NAME + case SWIFT_OBJC_INTERFACE_HEADER_DIR + case SWIFT_OPTIMIZATION_LEVEL + case SWIFT_VERSION + case TARGET_NAME + case TARGET_BUILD_DIR + case TVOS_DEPLOYMENT_TARGET + case USE_HEADERMAP + case USES_SWIFTPM_UNSAFE_FLAGS + case WATCHOS_DEPLOYMENT_TARGET + case XROS_DEPLOYMENT_TARGET + case MARKETING_VERSION + case CURRENT_PROJECT_VERSION + case SWIFT_EMIT_MODULE_INTERFACE + case GENERATE_RESOURCE_ACCESSORS + } + + public enum MultipleValueSetting: String, Decodable { + case EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES + case FRAMEWORK_SEARCH_PATHS + case GCC_PREPROCESSOR_DEFINITIONS + case HEADER_SEARCH_PATHS + case LD_RUNPATH_SEARCH_PATHS + case LIBRARY_SEARCH_PATHS + case OTHER_CFLAGS + case OTHER_CPLUSPLUSFLAGS + case OTHER_LDFLAGS + case OTHER_LDRFLAGS + case OTHER_SWIFT_FLAGS + case PRELINK_FLAGS + case SPECIALIZATION_SDK_OPTIONS + case SUPPORTED_PLATFORMS + case SWIFT_ACTIVE_COMPILATION_CONDITIONS + case SWIFT_MODULE_ALIASES + } + // swiftlint:enable identifier_name inclusive_language + + public enum Platform: String, CaseIterable, Decodable { + case macOS = "macos" + case macCatalyst = "maccatalyst" + case iOS = "ios" + case tvOS = "tvos" + case watchOS = "watchos" + case driverKit = "driverkit" + case linux + } + + public private(set) var platformSpecificSingleValueSettings = [Platform: [SingleValueSetting: String]]() + public private(set) var platformSpecificMultipleValueSettings = [Platform: [MultipleValueSetting: [String]]]() + public private(set) var singleValueSettings: [SingleValueSetting: String] = [:] + public private(set) var multipleValueSettings: [MultipleValueSetting: [String]] = [:] + + public subscript(_ setting: SingleValueSetting) -> String? { + get { singleValueSettings[setting] } + set { singleValueSettings[setting] = newValue } + } + + public subscript(_ setting: SingleValueSetting, for platform: Platform) -> String? { + get { platformSpecificSingleValueSettings[platform]?[setting] } + set { platformSpecificSingleValueSettings[platform, default: [:]][setting] = newValue } + } + + public subscript(_ setting: SingleValueSetting, default defaultValue: @autoclosure () -> String) -> String { + get { singleValueSettings[setting, default: defaultValue()] } + set { singleValueSettings[setting] = newValue } + } + + public subscript(_ setting: MultipleValueSetting) -> [String]? { + get { multipleValueSettings[setting] } + set { multipleValueSettings[setting] = newValue } + } + + public subscript(_ setting: MultipleValueSetting, for platform: Platform) -> [String]? { + get { platformSpecificMultipleValueSettings[platform]?[setting] } + set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue } + } + + public subscript( + _ setting: MultipleValueSetting, + default defaultValue: @autoclosure () -> [String] + ) -> [String] { + get { multipleValueSettings[setting, default: defaultValue()] } + set { multipleValueSettings[setting] = newValue } + } + + public subscript( + _ setting: MultipleValueSetting, + for platform: Platform, + default defaultValue: @autoclosure () -> [String] + ) -> [String] { + get { platformSpecificMultipleValueSettings[platform, default: [:]][setting, default: defaultValue()] } + set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue } + } + + public init() {} + + private enum CodingKeys: CodingKey { + case platformSpecificSingleValueSettings, platformSpecificMultipleValueSettings, singleValueSettings, multipleValueSettings + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + platformSpecificSingleValueSettings = try container.decodeIfPresent([Platform: [SingleValueSetting: String]].self, forKey: .platformSpecificSingleValueSettings) ?? .init() + platformSpecificMultipleValueSettings = try container.decodeIfPresent([Platform: [MultipleValueSetting: [String]]].self, forKey: .platformSpecificMultipleValueSettings) ?? .init() + singleValueSettings = try container.decodeIfPresent([SingleValueSetting: String].self, forKey: .singleValueSettings) ?? [:] + multipleValueSettings = try container.decodeIfPresent([MultipleValueSetting: [String]] .self, forKey: .multipleValueSettings) ?? [:] + } + } +} + +/// Represents a filetype recognized by the Xcode build system. +public struct XCBuildFileType: CaseIterable { + public static let xcdatamodeld: XCBuildFileType = XCBuildFileType( + fileType: "xcdatamodeld", + fileTypeIdentifier: "wrapper.xcdatamodeld" + ) + + public static let xcdatamodel: XCBuildFileType = XCBuildFileType( + fileType: "xcdatamodel", + fileTypeIdentifier: "wrapper.xcdatamodel" + ) + + public static let xcmappingmodel: XCBuildFileType = XCBuildFileType( + fileType: "xcmappingmodel", + fileTypeIdentifier: "wrapper.xcmappingmodel" + ) + + public static let allCases: [XCBuildFileType] = [ + .xcdatamodeld, + .xcdatamodel, + .xcmappingmodel + ] + + public let fileTypes: Set + public let fileTypeIdentifier: String + + private init(fileTypes: Set, fileTypeIdentifier: String) { + self.fileTypes = fileTypes + self.fileTypeIdentifier = fileTypeIdentifier + } + + private init(fileType: String, fileTypeIdentifier: String) { + self.init(fileTypes: [fileType], fileTypeIdentifier: fileTypeIdentifier) + } +} + +extension PIF.FileReference { + // fileprivate static func fileTypeIdentifier(forPath path: String) -> String { + // let pathExtension: String? + // if let path = try? URL(validating: path) { + // pathExtension = path.extension + // } else if let path = try? RelativePath(validating: path) { + // pathExtension = path.extension + // } else { + // pathExtension = nil + // } + + // switch pathExtension { + // case "a": + // return "archive.ar" + // case "s", "S": + // return "sourcecode.asm" + // case "c": + // return "sourcecode.c.c" + // case "cl": + // return "sourcecode.opencl" + // case "cpp", "cp", "cxx", "cc", "c++", "C", "tcc": + // return "sourcecode.cpp.cpp" + // case "d": + // return "sourcecode.dtrace" + // case "defs", "mig": + // return "sourcecode.mig" + // case "m": + // return "sourcecode.c.objc" + // case "mm", "M": + // return "sourcecode.cpp.objcpp" + // case "metal": + // return "sourcecode.metal" + // case "l", "lm", "lmm", "lpp", "lp", "lxx": + // return "sourcecode.lex" + // case "swift": + // return "sourcecode.swift" + // case "y", "ym", "ymm", "ypp", "yp", "yxx": + // return "sourcecode.yacc" + + // case "xcassets": + // return "folder.assetcatalog" + // case "xcstrings": + // return "text.json.xcstrings" + // case "storyboard": + // return "file.storyboard" + // case "xib": + // return "file.xib" + + // case "xcframework": + // return "wrapper.xcframework" + + // default: + // return pathExtension.flatMap({ pathExtension in + // XCBuildFileType.allCases.first(where: ({ $0.fileTypes.contains(pathExtension) })) + // })?.fileTypeIdentifier ?? "file" + // } + // } +} + +private struct UntypedTarget: Decodable { + struct TargetContents: Decodable { + let type: String + } + let contents: TargetContents +} +// swiftlint:enable file_length type_body_length diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift new file mode 100644 index 0000000..b3379ed --- /dev/null +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -0,0 +1,49 @@ +import Foundation + +public class PIFParser { + private let cachePath: URL + + public enum Error: Swift.Error { + case workspaceNotFound + } + + public init(cachePath: URL) { + self.cachePath = cachePath + } + + public func parse() throws -> PIF.Workspace { + let workspace = try workspacePath() + let data = try Data(contentsOf: workspace) + + return try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) + } + + private func workspacePath() throws -> URL { + let path = cachePath.appendingPathComponent("workspace") + + let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) + .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } + + precondition(workspaces.count == 1, "Encountered more than one workspace - it is expected that a single workspace exists: \(workspaces)") + + guard workspaces.count > 0 else { + throw Error.workspaceNotFound + } + + return workspaces[0] + } +} + +extension PIF { + class PIFDecoder: JSONDecoder { + internal init(cache path: URL) { + super.init() + + userInfo[.pifCachePath] = path + } + } +} + +extension CodingUserInfoKey { + static let pifCachePath: CodingUserInfoKey = CodingUserInfoKey(rawValue: "PIFCachePath")! +} diff --git a/PIF/Sources/pif-parser/pif-parser.swift b/PIF/Sources/pif-parser/pif-parser.swift new file mode 100644 index 0000000..78db443 --- /dev/null +++ b/PIF/Sources/pif-parser/pif-parser.swift @@ -0,0 +1,27 @@ +// +// pif-parser.swift +// +// +// Created by Thomas Hedderwick on 03/05/2024. +// + +import Foundation +import PIFSupport + +@main +struct PIFParser { + static func main() throws { + guard CommandLine.arguments.count == 2 else { + print("USAGE: \(CommandLine.arguments.first!) [PIFCache path]") + return + } + + let cachePath = URL(fileURLWithPath: CommandLine.arguments[1]) + let parser = PIFSupport.PIFParser(cachePath: cachePath) + let workspace = try parser.parse() + + print("workspace: \(workspace.guid):") + print("projects: \(workspace.projects.map { $0.guid }.joined(separator: "\n"))\n") + print("targets: \(workspace.projects.flatMap { $0.targets }.map { $0.guid }.joined(separator: "\n"))") + } +} diff --git a/PIF/Tests/PIFTests/PIFTests.swift b/PIF/Tests/PIFTests/PIFTests.swift new file mode 100644 index 0000000..000a103 --- /dev/null +++ b/PIF/Tests/PIFTests/PIFTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import PIF + +final class PIFTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} From 43f8ddaa14aac0971a3ebafaa34db5e09d86ded8 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 7 May 2024 15:44:27 +0200 Subject: [PATCH 32/61] Bring PIFSupport into Gen IR --- PIF/Sources/PIFSupport/PIFSupport.swift | 13 +++---- Package.swift | 6 ++- Sources/GenIR/GenIR.swift | 3 ++ Sources/GenIR/PIFCache.swift | 51 +++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 Sources/GenIR/PIFCache.swift diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index b3379ed..2b8665a 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -2,23 +2,20 @@ import Foundation public class PIFParser { private let cachePath: URL + public let workspace: PIF.Workspace public enum Error: Swift.Error { case workspaceNotFound } - public init(cachePath: URL) { + public init(cachePath: URL) throws { self.cachePath = cachePath - } - - public func parse() throws -> PIF.Workspace { - let workspace = try workspacePath() - let data = try Data(contentsOf: workspace) - return try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) + let data = try Data(contentsOf: try Self.workspacePath(in: cachePath)) + workspace = try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) } - private func workspacePath() throws -> URL { + private static func workspacePath(in cachePath: URL) throws -> URL { let path = cachePath.appendingPathComponent("workspace") let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) diff --git a/Package.swift b/Package.swift index 566f581..612c472 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,8 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(path: "PBXProjParser") + .package(path: "PBXProjParser"), + .package(path: "PIF") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -24,7 +25,8 @@ let package = Package( dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), - .product(name: "PBXProjParser", package: "PBXProjParser") + .product(name: "PBXProjParser", package: "PBXProjParser"), + .product(name: "PIFSupport", package: "PIF") ], path: "Sources/GenIR" ), diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 58694a0..d2687cb 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -118,6 +118,9 @@ struct IREmitterCommand: ParsableCommand { let log = try logParser(for: log) try log.parse(&targets) + // Find and parse the PIF cache + let pifCache = try PIFCache(buildCache: log.buildCachePath) + let buildCacheManipulator = try BuildCacheManipulator( buildCachePath: log.buildCachePath, buildSettings: log.settings, diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift new file mode 100644 index 0000000..4f4373d --- /dev/null +++ b/Sources/GenIR/PIFCache.swift @@ -0,0 +1,51 @@ +import Foundation +import PIFSupport + +struct PIFCache { + private let buildCache: URL + private let pifCachePath: URL + private let workspace: PIF.Workspace + + enum Error: Swift.Error { + case nonexistentCache(String) + case pifError(String) + } + + init(buildCache: URL) throws { + self.buildCache = buildCache + self.pifCachePath = try Self.pifCachePath(in: buildCache) + + do { + let cache = try PIFParser(cachePath: pifCachePath) + workspace = cache.workspace + } catch { + throw Error.pifError(error.localizedDescription) + } + } + + private static func pifCachePath(in buildCache: URL) throws -> URL { + // TODO: test this variation, because I haven't seen this personally + let cmakePIFCachePath = buildCache + .deletingLastPathComponent() + .appendingPathComponent("XCBuildData") + .appendingPathComponent("PIFCache") + + let regularPIFCachePath = buildCache + .appendingPathComponent("Intermediates.noindex") + .appendingPathComponent("XCBuildData") + .appendingPathComponent("PIFCache") + + if FileManager.default.directoryExists(at: cmakePIFCachePath) { + return cmakePIFCachePath + } else if FileManager.default.directoryExists(at: regularPIFCachePath) { + return regularPIFCachePath + } else { + throw Error.nonexistentCache( + """ + Couldn't find cache at: \(regularPIFCachePath). Ensure a clean build **AND** \ + ensure `xcodebuild clean` happens separately from `xcodebuild archive` command + """ + ) + } + } +} From 300319e35bc1499377bb33b7c0731c21b42fc6f9 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Wed, 8 May 2024 13:11:29 +0200 Subject: [PATCH 33/61] Make the DependencyGraph generic This allows us to switch out the Node value without massive changes --- .../Dependency Graph/DependencyGraph.swift | 58 ++++++++--------- .../DependencyGraphBuilder.swift | 63 ++++++++----------- Sources/GenIR/Dependency Graph/Edge.swift | 10 +-- Sources/GenIR/Dependency Graph/Node.swift | 29 +++++---- Sources/GenIR/OutputPostprocessor.swift | 14 +++-- Sources/GenIR/PIFCache.swift | 42 ++++++++++++- Sources/GenIR/Targets/Target.swift | 6 ++ Sources/GenIR/Targets/Targets.swift | 35 ++++++----- 8 files changed, 153 insertions(+), 104 deletions(-) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index b2d4098..7cd0806 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -7,41 +7,41 @@ import Foundation -/// A directed graph that maps dependencies between targets (nodes) via edges (directions between nodes) -class DependencyGraph { +/// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) +class DependencyGraph { /// All the nodes in the graph - private(set) var nodes: [Node] = [] - - /// Adds a target to the graph - /// - Parameter target: the target to add - /// - Returns: the node added, iff a node for this target didn't already exist in the graph - func addNode(target: Target) -> Node? { - if findNode(for: target) != nil { - return nil + private(set) var nodes: [Node] = [] + + /// Adds a value to the graph + /// - Parameter value: the value to add + /// - Returns: the node added + func addNode(value: Value) -> Node { + if let node = findNode(for: value) { + return node } - let node = Node(target) + let node = Node(value) nodes.append(node) return node } - /// Finds a target's node in the graph - /// - Parameter target: the target to look for - /// - Returns: the node for the given target, if found - func findNode(for target: Target) -> Node? { - nodes.first(where: { $0.target == target }) + /// Finds a value's node in the graph + /// - Parameter value: the value to look for + /// - Returns: the node for the given value, if found + func findNode(for value: Value) -> Node? { + nodes.first(where: { $0.value == value }) } - /// Builds a dependency 'chain' for a target using a depth-first search - /// - Parameter target: the target to get a chain for + /// Builds a dependency 'chain' for a value using a depth-first search + /// - Parameter value: the value to get a chain for /// - Returns: the chain of nodes, starting - func chain(for target: Target) -> [Node] { - guard let targetNode = findNode(for: target) else { - logger.debug("Couldn't find node for target: \(target.name)") + func chain(for value: Value) -> [Node] { + guard let node = findNode(for: value) else { + logger.debug("Couldn't find node for value: \(value.name)") return [] } - return depthFirstSearch(startingAt: targetNode) + return depthFirstSearch(startingAt: node) } func toDot(_ path: String) throws { @@ -60,13 +60,13 @@ class DependencyGraph { /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach - private func depthFirstSearch(startingAt node: Node) -> [Node] { - logger.debug("----\nSearching for: \(node.target.name)") - var visited = Set() - var chain = [Node]() + private func depthFirstSearch(startingAt node: Node) -> [Node] { + logger.debug("----\nSearching for: \(node.value.name)") + var visited = Set>() + var chain = [Node]() - func depthFirst(node: Node) { - logger.debug("inserting node: \(node.target.name)") + func depthFirst(node: Node) { + logger.debug("inserting node: \(node.value.name)") visited.insert(node) logger.debug("visited: \(visited)") @@ -80,7 +80,7 @@ class DependencyGraph { } } - logger.debug("appending to chain: \(node.target.name)") + logger.debug("appending to chain: \(node.value.name)") chain.append(node) } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index e2985d0..cdc6b49 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -5,52 +5,43 @@ // Created by Thomas Hedderwick on 28/08/2023. // -class DependencyGraphBuilder { - /// Builds a dependency graph for the given collection of targets - /// - Parameter targets: the targets to build a graph for - /// - Returns: the dependency graph - static func build(targets: Targets) -> DependencyGraph { - let graph = DependencyGraph() - - targets.forEach { - add(target: $0, in: targets, to: graph) - } - - return graph +protocol DependencyProviding { + associatedtype Value: NodeValue + func dependencies(for value: Value) -> [Value] +} +class DependencyGraphBuilder where Value == Provider.Value { + private let provider: Provider + let graph = DependencyGraph() + + /// Inits the Graph Builder + /// - Parameter provider: the dependency provider for the values + init(provider: Provider, values: [Value]) { + self.provider = provider + values.forEach { add(value: $0) } } - /// Adds a target (and it's dependencies) to the graph + /// Adds a value (and it's dependencies) to the graph /// - Parameters: - /// - graph: the graph to add a target to - /// - target: the target to add - /// - targets: the targets containing the target and it's dependencies - static func add(target: Target, in targets: Targets, to graph: DependencyGraph) { - logger.debug("Adding target: \(target.name) to graph") - - guard let node = graph.addNode(target: target) else { - logger.debug("Already inserted node: \(target.name). Skipping.") - return + /// - value: the value to add + @discardableResult + private func add(value: Value) -> Node { + logger.debug("Adding value: \(value.name) to graph") + + if let existingNode = graph.findNode(for: value) { + logger.debug("Already inserted node: \(existingNode.name). Skipping") + return existingNode } - let dependencies = targets.calculateDependencies(for: target) + let dependencies = provider.dependencies(for: value) + let node = graph.addNode(value: value) for dependency in dependencies { - guard let dependencyTarget = targets.target(for: dependency) else { - logger.debug("Couldn't lookup dependency in targets: \(dependency)") - continue - } + let dependencyNode = add(value: dependency) - add(target: dependencyTarget, in: targets, to: graph) - - guard let dependencyNode = graph.findNode(for: dependencyTarget) else { - logger.debug("Couldn't find node for target (\(dependencyTarget.name)) even though it was just inserted?") - continue - } - - // We add both an edge from and to the node and dependency - this way we can do bidirectional - // searches for a given node and see what it's dependencies are and who depends on it node.add(edge: .init(to: dependencyNode, from: node, relationship: .dependency)) dependencyNode.add(edge: .init(to: node, from: dependencyNode, relationship: .depender)) } + + return node } } diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift index 4e62019..c78de34 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -6,12 +6,12 @@ // // swiftlint:disable identifier_name -/// An edge describes the relationship between two Nodes in a graph -class Edge { +/// An edge describes the relationship between two Nodes in a graph +class Edge { /// The source node - let to: Node + let to: Node /// The destination node - let from: Node + let from: Node /// The relationship between the two nodes let relationship: Relationship @@ -23,7 +23,7 @@ class Edge { case depender } - init(to: Node, from: Node, relationship: Relationship) { + init(to: Node, from: Node, relationship: Relationship) { self.to = to self.from = from self.relationship = relationship diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index dd9a8c1..f576253 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -7,22 +7,27 @@ import Foundation -class Node { +protocol NodeValue: Hashable { + /// The name of this node, mostly used for debugging and printing + var name: String { get } +} + +class Node { /// The edges from and to this node - private(set) var edges = [Edge]() - /// The target this node represents - let target: Target + private(set) var edges = [Edge]() + /// The value this node represents + let value: Value /// The name of this node, mostly used for debugging and printing let name: String - init(_ target: Target) { - self.target = target - self.name = target.name + init(_ value: Value) { + self.value = value + self.name = value.name } /// Adds an edge to this node /// - Parameter edge: the edge to add - func add(edge: Edge) { + func add(edge: Edge) { if edges.filter({ $0.to.name == edge.to.name }).count == 0 { edges.append(edge) } @@ -31,7 +36,7 @@ class Node { extension Node: Equatable { static func == (_ lhs: Node, rhs: Node) -> Bool { - lhs.target == rhs.target && lhs.edges == rhs.edges + lhs.value == rhs.value && lhs.edges == rhs.edges } } @@ -40,9 +45,9 @@ extension Node: CustomStringConvertible { var description = "" if !edges.isEmpty { - description += "[Node: \(target.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.target.name})] " + description += "[Node: \(value.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.value.name})] " } else { - description += "[Node: \(target.name)] " + description += "[Node: \(value.name)] " } return description @@ -52,6 +57,6 @@ extension Node: CustomStringConvertible { extension Node: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(name) - hasher.combine(target) + hasher.combine(value) } } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 643f8c2..2d397fe 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -24,7 +24,7 @@ class OutputPostprocessor { /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk private let dynamicDependencyToPath: [String: URL] - private let graph: DependencyGraph + private let graph: DependencyGraph private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] @@ -36,7 +36,8 @@ class OutputPostprocessor { dynamicDependencyToPath = dynamicDependencies(in: self.archive) - graph = DependencyGraphBuilder.build(targets: targets) + let builder = DependencyGraphBuilder(provider: targets, values: Array(targets.targets)) + graph = builder.graph if dumpGraph { do { try graph.toDot(output.appendingPathComponent("graph.dot").filePath) @@ -83,7 +84,8 @@ class OutputPostprocessor { private func process( target: Target ) throws -> Set { - let chain = graph.chain(for: target) + // let chain = graph.chain(for: target) + let chain = [Node]() logger.debug("Chain for target: \(target.nameForOutput):\n") chain.forEach { logger.debug("\($0)") } @@ -94,7 +96,7 @@ class OutputPostprocessor { for node in chain { logger.debug("Processing Node: \(node.name)") // Ensure node is not a dynamic dependency - guard dynamicDependencyToPath[node.target.nameForOutput] == nil else { continue } + guard dynamicDependencyToPath[node.value.nameForOutput] == nil else { continue } // Only care about moving dependencies into dependers - check this node's edges to dependent relationships let dependers = node.edges @@ -102,13 +104,13 @@ class OutputPostprocessor { .map { $0.to } // Move node's IR into depender's IR folder - guard let nodeFolderPath = node.target.irFolderPath else { + guard let nodeFolderPath = node.value.irFolderPath else { logger.debug("IR folder for node: \(node) is nil") continue } for depender in dependers { - guard let dependerFolderPath = depender.target.irFolderPath else { + guard let dependerFolderPath = depender.value.irFolderPath else { logger.debug("IR folder for depender node \(depender) is nil") continue } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 4f4373d..c45f555 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -1,7 +1,9 @@ import Foundation import PIFSupport -struct PIFCache { +class PIFCache { + public typealias GUID = String + private let buildCache: URL private let pifCachePath: URL private let workspace: PIF.Workspace @@ -48,4 +50,42 @@ struct PIFCache { ) } } + + var projects: [PIF.Project] { + workspace.projects + } + + private lazy var projectsByGUID: [GUID: PIF.Project] = { + workspace + .projects + .reduce(into: [GUID: PIF.Project]()) { result, element in + result[element.guid] = element + } + }() + + func project(for guid: GUID) -> PIF.Project? { + projectsByGUID[guid] + } + + // TODO: do we need to handle Aggregate targets here? Probably - investigate and update to BaseTarget if so + var targets: [PIF.Target] { + workspace + .projects + .flatMap { + $0 + .targets + .compactMap { $0 as? PIF.Target } + } + } + + private lazy var targetsByGUID: [GUID: PIF.Target] = { + targets + .reduce(into: [GUID: PIF.Target]()) { result, element in + result[element.guid] = element + } + }() + + func target(for guid: GUID) -> PIF.Target? { + targetsByGUID[guid] + } } diff --git a/Sources/GenIR/Targets/Target.swift b/Sources/GenIR/Targets/Target.swift index 6d4835d..d77af00 100644 --- a/Sources/GenIR/Targets/Target.swift +++ b/Sources/GenIR/Targets/Target.swift @@ -132,3 +132,9 @@ extension Target: CustomStringConvertible { """ } } + +extension Target: NodeValue { + var value: Self { + self + } +} diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift index 7d9e582..3c6dfe3 100644 --- a/Sources/GenIR/Targets/Targets.swift +++ b/Sources/GenIR/Targets/Targets.swift @@ -85,21 +85,6 @@ class Targets { return nil } - - // TODO: once we stabilize Targets, this should return a Set not [String] - func calculateDependencies(for target: Target) -> [String] { - // TODO: eventually we'd like to move some of the project dependencies calculations here - var dependencies = project.dependencies(for: target.name) - - if dependencies.count == 0, let productName = target.productName { - // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... - dependencies = project.dependencies(for: productName) - } - - logger.debug("Calculated dependencies for target: \(target.name). Dependencies: \(dependencies)") - - return dependencies - } } extension Targets: Collection { @@ -123,3 +108,23 @@ extension Targets: Collection { targets.makeIterator() } } + +extension Targets: DependencyProviding { + typealias Value = Target + + func dependencies(for value: Target) -> [Target] { + // // TODO: once we stabilize Targets, this should return a Set not [String] + // func calculateDependencies(for target: Target) -> [String] { + // TODO: eventually we'd like to move some of the project dependencies calculations here + var dependencies = project.dependencies(for: value.name) + + if dependencies.count == 0, let productName = value.productName { + // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... + dependencies = project.dependencies(for: productName) + } + + logger.debug("Calculated dependencies for target: \(value.name). Dependencies: \(dependencies)") + + return dependencies.compactMap { target(for: $0) } + } +} From c4fc00c490002d7bae9bbd949a3151881ecef6c8 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 10 May 2024 14:13:06 +0200 Subject: [PATCH 34/61] Adopt PIF Targets into the rest of the project --- Sources/GenIR/CompilerCommandRunner.swift | 9 +- .../Dependency Graph/DependencyGraph.swift | 23 +-- .../DependencyGraphBuilder.swift | 8 +- Sources/GenIR/Dependency Graph/Node.swift | 20 +-- Sources/GenIR/GenIR.swift | 32 +++- Sources/GenIR/OutputPostprocessor.swift | 78 +++++----- Sources/GenIR/PIFCache.swift | 85 ++++++++--- Sources/GenIR/Target.swift | 41 +++++ Sources/GenIR/Targets/Target.swift | 140 ------------------ Sources/GenIR/Targets/Targets.swift | 130 ---------------- Sources/GenIR/XcodeLogParser.swift | 38 ++--- 11 files changed, 219 insertions(+), 385 deletions(-) create mode 100644 Sources/GenIR/Target.swift delete mode 100644 Sources/GenIR/Targets/Target.swift delete mode 100644 Sources/GenIR/Targets/Targets.swift diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index bec6c29..ac8edf4 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -42,7 +42,7 @@ struct CompilerCommandRunner { } /// Starts the runner - func run(targets: Targets) throws { + func run(targets: [Target]) throws { // Quick, do a hack! try buildCacheManipulator.manipulate() @@ -50,7 +50,10 @@ struct CompilerCommandRunner { defer { try? fileManager.removeItem(at: tempDirectory) } logger.debug("Using temp directory as working directory: \(tempDirectory.filePath)") - let totalCommands = targets.totalCommandCount + let totalCommands = targets + .map { $0.commands.count } + .reduce(0, +) + logger.info("Total commands to run: \(totalCommands)") var totalModulesRun = 0 @@ -58,7 +61,7 @@ struct CompilerCommandRunner { for target in targets { logger.info("Operating on target: \(target.name). Total modules processed: \(totalModulesRun)") - totalModulesRun += try run(commands: target.commands, for: target.nameForOutput, at: tempDirectory) + totalModulesRun += try run(commands: target.commands, for: target.productName, at: tempDirectory) } try fileManager.moveItemReplacingExisting(from: tempDirectory, to: output) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 7cd0806..7db2f3d 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -10,7 +10,7 @@ import Foundation /// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) class DependencyGraph { /// All the nodes in the graph - private(set) var nodes: [Node] = [] + private(set) var nodes = [String: Node]() /// Adds a value to the graph /// - Parameter value: the value to add @@ -21,7 +21,7 @@ class DependencyGraph { } let node = Node(value) - nodes.append(node) + nodes[value.valueName] = node return node } @@ -29,7 +29,7 @@ class DependencyGraph { /// - Parameter value: the value to look for /// - Returns: the node for the given value, if found func findNode(for value: Value) -> Node? { - nodes.first(where: { $0.value == value }) + nodes[value.valueName] } /// Builds a dependency 'chain' for a value using a depth-first search @@ -37,7 +37,7 @@ class DependencyGraph { /// - Returns: the chain of nodes, starting func chain(for value: Value) -> [Node] { guard let node = findNode(for: value) else { - logger.debug("Couldn't find node for value: \(value.name)") + logger.debug("Couldn't find node for value: \(value.valueName)") return [] } @@ -47,9 +47,14 @@ class DependencyGraph { func toDot(_ path: String) throws { var contents = "digraph DependencyGraph {\n" - for node in nodes { + for node in nodes.values { for edge in node.edges.filter({ $0.relationship == .dependency }) { - contents.append("\(node.name.replacingOccurrences(of: "-", with: "_")) -> \(edge.to.name.replacingOccurrences(of: "-", with: "_"))\n") + func dotSanitized(for name: String) -> String { + name + .replacingOccurrences(of: "-", with: "_") + .replacingOccurrences(of: ".", with: "_") + } + contents.append("\(dotSanitized(for: node.valueName)) -> \(dotSanitized(for: edge.to.valueName))\n") } } @@ -61,12 +66,12 @@ class DependencyGraph { /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach private func depthFirstSearch(startingAt node: Node) -> [Node] { - logger.debug("----\nSearching for: \(node.value.name)") + logger.debug("----\nSearching for: \(node.value.valueName)") var visited = Set>() var chain = [Node]() func depthFirst(node: Node) { - logger.debug("inserting node: \(node.value.name)") + logger.debug("inserting node: \(node.value.valueName)") visited.insert(node) logger.debug("visited: \(visited)") @@ -80,7 +85,7 @@ class DependencyGraph { } } - logger.debug("appending to chain: \(node.value.name)") + logger.debug("appending to chain: \(node.value.valueName)") chain.append(node) } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index cdc6b49..9e88715 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -9,7 +9,8 @@ protocol DependencyProviding { associatedtype Value: NodeValue func dependencies(for value: Value) -> [Value] } -class DependencyGraphBuilder where Value == Provider.Value { + +class DependencyGraphBuilder where Value == Provider.Value { private let provider: Provider let graph = DependencyGraph() @@ -25,13 +26,12 @@ class DependencyGraphBuilder wh /// - value: the value to add @discardableResult private func add(value: Value) -> Node { - logger.debug("Adding value: \(value.name) to graph") - if let existingNode = graph.findNode(for: value) { - logger.debug("Already inserted node: \(existingNode.name). Skipping") return existingNode } + logger.debug("Adding value: \(value.valueName) to graph") + let dependencies = provider.dependencies(for: value) let node = graph.addNode(value: value) diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index f576253..659dad4 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -8,8 +8,8 @@ import Foundation protocol NodeValue: Hashable { - /// The name of this node, mostly used for debugging and printing - var name: String { get } + /// The name of this node, this should be unique + var valueName: String { get } } class Node { @@ -17,18 +17,20 @@ class Node { private(set) var edges = [Edge]() /// The value this node represents let value: Value - /// The name of this node, mostly used for debugging and printing - let name: String + /// The name of this node + var valueName: String { + value.valueName + } init(_ value: Value) { self.value = value - self.name = value.name } /// Adds an edge to this node /// - Parameter edge: the edge to add func add(edge: Edge) { - if edges.filter({ $0.to.name == edge.to.name }).count == 0 { + // TODO: slow - change. + if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { edges.append(edge) } } @@ -45,9 +47,9 @@ extension Node: CustomStringConvertible { var description = "" if !edges.isEmpty { - description += "[Node: \(value.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.value.name})] " + description += "[Node: \(value.valueName), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.value.valueName})] " } else { - description += "[Node: \(value.name)] " + description += "[Node: \(value.valueName)] " } return description @@ -56,7 +58,7 @@ extension Node: CustomStringConvertible { extension Node: Hashable { func hash(into hasher: inout Hasher) { - hasher.combine(name) + hasher.combine(valueName) hasher.combine(value) } } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index d2687cb..b7563c6 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -2,6 +2,7 @@ import Foundation import ArgumentParser import Logging import PBXProjParser +import PIFSupport /// Global logger object var logger = Logger(label: Bundle.main.bundleIdentifier ?? "com.veracode.gen-ir", factory: StdOutLogHandler.init) @@ -112,11 +113,11 @@ struct IREmitterCommand: ParsableCommand { dumpDependencyGraph: Bool ) throws { let output = archive.appendingPathComponent("IR") - let project = try ProjectParser(path: project, logLevel: level) - var targets = Targets(for: project) + // let project = try ProjectParser(path: project, logLevel: level) + // var targets = Targets(for: project) let log = try logParser(for: log) - try log.parse(&targets) + try log.parse() // Find and parse the PIF cache let pifCache = try PIFCache(buildCache: log.buildCachePath) @@ -128,6 +129,12 @@ struct IREmitterCommand: ParsableCommand { dryRun: dryRun ) + let targets = pifCache + .targets + .map { + Target(baseTarget: $0, commands: log.targetCommands[$0.name] ?? []) + } + let runner = CompilerCommandRunner( output: output, buildCacheManipulator: buildCacheManipulator, @@ -135,13 +142,26 @@ struct IREmitterCommand: ParsableCommand { ) try runner.run(targets: targets) + let provider = PIFDependencyProvider(targets: targets, cache: pifCache) + let builder = DependencyGraphBuilder(provider: provider, values: targets) + let graph = builder.graph + + if dumpDependencyGraph { + do { + try graph.toDot(output.appendingPathComponent("graph.dot").filePath) + } catch { + logger.error("toDot error: \(error)") + } + } + let postprocessor = try OutputPostprocessor( archive: archive, output: output, - targets: targets, - dumpGraph: dumpDependencyGraph + graph: graph, + targets: targets ) - try postprocessor.process(targets: &targets) + + try postprocessor.process() } /// Gets an `XcodeLogParser` for a path diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 2d397fe..4b3877a 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -25,55 +25,49 @@ class OutputPostprocessor { private let dynamicDependencyToPath: [String: URL] private let graph: DependencyGraph + private let targets: [Target] private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] + private let targetsToPaths: [Target: URL] private let manager: FileManager = .default - init(archive: URL, output: URL, targets: Targets, dumpGraph: Bool) throws { + init(archive: URL, output: URL, graph: DependencyGraph, targets: [Target]) throws { self.output = output self.archive = archive + self.graph = graph + self.targets = targets - dynamicDependencyToPath = dynamicDependencies(in: self.archive) - - let builder = DependencyGraphBuilder(provider: targets, values: Array(targets.targets)) - graph = builder.graph - if dumpGraph { - do { - try graph.toDot(output.appendingPathComponent("graph.dot").filePath) - } catch { - logger.error("toDot error: \(error)") + let namesToTargets = targets + .reduce(into: [String: Target]()) { partial, target in + partial[target.productName] = target } - } - } - /// Starts the OutputPostprocessor - /// - Parameter targets: the targets to operate on - func process(targets: inout Targets) throws { - try manager.directories(at: output, recursive: false) - .forEach { path in - let product = path.lastPathComponent.deletingPathExtension() - - guard let target = targets.target(for: product) else { - logger.error("Failed to look up target for product: \(product)") - return + targetsToPaths = try manager + .directories(at: output, recursive: false) + .reduce(into: [Target: URL]()) { partial, path in + if let target = namesToTargets[path.lastPathComponent] { + partial[target] = path + } else { + logger.error("Failed to look up target for path: \(path)") } - - target.irFolderPath = path } - // TODO: remove 'static' deps so we don't duplicate them in the submission? - _ = try manager.directories(at: output, recursive: false) - .flatMap { path in - let product = path.lastPathComponent.deletingPathExtension() + let namedTargetSet = Set(namesToTargets.values) + let pathTargetSet = Set(targetsToPaths.keys) + let difference = namedTargetSet.symmetricDifference(pathTargetSet) - guard let target = targets.target(for: product) else { - logger.error("Failed to look up target for product: \(product)") - return Set() - } + logger.debug("target set difference: \(difference)") - return try process(target: target) - } + dynamicDependencyToPath = dynamicDependencies(in: self.archive) + } + + /// Starts the OutputPostprocessor + /// - Parameter targets: the targets to operate on + func process() throws { + // TODO: remove 'static' deps so we don't duplicate them in the submission? + _ = try targetsToPaths + .map { try process(target: $0.key, at: $0.value) } } /// Processes an individual target @@ -82,21 +76,21 @@ class OutputPostprocessor { /// - path: the output path /// - Returns: private func process( - target: Target + target: Target, + at path: URL ) throws -> Set { - // let chain = graph.chain(for: target) - let chain = [Node]() + let chain = graph.chain(for: target) - logger.debug("Chain for target: \(target.nameForOutput):\n") + logger.debug("Chain for target: \(target.productName):\n") chain.forEach { logger.debug("\($0)") } // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent var processed = Set() for node in chain { - logger.debug("Processing Node: \(node.name)") + logger.debug("Processing Node: \(node.valueName)") // Ensure node is not a dynamic dependency - guard dynamicDependencyToPath[node.value.nameForOutput] == nil else { continue } + guard dynamicDependencyToPath[node.value.productName] == nil else { continue } // Only care about moving dependencies into dependers - check this node's edges to dependent relationships let dependers = node.edges @@ -104,13 +98,13 @@ class OutputPostprocessor { .map { $0.to } // Move node's IR into depender's IR folder - guard let nodeFolderPath = node.value.irFolderPath else { + guard let nodeFolderPath = targetsToPaths[node.value] else { logger.debug("IR folder for node: \(node) is nil") continue } for depender in dependers { - guard let dependerFolderPath = depender.value.irFolderPath else { + guard let dependerFolderPath = targetsToPaths[depender.value] else { logger.debug("IR folder for depender node \(depender) is nil") continue } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index c45f555..f1e174b 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -55,37 +55,76 @@ class PIFCache { workspace.projects } - private lazy var projectsByGUID: [GUID: PIF.Project] = { + // private lazy var projectsByGUID: [GUID: PIF.Project] = { + // workspace + // .projects + // .reduce(into: [GUID: PIF.Project]()) { result, element in + // result[element.guid] = element + // } + // }() + + // func project(for guid: GUID) -> PIF.Project? { + // projectsByGUID[guid] + // } + + // TODO: We cab possibly filter out some targets here for performance + var targets: [PIF.BaseTarget] { workspace .projects - .reduce(into: [GUID: PIF.Project]()) { result, element in - result[element.guid] = element - } - }() + .flatMap { $0.targets } + } + + // private lazy var targetsByGUID: [GUID: PIF.BaseTarget] = { + // targets + // .reduce(into: [GUID: PIF.BaseTarget]()) { result, element in + // result[element.guid] = element + // } + // }() - func project(for guid: GUID) -> PIF.Project? { - projectsByGUID[guid] + // func target(for guid: GUID) -> PIF.BaseTarget? { + // targetsByGUID[guid] + // } +} + +extension PIF.BaseTarget: NodeValue { + var valueName: String { + if let target = self as? PIF.Target, !target.productName.isEmpty { + return target.productName + } + + return name } +} - // TODO: do we need to handle Aggregate targets here? Probably - investigate and update to BaseTarget if so - var targets: [PIF.Target] { - workspace - .projects - .flatMap { - $0 - .targets - .compactMap { $0 as? PIF.Target } - } +extension PIF.BaseTarget: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) } - private lazy var targetsByGUID: [GUID: PIF.Target] = { - targets - .reduce(into: [GUID: PIF.Target]()) { result, element in - result[element.guid] = element + public static func == (lhs: PIF.BaseTarget, rhs: PIF.BaseTarget) -> Bool { + ObjectIdentifier(lhs) == ObjectIdentifier(rhs) + } +} + +struct PIFDependencyProvider: DependencyProviding { + private let targets: [Target] + private let cache: PIFCache + private var guidToTargets: [PIFCache.GUID: Target] + + init(targets: [Target], cache: PIFCache) { + self.targets = targets + self.cache = cache + + self.guidToTargets = targets + .reduce(into: [PIFCache.GUID: Target]()) { partial, target in + partial[target.baseTarget.guid] = target } - }() + } - func target(for guid: GUID) -> PIF.Target? { - targetsByGUID[guid] + func dependencies(for value: Target) -> [Target] { + value + .baseTarget + .dependencies + .map { guidToTargets[$0.targetGUID]! } } } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift new file mode 100644 index 0000000..f480f4a --- /dev/null +++ b/Sources/GenIR/Target.swift @@ -0,0 +1,41 @@ +// +// Target.swift +// +// +// Created by Thomas Hedderwick on 28/02/2023. +// + +import Foundation +import PBXProjParser +import PIFSupport + +class Target { + var name: String { baseTarget.name } + var productName: String { + (baseTarget as? PIF.Target)?.productName ?? baseTarget.name + } + // TODO: we need to handle SPM's insane naming scheme for products here ^ + + let baseTarget: PIF.BaseTarget + let commands: [CompilerCommand] + + init(baseTarget: PIF.BaseTarget, commands: [CompilerCommand]) { + self.baseTarget = baseTarget + self.commands = commands + } +} + +extension Target: NodeValue { + var value: Self { self } + var valueName: String { productName } +} + +extension Target: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + + static func == (lhs: Target, rhs: Target) -> Bool { + ObjectIdentifier(lhs) == ObjectIdentifier(rhs) + } +} diff --git a/Sources/GenIR/Targets/Target.swift b/Sources/GenIR/Targets/Target.swift deleted file mode 100644 index d77af00..0000000 --- a/Sources/GenIR/Targets/Target.swift +++ /dev/null @@ -1,140 +0,0 @@ -// -// Target.swift -// -// -// Created by Thomas Hedderwick on 28/02/2023. -// - -import Foundation -import PBXProjParser - -/// Represents the concept of a 'Target' that is loosely: a target in Xcode parlance, a swift package product. -class Target { - /// The name of the target - let name: String - - /// The product name of the target, if one exists - lazy var productName: String? = { - switch backingTarget { - case .native(let target): - return target.productName - case .packageDependency(let spm): - return spm.productName - default: - return nil - } - }() - - /// The various types of target we're wrapping - enum BackingTarget: Hashable { - /// The Native Target this Target represents - case native(PBXNativeTarget) - - /// The Swift Dependency this Target represents - case packageDependency(XCSwiftPackageProductDependency) - } - - /// The backing object this Target represents - var backingTarget: BackingTarget? - - /// A list of CompilerCommands relating to this target - var commands: [CompilerCommand] = [] - - // TODO: Remove - /// A list of dependencies of this Target - private(set) var dependencies: [String] = [] - - /// The project parser relating to this target - let project: ProjectParser? - - /// The name to use when writing IR to disk, prefer the product name if possible. - lazy var nameForOutput: String = { - switch backingTarget { - case .native(let target): - return path(for: target) ?? productName ?? name - case .packageDependency, .none: - return productName ?? name - } - }() - - /// Gets the path for native targets - lazy var path: String? = { - switch backingTarget { - case .native(let target): - return path(for: target) - case .packageDependency, .none: - return nil - } - }() - - /// The path to the IR folder on disk - var irFolderPath: URL? - - init( - name: String, - backingTarget: BackingTarget? = nil, - commands: [CompilerCommand] = [], - dependencies: [String] = [], - project: ProjectParser? = nil - ) { - self.name = name - self.backingTarget = backingTarget - self.commands = commands - self.dependencies = dependencies - self.project = project - } - - /// Gets the 'path' (normally the name of the target's product) for a given target - private func path(for target: PBXNativeTarget) -> String? { - guard let model = project?.model(for: target.name) else { - logger.debug("Failed to get model for target: \(target)") - return nil - } - - guard let productReference = target.productReference else { - logger.debug("Failed to get product reference for target: \(target). Possibly a SPM Package description?") - return nil - } - - guard let reference = model.object(forKey: productReference, as: PBXFileReference.self) else { - logger.error("Failed to get object for target productReference: \(productReference)") - return nil - } - - return (reference.path as NSString).lastPathComponent as String - } -} - -// MARK: - Protocol Conformance for Hashable storage -extension Target: Equatable { - static func == (lhs: Target, rhs: Target) -> Bool { - lhs.name == rhs.name && - lhs.productName == rhs.productName && - lhs.backingTarget == rhs.backingTarget && - lhs.commands == rhs.commands && - lhs.dependencies == rhs.dependencies - } -} - -extension Target: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(name) - hasher.combine(productName) - hasher.combine(backingTarget) - } -} - -extension Target: CustomStringConvertible { - var description: String { - """ - Target(name: \(name), product: \(productName ?? "nil"), \ - commands: \(commands.count), backing target: \(String(describing: backingTarget))) - """ - } -} - -extension Target: NodeValue { - var value: Self { - self - } -} diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift deleted file mode 100644 index 3c6dfe3..0000000 --- a/Sources/GenIR/Targets/Targets.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// Target.swift -// -// -// Created by Thomas Hedderwick on 05/04/2023. -// - -import Foundation -import PBXProjParser - -/// Represents a collection of `Target`s -class Targets { - /// The underlying storage of `Target`s - private(set) var targets: Set = [] - - /// The project targets where parsed from - private let project: ProjectParser - - init(for project: ProjectParser) { - self.project = project - - project.targets.forEach { insert(native: $0) } - project.packages.forEach { insert(package: $0) } - } - - /// The sum of all commands for all stored targets - var totalCommandCount: Int { - targets - .map { $0.commands.count } - .reduce(0, +) - } - - /// Inserts the given native target into the container if it's not already present - /// - Parameter target: the element to insert - /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. - @discardableResult - func insert(native target: PBXNativeTarget) -> (inserted: Bool, memberAfterInsert: Element) { - let newTarget = Target( - name: target.name, - backingTarget: .native(target), - project: project - ) - - return targets.insert(newTarget) - } - - /// Inserts the given package into the container if it's not already present - /// - Parameter package: the element to insert - /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. - @discardableResult - func insert(package target: XCSwiftPackageProductDependency) -> (inserted: Bool, memberAfterInsert: Element) { - // TODO: when we can handle SPM transitive deps, should we look up the name here? Can we even do that? - let newTarget = Target( - name: target.productName, - backingTarget: .packageDependency(target), - project: project - ) - - return targets.insert(newTarget) - } - - /// Inserts the given target into the container if it's not already present - /// - Parameter target: the element to insert - /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. - @discardableResult - func insert(target: Target) -> (inserted: Bool, memberAfterInsert: Element) { - targets.insert(target) - } - - // TODO: maybe specialize a product vs name lookup for those sweet sweet milliseconds - func target(for key: String) -> Target? { - for target in targets { - if target.name == key { - return target - } else if target.productName == key { - return target - } else if target.path == key { - return target - } - } - - for target in targets where target.nameForOutput.deletingPathExtension() == key { - return target - } - - return nil - } -} - -extension Targets: Collection { - typealias CollectionType = Set - typealias Index = CollectionType.Index - typealias Element = CollectionType.Element - - var startIndex: Index { targets.startIndex } - var endIndex: Index { targets.endIndex } - -// TODO: Add subscripting support for looking up targets by name or product - subscript(index: Index) -> CollectionType.Element { - targets[index] - } - - func index(after index: Index) -> Index { - targets.index(after: index) - } - - func makeIterator() -> CollectionType.Iterator { - targets.makeIterator() - } -} - -extension Targets: DependencyProviding { - typealias Value = Target - - func dependencies(for value: Target) -> [Target] { - // // TODO: once we stabilize Targets, this should return a Set not [String] - // func calculateDependencies(for target: Target) -> [String] { - // TODO: eventually we'd like to move some of the project dependencies calculations here - var dependencies = project.dependencies(for: value.name) - - if dependencies.count == 0, let productName = value.productName { - // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... - dependencies = project.dependencies(for: productName) - } - - logger.debug("Calculated dependencies for target: \(value.name). Dependencies: \(dependencies)") - - return dependencies.compactMap { target(for: $0) } - } -} diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index 083a16b..ced8b74 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -17,6 +17,7 @@ class XcodeLogParser { private(set) var settings: [String: String] = [:] /// The path to the Xcode build cache private(set) var buildCachePath: URL! + private(set) var targetCommands: [String: [CompilerCommand]] = [:] enum Error: Swift.Error { case noCommandsFound(String) @@ -32,11 +33,11 @@ class XcodeLogParser { /// Start parsing the build log /// - Parameter targets: The global list of targets - func parse(_ targets: inout Targets) throws { - parseBuildLog(log, &targets) + func parse() throws { + parseBuildLog(log) - if targets.isEmpty { - logger.debug("Found no targets in log: \(log)") + if targetCommands.isEmpty { + logger.debug("Found no targets in log") throw Error.noTargetsFound( """ @@ -45,8 +46,13 @@ class XcodeLogParser { ) } - if targets.totalCommandCount == 0 { - logger.debug("Found no commands in log: \(log)") + let totalCommandCount = targetCommands + .values + .compactMap { $0.count } + .reduce(0, +) + + if totalCommandCount == 0 { + logger.debug("Found no commands in log") throw Error.noCommandsFound( """ @@ -60,12 +66,11 @@ class XcodeLogParser { } } - /// Parses an array representing the contents of an Xcode build log + /// Parses an array representing the contents of an Xcode build log /// - Parameters: /// - lines: contents of the Xcode build log lines - /// - targets: the container to add found targets to - private func parseBuildLog(_ lines: [String], _ targets: inout Targets) { - var currentTarget: Target? + private func parseBuildLog(_ lines: [String]) { + var currentTarget: String? var seenTargets = Set() for (index, line) in lines.enumerated() { @@ -97,17 +102,12 @@ class XcodeLogParser { .deletingLastPathComponent() } - if let target = target(from: line), currentTarget?.name != target { + if let target = target(from: line), currentTarget != target { if seenTargets.insert(target).inserted { logger.debug("Found target: \(target)") } - if let targetObject = targets.target(for: target) { - currentTarget = targetObject - } else { - currentTarget = .init(name: target) - targets.insert(target: currentTarget!) - } + currentTarget = target } guard let currentTarget else { @@ -121,9 +121,9 @@ class XcodeLogParser { continue } - logger.debug("Found \(compilerCommand.compiler.rawValue) compiler command for target: \(currentTarget.name)") + logger.debug("Found \(compilerCommand.compiler.rawValue) compiler command for target: \(currentTarget)") - currentTarget.commands.append(compilerCommand) + targetCommands[currentTarget, default: [CompilerCommand]()].append(compilerCommand) } } From c1dc4ba558290ed74fb1b113d408d610ad911d06 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 13 May 2024 10:44:22 +0200 Subject: [PATCH 35/61] Helper function for creating Targets --- Sources/GenIR/GenIR.swift | 6 +----- Sources/GenIR/Target.swift | 10 ++++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index b7563c6..465ce17 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -129,11 +129,7 @@ struct IREmitterCommand: ParsableCommand { dryRun: dryRun ) - let targets = pifCache - .targets - .map { - Target(baseTarget: $0, commands: log.targetCommands[$0.name] ?? []) - } + let targets = Target.targets(from: pifCache.targets, with: log.targetCommands) let runner = CompilerCommandRunner( output: output, diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index f480f4a..cce6585 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -25,6 +25,16 @@ class Target { } } +extension Target { + static func targets(from targets: [PIF.BaseTarget], with targetsToCommands: [String: [CompilerCommand]]) -> [Target] { + targets + .map { + Target(baseTarget: $0, commands: targetsToCommands[$0.name] ?? []) + } + } +} + + extension Target: NodeValue { var value: Self { self } var valueName: String { productName } From 88ef1f95165342133957231da73e0f363a8091cd Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 13 May 2024 15:08:19 +0200 Subject: [PATCH 36/61] Update tests to use new PIF targets One test is failing because the output postprocessor is not moving dependencies - that still needs to be addressed --- PIF/Sources/PIFSupport/PIF.swift | 9 +- Sources/GenIR/PIFCache.swift | 101 ++++++++++++++---- Sources/GenIR/Target.swift | 3 +- .../App/App.xcodeproj/project.pbxproj | 12 ++- .../FrameworkA.xcodeproj/project.pbxproj | 12 ++- .../FrameworkB.xcodeproj/project.pbxproj | 12 ++- Tests/GenIRTests/DependencyGraphTests.swift | 34 +++--- Tests/GenIRTests/MultipleAppTests.swift | 20 ++-- .../OutputPostprocessorFileMoverTests.swift | 12 +-- Tests/GenIRTests/TestContext.swift | 45 +++++++- Tests/GenIRTests/UmbrellaTests.swift | 40 +++---- .../GenIRTests/WorkspaceContainerTests.swift | 9 +- Tests/GenIRTests/WorkspaceTests.swift | 13 ++- Tests/GenIRTests/gen_irTests.swift | 7 +- 14 files changed, 218 insertions(+), 111 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index ef342cb..9674784 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -114,11 +114,7 @@ public enum PIF { projects = try projectContents .map { - // do { - return try PIFDecoder(cache: cachePath).decode(PIF.Project.self, from: $0) - // } catch { - // fatalError(String(data: $0, encoding: .utf8)!) - // } + try PIFDecoder(cache: cachePath).decode(PIF.Project.self, from: $0) } } } @@ -203,6 +199,9 @@ public enum PIF { /// Indicates that the path is relative to the SDKROOT case sdkRoot = "SDKROOT" + + /// Indicates that the path is relative to the DEVELOPER_DIR (normally in the Xcode.app bundle) + case developerDir = "DEVELOPER_DIR" } public let guid: GUID diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index f1e174b..9de608d 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -2,15 +2,13 @@ import Foundation import PIFSupport class PIFCache { - public typealias GUID = String - private let buildCache: URL private let pifCachePath: URL private let workspace: PIF.Workspace enum Error: Swift.Error { case nonexistentCache(String) - case pifError(String) + case pifError(Swift.Error) } init(buildCache: URL) throws { @@ -21,7 +19,7 @@ class PIFCache { let cache = try PIFParser(cachePath: pifCachePath) workspace = cache.workspace } catch { - throw Error.pifError(error.localizedDescription) + throw Error.pifError(error) } } @@ -74,6 +72,63 @@ class PIFCache { .flatMap { $0.targets } } + private lazy var namesToTargets: [String: PIF.BaseTarget] = { + targets + .reduce(into: [String: PIF.BaseTarget]()) { partial, target in + partial[target.name] = target + } + }() + + private lazy var productNamesToTargets: [String: PIF.BaseTarget] = { + targets + .compactMap { $0 as? PIF.Target } + .reduce(into: [String: PIF.BaseTarget]()) { partial, target in + partial[target.productName] = target + } + }() + + private func fileReferences(for project: PIF.Project) -> [PIF.FileReference] { + func resolveChildren(starting children: [PIF.Reference], result: inout [PIF.FileReference]) { + for child in children { + if let fileReference = child as? PIF.FileReference { + result.append(fileReference) + } else if let group = child as? PIF.Group { + resolveChildren(starting: group.children, result: &result) + } else { + print("Unhandled reference type: \(child)") + } + } + } + + var result = [PIF.FileReference]() + resolveChildren(starting: project.groupTree.children, result: &result) + return result + } + + lazy var frameworks: [PIF.GUID: PIF.Target] = { + // Here we have to get all the wrapper.framework references from the group, and then attempt to map them to targets + let frameworkFileReferences = projects + .flatMap { fileReferences(for: $0) } + .filter { $0.fileType == "wrapper.framework" } + // TODO: do we filter on sourceTree == "BUILT_PRODUCTS_DIR" here too? + + // Now, stupidly, we have to do a name lookup on the path and use that to look up a target + let frameworks = targets + .compactMap { $0 as? PIF.Target } + .filter { $0.productType == .framework } + .reduce(into: [String: PIF.Target]()) { partial, target in + partial[target.productName] = target + } + + return frameworkFileReferences + // .compactMap { frameworks[$0.path] } // TODO: I think we should get the last path component as the key here - check that + .reduce(into: [PIF.GUID: PIF.Target]()) { partial, fileReference in + // partial[target.guid] = target + // Use the _file reference_ GUID as the key here - we're looking up frameworks by their file reference and not target GUID! + partial[fileReference.guid] = frameworks[fileReference.path] + } + }() + // private lazy var targetsByGUID: [GUID: PIF.BaseTarget] = { // targets // .reduce(into: [GUID: PIF.BaseTarget]()) { result, element in @@ -86,16 +141,6 @@ class PIFCache { // } } -extension PIF.BaseTarget: NodeValue { - var valueName: String { - if let target = self as? PIF.Target, !target.productName.isEmpty { - return target.productName - } - - return name - } -} - extension PIF.BaseTarget: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) @@ -109,22 +154,42 @@ extension PIF.BaseTarget: Hashable { struct PIFDependencyProvider: DependencyProviding { private let targets: [Target] private let cache: PIFCache - private var guidToTargets: [PIFCache.GUID: Target] + private var guidToTargets: [PIF.GUID: Target] init(targets: [Target], cache: PIFCache) { self.targets = targets self.cache = cache self.guidToTargets = targets - .reduce(into: [PIFCache.GUID: Target]()) { partial, target in + .reduce(into: [PIF.GUID: Target]()) { partial, target in partial[target.baseTarget.guid] = target } } func dependencies(for value: Target) -> [Target] { - value + // Direct dependencies + let dependencyTargets = value .baseTarget .dependencies - .map { guidToTargets[$0.targetGUID]! } + .compactMap { guidToTargets[$0.targetGUID] } + + // Framework build phase dependencies + let frameworkBuildPhases = value + .baseTarget + .buildPhases + .compactMap { $0 as? PIF.FrameworksBuildPhase } + + let frameworks = frameworkBuildPhases + .flatMap { $0.buildFiles } + .compactMap { + switch $0.reference { + case .file(let guid): return guid + case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency + } + } + .compactMap { cache.frameworks[$0] } + .compactMap { guidToTargets[$0.guid] } + + return dependencyTargets + frameworks } } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index cce6585..f33723b 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -34,10 +34,9 @@ extension Target { } } - extension Target: NodeValue { var value: Self { self } - var valueName: String { productName } + var valueName: String { name } } extension Target: Hashable { diff --git a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj index 8e3249d..879dacb 100644 --- a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj +++ b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj @@ -215,6 +215,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -275,6 +276,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -302,9 +304,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -319,6 +323,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.test.App; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -330,9 +335,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -347,6 +354,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.test.App; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj index 3bfedf6..b3429ba 100644 --- a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj @@ -172,6 +172,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; @@ -235,6 +236,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -264,9 +266,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -284,6 +288,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -296,9 +301,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -316,6 +323,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj index 2c9c860..b51d48a 100644 --- a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; @@ -271,6 +272,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -300,9 +302,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -320,6 +324,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkB; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -332,9 +337,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -352,6 +359,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkB; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 300b933..31edaf8 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -3,45 +3,39 @@ import XCTest import PBXProjParser final class DependencyGraphTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("WorkspaceTest") .appendingPathComponent("Workspace.xcworkspace") }() - - static private var scheme = "App" + let scheme = "App" func testChains() throws { // Test Setup - let context = try TestContext() - let process = try context.build(test: Self.testPath, scheme: Self.scheme) + let context = TestContext() + let process = try context.build(test: testPath, scheme: scheme) XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") - let project = try ProjectParser(path: Self.testPath, logLevel: .debug) - var targets = Targets(for: project) - - let buildLog = try String(contentsOf: context.buildLog).components(separatedBy: .newlines) - let logParser = XcodeLogParser(log: buildLog) + let targets = context.targets + let graph = context.graph - try logParser.parse(&targets) - - let graph = DependencyGraphBuilder.build(targets: targets) - let appTarget = try XCTUnwrap(targets.target(for: "App"), "Failed to get App target from targets") + let appTarget = try XCTUnwrap(targets.first(where: { $0.name == "App"}), "Failed to get App target from targets") let app = try XCTUnwrap(graph.findNode(for: appTarget), "Failed to find App node in graph") // App should have two nodes - Framework & Common XCTAssertTrue(app.edges.count == 2, "App's edges is not equal to 2") - _ = try XCTUnwrap(app.edges.first(where: { $0.to.name == "Framework" }), "Failed to get Framework edge from App") - let commonEdge = try XCTUnwrap(app.edges.first(where: { $0.to.name == "Common" }), "Failed to get Common edge from App") + _ = try XCTUnwrap(app.edges.first(where: { $0.to.valueName == "Framework" }), "Failed to get Framework edge from App") + let commonEdge = try XCTUnwrap(app.edges.first(where: { $0.to.valueName == "Common" }), "Failed to get Common edge from App") - let frameworkTarget = try XCTUnwrap(targets.target(for: "Framework"), "Failed to get Framework from targets") + let frameworkTarget = try XCTUnwrap(targets.first(where: { $0.name == "Framework"}), "Failed to get Framework from targets") let framework = try XCTUnwrap(graph.findNode(for: frameworkTarget), "Failed to find Framework node in graph") // Framework should have two dependency edges - Common & SFSafeSymbols and one depender edge - App XCTAssertTrue(framework.edges.count == 3, "Framework's edges is not equal to 3") - let symbolsEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "SFSafeSymbols" }), "Failed to get SFSafeSymbols edge from Framework") - let frameworkCommonEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "Common" }), "Failed to get SFSafeSymbols edge from Framework") - let frameworkAppEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "App" }), "Failed to get App edge from Framework") + let symbolsEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.valueName == "SFSafeSymbols" }), "Failed to get SFSafeSymbols edge from Framework") + let frameworkCommonEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.valueName == "Common" }), "Failed to get SFSafeSymbols edge from Framework") + let frameworkAppEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.valueName == "App" }), "Failed to get App edge from Framework") + XCTAssertNotEqual(commonEdge, frameworkCommonEdge, "App's Common edge is equal to Framework's Common edge - they should have different from values") XCTAssertEqual(symbolsEdge.relationship, .dependency) XCTAssertEqual(frameworkCommonEdge.relationship, .dependency) diff --git a/Tests/GenIRTests/MultipleAppTests.swift b/Tests/GenIRTests/MultipleAppTests.swift index 5d946c3..9791fa0 100644 --- a/Tests/GenIRTests/MultipleAppTests.swift +++ b/Tests/GenIRTests/MultipleAppTests.swift @@ -1,28 +1,22 @@ import XCTest @testable import gen_ir -import PBXProjParser final class MultipleAppTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("MultipleApp") .appendingPathComponent("MultipleApp.xcodeproj") }() + let scheme = "MultipleApp" func testExpectedTargetLookup() throws { - let context = try TestContext() - let result = try context.build(test: Self.testPath, scheme: "MultipleApp") - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: "MultipleApp") - let project: ProjectParser = try ProjectParser(path: Self.testPath, logLevel: .debug) - var targets = Targets(for: project) + let targets = context.targets - let logContents = try String(contentsOf: context.buildLog).components(separatedBy: .newlines) - let log = XcodeLogParser(log: logContents) - try log.parse(&targets) - - let app = try XCTUnwrap(targets.target(for: "MultipleApp")) - let copy = try XCTUnwrap(targets.target(for: "MultipleApp Copy")) + let app = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp" })) + let copy = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp Copy" })) XCTAssertEqual(app.commands.count, 3) XCTAssertEqual(copy.commands.count, 3) diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index b4ca4e8..ba4b784 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -3,20 +3,20 @@ import XCTest import PBXProjParser final class OutputPostprocessorFileMoverTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("OutputPostprocessorFileMoverTests") .appendingPathComponent("OutputPostprocessorFileMoverTests.xcodeproj") }() + let scheme = "OutputPostprocessorFileMoverTests" func testFileMoving() throws { - let context = try TestContext() - let result = try context.build(test: Self.testPath, scheme: "OutputPostprocessorFileMoverTests") - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme) var runner = IREmitterCommand() try runner.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -25,13 +25,11 @@ final class OutputPostprocessorFileMoverTests: XCTestCase { ) // Check the output path for unique Image files - print("archivePAth: \(context.archive)") let appIRPath = context.archive .appendingPathComponent("IR") .appendingPathComponent("OutputPostprocessorFileMoverTests.app") let files = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) let imageFilesCount = files.filter { $0.lastPathComponent.starts(with: "Image") }.count - print("imageFilesCount: \(imageFilesCount)") // Only expecting two - one of the dependencies is dynamic and won't be moved. XCTAssertEqual(imageFilesCount, 2, "2 Image*.bc files expected, \(imageFilesCount) were found.") diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 661ebfb..8132ac5 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -1,10 +1,12 @@ import Foundation @testable import gen_ir +import XCTest class TestContext { enum Error: Swift.Error { case commandFailed(Process.ReturnValue) case invalidArgument(String) + case notBuilt } static let baseTestingPath: URL = { @@ -36,6 +38,7 @@ class TestContext { ) } + @discardableResult func build( test path: URL, scheme: String, @@ -74,18 +77,56 @@ class TestContext { throw Error.commandFailed(process) } else if let stdout = process.stdout { try stdout.write(to: buildLog, atomically: true, encoding: .utf8) + buildLogContents = stdout.components(separatedBy: .newlines) } + built = true + return process } let archive: URL let buildLog: URL let temporaryDirectory: URL + private(set) var built = false + private(set) var buildLogContents = [String]() - init() throws { - temporaryDirectory = try FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") + init() { + // swiftlint:disable force_try + temporaryDirectory = try! FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") archive = temporaryDirectory.appendingPathComponent("x.xcarchive") buildLog = temporaryDirectory.appendingPathComponent("build.log") } + + deinit { + try! FileManager.default.removeItem(at: temporaryDirectory) + // swiftlint:enable force_try + } + + lazy var logParser: XcodeLogParser = { + XCTAssertTrue(built, "Requests a log parser without building the project") + let parser = XcodeLogParser(log: buildLogContents) + do { + try parser.parse() + } catch { + fatalError("XcodeLogParser error: \(error)") + } + return parser + }() + + lazy var pifCache: PIFCache = { + do { + return try PIFCache(buildCache: logParser.buildCachePath) + } catch { + fatalError("PIFCache init failed with error: \(error)") + } + }() + + lazy var targets: [Target] = { + Target.targets(from: pifCache.targets, with: logParser.targetCommands) + }() + + lazy var graph: DependencyGraph = { + DependencyGraphBuilder(provider: PIFDependencyProvider(targets: targets, cache: pifCache), values: targets).graph + }() } diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index 167b05f..eac8475 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -3,15 +3,15 @@ import XCTest import PBXProjParser final class UmbrellaTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("Umbrella") .appendingPathComponent("Umbrella.xcworkspace") }() - static private var scheme = "Umbrella" + let scheme = "Umbrella" - static private let targetsToFiles = [ + let targetsToFiles = [ "Common.framework": ["Common_vers.bc", "Common-dummy.bc", "OrgModel.bc"].sorted(), "Networking.framework": ["Networking_vers.bc", "Networking-dummy.bc", "Networking.bc"].sorted(), "Pods_Umbrella.framework": ["Pods_Umbrella_vers.bc", "Pods-Umbrella-dummy.bc"].sorted(), @@ -19,18 +19,14 @@ final class UmbrellaTests: XCTestCase { ] func testUmbrellaTargets() throws { - let context = try TestContext() - let process = try context.build(test: Self.testPath, scheme: Self.scheme) - XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") - - let projectParser = try ProjectParser(path: Self.testPath, logLevel: .info) - let targets = Targets(for: projectParser) + let context = TestContext() + try context.build(test: testPath, scheme: scheme) + let targets = context.targets XCTAssertEqual(targets.count, 4, "Expected 4 targets, got \(targets.count)") let expectedTargetNames = ["Umbrella", "Common", "Networking", "Pods-Umbrella"].sorted() let actualTargetNames = targets.map { $0.name }.sorted() - XCTAssertEqual( actualTargetNames, expectedTargetNames, "Expected target names: \(expectedTargetNames), got: \(actualTargetNames)" @@ -38,16 +34,14 @@ final class UmbrellaTests: XCTestCase { } func testSkipInstallNo() throws { - let context = try TestContext() - defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } - let result = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO"]) - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme, additionalArguments: ["SKIP_INSTALL=NO"]) let output = context.archive.appendingPathComponent("IR") var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -58,28 +52,26 @@ final class UmbrellaTests: XCTestCase { let directories = try FileManager.default.directories(at: output, recursive: false) let targets = directories.map { $0.lastPathComponent } - XCTAssertEqual(targets.sorted(), Self.targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") + XCTAssertEqual(targets.sorted(), targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") for directory in directories { let contents = try FileManager.default.files(at: directory, withSuffix: "bc") .map { $0.lastPathComponent } .sorted() - XCTAssertEqual(contents, Self.targetsToFiles[directory.lastPathComponent]) + XCTAssertEqual(contents, targetsToFiles[directory.lastPathComponent]) } } func testCustomDerivedDataAndSkipInstallNo() throws { - let context = try TestContext() - defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } - let result = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) let output = context.archive.appendingPathComponent("IR") var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -90,14 +82,14 @@ final class UmbrellaTests: XCTestCase { let directories = try FileManager.default.directories(at: output, recursive: false) let targets = directories.map { $0.lastPathComponent } - XCTAssertEqual(targets.sorted(), Self.targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") + XCTAssertEqual(targets.sorted(), targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") for directory in directories { let contents = try FileManager.default.files(at: directory, withSuffix: "bc") .map { $0.lastPathComponent } .sorted() - XCTAssertEqual(contents, Self.targetsToFiles[directory.lastPathComponent]) + XCTAssertEqual(contents, targetsToFiles[directory.lastPathComponent]) } } } diff --git a/Tests/GenIRTests/WorkspaceContainerTests.swift b/Tests/GenIRTests/WorkspaceContainerTests.swift index e910bf5..8e1275e 100644 --- a/Tests/GenIRTests/WorkspaceContainerTests.swift +++ b/Tests/GenIRTests/WorkspaceContainerTests.swift @@ -1,17 +1,18 @@ import XCTest @testable import gen_ir -import PBXProjParser final class WorkspaceContainerTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("WorkspaceContainerTest") .appendingPathComponent("WorkspaceContainerTest.xcworkspace") }() + let scheme = "App" func testWeirdGroupTagLocationParsing() throws { - let parser = try ProjectParser(path: Self.testPath, logLevel: .debug) - let targets = parser.targets + let context = TestContext() + try context.build(test: testPath, scheme: scheme) + let targets = context.targets XCTAssert(targets.count == 3) XCTAssertNotNil(targets.first(where: { $0.name == "App" })) diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 7c2851a..0b2530f 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -3,13 +3,13 @@ import XCTest import PBXProjParser final class WorkspaceTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("WorkspaceTest") .appendingPathComponent("Workspace.xcworkspace") }() - static private var scheme = "App" + let scheme = "App" static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] static let commonIRFiles: Set = ["Common_vers.bc", "Model.bc"] @@ -50,14 +50,13 @@ final class WorkspaceTests: XCTestCase { ] func testWorkspace() throws { - let context = try TestContext() - let process = try context.build(test: Self.testPath, scheme: Self.scheme) - XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme) var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -69,7 +68,7 @@ final class WorkspaceTests: XCTestCase { let appIRPath = context.archive.appending(path: "IR/App.app/") let commonIRPath = context.archive.appending(path: "IR/Common.framework/") let frameworkIRPath = context.archive.appending(path: "IR/Framework.framework/") - let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols/") + let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols.framework/") let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) diff --git a/Tests/GenIRTests/gen_irTests.swift b/Tests/GenIRTests/gen_irTests.swift index ffdfd9f..7f55708 100644 --- a/Tests/GenIRTests/gen_irTests.swift +++ b/Tests/GenIRTests/gen_irTests.swift @@ -1,15 +1,16 @@ import XCTest @testable import gen_ir -import PBXProjParser final class GenIRTests: XCTestCase { func testManyTargetTestTargets() throws { + let context = TestContext() let projectPath = TestContext.baseTestingPath .appendingPathComponent("TestAssets") .appendingPathComponent("ManyTargetTest") .appendingPathComponent("ManyTargetTest.xcodeproj") - let project = try ProjectParser(path: projectPath, logLevel: logger.logLevel) - let targets = Targets(for: project) + + try context.build(test: projectPath, scheme: "ManyTargetTest") + let targets = context.targets XCTAssert(targets.count == 3, "Targets count expected to be 3, was \(targets.count)") From 7f1a9b95b3e19c238a879636fb8b53391f866e89 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 13 May 2024 15:15:38 +0200 Subject: [PATCH 37/61] Remove references to PBXProjectParser --- .swiftlint.yml | 2 +- PBXProjParser/.gitignore | 9 - PBXProjParser/Package.resolved | 14 - PBXProjParser/Package.swift | 38 - PBXProjParser/README.md | 12 - .../Extensions/DecodingExtensions.swift | 102 --- .../PBXProjParser/Models/PBXBuildFile.swift | 46 -- .../PBXProjParser/Models/PBXBuildPhase.swift | 99 --- .../Models/PBXContainerItemProxy.swift | 41 - .../Models/PBXFileReference.swift | 51 -- .../PBXProjParser/Models/PBXGroup.swift | 32 - .../PBXProjParser/Models/PBXObject.swift | 110 --- .../PBXProjParser/Models/PBXProj.swift | 111 --- .../PBXProjParser/Models/PBXProject.swift | 64 -- .../PBXProjParser/Models/PBXTarget.swift | 170 ---- .../Models/PBXVariantGroup.swift | 34 - .../Models/XCBuildConfiguration.swift | 34 - .../Models/XCConfigurationList.swift | 34 - .../XCSwiftPackageProductDependency.swift | 48 -- .../Sources/PBXProjParser/ProjectParser.swift | 121 --- .../PBXProjParser/Workspace/Reference.swift | 86 -- .../Workspace/WorkspaceParser.swift | 88 --- .../Sources/PBXProjParser/XcodeProject.swift | 231 ------ .../PBXProjParser/XcodeWorkspace.swift | 61 -- .../Sources/projparser/projparser.swift | 22 - .../PBXProjParserTests.swift | 47 -- .../project.pbxproj | 418 ---------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcschemes/DoubleTargetTest.xcscheme | 78 -- .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../Pods/Local Podspecs/MyBundle.podspec.json | 27 - .../DoubleTargetTest/Pods/Manifest.lock | 16 - .../Pods/Pods.xcodeproj/project.pbxproj | 734 ------------------ .../MyBundle/MyBundle-Info.plist | 26 - .../MyBundle/MyBundle-dummy.m | 5 - .../MyBundle/MyBundle-prefix.pch | 12 - .../MyBundle/MyBundle-umbrella.h | 16 - .../MyBundle/MyBundle.debug.xcconfig | 13 - .../MyBundle/MyBundle.modulemap | 6 - .../MyBundle/MyBundle.release.xcconfig | 13 - ...esourceBundle-MyBundle-MyBundle-Info.plist | 24 - .../Pods-DoubleTargetTest-Info.plist | 26 - ...DoubleTargetTest-acknowledgements.markdown | 26 - ...ds-DoubleTargetTest-acknowledgements.plist | 58 -- .../Pods-DoubleTargetTest-dummy.m | 5 - ...st-frameworks-Debug-input-files.xcfilelist | 2 - ...t-frameworks-Debug-output-files.xcfilelist | 1 - ...-frameworks-Release-input-files.xcfilelist | 2 - ...frameworks-Release-output-files.xcfilelist | 1 - .../Pods-DoubleTargetTest-frameworks.sh | 186 ----- .../Pods-DoubleTargetTest-umbrella.h | 16 - .../Pods-DoubleTargetTest.debug.xcconfig | 15 - .../Pods-DoubleTargetTest.modulemap | 6 - .../Pods-DoubleTargetTest.release.xcconfig | 15 - Package.swift | 2 - Sources/GenIR/CompilerCommandRunner.swift | 1 - Sources/GenIR/GenIR.swift | 1 - Sources/GenIR/OutputPostprocessor.swift | 1 - Sources/GenIR/Target.swift | 1 - Tests/GenIRTests/DependencyGraphTests.swift | 1 - .../OutputPostprocessorFileMoverTests.swift | 1 - Tests/GenIRTests/UmbrellaTests.swift | 1 - Tests/GenIRTests/WorkspaceTests.swift | 1 - 65 files changed, 1 insertion(+), 3496 deletions(-) delete mode 100644 PBXProjParser/.gitignore delete mode 100644 PBXProjParser/Package.resolved delete mode 100644 PBXProjParser/Package.swift delete mode 100644 PBXProjParser/README.md delete mode 100644 PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/ProjectParser.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/XcodeProject.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift delete mode 100644 PBXProjParser/Sources/projparser/projparser.swift delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist delete mode 100755 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig diff --git a/.swiftlint.yml b/.swiftlint.yml index 8b2d54e..993bb96 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,7 +2,7 @@ excluded: - .build/ - .swiftpm/ - .vscode/ - - PBXProjParser/.build/ # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet + # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet - GenIRLogging/.build/ - TestAssets/ diff --git a/PBXProjParser/.gitignore b/PBXProjParser/.gitignore deleted file mode 100644 index 3b29812..0000000 --- a/PBXProjParser/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -/.build -/Packages -/*.xcodeproj -xcuserdata/ -DerivedData/ -.swiftpm/config/registries.json -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -.netrc diff --git a/PBXProjParser/Package.resolved b/PBXProjParser/Package.resolved deleted file mode 100644 index 40da984..0000000 --- a/PBXProjParser/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", - "state" : { - "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", - "version" : "1.5.2" - } - } - ], - "version" : 2 -} diff --git a/PBXProjParser/Package.swift b/PBXProjParser/Package.swift deleted file mode 100644 index 4e57d05..0000000 --- a/PBXProjParser/Package.swift +++ /dev/null @@ -1,38 +0,0 @@ -// swift-tools-version: 5.7 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "PBXProjParser", - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "PBXProjParser", - targets: ["PBXProjParser"]) - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0") - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "PBXProjParser", - dependencies: [ - .product(name: "Logging", package: "swift-log") - ]), - .testTarget( - name: "PBXProjParserTests", - dependencies: ["PBXProjParser"]), - .executableTarget( - name: "projparser", - dependencies: [ - .product(name: "Logging", package: "swift-log"), - "PBXProjParser" - ] - ) - ] -) diff --git a/PBXProjParser/README.md b/PBXProjParser/README.md deleted file mode 100644 index aea5328..0000000 --- a/PBXProjParser/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# PBXProjParser - -As the name alludes to, this package handles parsing a pbxproj via xcodeproj or xcworkspace folders. - -It also contains some helper functions and structures to wrap an API around a pretty terrible file format, as well as an executable target for testing locally. - -## Note - -A _lot_ of the parsing is hidden behind a compiler flag for a couple reasons: - -- This is a largely undocumented format, and what little documentation is out there is often either slightly wrong or outdated. -- Only a handful of information is required for `Gen IR` so to speed things up/have less decoding issues we don't parse most of the files although the structures exist to do so diff --git a/PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift b/PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift deleted file mode 100644 index a9ed543..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// DecodingExtensions.swift -// -// -// Created by Thomas Hedderwick on 03/02/2023. -// - -import Foundation - -// https://gist.github.com/mikebuss/17142624da4baf9cdcc337861e256533 - -struct PlistCodingKeys: CodingKey { - var stringValue: String - - init(stringValue: String) { - self.stringValue = stringValue - } - - var intValue: Int? - - init?(intValue: Int) { - self.init(stringValue: "\(intValue)") - self.intValue = intValue - } -} - -extension KeyedDecodingContainer { - func decode(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any] { - let container = try self.nestedContainer(keyedBy: PlistCodingKeys.self, forKey: key) - return try container.decode(type) - } - - func decodeIfPresent(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any]? { - return try? self.nestedContainer(keyedBy: PlistCodingKeys.self, forKey: key).decode(type) - } - - func decode(_ type: [Any].Type, forKey key: K) throws -> [Any] { - var container = try self.nestedUnkeyedContainer(forKey: key) - return try container.decode(type) - } - - func decodeIfPresent(_ type: [Any].Type, forKey key: K) throws -> [Any]? { - guard var container = try? self.nestedUnkeyedContainer(forKey: key) else { - return nil - } - - return try? container.decode(type) - } - - func decode(_ type: [String: Any].Type) throws -> [String: Any] { - var dictionary = [String: Any]() - - for key in allKeys { - if let boolValue = try? decode(Bool.self, forKey: key) { - dictionary[key.stringValue] = boolValue - } else if let stringValue = try? decode(String.self, forKey: key) { - dictionary[key.stringValue] = stringValue - } else if let intValue = try? decode(Int.self, forKey: key) { - dictionary[key.stringValue] = intValue - } else if let doubleValue = try? decode(Double.self, forKey: key) { - dictionary[key.stringValue] = doubleValue - } else if let nestedDictionary = try? decode([String: Any].self, forKey: key) { - dictionary[key.stringValue] = nestedDictionary - } else if let nestedArray = try? decode([Any].self, forKey: key) { - dictionary[key.stringValue] = nestedArray - } - } - return dictionary - } -} - -extension UnkeyedDecodingContainer { - mutating func decode(_ type: [Any].Type) throws -> [Any] { - var array: [Any] = [] - - while isAtEnd == false { - let value: String? = try decode(String?.self) - if value == nil { - continue - } - if let value = try? decode(Bool.self) { - array.append(value) - } else if let value = try? decode(Int.self) { - array.append(value) - } else if let value = try? decode(Double.self) { - array.append(value) - } else if let value = try? decode(String.self) { - array.append(value) - } else if let nestedDictionary = try? decode([String: Any].self) { - array.append(nestedDictionary) - } else if let nestedArray = try? decode([Any].self) { - array.append(nestedArray) - } - } - return array - } - - mutating func decode(_ type: [String: Any].Type) throws -> [String: Any] { - let nestedContainer = try self.nestedContainer(keyedBy: PlistCodingKeys.self) - return try nestedContainer.decode(type) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift deleted file mode 100644 index 25bc290..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// PBXBuildFile.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXBuildFile: PBXObject { - public let productRef: String? - public let fileRef: String? - - #if FULL_PBX_PARSING - public let platformFilter: String? - public let platformFilters: [String]? - public let settings: [String: Any]? - #endif - - private enum CodingKeys: String, CodingKey { - case productRef - case fileRef - - #if FULL_PBX_PARSING - case platformFilter - case platformFilters - case settings - #endif - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - productRef = try container.decodeIfPresent(String.self, forKey: .productRef) - fileRef = try container.decodeIfPresent(String.self, forKey: .fileRef) - - #if FULL_PBX_PARSING - platformFilter = try container.decodeIfPresent(String.self, forKey: .platformFilter) - platformFilters = try container.decodeIfPresent([String].self, forKey: .platformFilters) - settings = try container.decodeIfPresent([String: Any].self, forKey: .settings) - #endif - - try super.init(from: decoder) - } -} - -public class PBXBuildRule: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift deleted file mode 100644 index cbc5d70..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// PBXBuildPhase.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXBuildPhase: PBXObject { - public let files: [String] -#if FULL_PBX_PARSING - public let alwaysOutOfDate: String? - public let buildActionMask: UInt32 - public let runOnlyForDeploymentPostprocessing: Int -#endif - - private enum CodingKeys: String, CodingKey { - case files - #if FULL_PBX_PARSING - case alwaysOutOfDate - case buildActionMask - case runOnlyForDeploymentPostprocessing - #endif - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - alwaysOutOfDate = try container.decodeIfPresent(String.self, forKey: .alwaysOutOfDate) - - let mask = try container.decode(String.self, forKey: .buildActionMask) - buildActionMask = UInt32(mask) ?? 0 - - let flag = try container.decode(String.self, forKey: .runOnlyForDeploymentPostprocessing) - runOnlyForDeploymentPostprocessing = Int(flag) ?? 0 - #endif - - files = try container.decodeIfPresent([String].self, forKey: .files) ?? [] - - try super.init(from: decoder) - } -} - -public class PBXCopyFilesBuildPhase: PBXBuildPhase { -#if FULL_PBX_PARSING - let dstPath: String - let dstSubfolderSpec: String - - private enum CodingKeys: String, CodingKey { - case dstPath - case dstSubfolderSpec - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - dstPath = try container.decode(String.self, forKey: .dstPath) - dstSubfolderSpec = try container.decode(String.self, forKey: .dstSubfolderSpec) - - try super.init(from: decoder) - } -#endif -} - -public class PBXShellScriptBuildPhase: PBXBuildPhase { -#if FULL_PBX_PARSING - let inputPaths: [String]? - let outputPaths: [String]? - let shellPath: String - let shellScript: String - - private enum CodingKeys: String, CodingKey { - case inputPaths - case outputPaths - case shellPath - case shellScript - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - inputPaths = try container.decodeIfPresent([String].self, forKey: .inputPaths) - outputPaths = try container.decodeIfPresent([String].self, forKey: .outputPaths) - shellPath = try container.decode(String.self, forKey: .shellPath) - shellScript = try container.decode(String.self, forKey: .shellScript) - - try super.init(from: decoder) - } -#endif -} - -public class PBXFrameworksBuildPhase: PBXBuildPhase {} -public class PBXHeadersBuildPhase: PBXBuildPhase {} -public class PBXResourcesBuildPhase: PBXBuildPhase {} -public class PBXSourcesBuildPhase: PBXBuildPhase {} -public class PBXAppleScriptBuildPhase: PBXBuildPhase {} -public class PBXRezBuildPhase: PBXBuildPhase {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift deleted file mode 100644 index 079eb04..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// PBXContainerItemProxy.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXContainerItemProxy: PBXObject { - #if FULL_PBX_PARSING - public let containerPortal: String - public let proxyType: String - public let remoteInfo: String - #endif - public let remoteGlobalIDString: String - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case containerPortal - case proxyType - case remoteInfo - #endif - case remoteGlobalIDString - - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - containerPortal = try container.decode(String.self, forKey: .containerPortal) - proxyType = try container.decode(String.self, forKey: .proxyType) - remoteInfo = try container.decode(String.self, forKey: .remoteInfo) - #endif - - remoteGlobalIDString = try container.decode(String.self, forKey: .remoteGlobalIDString) - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift deleted file mode 100644 index 9dea7fd..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// PBXFileReference.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXFileReference: PBXObject { - #if FULL_PBX_PARSING - public let fileEncoding: String? - public let includeInIndex: String? - public let lastKnownFileType: String? - public let name: String? - public let sourceTree: String - #endif - public let explicitFileType: String? - public let path: String - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case fileEncoding - case includeInIndex - case lastKnownFileType - case name - case sourceTree - #endif - case explicitFileType - case path - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - explicitFileType = try container.decodeIfPresent(String.self, forKey: .explicitFileType) - path = try container.decode(String.self, forKey: .path) - - #if FULL_PBX_PARSING - fileEncoding = try container.decodeIfPresent(String.self, forKey: .fileEncoding) - includeInIndex = try container.decodeIfPresent(String.self, forKey: .includeInIndex) - lastKnownFileType = try container.decodeIfPresent(String.self, forKey: .lastKnownFileType) - name = try container.decodeIfPresent(String.self, forKey: .name) - sourceTree = try container.decode(String.self, forKey: .sourceTree) - #endif - - try super.init(from: decoder) - } -} - -public class PBXReferenceProxy: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift deleted file mode 100644 index 14f2f33..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// PBXGroup.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXGroup: PBXObject { -#if FULL_PBX_PARSING - public let children: [String] - public let name: String? - public let sourceTree: String - - private enum CodingKeys: String, CodingKey { - case children - case name - case sourceTree - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - children = try container.decode([String].self, forKey: .children) - name = try container.decodeIfPresent(String.self, forKey: .name) - sourceTree = try container.decode(String.self, forKey: .sourceTree) - - try super.init(from: decoder) - } -#endif -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift deleted file mode 100644 index 4dbd1f2..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// File.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -/// Base class for all PBX objects -public class PBXObject: Decodable { - /// Objects class name - public let isa: ObjectType - // / The 'UUID-like' reference key found at the start of an object declaration - public var reference: String! - - public enum ObjectType: String, Decodable, CaseIterable { - case buildFile = "PBXBuildFile" - case appleScriptBuildPhase = "PBXAppleScriptBuildPhase" - case copyFilesBuildPhase = "PBXCopyFilesBuildPhase" - case frameworksBuildPhase = "PBXFrameworksBuildPhase" - case headersBuildPhase = "PBXHeadersBuildPhase" - case resourcesBuildPhase = "PBXResourcesBuildPhase" - case shellScriptBuildPhase = "PBXShellScriptBuildPhase" - case sourcesBuildPhase = "PBXSourcesBuildPhase" - case containerItemProxy = "PBXContainerItemProxy" - case fileReference = "PBXFileReference" - case group = "PBXGroup" - case variantGroup = "PBXVariantGroup" - case aggregateTarget = "PBXAggregateTarget" - case legacyTarget = "PBXLegacyTarget" - case nativeTarget = "PBXNativeTarget" - case project = "PBXProject" - case targetDependency = "PBXTargetDependency" - case buildConfiguration = "XCBuildConfiguration" - case configurationList = "XCConfigurationList" - case swiftPackageProductDependency = "XCSwiftPackageProductDependency" - case localSwiftPackageReference = "XCLocalSwiftPackageReference" - case remoteSwiftPackageReference = "XCRemoteSwiftPackageReference" - case referenceProxy = "PBXReferenceProxy" - case versionGroup = "XCVersionGroup" - case buildRule = "PBXBuildRule" - case rezBuildPhase = "PBXRezBuildPhase" - - // swiftlint:disable cyclomatic_complexity - public func getType() -> PBXObject.Type { - switch self { - case .buildFile: return PBXBuildFile.self - case .appleScriptBuildPhase: return PBXAppleScriptBuildPhase.self - case .copyFilesBuildPhase: return PBXCopyFilesBuildPhase.self - case .frameworksBuildPhase: return PBXFrameworksBuildPhase.self - case .headersBuildPhase: return PBXHeadersBuildPhase.self - case .resourcesBuildPhase: return PBXResourcesBuildPhase.self - case .shellScriptBuildPhase: return PBXShellScriptBuildPhase.self - case .sourcesBuildPhase: return PBXSourcesBuildPhase.self - case .containerItemProxy: return PBXContainerItemProxy.self - case .fileReference: return PBXFileReference.self - case .group: return PBXGroup.self - case .variantGroup: return PBXVariantGroup.self - case .aggregateTarget: return PBXAggregateTarget.self - case .legacyTarget: return PBXLegacyTarget.self - case .nativeTarget: return PBXNativeTarget.self - case .project: return PBXProject.self - case .targetDependency: return PBXTargetDependency.self - case .buildConfiguration: return XCBuildConfiguration.self - case .configurationList: return XCConfigurationList.self - case .swiftPackageProductDependency: return XCSwiftPackageProductDependency.self - case .remoteSwiftPackageReference: return XCRemoteSwiftPackageReference.self - case .localSwiftPackageReference: return XCLocalSwiftPackageReference.self - case .referenceProxy: return PBXReferenceProxy.self - case .versionGroup: return XCVersionGroup.self - case .buildRule: return PBXBuildRule.self - case .rezBuildPhase: return PBXRezBuildPhase.self - } - // swiftlint:enable cyclomatic_complexity - } -} - -} - -/// Single case enum that decodes and holds a reference to an underlying `PBXObject` subclass -enum Object: Decodable { - /// The wrapped object - case object(PBXObject) - - private enum CodingKeys: String, CodingKey { - case isa - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let isa = try container.decode(PBXObject.ObjectType.self, forKey: .isa) - let singleContainer = try decoder.singleValueContainer() - - self = .object(try singleContainer.decode(isa.getType())) - } - - func unwrap() -> PBXObject { - if case .object(let object) = self { - return object - } - - fatalError( - """ - Failed to unwrap the underlying PBXObject, this should only happen if someone adds a case to `Object` and \ - didn't handle it. - """ - ) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift deleted file mode 100644 index 05c4033..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// pbxproj.swift -// -// -// Created by Thomas Hedderwick on 27/01/2023. -// - -import Foundation - -/* - Note: This is based _largely_ on prior art. Thanks to: - http://www.monobjc.net/xcode-project-file-format.html - Cocoapods - - Some of the references here are reverse engineered from the `XCBuild.framework` from Xcode: - /Applications/Xcode.app/Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/Frameworks/XCBProjectModel.framework/Versions/A/XCBProjectModel - */ - -// NOTE! Big thanks to http://www.monobjc.net/xcode-project-file-format.html for the file format reference - a lot of the layout here is based on that work - -/// Represents a pbxproj file -public class PBXProj: Decodable { - /// Version of the pbxproj - let archiveVersion: String - /// ??? - let classes: [String: String] - /// Version of the `objects` - let objectVersion: String - /// Mapping of UUID to their corresponding object - let objects: [String: Object] - /// UUID of the root object (probably a PBXProject - let rootObject: String - - enum Error: Swift.Error { - case projectNotFound(String) - } - - /// Decodes a `pbxproj` object from the contents of `path` - /// - Parameter path: path to `project.pbxproj` to parse - /// - Returns: a deserialized pbxproj structure - static func contentsOf(_ path: URL) throws -> PBXProj { - let data: Data - - do { - data = try Data(contentsOf: path) - } catch { - logger.error("Failed to get contents of path: \(path), please check that this path exists and is readable.") - throw error - } - - let decoder = PropertyListDecoder() - - do { - let project = try decoder.decode(Self.self, from: data) - project.fixup() - return project - } catch { - logger.error( - "Failed to decode the pbxproj for path: \(path). Please report this as an error with the pbxproj!" - ) - throw error - } - } - - /// Fixes `Object`s by unwrapping them and assigning the UUID key that represents them to the reference field - private func fixup() { - objects.forEach { (key, object) in - object.unwrap().reference = key - } - } -} - -/// Helper functions for operating on the project structure -public extension PBXProj { - /// Returns the object for a given key as the given type - /// - Parameters: - /// - key: the reference key for the object - /// - type: the type the object should be cast to - /// - Returns: the object, if the reference exists and the type conversion succeeded. Otherwise, nil. - func object(forKey key: String, as type: T.Type = T.self) -> T? { - objects[key]?.unwrap() as? T - } - - /// Returns all the objects of a given type in the project structure - /// - Parameter objectType: the type of objects to find - /// - Parameter type: the type to cast the object to - /// - Returns: an array of all the typed objects found in the project structure - func objects(of objectType: PBXObject.ObjectType, as type: T.Type) -> [T] { - objects - .map { $1.unwrap() } - .filter { $0.isa == objectType } - .compactMap { $0 as? T } - } - - func project() throws -> PBXProject { - guard let project: PBXProject = object(forKey: rootObject) else { - throw Error.projectNotFound( - "The root object of the pbxproj doesn't exist, or isn't castable to PBXProject... this shouldn't happen" - ) - } - - return project - } - - /// Returns a list of objects for a given list of references - /// - Parameter references: a list of references to lookup - /// - Returns: a list of objects that matches a reference in the references list - func objects(for references: [String]) -> [T] { - references.compactMap { object(forKey: $0) } - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift deleted file mode 100644 index 45c50ca..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// PBXProject.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXProject: PBXObject { - #if FULL_PBX_PARSING - public let attributes: [String: Any] - public let buildConfigurationList: String - public let compatibilityVersion: String - public let developmentRegion: String - public let hasScannedForEncodings: String - public let knownRegions: [String] - public let mainGroup: String - public let productRefGroup: String - public let projectDirPath: String - public let projectReferences: [[String: String]]? - public let projectRoot: String - #endif - public let packageReferences: [String] - public let targets: [String] /// Hold references to targets via their identifiers - - private enum CodingKeys: String, CodingKey { - case attributes - case buildConfigurationList - case compatibilityVersion - case developmentRegion - case hasScannedForEncodings - case knownRegions - case mainGroup - case productRefGroup - case projectDirPath - case projectReferences - case packageReferences - case projectRoot - case targets - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) -#if FULL_PBX_PARSING - // We currently don't decode this as it's painful and we don't need it - attributes = [:] - buildConfigurationList = try container.decode(String.self, forKey: .buildConfigurationList) - compatibilityVersion = try container.decode(String.self, forKey: .compatibilityVersion) - developmentRegion = try container.decode(String.self, forKey: .developmentRegion) - hasScannedForEncodings = try container.decode(String.self, forKey: .hasScannedForEncodings) - knownRegions = try container.decode([String].self, forKey: .knownRegions) - mainGroup = try container.decode(String.self, forKey: .mainGroup) - productRefGroup = try container.decode(String.self, forKey: .productRefGroup) - projectDirPath = try container.decode(String.self, forKey: .projectDirPath) - projectReferences = try container.decodeIfPresent([[String: String]].self, forKey: .projectReferences) - projectRoot = try container.decode(String.self, forKey: .projectRoot) -#endif - packageReferences = try container.decodeIfPresent([String].self, forKey: .packageReferences) ?? [] - targets = try container.decode([String].self, forKey: .targets) - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift deleted file mode 100644 index 26b458c..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// PBXTarget.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXTarget: PBXObject { - #if FULL_PBX_PARSING - public let buildConfigurationList: String - public let comments: String? - #endif - public let productName: String? - public let name: String - public let dependencies: [String] - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case buildConfigurationList - case comments - #endif - case productName - case name - case dependencies - - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - buildConfigurationList = try container.decode(String.self, forKey: .buildConfigurationList) - comments = try container.decodeIfPresent(String.self, forKey: .comments) - #endif - productName = try container.decodeIfPresent(String.self, forKey: .productName) - name = try container.decode(String.self, forKey: .name) - dependencies = try container.decode([String].self, forKey: .dependencies) - - try super.init(from: decoder) - } -} - -public class PBXAggregateTarget: PBXTarget { - public let buildPhases: [String] - - private enum CodingKeys: String, CodingKey { - case buildPhases - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - buildPhases = try container.decode([String].self, forKey: .buildPhases) - - try super.init(from: decoder) - } -} - -public class PBXLegacyTarget: PBXTarget {} - -public class PBXNativeTarget: PBXTarget { - #if FULL_PBX_PARSING - public let productInstallPath: String? - #endif - public let buildPhases: [String] - public let productType: String? - public let productReference: String? - public let packageProductDependencies: [String] - - private(set) var targetDependencies: [String: TargetDependency] = [:] - - public enum TargetDependency { - case native(PBXNativeTarget) - case package(XCSwiftPackageProductDependency) - case externalProjectFramework(String) - - public var name: String { - switch self { - case .native(let target): - return target.name - case .package(let package): - return package.productName - case .externalProjectFramework(let filename): - return (filename as NSString).deletingPathExtension - } - } - } - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case productInstallPath - #endif - case buildPhases - case productType - case productReference - case packageProductDependencies - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - productInstallPath = try container.decodeIfPresent(String.self, forKey: .productInstallPath) - #endif - buildPhases = try container.decodeIfPresent([String].self, forKey: .buildPhases) ?? [] - productType = try container.decodeIfPresent(String.self, forKey: .productType) - productReference = try container.decodeIfPresent(String.self, forKey: .productReference) - packageProductDependencies = try container.decodeIfPresent([String].self, forKey: .packageProductDependencies) ?? [] - - try super.init(from: decoder) - } - - func add(dependency: TargetDependency) { - targetDependencies[dependency.name] = dependency - } -} - -extension PBXNativeTarget: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(isa) - hasher.combine(reference) - hasher.combine(productName) - hasher.combine(name) - } -} - -extension PBXNativeTarget: Equatable { - public static func == (lhs: PBXNativeTarget, rhs: PBXNativeTarget) -> Bool { - // This should be enough as references _should_ be unique to the object - lhs.reference == rhs.reference - } -} - -extension PBXNativeTarget: CustomStringConvertible { - public var description: String { - #if FULL_PBX_PARSING - """ - - """ - #else - """ - - """ - #endif - } -} - -public class PBXTargetDependency: PBXObject { - public let target: String? - public let targetProxy: String? - - private enum CodingKeys: String, CodingKey { - case target - case targetProxy - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - target = try container.decodeIfPresent(String.self, forKey: .target) - targetProxy = try container.decodeIfPresent(String.self, forKey: .targetProxy) - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift deleted file mode 100644 index d086ea1..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// PBXVariantGroup.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXVariantGroup: PBXObject { - #if FULL_PBX_PARSING - public let children: [String] - public let name: String - public let sourceTree: String - - private enum CodingKeys: String, CodingKey { - case children - case name - case sourceTree - } - #endif - - required init(from decoder: Decoder) throws { - #if FULL_PBX_PARSING - let container = try decoder.container(keyedBy: CodingKeys.self) - - children = try container.decode([String].self, forKey: .children) - name = try container.decode(String.self, forKey: .name) - sourceTree = try container.decode(String.self, forKey: .sourceTree) - #endif - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift b/PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift deleted file mode 100644 index b93d554..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// XCBuildConfiguration.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class XCBuildConfiguration: PBXObject { - #if FULL_PBX_PARSING - public var baseConfigurationReference: String? - public var buildSettings: [String: Any] - public var name: String - - private enum CodingKeys: String, CodingKey { - case baseConfigurationReference - case buildSettings - case name - } - #endif - - required init(from decoder: Decoder) throws { - #if FULL_PBX_PARSING - let container = try decoder.container(keyedBy: CodingKeys.self) - - baseConfigurationReference = try container.decodeIfPresent(String.self, forKey: .baseConfigurationReference) - buildSettings = try container.decode([String: Any].self, forKey: .buildSettings) - name = try container.decode(String.self, forKey: .name) - #endif - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift b/PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift deleted file mode 100644 index 4e12a1f..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// XCConfigurationList.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class XCConfigurationList: PBXObject { - #if FULL_PBX_PARSING - public let buildConfigurations: [String] - public let defaultConfigurationIsVisible: String - public let defaultConfigurationName: String - - private enum CodingKeys: String, CodingKey { - case buildConfigurations - case defaultConfigurationIsVisible - case defaultConfigurationName - } - #endif - - required init(from decoder: Decoder) throws { - #if FULL_PBX_PARSING - let container = try decoder.container(keyedBy: CodingKeys.self) - - buildConfigurations = try container.decode([String].self, forKey: .buildConfigurations) - defaultConfigurationIsVisible = try container.decode(String.self, forKey: .defaultConfigurationIsVisible) - defaultConfigurationName = try container.decode(String.self, forKey: .defaultConfigurationName) - #endif - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift b/PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift deleted file mode 100644 index 9274529..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// XCSwiftPackageProductDependency.swift -// -// -// Created by Thomas Hedderwick on 15/02/2023. -// - -import Foundation - -public class XCSwiftPackageProductDependency: PBXObject { - public let package: String? - public let productName: String - - private enum CodingKeys: CodingKey { - case package - case productName - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - package = try container.decodeIfPresent(String.self, forKey: .package) - productName = try container.decode(String.self, forKey: .productName) - - try super.init(from: decoder) - } -} - -extension XCSwiftPackageProductDependency: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(isa) - hasher.combine(reference) - hasher.combine(package) - hasher.combine(productName) - } -} - -extension XCSwiftPackageProductDependency: Equatable { - public static func == (lhs: XCSwiftPackageProductDependency, rhs: XCSwiftPackageProductDependency) -> Bool { - lhs.reference == rhs.reference && - lhs.package == rhs.package && - lhs.productName == rhs.productName - } -} - -public class XCRemoteSwiftPackageReference: PBXObject {} -public class XCLocalSwiftPackageReference: PBXObject {} -public class XCVersionGroup: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift b/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift deleted file mode 100644 index 8e4fbc6..0000000 --- a/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift +++ /dev/null @@ -1,121 +0,0 @@ -import Foundation -import Logging - -var logger: Logger = .init(label: "com.veracode.PBXProjParser") - -/// An Xcode project parser (note: not an Xcode Project parser!) -public struct ProjectParser { - /// Path to the xcodeproj or xcworkspace bundle - let path: URL - - /// The type of project - let type: ProjectType - - /// All the native targets for the project - public var targets: [PBXNativeTarget] { - switch type { - case .project(let project): - return project.targets - case .workspace(let workspace): - return workspace.targets - } - } - - /// All the packages for the project - public var packages: [XCSwiftPackageProductDependency] { - switch type { - case .project(let project): - return project.packages - case .workspace(let workspace): - return workspace.packages - } - } - - /// Type of project this parser is working on - enum ProjectType { - /// A single Xcode Project - case project(XcodeProject) - /// An Xcode Workspace, which is a collection of Xcode Projects with some metadata - case workspace(XcodeWorkspace) - } - - public enum Error: Swift.Error { - case invalidPath(String) - } - - public init(path: URL, logLevel level: Logger.Level) throws { - self.path = path - logger.logLevel = level - - switch path.pathExtension { - case "xcodeproj": - let project = try XcodeProject(path: path) - type = .project(project) - case "xcworkspace": - let workspace = try XcodeWorkspace(path: path) - type = .workspace(workspace) - default: - throw Error.invalidPath("Path should be a xcodeproj or xcworkspace, got: \(path.lastPathComponent)") - } - } - - /// Returns a list of dependencies for a given target - /// - Parameter target: the target to get dependencies for - /// - Returns: an array of dependency references - public func dependencies(for target: String) -> [String] { - guard let project = project(for: target) else { - logger.error("Couldn't find project for target: \(target)") - return [] - } - - guard let target = project.target(for: target) else { - // SPMs don't list their dependencies in the pbxproj, skip warning about them - if project.package(for: target) == nil { - // TODO: once SPM dependencies work better, move this back to error level warning - logger.debug( - """ - Failed to find a target: \(target) in project: \(project.path). \ - Possible targets: \(project.targets.map { ($0.name, $0.productName ?? "nil")}). \ - Possible Packages: \(project.packages.map { $0.productName}) - """ - ) - } - - return [] - } - - return target.targetDependencies - .values - .map { dependency in - switch dependency { - case .native(let target): - if let path = project.path(for: target) { - return path - } - - fallthrough - default: - return dependency.name - } - } - } - - /// Gets a project for a given target - /// - Parameter target: the target to search for - /// - Returns: a `XcodeProject` that holds the target, if one was found - private func project(for target: String) -> XcodeProject? { - switch type { - case .project(let project): - return project - case .workspace(let workspace): - return workspace.targetsToProject[target] - } - } - - /// Gets the project model for a given target - /// - Parameter target: the target to search for - /// - Returns: a `PBXProj` that represents the pbxproj this target is a part of, if one was found - public func model(for target: String) -> PBXProj? { - project(for: target)?.model - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift deleted file mode 100644 index a205cdf..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// Reference.swift -// -// -// Created by Thomas Hedderwick on 18/10/2023. -// -import Foundation - -protocol Reference { - var location: Location { get } - static var elementName: String { get } -} - -enum Location { - // TODO: Find where we can get a definitive list of these. Xcode must have them somewhere? - case container(String) - case group(String) - - enum Error: Swift.Error { - case invalidLocation(String) - } - - init(_ location: String) throws { - let split = location - .split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false) - .map(String.init) - - guard - let key = split.first, - let value = split.last - else { throw Error.invalidLocation("Couldn't extract key/value pair from split: \(split)") } - - switch key { - case "container": self = .container(value) - case "group": self = .group(value) - default: throw Error.invalidLocation("Key didn't match a supported location key: \(key)") - } - } - - var path: String { - switch self { - case .container(let path): return path - case .group(let path): return path - } - } -} - -class Group: Reference { - static let elementName: String = "Group" - - let location: Location - let name: String? - var references: [Reference] = [] - - init(location: String, name: String?) throws { - self.location = try .init(location) - self.name = name - } -} - -struct FileRef: Reference { - static let elementName: String = "FileRef" - - let location: Location - let enclosingGroup: Group? - - init(location: String, enclosingGroup: Group? = nil) throws { - self.location = try .init(location) - self.enclosingGroup = enclosingGroup - } - - var path: String { - guard - let enclosingGroup - else { return location.path } - - switch enclosingGroup.location { - case let .group(path), let .container(path): - if path.last == "/" { - return path + location.path - } - - return path + "/" + location.path - } - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift deleted file mode 100644 index 4bca3fe..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// WorkspaceParser.swift -// -// -// Created by Thomas Hedderwick on 18/10/2023. -// - -import Foundation - -struct Workspace { - private(set) var fileReferences: [FileRef] = [] - private(set) var groupReferences: [Group] = [] -} - -struct WorkspaceParser { - static func parse(_ path: URL) throws -> Workspace { - // Parse the `contents.xcworkspacedata` (XML) file and get the list of projects - let contentsPath = path.appendingPathComponent("contents.xcworkspacedata") - - let data = try Data(contentsOf: contentsPath) - let delegate = WorkspaceDataParserDelegate() - let parser = XMLParser(data: data) - parser.delegate = delegate - parser.parse() - - return .init( - fileReferences: delegate.fileReferences, - groupReferences: delegate.groupReferences - ) - } -} - -private class WorkspaceDataParserDelegate: NSObject, XMLParserDelegate { - private(set) var fileReferences: [FileRef] = [] - private(set) var groupReferences: [Group] = [] - - static let supportedElements = [Group.elementName, FileRef.elementName] - - private var groupPath: [Group] = [] - - func parser( - _ parser: XMLParser, - didStartElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?, - attributes attributeDict: [String: String] = [:] - ) { - guard Self.supportedElements.contains(elementName) else { - logger.debug("Skipping parsing of unsupported element: \(elementName)") - return - } - - guard - let location = attributeDict["location"] - else { - logger.debug("Location attribute for element \(elementName) is nil, this shouldn't be the case: \(attributeDict)") - return - } - - do { - switch elementName { - case Group.elementName: - let group = try Group(location: location, name: attributeDict["name"]) - groupPath.append(group) - groupReferences.append(group) - case FileRef.elementName: - let file = try FileRef(location: location, enclosingGroup: groupPath.last) - fileReferences.append(file) - groupPath.last?.references.append(file) - // Ignore any element that doesn't match the search space - default: - break - } - } catch { - logger.debug("Parsing element: \(elementName) failed. Reason: \(error)") - } - } - - func parser( - _ parser: XMLParser, - didEndElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String? - ) { - guard elementName == Group.elementName else { return } - groupPath.removeLast() - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift deleted file mode 100644 index 90e9a77..0000000 --- a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift +++ /dev/null @@ -1,231 +0,0 @@ -// -// XcodeProject.swift -// -// -// Created by Thomas Hedderwick on 27/01/2023. -// - -import Foundation - -/// Represents an xcodeproj bundle -public struct XcodeProject { - /// Path to the Workspace - public let path: URL - - /// The underlying pbxproj model - public let model: PBXProj - - /// The 'project' object for the pbxproj - let project: PBXProject - - /// All the native targets in this project - let targets: [PBXNativeTarget] - - /// All the swift packages in this project - let packages: [XCSwiftPackageProductDependency] - - enum Error: Swift.Error { - case invalidPBXProj(String) - } - - public init(path: URL) throws { - self.path = path - model = try PBXProj.contentsOf(path.appendingPathComponent("project.pbxproj")) - project = try model.project() - - targets = model.objects(for: project.targets) - .filter { - // Cocoapods likes to insert resource bundles as native targets. On iOS resource bundles - // cannot contain executables, therefore we should ignore them - IR will never be generated for them. - $0.productType != "com.apple.product-type.bundle" - } - - packages = model.objects(of: .swiftPackageProductDependency, as: XCSwiftPackageProductDependency.self) - - // get all the direct dependencies - targets.forEach { determineDirectDependencies($0) } - - targets.forEach { target in - logger.debug("target: \(target.name). Dependencies: \(target.targetDependencies.map { $0.1.name })") - } - - packages.forEach { package in - logger.debug("package: \(package.productName)") - } - } - - /// Gets the native target for a given name - /// - Parameter name: the target name or product name to lookup - /// - Returns: the native target corresponding to the name provided - func target(for name: String) -> PBXNativeTarget? { - if let target = targets.filter({ $0.name == name }).first { - return target - } else if let target = targets.filter({ $0.productName == name }).first { - return target - } - - return nil - } - - /// Gets the package dependency object for a given name - /// - Parameter name: the product name to lookup - /// - Returns: the swift package product dependency object corresponding to the name provided - func package(for key: String) -> XCSwiftPackageProductDependency? { - packages.filter({ $0.productName == key }).first - } - - /// Determines the target & swift package dependencies for a target - /// - Parameter target: the target to get direct dependencies for - private func determineDirectDependencies(_ target: PBXNativeTarget) { - // Calculate the native target dependencies - target.dependencies - .compactMap { model.object(forKey: $0, as: PBXTargetDependency.self) } - .compactMap { dependency in - if let target = dependency.target { - return target - } - - guard - let targetProxy = dependency.targetProxy, - let proxy = model.object(forKey: targetProxy, as: PBXContainerItemProxy.self) - else { - return nil - } - - return proxy.remoteGlobalIDString - } - .compactMap { model.object(forKey: $0, as: PBXNativeTarget.self) } - .forEach { target.add(dependency: .native($0)) } - - // Calculate the swift package dependencies - target.packageProductDependencies - .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } - .forEach { target.add(dependency: .package($0)) } - - // Calculate dependencies from "Embed Frameworks" copy files build phase - let embeddedFrameworks = determineEmbeddedFrameworksDependencies(target, with: model) - - // Calculate the dependencies from "Link Binary with Library" build phase - let linkLibraries = determineBuildPhaseFrameworkDependencies(target, with: model) - - let buildFiles = embeddedFrameworks + linkLibraries - - // Now, we have two potential targets - file & package dependencies. - // File dependencies will likely have a reference in another Xcode Project. We might not have seen said project yet, so we need to offload discovery until after we've parsed all projects... - // Package dependencies will be a swift package - those we can handle easily :) - - // ONE: package dependencies - they are the easiest - buildFiles - .compactMap { $0.productRef } - .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } - .forEach { target.add(dependency: .package($0)) } - - // TWO: Resolve dependencies to... a thing that refers to something in the other project - let fileReferences = buildFiles - .compactMap { $0.fileRef } - .compactMap { model.object(forKey: $0, as: PBXFileReference.self) } - - fileReferences - .filter { $0.explicitFileType == "wrapper.framework" } - .compactMap { $0.path } // TODO: do we want to last path component the path here? Need to figure out matching... - .filter { !$0.contains("System/Library/Frameworks/")} // System frameworks will contain this path - .forEach { target.add(dependency: .externalProjectFramework($0)) } - } - - /// Determines transitive dependencies by looping through direct dependencies and finding the items they depend on - /// - Parameter target: the target to find transitive dependencies for - private func determineTransitiveDependencies(_ target: PBXNativeTarget) { - logger.debug("Target: \(target.name). Deps: \(target.targetDependencies.map { $0.0 })") - - var targetDependencies = target.targetDependencies.map { $0.1 } - var seen = Set() - var count = 50 // recursion guard - - while !targetDependencies.isEmpty, count != 0 { - let dependency = targetDependencies.removeFirst() - count -= 1 - - if seen.contains(dependency.name) { - continue - } - - seen.insert(dependency.name) - - switch dependency { - case .native(let nativeTarget): - logger.debug("Adding native dependency: \(dependency.name), deps: \(nativeTarget.targetDependencies.map { $0.0 })") - targetDependencies.append(contentsOf: nativeTarget.targetDependencies.map { $0.1 }) - nativeTarget.targetDependencies.forEach { target.add(dependency: $0.1) } - case .package: - // Packages don't have a transitive dependency field like native targets do, so we can't find dependency of a dependency from the project file - logger.debug("Adding package dependency: \(dependency.name)") - target.add(dependency: dependency) - case .externalProjectFramework: - // Can't move IR dependencies for prebuilt frameworks - continue - } - } - - logger.debug("--- FINAL ---") - logger.debug("Target: \(target.name), deps: \(target.targetDependencies.map { $0.0 })") - } - - /// Gets the 'path' (normally the name of the target's product) for a given target - /// - Parameters: - /// - target: the target to get the path for - /// - removeExtension: should the file extension be removed from the returned path - /// - Returns: the path, if one was found - func path(for target: PBXNativeTarget, removeExtension: Bool = false) -> String? { - guard let productReference = target.productReference else { - logger.debug("Failed to get product reference for target: \(target). Possibly a SPM Package description?") - return nil - } - - guard let reference = model.object(forKey: productReference, as: PBXFileReference.self) else { - logger.error("Failed to get object for target productReference: \(productReference)") - return nil - } - - var path = ((reference.path as NSString).lastPathComponent as String) - - if removeExtension, let index = path.firstIndex(of: ".") { - path = String(path[path.startIndex.. [PBXBuildFile] { - // Find the 'Link Binary with Libraries' build phase - let buildPhase = target.buildPhases - .compactMap { model.object(forKey: $0, as: PBXFrameworksBuildPhase.self) } - .first - - guard let buildPhase else { - logger.debug("No PBXFrameworkBuild phase for target: \(target) found, continuing.") - return [] - } - - return buildPhase - .files - .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } -} - -private func determineEmbeddedFrameworksDependencies(_ target: PBXNativeTarget, with model: PBXProj) -> [PBXBuildFile] { - // Find the "Embed Frameworks" build phase (copy files build phase) - let buildPhases = target - .buildPhases - .compactMap { model.object(forKey: $0, as: PBXCopyFilesBuildPhase.self) } - - return buildPhases - .flatMap { $0.files } - .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } - .filter { file in - guard let ref = file.fileRef else { return false } - guard let object = model.object(forKey: ref, as: PBXFileReference.self) else { return false } - guard object.explicitFileType == "wrapper.framework" else { return false } - - return true - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift b/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift deleted file mode 100644 index 608dfea..0000000 --- a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// XcodeWorkspace.swift -// -// -// Created by Thomas Hedderwick on 27/01/2023. -// - -import Foundation - -/// Represents an xcworkspace - which is a set of xcodeproj bundles -class XcodeWorkspace { - /// Path to the Workspace - let path: URL - /// Path to the various underlying xcodeproj bundles - private(set) var projectPaths: [URL] - /// List of projects this workspace references - let projects: [XcodeProject] - - /// A mapping of targets to the projects that define them - let targetsToProject: [String: XcodeProject] - - init(path: URL) throws { - self.path = path - - // Parse the `contents.xcworkspacedata` (XML) file and get the list of projects - let workspace = try WorkspaceParser.parse(path) - let paths = workspace - .fileReferences - .map { $0.path } - .filter { $0.hasSuffix("xcodeproj") } - - let baseFolder = path.deletingLastPathComponent() - projectPaths = paths - .map { baseFolder.appendingPathComponent($0, isDirectory: true) } - - projects = try projectPaths.map(XcodeProject.init(path:)) - - targetsToProject = projects.reduce(into: [String: XcodeProject](), { partialResult, project in - project.targets.forEach { (target) in - partialResult[target.name] = project - if let productName = target.productName, productName != target.name { - partialResult[productName] = project - } - } - - project.packages.forEach { (target) in - partialResult[target.productName] = project - } - }) - } - - /// All native targets in the workspace - var targets: [PBXNativeTarget] { - projects.flatMap { $0.targets } - } - - /// All packages in the workspace - var packages: [XCSwiftPackageProductDependency] { - projects.flatMap { $0.packages } - } -} diff --git a/PBXProjParser/Sources/projparser/projparser.swift b/PBXProjParser/Sources/projparser/projparser.swift deleted file mode 100644 index ff2cb85..0000000 --- a/PBXProjParser/Sources/projparser/projparser.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// projparser.swift -// -// -// Created by Thomas Hedderwick on 24/02/2023. -// - -import Foundation -import PBXProjParser - -@main -struct ProjParser { - static func main() throws { - guard CommandLine.arguments.count == 2 else { - print("USAGE: \(CommandLine.arguments.first!) [project path]") - return - } - - let projectPath = URL(fileURLWithPath: CommandLine.arguments[1]) - _ = try ProjectParser(path: projectPath, logLevel: .debug) - } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift b/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift deleted file mode 100644 index 3d9d527..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift +++ /dev/null @@ -1,47 +0,0 @@ -import XCTest -@testable import PBXProjParser - -final class PBXProjParserTests: XCTestCase { - func testDoubleTargetsHaveValidTargets() throws { - let path = URL(fileURLWithPath: - "Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace" - ) - - let parser = try ProjectParser(path: path, logLevel: .debug) - - let targets = parser.targets - let targetNames = targets.map { $0.name } - - let knownNames = ["DoubleTargetTest", "MyBundle", "Pods-DoubleTargetTest"] - - XCTAssert(targetNames.count == knownNames.count, "The list of targets doesn't match the size of allowed names") - - for name in targetNames { - XCTAssert(knownNames.contains(name), "\(name) wasn't in knownNames: \(knownNames)") - } - } - - func testDoubleTargetsHaveValidDependencies() throws { - let path = URL(fileURLWithPath: - "Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace" - ) - - let parser = try ProjectParser(path: path, logLevel: .debug) - let dependencies = parser.targets.reduce(into: [String: [String]]()) { partialResult, target in - partialResult[target.name] = parser.dependencies(for: target.name) - } - - let knownDependencies = [ - // TODO: This should probably handle Cocoapods doing their stupid embedding thing without listing it as a dependency... - "DoubleTargetTest": [], - // TODO: should we also disregard Cocoapods doing their stupid bundle as a native target even though it isn't - "MyBundle": ["MyBundle.bundle"], - "Pods-DoubleTargetTest": ["MyBundle.framework", "MyBundle.bundle"] - ] - - XCTAssert( - dependencies == knownDependencies, - "Dependencies: \(dependencies) doesn't equal known dependencies: \(knownDependencies)" - ) - } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj deleted file mode 100644 index ccd69a9..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj +++ /dev/null @@ -1,418 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - 35460152C27D425AD341441A /* Pods_DoubleTargetTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85C8A3B68F9E974DC6038BFF /* Pods_DoubleTargetTest.framework */; }; - CE25174229C9C2C100A2E14B /* DoubleTargetTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE25174129C9C2C100A2E14B /* DoubleTargetTestApp.swift */; }; - CE25174429C9C2C100A2E14B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE25174329C9C2C100A2E14B /* ContentView.swift */; }; - CE25174629C9C2C200A2E14B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE25174529C9C2C200A2E14B /* Assets.xcassets */; }; - CE25174929C9C2C200A2E14B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE25174829C9C2C200A2E14B /* Preview Assets.xcassets */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 815B8E932FA5793B81A510EC /* Pods-DoubleTargetTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DoubleTargetTest.release.xcconfig"; path = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig"; sourceTree = ""; }; - 85C8A3B68F9E974DC6038BFF /* Pods_DoubleTargetTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DoubleTargetTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BC9AAD5FA65122E6FCDEC90E /* Pods-DoubleTargetTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DoubleTargetTest.debug.xcconfig"; path = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig"; sourceTree = ""; }; - CE25173E29C9C2C100A2E14B /* DoubleTargetTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DoubleTargetTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; - CE25174129C9C2C100A2E14B /* DoubleTargetTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleTargetTestApp.swift; sourceTree = ""; }; - CE25174329C9C2C100A2E14B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - CE25174529C9C2C200A2E14B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - CE25174829C9C2C200A2E14B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - CE25173B29C9C2C100A2E14B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 35460152C27D425AD341441A /* Pods_DoubleTargetTest.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 01D7ED154039FF16EE28AD13 /* Pods */ = { - isa = PBXGroup; - children = ( - BC9AAD5FA65122E6FCDEC90E /* Pods-DoubleTargetTest.debug.xcconfig */, - 815B8E932FA5793B81A510EC /* Pods-DoubleTargetTest.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 04DE6891E5415C30C89A0396 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 85C8A3B68F9E974DC6038BFF /* Pods_DoubleTargetTest.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - CE25173529C9C2C100A2E14B = { - isa = PBXGroup; - children = ( - CE25174029C9C2C100A2E14B /* DoubleTargetTest */, - CE25173F29C9C2C100A2E14B /* Products */, - 01D7ED154039FF16EE28AD13 /* Pods */, - 04DE6891E5415C30C89A0396 /* Frameworks */, - ); - sourceTree = ""; - }; - CE25173F29C9C2C100A2E14B /* Products */ = { - isa = PBXGroup; - children = ( - CE25173E29C9C2C100A2E14B /* DoubleTargetTest.app */, - ); - name = Products; - sourceTree = ""; - }; - CE25174029C9C2C100A2E14B /* DoubleTargetTest */ = { - isa = PBXGroup; - children = ( - CE25174129C9C2C100A2E14B /* DoubleTargetTestApp.swift */, - CE25174329C9C2C100A2E14B /* ContentView.swift */, - CE25174529C9C2C200A2E14B /* Assets.xcassets */, - CE25174729C9C2C200A2E14B /* Preview Content */, - ); - path = DoubleTargetTest; - sourceTree = ""; - }; - CE25174729C9C2C200A2E14B /* Preview Content */ = { - isa = PBXGroup; - children = ( - CE25174829C9C2C200A2E14B /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - CE25173D29C9C2C100A2E14B /* DoubleTargetTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = CE25174C29C9C2C200A2E14B /* Build configuration list for PBXNativeTarget "DoubleTargetTest" */; - buildPhases = ( - 60C22A37CD715212D57E629D /* [CP] Check Pods Manifest.lock */, - CE25173A29C9C2C100A2E14B /* Sources */, - CE25173B29C9C2C100A2E14B /* Frameworks */, - CE25173C29C9C2C100A2E14B /* Resources */, - 16B418F3FB824614A4DADAF1 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DoubleTargetTest; - productName = DoubleTargetTest; - productReference = CE25173E29C9C2C100A2E14B /* DoubleTargetTest.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - CE25173629C9C2C100A2E14B /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1420; - LastUpgradeCheck = 1420; - TargetAttributes = { - CE25173D29C9C2C100A2E14B = { - CreatedOnToolsVersion = 14.2; - }; - }; - }; - buildConfigurationList = CE25173929C9C2C100A2E14B /* Build configuration list for PBXProject "DoubleTargetTest" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = CE25173529C9C2C100A2E14B; - productRefGroup = CE25173F29C9C2C100A2E14B /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - CE25173D29C9C2C100A2E14B /* DoubleTargetTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - CE25173C29C9C2C100A2E14B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CE25174929C9C2C200A2E14B /* Preview Assets.xcassets in Resources */, - CE25174629C9C2C200A2E14B /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 16B418F3FB824614A4DADAF1 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 60C22A37CD715212D57E629D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-DoubleTargetTest-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - CE25173A29C9C2C100A2E14B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CE25174429C9C2C100A2E14B /* ContentView.swift in Sources */, - CE25174229C9C2C100A2E14B /* DoubleTargetTestApp.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - CE25174A29C9C2C200A2E14B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - CE25174B29C9C2C200A2E14B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - CE25174D29C9C2C200A2E14B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = BC9AAD5FA65122E6FCDEC90E /* Pods-DoubleTargetTest.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"DoubleTargetTest/Preview Content\""; - DEVELOPMENT_TEAM = M2QJQZX6EU; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.veracode.DoubleTargetTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - CE25174E29C9C2C200A2E14B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 815B8E932FA5793B81A510EC /* Pods-DoubleTargetTest.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"DoubleTargetTest/Preview Content\""; - DEVELOPMENT_TEAM = M2QJQZX6EU; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.veracode.DoubleTargetTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - CE25173929C9C2C100A2E14B /* Build configuration list for PBXProject "DoubleTargetTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CE25174A29C9C2C200A2E14B /* Debug */, - CE25174B29C9C2C200A2E14B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - CE25174C29C9C2C200A2E14B /* Build configuration list for PBXNativeTarget "DoubleTargetTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CE25174D29C9C2C200A2E14B /* Debug */, - CE25174E29C9C2C200A2E14B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = CE25173629C9C2C100A2E14B /* Project object */; -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme deleted file mode 100644 index 9c2834b..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index a81118b..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json deleted file mode 100644 index 0e86514..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "MyBundle", - "version": "0.1.0", - "summary": "A short description of MyBundle.", - "description": "TODO: Add long description of the pod here.", - "homepage": "https://github.com/thedderwick/MyBundle", - "license": { - "type": "MIT", - "file": "LICENSE" - }, - "authors": { - "thedderwick": "thedderwick@veracode.com" - }, - "source": { - "git": "https://github.com/thedderwick/MyBundle.git", - "tag": "0.1.0" - }, - "platforms": { - "ios": "10.0" - }, - "source_files": "MyBundle/Classes/**/*", - "resource_bundles": { - "MyBundle": [ - "MyBundle/Assets/*.png" - ] - } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock deleted file mode 100644 index 71ccc8f..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - MyBundle (0.1.0) - -DEPENDENCIES: - - MyBundle (from `/Users/thedderwick/dev/ios_tests/double_target_test/DoubleTargetTest/MyBundle`) - -EXTERNAL SOURCES: - MyBundle: - :path: "/Users/thedderwick/dev/ios_tests/double_target_test/DoubleTargetTest/MyBundle" - -SPEC CHECKSUMS: - MyBundle: e1f22e3445c35cc16854bc29f5159058eb370c1a - -PODFILE CHECKSUM: c56d07086d026876301d9d46d35896e3a7be8a1b - -COCOAPODS: 1.11.3 diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index 02766b2..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,734 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - 0505D039F580D8C1C4DA3135641F7BD3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; - 33DD7B8459A3A39525345E644BEEF41A /* Pods-DoubleTargetTest-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F65DA5A28E59EDF453B5AAA1A8D8949E /* Pods-DoubleTargetTest-dummy.m */; }; - 44651C95EE6FC2391DD4F3A167F41A44 /* MyBundle-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B24F4C49C16832C3D1AF6AED1995658 /* MyBundle-dummy.m */; }; - 674BA68B5EAA5F9008004BF3F5C02EC5 /* MyBundle-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E212D006779EE067440F2CB7785A4B /* MyBundle-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A67798D7C4577858327517567612E2CD /* Pods-DoubleTargetTest-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A0B870A89DF602D8AAEA39600D8FDFDB /* Pods-DoubleTargetTest-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E25F7D067CF0E1D9B934CEECF2384CB4 /* ReplaceMe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F486712B4AA55E662E1657576D8133B /* ReplaceMe.swift */; }; - EE3C3BE914FAF168E38D719BEC2768D8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; - FC2645FD54DC4BE00237174806868D1D /* MyBundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 34E01361C037C7F47C9C4A6FD583961C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 5E26517DF39E585BCF742D88F142F59B; - remoteInfo = "MyBundle-MyBundle"; - }; - A6F9652FCB6D63EEE2F28D6506461B50 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D44D2D0674B19633F70576544F03D1F6; - remoteInfo = MyBundle; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 05D2F3E34E29E2AEEABAF781437B56B1 /* MyBundle.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = MyBundle.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 224DC3273E1C05AE11AF83EA85B8FD36 /* ResourceBundle-MyBundle-MyBundle-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-MyBundle-MyBundle-Info.plist"; sourceTree = ""; }; - 2B24F4C49C16832C3D1AF6AED1995658 /* MyBundle-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MyBundle-dummy.m"; sourceTree = ""; }; - 57786B8140E731F1F6FF148B154D46B9 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 5FBA60238FECB2540F038F4AB9345480 /* Pods-DoubleTargetTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DoubleTargetTest.release.xcconfig"; sourceTree = ""; }; - 6A70FA6E6FCA926AAA416C9CB244CEDE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 736A5947EB69EC1CF1800C6EE8A9A4D8 /* MyBundle-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MyBundle-Info.plist"; sourceTree = ""; }; - 7F486712B4AA55E662E1657576D8133B /* ReplaceMe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReplaceMe.swift; path = MyBundle/Classes/ReplaceMe.swift; sourceTree = ""; }; - 80BF02E1C33CF6CD2922D48629338A1B /* MyBundle-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MyBundle-prefix.pch"; sourceTree = ""; }; - 8138D938080D00037C41432BBDEF69FB /* Pods_DoubleTargetTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DoubleTargetTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - A0B870A89DF602D8AAEA39600D8FDFDB /* Pods-DoubleTargetTest-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-DoubleTargetTest-umbrella.h"; sourceTree = ""; }; - C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MyBundle.debug.xcconfig; sourceTree = ""; }; - C72825197160F688488EC69B39E06FDC /* MyBundle.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MyBundle.modulemap; sourceTree = ""; }; - CA43AB7A1931E11A8E1FA62FC3D322F8 /* Pods-DoubleTargetTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DoubleTargetTest.debug.xcconfig"; sourceTree = ""; }; - D65C2A1ACEDD3BF60470333B525BD1DC /* Pods-DoubleTargetTest-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-DoubleTargetTest-acknowledgements.markdown"; sourceTree = ""; }; - D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - D989667258FDEA181A33B4DCADEBFEF1 /* Pods-DoubleTargetTest-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-DoubleTargetTest-acknowledgements.plist"; sourceTree = ""; }; - DC48A4403E54985F3959D0D6E0045204 /* Pods-DoubleTargetTest.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-DoubleTargetTest.modulemap"; sourceTree = ""; }; - E511354FF29F23818915A2B3632C07CC /* Pods-DoubleTargetTest-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-DoubleTargetTest-Info.plist"; sourceTree = ""; }; - E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MyBundle.release.xcconfig; sourceTree = ""; }; - ED7431F391879458DE73243628EBC744 /* Pods-DoubleTargetTest-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-DoubleTargetTest-frameworks.sh"; sourceTree = ""; }; - F3E212D006779EE067440F2CB7785A4B /* MyBundle-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MyBundle-umbrella.h"; sourceTree = ""; }; - F65DA5A28E59EDF453B5AAA1A8D8949E /* Pods-DoubleTargetTest-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-DoubleTargetTest-dummy.m"; sourceTree = ""; }; - FF65CA6FF03DFFFE7F7FFCF321A2EF48 /* MyBundle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MyBundle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 2A12A09328EBBF7ECA09B932C9A7C0E8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C5E0F87B0CA6C796BA0B765746BF2AFB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0505D039F580D8C1C4DA3135641F7BD3 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EABB4E9BB1FC17E80981BA9096434936 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - EE3C3BE914FAF168E38D719BEC2768D8 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 07FF93065E54DBA0271C6409D3DB7967 /* Targets Support Files */ = { - isa = PBXGroup; - children = ( - 1D5473B0E8035066E3E94EEDE7E5A8AE /* Pods-DoubleTargetTest */, - ); - name = "Targets Support Files"; - sourceTree = ""; - }; - 1D5473B0E8035066E3E94EEDE7E5A8AE /* Pods-DoubleTargetTest */ = { - isa = PBXGroup; - children = ( - DC48A4403E54985F3959D0D6E0045204 /* Pods-DoubleTargetTest.modulemap */, - D65C2A1ACEDD3BF60470333B525BD1DC /* Pods-DoubleTargetTest-acknowledgements.markdown */, - D989667258FDEA181A33B4DCADEBFEF1 /* Pods-DoubleTargetTest-acknowledgements.plist */, - F65DA5A28E59EDF453B5AAA1A8D8949E /* Pods-DoubleTargetTest-dummy.m */, - ED7431F391879458DE73243628EBC744 /* Pods-DoubleTargetTest-frameworks.sh */, - E511354FF29F23818915A2B3632C07CC /* Pods-DoubleTargetTest-Info.plist */, - A0B870A89DF602D8AAEA39600D8FDFDB /* Pods-DoubleTargetTest-umbrella.h */, - CA43AB7A1931E11A8E1FA62FC3D322F8 /* Pods-DoubleTargetTest.debug.xcconfig */, - 5FBA60238FECB2540F038F4AB9345480 /* Pods-DoubleTargetTest.release.xcconfig */, - ); - name = "Pods-DoubleTargetTest"; - path = "Target Support Files/Pods-DoubleTargetTest"; - sourceTree = ""; - }; - 20FDC45F1DD3EF707467BF569B570407 /* Pod */ = { - isa = PBXGroup; - children = ( - 6A70FA6E6FCA926AAA416C9CB244CEDE /* LICENSE */, - 05D2F3E34E29E2AEEABAF781437B56B1 /* MyBundle.podspec */, - 57786B8140E731F1F6FF148B154D46B9 /* README.md */, - ); - name = Pod; - sourceTree = ""; - }; - 2DA60530B2F5B4007868F26C4A31DCB8 /* Support Files */ = { - isa = PBXGroup; - children = ( - C72825197160F688488EC69B39E06FDC /* MyBundle.modulemap */, - 2B24F4C49C16832C3D1AF6AED1995658 /* MyBundle-dummy.m */, - 736A5947EB69EC1CF1800C6EE8A9A4D8 /* MyBundle-Info.plist */, - 80BF02E1C33CF6CD2922D48629338A1B /* MyBundle-prefix.pch */, - F3E212D006779EE067440F2CB7785A4B /* MyBundle-umbrella.h */, - C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */, - E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */, - 224DC3273E1C05AE11AF83EA85B8FD36 /* ResourceBundle-MyBundle-MyBundle-Info.plist */, - ); - name = "Support Files"; - path = "../Pods/Target Support Files/MyBundle"; - sourceTree = ""; - }; - 530F2AFE538911ABCDFFFAD240B6C43F /* Development Pods */ = { - isa = PBXGroup; - children = ( - 779A4C1FC7F0153F4E499ED3B1262F9D /* MyBundle */, - ); - name = "Development Pods"; - sourceTree = ""; - }; - 578452D2E740E91742655AC8F1636D1F /* iOS */ = { - isa = PBXGroup; - children = ( - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, - ); - name = iOS; - sourceTree = ""; - }; - 779A4C1FC7F0153F4E499ED3B1262F9D /* MyBundle */ = { - isa = PBXGroup; - children = ( - 7F486712B4AA55E662E1657576D8133B /* ReplaceMe.swift */, - 20FDC45F1DD3EF707467BF569B570407 /* Pod */, - 2DA60530B2F5B4007868F26C4A31DCB8 /* Support Files */, - ); - name = MyBundle; - path = /Users/thedderwick/dev/ios_tests/double_target_test/DoubleTargetTest/MyBundle; - sourceTree = ""; - }; - 8820BDE64C1284039B9392B149BBF3F1 /* Products */ = { - isa = PBXGroup; - children = ( - FF65CA6FF03DFFFE7F7FFCF321A2EF48 /* MyBundle.framework */, - D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */, - 8138D938080D00037C41432BBDEF69FB /* Pods_DoubleTargetTest.framework */, - ); - name = Products; - sourceTree = ""; - }; - CF1408CF629C7361332E53B88F7BD30C = { - isa = PBXGroup; - children = ( - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, - 530F2AFE538911ABCDFFFAD240B6C43F /* Development Pods */, - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, - 8820BDE64C1284039B9392B149BBF3F1 /* Products */, - 07FF93065E54DBA0271C6409D3DB7967 /* Targets Support Files */, - ); - sourceTree = ""; - }; - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 578452D2E740E91742655AC8F1636D1F /* iOS */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - DFDB214017CAB64FA5759DEEE88C6482 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 674BA68B5EAA5F9008004BF3F5C02EC5 /* MyBundle-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EF90799616133DD7D69BDFBBA5CEA166 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A67798D7C4577858327517567612E2CD /* Pods-DoubleTargetTest-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 2C5F8EFB5430A4211A0B84AAF4F13F9C /* Pods-DoubleTargetTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4BA9A3509482DBEAB6E2815BA49CFF38 /* Build configuration list for PBXNativeTarget "Pods-DoubleTargetTest" */; - buildPhases = ( - EF90799616133DD7D69BDFBBA5CEA166 /* Headers */, - B3CB7EA40906C61E205292A9A351D3CB /* Sources */, - EABB4E9BB1FC17E80981BA9096434936 /* Frameworks */, - 87E5F63D5CCB13C8680E20FD9006DE94 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 72A1629C1F4E73D2E84B18F70BC27E45 /* PBXTargetDependency */, - ); - name = "Pods-DoubleTargetTest"; - productName = Pods_DoubleTargetTest; - productReference = 8138D938080D00037C41432BBDEF69FB /* Pods_DoubleTargetTest.framework */; - productType = "com.apple.product-type.framework"; - }; - 5E26517DF39E585BCF742D88F142F59B /* MyBundle-MyBundle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 653128552AEA47E651B38399510A6921 /* Build configuration list for PBXNativeTarget "MyBundle-MyBundle" */; - buildPhases = ( - 8E15891098856B42AEF03BBCF6C6CF8B /* Sources */, - 2A12A09328EBBF7ECA09B932C9A7C0E8 /* Frameworks */, - 51CCDFF654B64A04999C81E4263E6D99 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "MyBundle-MyBundle"; - productName = MyBundle; - productReference = D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */; - productType = "com.apple.product-type.bundle"; - }; - D44D2D0674B19633F70576544F03D1F6 /* MyBundle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2F9F1F2725B9CBC6E4E1D2BC535F21BB /* Build configuration list for PBXNativeTarget "MyBundle" */; - buildPhases = ( - DFDB214017CAB64FA5759DEEE88C6482 /* Headers */, - C44C0BBB4071B406F57E947248DC4312 /* Sources */, - C5E0F87B0CA6C796BA0B765746BF2AFB /* Frameworks */, - 4CB90525191925AD5A3E2E39B45FECCD /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - C624302602CF69389AEA79A23EA0372D /* PBXTargetDependency */, - ); - name = MyBundle; - productName = MyBundle; - productReference = FF65CA6FF03DFFFE7F7FFCF321A2EF48 /* MyBundle.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BFDFE7DC352907FC980B868725387E98 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1300; - }; - buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - Base, - en, - ); - mainGroup = CF1408CF629C7361332E53B88F7BD30C; - productRefGroup = 8820BDE64C1284039B9392B149BBF3F1 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D44D2D0674B19633F70576544F03D1F6 /* MyBundle */, - 5E26517DF39E585BCF742D88F142F59B /* MyBundle-MyBundle */, - 2C5F8EFB5430A4211A0B84AAF4F13F9C /* Pods-DoubleTargetTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 4CB90525191925AD5A3E2E39B45FECCD /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FC2645FD54DC4BE00237174806868D1D /* MyBundle.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 51CCDFF654B64A04999C81E4263E6D99 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 87E5F63D5CCB13C8680E20FD9006DE94 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 8E15891098856B42AEF03BBCF6C6CF8B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3CB7EA40906C61E205292A9A351D3CB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33DD7B8459A3A39525345E644BEEF41A /* Pods-DoubleTargetTest-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C44C0BBB4071B406F57E947248DC4312 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 44651C95EE6FC2391DD4F3A167F41A44 /* MyBundle-dummy.m in Sources */, - E25F7D067CF0E1D9B934CEECF2384CB4 /* ReplaceMe.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 72A1629C1F4E73D2E84B18F70BC27E45 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MyBundle; - target = D44D2D0674B19633F70576544F03D1F6 /* MyBundle */; - targetProxy = A6F9652FCB6D63EEE2F28D6506461B50 /* PBXContainerItemProxy */; - }; - C624302602CF69389AEA79A23EA0372D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "MyBundle-MyBundle"; - target = 5E26517DF39E585BCF742D88F142F59B /* MyBundle-MyBundle */; - targetProxy = 34E01361C037C7F47C9C4A6FD583961C /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 20B1B8D2F3D27105B0D81D14421C20CA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MyBundle"; - DEVELOPMENT_TEAM = ""; - IBSC_MODULE = MyBundle; - INFOPLIST_FILE = "Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - PRODUCT_NAME = MyBundle; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - 446BA1B9A1F1F2F95D04E5F82EB1B0F4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_DEBUG=1", - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Debug; - }; - 5D47E2EFAC6FAC81A782D9BFD8736F53 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_RELEASE=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Release; - }; - 962BDC6723AD0C2121208533EE1026E7 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CA43AB7A1931E11A8E1FA62FC3D322F8 /* Pods-DoubleTargetTest.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 9C1B3E43E825EB8D4540CCEEDD20FEFC /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5FBA60238FECB2540F038F4AB9345480 /* Pods-DoubleTargetTest.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - AF63CE00DDA4FBC37E5031858C625514 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MyBundle"; - DEVELOPMENT_TEAM = ""; - IBSC_MODULE = MyBundle; - INFOPLIST_FILE = "Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - PRODUCT_NAME = MyBundle; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - B8B137A9E60B7D7D73FFD3D06FC029D7 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MyBundle/MyBundle-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MyBundle/MyBundle-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MyBundle/MyBundle.modulemap"; - PRODUCT_MODULE_NAME = MyBundle; - PRODUCT_NAME = MyBundle; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - E1E03382DD524E8939F7CC62A62FDE3A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MyBundle/MyBundle-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MyBundle/MyBundle-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MyBundle/MyBundle.modulemap"; - PRODUCT_MODULE_NAME = MyBundle; - PRODUCT_NAME = MyBundle; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 2F9F1F2725B9CBC6E4E1D2BC535F21BB /* Build configuration list for PBXNativeTarget "MyBundle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - E1E03382DD524E8939F7CC62A62FDE3A /* Debug */, - B8B137A9E60B7D7D73FFD3D06FC029D7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 446BA1B9A1F1F2F95D04E5F82EB1B0F4 /* Debug */, - 5D47E2EFAC6FAC81A782D9BFD8736F53 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4BA9A3509482DBEAB6E2815BA49CFF38 /* Build configuration list for PBXNativeTarget "Pods-DoubleTargetTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 962BDC6723AD0C2121208533EE1026E7 /* Debug */, - 9C1B3E43E825EB8D4540CCEEDD20FEFC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 653128552AEA47E651B38399510A6921 /* Build configuration list for PBXNativeTarget "MyBundle-MyBundle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 20B1B8D2F3D27105B0D81D14421C20CA /* Debug */, - AF63CE00DDA4FBC37E5031858C625514 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist deleted file mode 100644 index 161a9d3..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 0.1.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m deleted file mode 100644 index e24399a..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MyBundle : NSObject -@end -@implementation PodsDummy_MyBundle -@end diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch deleted file mode 100644 index beb2a24..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h deleted file mode 100644 index f846ef6..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double MyBundleVersionNumber; -FOUNDATION_EXPORT const unsigned char MyBundleVersionString[]; - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig deleted file mode 100644 index 8a95b1f..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MyBundle -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/../MyBundle -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap deleted file mode 100644 index 23d5f54..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module MyBundle { - umbrella header "MyBundle-umbrella.h" - - export * - module * { export * } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig deleted file mode 100644 index 8a95b1f..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MyBundle -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/../MyBundle -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist deleted file mode 100644 index 99a40ff..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - BNDL - CFBundleShortVersionString - 0.1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - NSPrincipalClass - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist deleted file mode 100644 index 2243fe6..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown deleted file mode 100644 index 8bb471a..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown +++ /dev/null @@ -1,26 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## MyBundle - -Copyright (c) 2023 thedderwick - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Generated by CocoaPods - https://cocoapods.org diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist deleted file mode 100644 index 711f6ee..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist +++ /dev/null @@ -1,58 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2023 thedderwick <thedderwick@veracode.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - License - MIT - Title - MyBundle - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m deleted file mode 100644 index beca502..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_DoubleTargetTest : NSObject -@end -@implementation PodsDummy_Pods_DoubleTargetTest -@end diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index cd39096..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh -${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index 07517cb..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index cd39096..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh -${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index 07517cb..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh deleted file mode 100755 index 8fe186b..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h deleted file mode 100644 index 570ba23..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_DoubleTargetTestVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_DoubleTargetTestVersionString[]; - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig deleted file mode 100644 index c9bfcc2..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle/MyBundle.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "MyBundle" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap deleted file mode 100644 index 0fe645c..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_DoubleTargetTest { - umbrella header "Pods-DoubleTargetTest-umbrella.h" - - export * - module * { export * } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig deleted file mode 100644 index c9bfcc2..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle/MyBundle.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "MyBundle" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Package.swift b/Package.swift index 612c472..ac47272 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,6 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(path: "PBXProjParser"), .package(path: "PIF") ], targets: [ @@ -25,7 +24,6 @@ let package = Package( dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), - .product(name: "PBXProjParser", package: "PBXProjParser"), .product(name: "PIFSupport", package: "PIF") ], path: "Sources/GenIR" diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index ac8edf4..e5586d5 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -7,7 +7,6 @@ import Foundation import Logging -import PBXProjParser /// A model of the contents of an output file map json typealias OutputFileMap = [String: [String: String]] diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 465ce17..a35af57 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -1,7 +1,6 @@ import Foundation import ArgumentParser import Logging -import PBXProjParser import PIFSupport /// Global logger object diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 4b3877a..bddbef6 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -6,7 +6,6 @@ // import Foundation -import PBXProjParser private typealias SizeAndCreation = (Int, Date) diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index f33723b..1e72245 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -6,7 +6,6 @@ // import Foundation -import PBXProjParser import PIFSupport class Target { diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 31edaf8..2c72eda 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class DependencyGraphTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index ba4b784..acf034d 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class OutputPostprocessorFileMoverTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index eac8475..eba625a 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class UmbrellaTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 0b2530f..a7844fb 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class WorkspaceTests: XCTestCase { let testPath: URL = { From c7ea9056250c1f9f438fce23b804ce5ed2de577a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 14 May 2024 10:24:18 +0200 Subject: [PATCH 38/61] Initial support for SPM product to target resolution --- PIF/Sources/PIFSupport/PIF.swift | 11 ++----- .../Dependency Graph/DependencyGraph.swift | 2 +- Sources/GenIR/Dependency Graph/Node.swift | 1 - Sources/GenIR/GenIR.swift | 6 +++- Sources/GenIR/OutputPostprocessor.swift | 3 +- Sources/GenIR/PIFCache.swift | 33 +++++++++++++++---- Sources/GenIR/Target.swift | 8 +++-- 7 files changed, 44 insertions(+), 20 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index 9674784..db57d0b 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -308,9 +308,7 @@ public enum PIF { default: // TODO: use logger here print("unknown reference type: \(child.type ?? "")") - // TODO: support variantGroup etc return nil - // throw Error.decodingError("unknown reference type \(child.type ?? "")") } } } else { @@ -515,12 +513,9 @@ public enum PIF { return nil // TODO: we should probably handle these: /* - case copyFiles = "com.apple.buildphase.copy-files" - case frameworks = "com.apple.buildphase.frameworks" - case headers = "com.apple.buildphase.headers" - case resources = "com.apple.buildphase.resources" - case shellScript = "com.apple.buildphase.shell-script" - case sources = "com.apple.buildphase.sources"*/ + case copyFiles = "com.apple.buildphase.copy-files" + case shellScript = "com.apple.buildphase.shell-script" + case sources = "com.apple.buildphase.sources"*/ // throw Error.decodingError("unknown build phase \(type)") } } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 7db2f3d..7552665 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -75,7 +75,7 @@ class DependencyGraph { visited.insert(node) logger.debug("visited: \(visited)") - for edge in node.edges { + for edge in node.edges where edge.relationship == .dependency { logger.debug("edge to: \(edge.to)") if visited.insert(edge.to).inserted { logger.debug("inserted, recursing") diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index 659dad4..a2e9406 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -29,7 +29,6 @@ class Node { /// Adds an edge to this node /// - Parameter edge: the edge to add func add(edge: Edge) { - // TODO: slow - change. if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { edges.append(edge) } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index a35af57..0aab100 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -143,7 +143,11 @@ struct IREmitterCommand: ParsableCommand { if dumpDependencyGraph { do { - try graph.toDot(output.appendingPathComponent("graph.dot").filePath) + try graph.toDot(output + .deletingLastPathComponent() + .appendingPathComponent("graph.dot") + .filePath + ) } catch { logger.error("toDot error: \(error)") } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index bddbef6..acba0ac 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -78,9 +78,10 @@ class OutputPostprocessor { target: Target, at path: URL ) throws -> Set { + // TODO: we need better handling of swift package products and targets in the dependency graph or we fail to move dependencies here let chain = graph.chain(for: target) - logger.debug("Chain for target: \(target.productName):\n") + logger.debug("Chain for target: \(target.productName):\n\(chain.map { "\($0)\n" })") chain.forEach { logger.debug("\($0)") } // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 9de608d..d8e22b3 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -166,12 +166,32 @@ struct PIFDependencyProvider: DependencyProviding { } } + private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID { + let productToken = "PACKAGE-PRODUCT:" + let targetToken = "PACKAGE-TARGET:" + guard packageGUID.starts(with: productToken), let product = guidToTargets[packageGUID] else { return packageGUID } + + let productName = String(packageGUID.dropFirst(productToken.count)) + + // TODO: should this also use the framework build phase to determine a dependency? + let packageTargetDependencies = product + .baseTarget + .dependencies + .filter { $0.targetGUID.starts(with: targetToken) } + .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } + + precondition(packageTargetDependencies.count == 1, "Expecting one matching package target - found \(packageTargetDependencies.count): \(packageTargetDependencies). Returning first match") + + return packageTargetDependencies.first?.targetGUID ?? packageGUID + } + func dependencies(for value: Target) -> [Target] { // Direct dependencies - let dependencyTargets = value + let dependencyTargetGUIDs = value .baseTarget .dependencies - .compactMap { guidToTargets[$0.targetGUID] } + .map { $0.targetGUID } + .map { resolveSwiftPackage($0) } // Framework build phase dependencies let frameworkBuildPhases = value @@ -179,7 +199,7 @@ struct PIFDependencyProvider: DependencyProviding { .buildPhases .compactMap { $0 as? PIF.FrameworksBuildPhase } - let frameworks = frameworkBuildPhases + let frameworkGUIDs = frameworkBuildPhases .flatMap { $0.buildFiles } .compactMap { switch $0.reference { @@ -187,9 +207,10 @@ struct PIFDependencyProvider: DependencyProviding { case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency } } - .compactMap { cache.frameworks[$0] } - .compactMap { guidToTargets[$0.guid] } + .compactMap { cache.frameworks[$0]?.guid } + + let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } - return dependencyTargets + frameworks + return dependencyTargets } } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index 1e72245..d32fcf6 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -11,9 +11,13 @@ import PIFSupport class Target { var name: String { baseTarget.name } var productName: String { - (baseTarget as? PIF.Target)?.productName ?? baseTarget.name + if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { + return target.productName + } + + return baseTarget.name } - // TODO: we need to handle SPM's insane naming scheme for products here ^ + // TODO: we need to handle SPM's insane naming scheme for products here ^ including the potential of a dynamic variant let baseTarget: PIF.BaseTarget let commands: [CompilerCommand] From 9d9a335da6a2fb56a5620411f190a29b7380323d Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 14 May 2024 15:28:31 +0200 Subject: [PATCH 39/61] Clean up logging in PIFSupport, use product name for target node values --- PIF/Sources/PIFSupport/PIF.swift | 6 ++-- PIF/Sources/PIFSupport/PIFSupport.swift | 6 +++- .../Dependency Graph/DependencyGraph.swift | 34 ++++++++----------- Sources/GenIR/PIFCache.swift | 2 +- Sources/GenIR/Target.swift | 2 +- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index db57d0b..48d8145 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -306,8 +306,7 @@ public enum PIF { case FileReference.type: return try childrenContainer.decode(FileReference.self) default: - // TODO: use logger here - print("unknown reference type: \(child.type ?? "")") + logger.debug("unknown reference type: \(child.type ?? "")") return nil } } @@ -508,8 +507,7 @@ public enum PIF { case ResourcesBuildPhase.type: return try container.decode(ResourcesBuildPhase.self) default: - // TODO: replace with logger - print("unknown build phase: \(type)") + logger.debug("unknown build phase: \(type)") return nil // TODO: we should probably handle these: /* diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index 2b8665a..46b1857 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -1,4 +1,7 @@ import Foundation +import Logging + +var logger: Logger! public class PIFParser { private let cachePath: URL @@ -8,7 +11,8 @@ public class PIFParser { case workspaceNotFound } - public init(cachePath: URL) throws { + public init(cachePath: URL, logger log: Logger) throws { + logger = log self.cachePath = cachePath let data = try Data(contentsOf: try Self.workspacePath(in: cachePath)) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 7552665..e1cd9e6 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -44,24 +44,6 @@ class DependencyGraph { return depthFirstSearch(startingAt: node) } - func toDot(_ path: String) throws { - var contents = "digraph DependencyGraph {\n" - - for node in nodes.values { - for edge in node.edges.filter({ $0.relationship == .dependency }) { - func dotSanitized(for name: String) -> String { - name - .replacingOccurrences(of: "-", with: "_") - .replacingOccurrences(of: ".", with: "_") - } - contents.append("\(dotSanitized(for: node.valueName)) -> \(dotSanitized(for: edge.to.valueName))\n") - } - } - - contents.append("}") - try contents.write(toFile: path, atomically: true, encoding: .utf8) - } - /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach @@ -76,9 +58,8 @@ class DependencyGraph { logger.debug("visited: \(visited)") for edge in node.edges where edge.relationship == .dependency { - logger.debug("edge to: \(edge.to)") if visited.insert(edge.to).inserted { - logger.debug("inserted, recursing") + logger.debug("edge to: \(edge.to)") depthFirst(node: edge.to) } else { logger.debug("edge already in visited: \(visited)") @@ -92,6 +73,19 @@ class DependencyGraph { depthFirst(node: node) return chain } + + func toDot(_ path: String) throws { + var contents = "digraph DependencyGraph {\n" + + for node in nodes.values { + for edge in node.edges.filter({ $0.relationship == .dependency }) { + contents.append("\"\(node.valueName)\" -> \"\(edge.to.valueName)\"\n") + } + } + + contents.append("}") + try contents.write(toFile: path, atomically: true, encoding: .utf8) + } } extension DependencyGraph: CustomStringConvertible { diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index d8e22b3..6737d8a 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -16,7 +16,7 @@ class PIFCache { self.pifCachePath = try Self.pifCachePath(in: buildCache) do { - let cache = try PIFParser(cachePath: pifCachePath) + let cache = try PIFParser(cachePath: pifCachePath, logger: logger) workspace = cache.workspace } catch { throw Error.pifError(error) diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index d32fcf6..0f26a8a 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -39,7 +39,7 @@ extension Target { extension Target: NodeValue { var value: Self { self } - var valueName: String { name } + var valueName: String { productName } } extension Target: Hashable { From 9b86fedfa1dc9450f9dee8dfec46c8b1a25122b2 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 24 May 2024 13:55:21 +0200 Subject: [PATCH 40/61] Allow multiple workspaces. Check modification time to grab the most recent one. --- .github/workflows/build.yml | 4 +- PIF/Sources/PIFSupport/PIFSupport.swift | 29 +- Sources/GenIR/PIFCache.swift | 59 ++- .../SPMTest/MyBinaryDependency/.gitignore | 8 + .../SPMTest/MyBinaryDependency/Package.swift | 25 ++ .../Sources/_Stub/MyBinaryDependency.swift | 2 + .../MyBinaryDependencyTests.swift | 12 + .../SPMTest/MyCommonLibrary/.gitignore | 8 + .../SPMTest/MyCommonLibrary/Package.swift | 23 ++ .../MyCommonLibrary/MyCommonLibrary.swift | 6 + .../MyCommonLibraryTests.swift | 12 + .../PIFCaches/SPMTest/MyLibrary/.gitignore | 8 + .../PIFCaches/SPMTest/MyLibrary/Package.swift | 33 ++ .../Sources/MyLibrary/MyLibrary.swift | 10 + .../Tests/MyLibraryTests/MyLibraryTests.swift | 12 + .../SPMTest/MyTransitiveLibrary/.gitignore | 8 + .../SPMTest/MyTransitiveLibrary/Package.swift | 33 ++ .../MyTransitiveLibrary.swift | 10 + .../MyTransitiveLibraryTests.swift | 12 + ...hash=217e2718d5748fe803db10bf7703f87e-json | 192 +++++++++ ...hash=28350dce3dbf6c3324256d2ef84c721e-json | 207 ++++++++++ ...hash=60e18114455190230d62ad086fd6a1d5-json | 221 ++++++++++ ...hash=d061ee81d8927d5b63443e777e83938a-json | 192 +++++++++ ...ins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json | 192 +++++++++ ...hash=d5012c60a72570c6ac682d8666f0e4a1-json | 137 +++++++ ...hash=27eb3a98abb5101fe49563a2824e9794-json | 128 ++++++ ...hash=322b71ca67a33085a0152ac83f23a35f-json | 92 +++++ ...hash=5c5fac44a4728a01cbe089b857eb0636-json | 128 ++++++ ...hash=74b26d184242037107e47412d7443f08-json | 68 +++ ...hash=7fe91a1e2b8f90031d23796421a46a09-json | 50 +++ ...hash=85e5507488bfe17e9e4e18b38ecf6a38-json | 110 +++++ ...hash=8de09bc5397dcfbae930a9e8ab952e62-json | 125 ++++++ ...hash=9b39f33b643d76adea258dfa2de06201-json | 137 +++++++ ...hash=c5eeab26a0e301a2419ee7e6d1254b52-json | 85 ++++ ...hash=cd6e9e040a1e22f4a8571a7087b8628e-json | 103 +++++ ...hash=da546fca3b585ddf35a5a7d6d76bf04c-json | 57 +++ ...hash=e0ad90d36af3737ec5215a32f000294d-json | 125 ++++++ ...hash=e6cf06600384b639e73079abd6a4553c-json | 150 +++++++ ...hash=ec86d6a4683c2028c68c9a06f9947bf3-json | 157 +++++++ ...hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json | 150 +++++++ ...hash=fb7de2da6bb6f870c73ba0c83581f638-json | 75 ++++ ...ects=f62a50b6ced3d59774fc6ba5a392ffaa-json | 12 + .../SPMTest/SPMTest.xcodeproj/project.pbxproj | 387 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../SPMTest/Assets.xcassets/Contents.json | 6 + .../SPMTest/SPMTest/ContentView.swift | 26 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../SPMTest/SPMTest/SPMTestApp.swift | 17 + Tests/GenIRTests/PIFCacheTests.swift | 35 ++ 52 files changed, 3683 insertions(+), 40 deletions(-) create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift create mode 100644 Tests/GenIRTests/PIFCacheTests.swift diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4c66cc..f0ee536 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,11 +7,11 @@ on: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: πŸ”¨ Build run: | - sudo xcode-select -s /Applications/Xcode_14.2.app/ + sudo xcode-select -s /Applications/Xcode_15.4.app/ swift build \ No newline at end of file diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index 46b1857..dedfd15 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -9,6 +9,7 @@ public class PIFParser { public enum Error: Swift.Error { case workspaceNotFound + case filesystemError(Swift.Error) } public init(cachePath: URL, logger log: Logger) throws { @@ -25,13 +26,33 @@ public class PIFParser { let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } - precondition(workspaces.count == 1, "Encountered more than one workspace - it is expected that a single workspace exists: \(workspaces)") - - guard workspaces.count > 0 else { + guard !workspaces.isEmpty else { throw Error.workspaceNotFound } - return workspaces[0] + if workspaces.count == 1 { + return workspaces[0] + } + + // If multiple workspaces exist, it's because the something in the project changed between builds. Sort workspaces by the most recent. + func modificationDate(_ path: URL) -> Date { + (try? FileManager.default.attributesOfItem(atPath: path.path)[.modificationDate] as? Date) ?? Date() + } + + logger.debug("Found multiple workspaces, sorting by modification date and returning most recently modified workspace") + + let workspacesAndDates = workspaces + .map { + (modificationDate($0), $0) + } + + logger.debug("Comparing dates and workspaces: ") + workspacesAndDates.forEach { logger.debug("\($0) - \($1)") } + + return workspacesAndDates + .sorted { + $0.0 > $1.0 + }[0].1 } } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 6737d8a..e24f0e2 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -53,19 +53,6 @@ class PIFCache { workspace.projects } - // private lazy var projectsByGUID: [GUID: PIF.Project] = { - // workspace - // .projects - // .reduce(into: [GUID: PIF.Project]()) { result, element in - // result[element.guid] = element - // } - // }() - - // func project(for guid: GUID) -> PIF.Project? { - // projectsByGUID[guid] - // } - - // TODO: We cab possibly filter out some targets here for performance var targets: [PIF.BaseTarget] { workspace .projects @@ -110,35 +97,22 @@ class PIFCache { let frameworkFileReferences = projects .flatMap { fileReferences(for: $0) } .filter { $0.fileType == "wrapper.framework" } - // TODO: do we filter on sourceTree == "BUILT_PRODUCTS_DIR" here too? // Now, stupidly, we have to do a name lookup on the path and use that to look up a target let frameworks = targets .compactMap { $0 as? PIF.Target } .filter { $0.productType == .framework } .reduce(into: [String: PIF.Target]()) { partial, target in - partial[target.productName] = target + let key = target.productName.isEmpty ? target.guid : target.productName + partial[key] = target } return frameworkFileReferences - // .compactMap { frameworks[$0.path] } // TODO: I think we should get the last path component as the key here - check that .reduce(into: [PIF.GUID: PIF.Target]()) { partial, fileReference in - // partial[target.guid] = target // Use the _file reference_ GUID as the key here - we're looking up frameworks by their file reference and not target GUID! partial[fileReference.guid] = frameworks[fileReference.path] } }() - - // private lazy var targetsByGUID: [GUID: PIF.BaseTarget] = { - // targets - // .reduce(into: [GUID: PIF.BaseTarget]()) { result, element in - // result[element.guid] = element - // } - // }() - - // func target(for guid: GUID) -> PIF.BaseTarget? { - // targetsByGUID[guid] - // } } extension PIF.BaseTarget: Hashable { @@ -166,20 +140,33 @@ struct PIFDependencyProvider: DependencyProviding { } } - private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID { + private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID? { let productToken = "PACKAGE-PRODUCT:" let targetToken = "PACKAGE-TARGET:" guard packageGUID.starts(with: productToken), let product = guidToTargets[packageGUID] else { return packageGUID } let productName = String(packageGUID.dropFirst(productToken.count)) - // TODO: should this also use the framework build phase to determine a dependency? - let packageTargetDependencies = product + // TODO: should this also use the framework build phase to determine a dependency? Currently not needed because Gen IR doesn't care about prebuilt frameworks + // but for the completeness of the graph this could be a nice to have... + let dependencies = product .baseTarget .dependencies .filter { $0.targetGUID.starts(with: targetToken) } + + let packageTargetDependencies = dependencies .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } + if packageTargetDependencies.isEmpty && !dependencies.isEmpty { + // We likely have a stub target here (i.e. a precompiled framework) + // see https://github.com/apple/swift-package-manager/issues/6069 for more + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(dependencies)") + return nil + } else if packageTargetDependencies.isEmpty && dependencies.isEmpty { + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets.") + return nil + } + precondition(packageTargetDependencies.count == 1, "Expecting one matching package target - found \(packageTargetDependencies.count): \(packageTargetDependencies). Returning first match") return packageTargetDependencies.first?.targetGUID ?? packageGUID @@ -191,7 +178,7 @@ struct PIFDependencyProvider: DependencyProviding { .baseTarget .dependencies .map { $0.targetGUID } - .map { resolveSwiftPackage($0) } + .compactMap { resolveSwiftPackage($0) } // Framework build phase dependencies let frameworkBuildPhases = value @@ -199,7 +186,7 @@ struct PIFDependencyProvider: DependencyProviding { .buildPhases .compactMap { $0 as? PIF.FrameworksBuildPhase } - let frameworkGUIDs = frameworkBuildPhases + let referenceGUIDs = frameworkBuildPhases .flatMap { $0.buildFiles } .compactMap { switch $0.reference { @@ -207,7 +194,11 @@ struct PIFDependencyProvider: DependencyProviding { case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency } } - .compactMap { cache.frameworks[$0]?.guid } + + let frameworkGUIDs = referenceGUIDs + .compactMap { + cache.frameworks[$0]?.guid + } let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift new file mode 100644 index 0000000..a3dd03d --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let lottieXCFramework = Target.binaryTarget( + name: "MyBinaryDependency", + url: "https://github.com/airbnb/lottie-ios/releases/download/4.4.3/Lottie-Xcode-15.2.xcframework.zip", + checksum: "546b7e718ed806646b84645ecfb1e1d6a65ac0387ff3f8ecb92dbaf2116cd62c") + +let package = Package( + name: "MyBinaryDependency", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyBinaryDependency", + targets: ["MyBinaryDependency"/*, "_Stub"*/]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + lottieXCFramework, +// .target(name: "_Stub"), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift new file mode 100644 index 0000000..08b22b8 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift new file mode 100644 index 0000000..ff3f482 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyBinaryDependency + +final class MyBinaryDependencyTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift new file mode 100644 index 0000000..1729829 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift @@ -0,0 +1,23 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyCommonLibrary", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyCommonLibrary", + targets: ["MyCommonLibrary"]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyCommonLibrary"), + .testTarget( + name: "MyCommonLibraryTests", + dependencies: ["MyCommonLibrary"]), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift new file mode 100644 index 0000000..5cf8945 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift @@ -0,0 +1,6 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +public struct MyCommonLibrary { + public static let common = "CommonLibrary" +} diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift new file mode 100644 index 0000000..9bf54fe --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyCommonLibrary + +final class MyCommonLibraryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore b/TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift b/TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift new file mode 100644 index 0000000..9b5dea4 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyLibrary", + platforms: [.iOS(.v14)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyLibrary", + targets: ["MyLibrary"]), + ], + dependencies: [ + .package(path: "../MyTransitiveLibrary"), + .package(path: "../MyCommonLibrary") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyLibrary", + dependencies: [ + .product(name: "MyTransitiveLibrary", package: "MyTransitiveLibrary"), + .product(name: "MyCommonLibrary", package: "MyCommonLibrary") + ] + ), + .testTarget( + name: "MyLibraryTests", + dependencies: ["MyLibrary"]), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift b/TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift new file mode 100644 index 0000000..2b9ac7a --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift @@ -0,0 +1,10 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import MyTransitiveLibrary +import MyCommonLibrary + +public struct MyLibrary { + public static let version = "1.0.0, \(MyTransitiveLibrary.test) - \(MyCommonLibrary.common)" + public static let view = MyTransitiveLibrary.lottie +} diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift b/TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift new file mode 100644 index 0000000..22e954f --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyLibrary + +final class MyLibraryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift new file mode 100644 index 0000000..924288c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyTransitiveLibrary", + platforms: [.iOS(.v14)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyTransitiveLibrary", + targets: ["MyTransitiveLibrary"]), + ], + dependencies: [ + .package(path: "../MyCommonLibrary"), + .package(path: "../MyBinaryDependency") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyTransitiveLibrary", + dependencies: [ + .product(name: "MyCommonLibrary", package: "MyCommonLibrary"), + .product(name: "MyBinaryDependency", package: "MyBinaryDependency") + ] + ), + .testTarget( + name: "MyTransitiveLibraryTests", + dependencies: ["MyTransitiveLibrary"]), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift new file mode 100644 index 0000000..993d620 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift @@ -0,0 +1,10 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import MyCommonLibrary +@_exported import Lottie + +public struct MyTransitiveLibrary { + public static let test = "This is a test \(MyCommonLibrary.common)" + public static let lottie = LottieAnimationView() +} diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift new file mode 100644 index 0000000..08ffe2c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyTransitiveLibrary + +final class MyTransitiveLibraryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json new file mode 100644 index 0000000..a6f0cc4 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json @@ -0,0 +1,192 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_2::REF_0", + "path": "MyLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_3::REF_0", + "path": "MyLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyLibrary", + "projectIsPackage": "true", + "projectName": "MyLibrary", + "targets": [ + "TARGET@v12_hash=74b26d184242037107e47412d7443f08", + "TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e", + "TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201", + "TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json new file mode 100644 index 0000000..0c5bf2c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json @@ -0,0 +1,207 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_0", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_1", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_2::REF_0", + "path": "MyBinaryDependency.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "path": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_3::REF_0", + "path": "MyBinaryDependency.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "path": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency", + "path": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency", + "projectIsPackage": "true", + "projectName": "MyBinaryDependency", + "targets": [ + "TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c", + "TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f", + "TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636", + "TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json new file mode 100644 index 0000000..8458f70 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json @@ -0,0 +1,221 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_0", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_1", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_2", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_3", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_2::REF_0", + "path": "MyTransitiveLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_3::REF_0", + "path": "MyTransitiveLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary", + "projectIsPackage": "true", + "projectName": "MyTransitiveLibrary", + "targets": [ + "TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638", + "TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38", + "TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8", + "TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json new file mode 100644 index 0000000..8aedf87 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json @@ -0,0 +1,192 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_2::REF_0", + "path": "MyCommonLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_3::REF_0", + "path": "MyCommonLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary", + "projectIsPackage": "true", + "projectName": "MyCommonLibrary", + "targets": [ + "TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09", + "TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52", + "TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794", + "TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json new file mode 100644 index 0000000..6ce343f --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json @@ -0,0 +1,192 @@ +{ + "appPreferencesBuildSettings": {}, + "buildConfigurations": [ + { + "buildSettings": { + "ALWAYS_SEARCH_USER_PATHS": "NO", + "ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS": "YES", + "CLANG_ANALYZER_NONNULL": "YES", + "CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION": "YES_AGGRESSIVE", + "CLANG_CXX_LANGUAGE_STANDARD": "gnu++20", + "CLANG_ENABLE_MODULES": "YES", + "CLANG_ENABLE_OBJC_ARC": "YES", + "CLANG_ENABLE_OBJC_WEAK": "YES", + "CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING": "YES", + "CLANG_WARN_BOOL_CONVERSION": "YES", + "CLANG_WARN_COMMA": "YES", + "CLANG_WARN_CONSTANT_CONVERSION": "YES", + "CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS": "YES", + "CLANG_WARN_DIRECT_OBJC_ISA_USAGE": "YES_ERROR", + "CLANG_WARN_DOCUMENTATION_COMMENTS": "YES", + "CLANG_WARN_EMPTY_BODY": "YES", + "CLANG_WARN_ENUM_CONVERSION": "YES", + "CLANG_WARN_INFINITE_RECURSION": "YES", + "CLANG_WARN_INT_CONVERSION": "YES", + "CLANG_WARN_NON_LITERAL_NULL_CONVERSION": "YES", + "CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF": "YES", + "CLANG_WARN_OBJC_LITERAL_CONVERSION": "YES", + "CLANG_WARN_OBJC_ROOT_CLASS": "YES_ERROR", + "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "YES", + "CLANG_WARN_RANGE_LOOP_ANALYSIS": "YES", + "CLANG_WARN_STRICT_PROTOTYPES": "YES", + "CLANG_WARN_SUSPICIOUS_MOVE": "YES", + "CLANG_WARN_UNGUARDED_AVAILABILITY": "YES_AGGRESSIVE", + "CLANG_WARN_UNREACHABLE_CODE": "YES", + "CLANG_WARN__DUPLICATE_METHOD_MATCH": "YES", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "ENABLE_STRICT_OBJC_MSGSEND": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_USER_SCRIPT_SANDBOXING": "YES", + "GCC_C_LANGUAGE_STANDARD": "gnu17", + "GCC_DYNAMIC_NO_PIC": "NO", + "GCC_NO_COMMON_BLOCKS": "YES", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": "DEBUG=1 $(inherited)", + "GCC_WARN_64_TO_32_BIT_CONVERSION": "YES", + "GCC_WARN_ABOUT_RETURN_TYPE": "YES_ERROR", + "GCC_WARN_UNDECLARED_SELECTOR": "YES", + "GCC_WARN_UNINITIALIZED_AUTOS": "YES_AGGRESSIVE", + "GCC_WARN_UNUSED_FUNCTION": "YES", + "GCC_WARN_UNUSED_VARIABLE": "YES", + "IPHONEOS_DEPLOYMENT_TARGET": "17.4", + "LOCALIZATION_PREFERS_STRING_CATALOGS": "YES", + "MTL_ENABLE_DEBUG_INFO": "INCLUDE_SOURCE", + "MTL_FAST_MATH": "YES", + "ONLY_ACTIVE_ARCH": "YES", + "SDKROOT": "iphoneos", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG $(inherited)", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone" + }, + "guid": "0f958f39499d9f0b6b454948d2be549b42f5c53a127a9a1a044740c8d5a00ed1", + "name": "Debug" + }, + { + "buildSettings": { + "ALWAYS_SEARCH_USER_PATHS": "NO", + "ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS": "YES", + "CLANG_ANALYZER_NONNULL": "YES", + "CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION": "YES_AGGRESSIVE", + "CLANG_CXX_LANGUAGE_STANDARD": "gnu++20", + "CLANG_ENABLE_MODULES": "YES", + "CLANG_ENABLE_OBJC_ARC": "YES", + "CLANG_ENABLE_OBJC_WEAK": "YES", + "CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING": "YES", + "CLANG_WARN_BOOL_CONVERSION": "YES", + "CLANG_WARN_COMMA": "YES", + "CLANG_WARN_CONSTANT_CONVERSION": "YES", + "CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS": "YES", + "CLANG_WARN_DIRECT_OBJC_ISA_USAGE": "YES_ERROR", + "CLANG_WARN_DOCUMENTATION_COMMENTS": "YES", + "CLANG_WARN_EMPTY_BODY": "YES", + "CLANG_WARN_ENUM_CONVERSION": "YES", + "CLANG_WARN_INFINITE_RECURSION": "YES", + "CLANG_WARN_INT_CONVERSION": "YES", + "CLANG_WARN_NON_LITERAL_NULL_CONVERSION": "YES", + "CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF": "YES", + "CLANG_WARN_OBJC_LITERAL_CONVERSION": "YES", + "CLANG_WARN_OBJC_ROOT_CLASS": "YES_ERROR", + "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "YES", + "CLANG_WARN_RANGE_LOOP_ANALYSIS": "YES", + "CLANG_WARN_STRICT_PROTOTYPES": "YES", + "CLANG_WARN_SUSPICIOUS_MOVE": "YES", + "CLANG_WARN_UNGUARDED_AVAILABILITY": "YES_AGGRESSIVE", + "CLANG_WARN_UNREACHABLE_CODE": "YES", + "CLANG_WARN__DUPLICATE_METHOD_MATCH": "YES", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "ENABLE_NS_ASSERTIONS": "NO", + "ENABLE_STRICT_OBJC_MSGSEND": "YES", + "ENABLE_USER_SCRIPT_SANDBOXING": "YES", + "GCC_C_LANGUAGE_STANDARD": "gnu17", + "GCC_NO_COMMON_BLOCKS": "YES", + "GCC_WARN_64_TO_32_BIT_CONVERSION": "YES", + "GCC_WARN_ABOUT_RETURN_TYPE": "YES_ERROR", + "GCC_WARN_UNDECLARED_SELECTOR": "YES", + "GCC_WARN_UNINITIALIZED_AUTOS": "YES_AGGRESSIVE", + "GCC_WARN_UNUSED_FUNCTION": "YES", + "GCC_WARN_UNUSED_VARIABLE": "YES", + "IPHONEOS_DEPLOYMENT_TARGET": "17.4", + "LOCALIZATION_PREFERS_STRING_CATALOGS": "YES", + "MTL_ENABLE_DEBUG_INFO": "NO", + "MTL_FAST_MATH": "YES", + "SDKROOT": "iphoneos", + "SWIFT_COMPILATION_MODE": "wholemodule", + "VALIDATE_PRODUCT": "YES" + }, + "guid": "0f958f39499d9f0b6b454948d2be549bfc0d140c3c428c4be4300c4ad84f502b", + "name": "Release" + } + ], + "classPrefix": "", + "defaultConfigurationName": "Release", + "developmentRegion": "en", + "groupTree": { + "children": [ + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "0f958f39499d9f0b6b454948d2be549bfecf073ab7da34165ac491b2cb2608da", + "path": "SPMTestApp.swift", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "sourcecode.swift", + "guid": "0f958f39499d9f0b6b454948d2be549bb774bcb839cea612c4808e51e76f1d8a", + "path": "ContentView.swift", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "folder.assetcatalog", + "guid": "0f958f39499d9f0b6b454948d2be549b63f219360fcbccc1f13e995b6d2c55bd", + "path": "Assets.xcassets", + "sourceTree": "", + "type": "file" + }, + { + "children": [ + { + "fileType": "folder.assetcatalog", + "guid": "0f958f39499d9f0b6b454948d2be549be0988c6a6200faa66fc4e43736a838c5", + "path": "Preview Assets.xcassets", + "sourceTree": "", + "type": "file" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b2f11424f01062300b0bebb6c9130131b", + "name": "Preview Content", + "path": "Preview Content", + "sourceTree": "", + "type": "group" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b435ba3b373201685aef000e70e51db4d", + "name": "SPMTest", + "path": "SPMTest", + "sourceTree": "", + "type": "group" + }, + { + "guid": "0f958f39499d9f0b6b454948d2be549befe9d7653bff13bf45841ede70f783cd", + "name": "Products", + "path": "", + "sourceTree": "", + "type": "group" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b33bedd8701761212ef2c3a2c378a8e0b", + "name": "SPMTest", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "0f958f39499d9f0b6b454948d2be549b", + "path": "/Users/thedderwick/Desktop/SPMTest/SPMTest.xcodeproj", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest", + "targets": [ + "TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json new file mode 100644 index 0000000..4cc5168 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json @@ -0,0 +1,137 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "CODE_SIGN_IDENTITY": "", + "CODE_SIGN_STYLE": "Manual", + "CURRENT_PROJECT_VERSION": "1", + "DEVELOPMENT_ASSET_PATHS": "\"SPMTest/Preview Content\"", + "DEVELOPMENT_TEAM": "", + "ENABLE_PREVIEWS": "YES", + "GENERATE_INFOPLIST_FILE": "YES", + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation": "YES", + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents": "YES", + "INFOPLIST_KEY_UILaunchScreen_Generation": "YES", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad": "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone": "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "LD_RUNPATH_SEARCH_PATHS": "$(inherited) @executable_path/Frameworks", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "com.veracode.SPMTest", + "PRODUCT_NAME": "$(TARGET_NAME)", + "PROVISIONING_PROFILE_SPECIFIER": "", + "SWIFT_EMIT_LOC_STRINGS": "YES", + "SWIFT_VERSION": "5.0", + "TARGETED_DEVICE_FAMILY": "1,2" + }, + "guid": "0f958f39499d9f0b6b454948d2be549b4cca86b4d9d5847565233659a29761c4", + "name": "Debug" + }, + { + "buildSettings": { + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "CODE_SIGN_IDENTITY": "", + "CODE_SIGN_STYLE": "Manual", + "CURRENT_PROJECT_VERSION": "1", + "DEVELOPMENT_ASSET_PATHS": "\"SPMTest/Preview Content\"", + "DEVELOPMENT_TEAM": "", + "ENABLE_PREVIEWS": "YES", + "GENERATE_INFOPLIST_FILE": "YES", + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation": "YES", + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents": "YES", + "INFOPLIST_KEY_UILaunchScreen_Generation": "YES", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad": "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone": "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "LD_RUNPATH_SEARCH_PATHS": "$(inherited) @executable_path/Frameworks", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "com.veracode.SPMTest", + "PRODUCT_NAME": "$(TARGET_NAME)", + "PROVISIONING_PROFILE_SPECIFIER": "", + "SWIFT_EMIT_LOC_STRINGS": "YES", + "SWIFT_VERSION": "5.0", + "TARGETED_DEVICE_FAMILY": "1,2" + }, + "guid": "0f958f39499d9f0b6b454948d2be549bb821c619cfb8b2e724c5328e54f54e17", + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "fileReference": "0f958f39499d9f0b6b454948d2be549bb774bcb839cea612c4808e51e76f1d8a", + "guid": "0f958f39499d9f0b6b454948d2be549b7a45127807cf63f6589a4a0de9d1e716" + }, + { + "fileReference": "0f958f39499d9f0b6b454948d2be549bfecf073ab7da34165ac491b2cb2608da", + "guid": "0f958f39499d9f0b6b454948d2be549bd0950f9139cbf08f13714ef301b71636" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b1112b312486e6592252467de83ac12fa", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "guid": "0f958f39499d9f0b6b454948d2be549bae8c757dc1f71dc80677176e88f57513", + "targetReference": "PACKAGE-PRODUCT:MyLibrary" + }, + { + "guid": "0f958f39499d9f0b6b454948d2be549b1990e4c3872d798fb604b5b6dde1e9d7", + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549bc68ad644ebd05301e2324acbeb00d9ce", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [ + { + "fileReference": "0f958f39499d9f0b6b454948d2be549be0988c6a6200faa66fc4e43736a838c5", + "guid": "0f958f39499d9f0b6b454948d2be549b2bdba7efbecb87014e782e630cad919b" + }, + { + "fileReference": "0f958f39499d9f0b6b454948d2be549b63f219360fcbccc1f13e995b6d2c55bd", + "guid": "0f958f39499d9f0b6b454948d2be549b011725fb4c17cd464e9eeb9cd6e70496" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b42f6470020217a2716154a23e1ad07ff", + "type": "com.apple.buildphase.resources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyLibrary", + "name": "MyLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "name": "MyCommonLibrary" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549be609cf86f8e7120d04a2edc980fbcfa8", + "name": "SPMTest", + "predominantSourceCodeLanguage": "Xcode.SourceCodeLanguage.Swift", + "productReference": { + "guid": "0f958f39499d9f0b6b454948d2be549b6201de4660da2a235e2bcde1105d0dfd", + "name": "SPMTest.app", + "type": "product" + }, + "productTypeIdentifier": "com.apple.product-type.application", + "provisioningSourceData": [ + { + "bundleIdentifierFromInfoPlist": "$(PRODUCT_BUNDLE_IDENTIFIER)", + "configurationName": "Debug", + "provisioningStyle": 1 + }, + { + "bundleIdentifierFromInfoPlist": "$(PRODUCT_BUNDLE_IDENTIFIER)", + "configurationName": "Release", + "provisioningStyle": 1 + } + ], + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json new file mode 100644 index 0000000..d0beab8 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json @@ -0,0 +1,128 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyCommonLibrary", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyCommonLibrary", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic", + "guid": "PACKAGE-TARGET:MyCommonLibrary", + "name": "MyCommonLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyCommonLibrary", + "name": "MyCommonLibrary.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json new file mode 100644 index 0000000..bfc0190 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json @@ -0,0 +1,92 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyBinaryDependency", + "PRODUCT_MODULE_NAME": "MyBinaryDependency", + "PRODUCT_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyBinaryDependency product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyBinaryDependency", + "PRODUCT_MODULE_NAME": "MyBinaryDependency", + "PRODUCT_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyBinaryDependency product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_1", + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "true" + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:_Stub" + } + ], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:_Stub", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic", + "name": "MyBinaryDependency", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic", + "name": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json new file mode 100644 index 0000000..bf7f212 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json @@ -0,0 +1,128 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "_Stub", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "_Stub", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:_Stub::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:_Stub::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic", + "guid": "PACKAGE-TARGET:_Stub", + "name": "_Stub", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:_Stub", + "name": "_Stub.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json new file mode 100644 index 0000000..a3b683f --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json @@ -0,0 +1,68 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary", + "name": "MyLibrary", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json new file mode 100644 index 0000000..c869509 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json @@ -0,0 +1,50 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyCommonLibrary", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "name": "MyCommonLibrary", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json new file mode 100644 index 0000000..c28cc3c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json @@ -0,0 +1,110 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyBinaryDependency" + }, + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_1", + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::3", + "platformFilters": [], + "removeHeadersOnCopy": "true" + } + ], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json new file mode 100644 index 0000000..34f0e82 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json @@ -0,0 +1,125 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "_Stub", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "_Stub", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic", + "name": "_Stub", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic", + "name": "_Stub.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json new file mode 100644 index 0000000..93fdf4d --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json @@ -0,0 +1,137 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyLibrary", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyLibrary", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:MyLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "guid": "PACKAGE-TARGET:MyLibrary", + "name": "MyLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyLibrary", + "name": "MyLibrary.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json new file mode 100644 index 0000000..d7daddd --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json @@ -0,0 +1,85 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyCommonLibrary", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary_-138526C95691DCED_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json new file mode 100644 index 0000000..2b5a912 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json @@ -0,0 +1,103 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mylibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mylibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json new file mode 100644 index 0000000..8401536 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json @@ -0,0 +1,57 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:_Stub", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_0", + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "true" + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:_Stub" + } + ], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "name": "MyBinaryDependency", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json new file mode 100644 index 0000000..4b1f821 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json @@ -0,0 +1,125 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json new file mode 100644 index 0000000..b254780 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json @@ -0,0 +1,150 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1::0", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.frameworks" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json new file mode 100644 index 0000000..9eaaa2e --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json @@ -0,0 +1,157 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1::0", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + }, + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyBinaryDependency" + }, + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_3", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1::2", + "platformFilters": [], + "removeHeadersOnCopy": "true" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.frameworks" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json new file mode 100644 index 0000000..c3a4089 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json @@ -0,0 +1,150 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyTransitiveLibrary", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyTransitiveLibrary", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_2", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_1::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_1", + "type": "com.apple.buildphase.resources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary", + "name": "MyTransitiveLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyTransitiveLibrary", + "name": "MyTransitiveLibrary.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json new file mode 100644 index 0000000..0f6cb43 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json @@ -0,0 +1,75 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyBinaryDependency" + }, + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_0", + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::3", + "platformFilters": [], + "removeHeadersOnCopy": "true" + } + ], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "name": "MyTransitiveLibrary", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json b/TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json new file mode 100644 index 0000000..1a2d122 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json @@ -0,0 +1,12 @@ +{ + "guid": "1548de4dda862e04fcc1cb773900f3bf", + "name": "SPMTest", + "path": "/Users/thedderwick/Desktop/SPMTest/SPMTest.xcodeproj/project.xcworkspace", + "projects": [ + "PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1", + "PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e", + "PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a", + "PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e", + "PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3044785 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj @@ -0,0 +1,387 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + CEE341292C007A7E00BBA9C3 /* SPMTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE341282C007A7E00BBA9C3 /* SPMTestApp.swift */; }; + CEE3412B2C007A7E00BBA9C3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3412A2C007A7E00BBA9C3 /* ContentView.swift */; }; + CEE3412D2C007A7F00BBA9C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEE3412C2C007A7F00BBA9C3 /* Assets.xcassets */; }; + CEE341302C007A7F00BBA9C3 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEE3412F2C007A7F00BBA9C3 /* Preview Assets.xcassets */; }; + CEE341382C007CA900BBA9C3 /* MyLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = CEE341372C007CA900BBA9C3 /* MyLibrary */; }; + CEE3413B2C00812D00BBA9C3 /* MyCommonLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = CEE3413A2C00812D00BBA9C3 /* MyCommonLibrary */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CEE341252C007A7E00BBA9C3 /* SPMTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SPMTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CEE341282C007A7E00BBA9C3 /* SPMTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPMTestApp.swift; sourceTree = ""; }; + CEE3412A2C007A7E00BBA9C3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + CEE3412C2C007A7F00BBA9C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CEE3412F2C007A7F00BBA9C3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CEE341222C007A7E00BBA9C3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CEE341382C007CA900BBA9C3 /* MyLibrary in Frameworks */, + CEE3413B2C00812D00BBA9C3 /* MyCommonLibrary in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CEE3411C2C007A7E00BBA9C3 = { + isa = PBXGroup; + children = ( + CEE341272C007A7E00BBA9C3 /* SPMTest */, + CEE341262C007A7E00BBA9C3 /* Products */, + ); + sourceTree = ""; + }; + CEE341262C007A7E00BBA9C3 /* Products */ = { + isa = PBXGroup; + children = ( + CEE341252C007A7E00BBA9C3 /* SPMTest.app */, + ); + name = Products; + sourceTree = ""; + }; + CEE341272C007A7E00BBA9C3 /* SPMTest */ = { + isa = PBXGroup; + children = ( + CEE341282C007A7E00BBA9C3 /* SPMTestApp.swift */, + CEE3412A2C007A7E00BBA9C3 /* ContentView.swift */, + CEE3412C2C007A7F00BBA9C3 /* Assets.xcassets */, + CEE3412E2C007A7F00BBA9C3 /* Preview Content */, + ); + path = SPMTest; + sourceTree = ""; + }; + CEE3412E2C007A7F00BBA9C3 /* Preview Content */ = { + isa = PBXGroup; + children = ( + CEE3412F2C007A7F00BBA9C3 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CEE341242C007A7E00BBA9C3 /* SPMTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEE341332C007A7F00BBA9C3 /* Build configuration list for PBXNativeTarget "SPMTest" */; + buildPhases = ( + CEE341212C007A7E00BBA9C3 /* Sources */, + CEE341222C007A7E00BBA9C3 /* Frameworks */, + CEE341232C007A7E00BBA9C3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SPMTest; + packageProductDependencies = ( + CEE341372C007CA900BBA9C3 /* MyLibrary */, + CEE3413A2C00812D00BBA9C3 /* MyCommonLibrary */, + ); + productName = SPMTest; + productReference = CEE341252C007A7E00BBA9C3 /* SPMTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CEE3411D2C007A7E00BBA9C3 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1530; + LastUpgradeCheck = 1530; + TargetAttributes = { + CEE341242C007A7E00BBA9C3 = { + CreatedOnToolsVersion = 15.3; + }; + }; + }; + buildConfigurationList = CEE341202C007A7E00BBA9C3 /* Build configuration list for PBXProject "SPMTest" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CEE3411C2C007A7E00BBA9C3; + packageReferences = ( + CEE341362C007CA900BBA9C3 /* XCLocalSwiftPackageReference "MyLibrary" */, + CEE341392C00812D00BBA9C3 /* XCLocalSwiftPackageReference "MyCommonLibrary" */, + ); + productRefGroup = CEE341262C007A7E00BBA9C3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CEE341242C007A7E00BBA9C3 /* SPMTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CEE341232C007A7E00BBA9C3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEE341302C007A7F00BBA9C3 /* Preview Assets.xcassets in Resources */, + CEE3412D2C007A7F00BBA9C3 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CEE341212C007A7E00BBA9C3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEE3412B2C007A7E00BBA9C3 /* ContentView.swift in Sources */, + CEE341292C007A7E00BBA9C3 /* SPMTestApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CEE341312C007A7F00BBA9C3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CEE341322C007A7F00BBA9C3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CEE341342C007A7F00BBA9C3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"SPMTest/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.veracode.SPMTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEE341352C007A7F00BBA9C3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"SPMTest/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.veracode.SPMTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CEE341202C007A7E00BBA9C3 /* Build configuration list for PBXProject "SPMTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE341312C007A7F00BBA9C3 /* Debug */, + CEE341322C007A7F00BBA9C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEE341332C007A7F00BBA9C3 /* Build configuration list for PBXNativeTarget "SPMTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE341342C007A7F00BBA9C3 /* Debug */, + CEE341352C007A7F00BBA9C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + CEE341362C007CA900BBA9C3 /* XCLocalSwiftPackageReference "MyLibrary" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyLibrary; + }; + CEE341392C00812D00BBA9C3 /* XCLocalSwiftPackageReference "MyCommonLibrary" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyCommonLibrary; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CEE341372C007CA900BBA9C3 /* MyLibrary */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLibrary; + }; + CEE3413A2C00812D00BBA9C3 /* MyCommonLibrary */ = { + isa = XCSwiftPackageProductDependency; + productName = MyCommonLibrary; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = CEE3411D2C007A7E00BBA9C3 /* Project object */; +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift b/TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift new file mode 100644 index 0000000..2e83bfa --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift @@ -0,0 +1,26 @@ +// +// ContentView.swift +// SPMTest +// +// Created by Thomas Hedderwick on 24/05/2024. +// + +import SwiftUI +import MyLibrary + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("VERSION: \(MyLibrary.version)") + Text("view: \(MyLibrary.view)") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift b/TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift new file mode 100644 index 0000000..716f46c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift @@ -0,0 +1,17 @@ +// +// SPMTestApp.swift +// SPMTest +// +// Created by Thomas Hedderwick on 24/05/2024. +// + +import SwiftUI + +@main +struct SPMTestApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Tests/GenIRTests/PIFCacheTests.swift b/Tests/GenIRTests/PIFCacheTests.swift new file mode 100644 index 0000000..47bdcea --- /dev/null +++ b/Tests/GenIRTests/PIFCacheTests.swift @@ -0,0 +1,35 @@ +import XCTest +@testable import gen_ir +import PIFSupport + +final class PIFCacheTests: XCTestCase { + let testPath: URL = { + TestContext.testAssetPath + .appendingPathComponent("PIFCaches") + .appendingPathComponent("SPMTest") + .appendingPathComponent("SPMTest.xcodeproj") + }() + let scheme = "SPMTest" + var cachePath: URL { + testPath + .deletingLastPathComponent() + .appendingPathComponent("PIFCache") + } + + func testSPMTestChain() throws { + let context = TestContext() + try context.build(test: testPath, scheme: scheme) + let graph = context.graph + + let appTarget = try XCTUnwrap(context.targets.first(where: { $0.productName == "SPMTest.app" })) + let node = try XCTUnwrap(graph.findNode(for: appTarget)) + + XCTAssertEqual(node.edges.count, 2) + let chain = graph.chain(for: appTarget) + let nameSet = Set(chain.map { $0.value.name }) + + let expectedNameSet = Set(["SPMTest", "MyLibrary", "MyCommonLibrary", "MyTransitiveLibrary"]) + let nameDifference = nameSet.symmetricDifference(expectedNameSet) + XCTAssertTrue(nameDifference.isEmpty, "Difference found in name set (\(nameSet)) and expected name set: \(expectedNameSet) - difference: \(nameDifference)") + } +} From 07c0b9675f928213bae4579a093a17ebad90724a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 27 May 2024 14:40:07 +0200 Subject: [PATCH 41/61] Clean up unused code, unused CLI options, etc --- .swiftlint.yml | 17 +++- .../Extensions/FileManager+Extension.swift | 21 ----- .../GenIR/Extensions/String+Extension.swift | 10 --- Sources/GenIR/GenIR.swift | 84 ++++++------------- Sources/GenIR/OutputPostprocessor.swift | 14 +--- Sources/GenIR/PIFCache.swift | 57 +++++-------- .../OutputPostprocessorFileMoverTests.swift | 1 - Tests/GenIRTests/UmbrellaTests.swift | 2 - Tests/GenIRTests/WorkspaceTests.swift | 1 - 9 files changed, 64 insertions(+), 143 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 993bb96..d40d541 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,10 +1,10 @@ excluded: - - .build/ - .swiftpm/ - .vscode/ - # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet - - GenIRLogging/.build/ + - "**/.build/*" + - .build/ - TestAssets/ + - PIF/.build/ disabled_rules: - todo @@ -13,3 +13,14 @@ disabled_rules: line_length: warning: 200 ignores_comments: true + +missing_docs: + warning: + - private + - open + - fileprivate + - public + - internal + +opt_in_rules: + - missing_docs diff --git a/Sources/GenIR/Extensions/FileManager+Extension.swift b/Sources/GenIR/Extensions/FileManager+Extension.swift index 76a544c..b85b6b4 100644 --- a/Sources/GenIR/Extensions/FileManager+Extension.swift +++ b/Sources/GenIR/Extensions/FileManager+Extension.swift @@ -102,27 +102,6 @@ extension FileManager { try moveItem(at: source, to: destination) } - /// Copies an item, merging with the existing path. Replacement of existing paths is performed if specified. - /// - Parameters: - /// - source: the item to copy - /// - destination: the destination of the copy - /// - replacing: should existing items be replaced? - func copyItemMerging(at source: URL, to destination: URL, replacing: Bool = false) throws { - let sourceFiles = try contentsOfDirectory(at: source, includingPropertiesForKeys: nil) - - for sourceFile in sourceFiles { - let path = destination.appendingPathComponent(sourceFile.lastPathComponent) - - if replacing && fileExists(atPath: path.filePath) { - try removeItem(at: path) - } - - let destinationFile = uniqueFilename(directory: destination, filename: sourceFile.lastPathComponent) - - try copyItem(at: sourceFile, to: destinationFile) - } - } - /// Generates a unique filename for a file at the given directory. This attempts to emulates finders style of appending a 'version' number at the end of the filename /// - Parameters: /// - directory: the directory the file would exist in diff --git a/Sources/GenIR/Extensions/String+Extension.swift b/Sources/GenIR/Extensions/String+Extension.swift index ca6e499..4f6df5b 100644 --- a/Sources/GenIR/Extensions/String+Extension.swift +++ b/Sources/GenIR/Extensions/String+Extension.swift @@ -61,16 +61,6 @@ extension String { return results } - - func deletingPathExtension() -> String { - if let index = self.firstIndexWithEscapes(of: ".") { - var newSelf = self - newSelf.removeSubrange(index.. \ log.txt - $ \(programName) log.txt output_folder/ --project-path MyProject.xcodeproj + $ \(programName) log.txt x.xcarchive/ Example with pipe: $ xcodebuild clean && xcodebuild build -project MyProject.xcodeproj -configuration Debug -scheme MyScheme 2>&1 \ - | \(programName) - output_folder/ --project-path MyProject.xcodeproj + | \(programName) - x.xcarchive/ """, version: "v\(Versions.version)" ) @@ -45,8 +45,8 @@ struct IREmitterCommand: ParsableCommand { var xcarchivePath: URL /// Path to xcodeproj or xcworkspace file - @Option(help: "Path to your Xcode Project or Workspace file") - var projectPath: URL! + @Option(help: "DEPRECATED: This Option is deprecated and will go away in a future version.") + var projectPath: URL? /// Enables enhanced debug logging @Flag(help: "Enables debug level logging") @@ -63,17 +63,13 @@ struct IREmitterCommand: ParsableCommand { var dumpDependencyGraph = false mutating func validate() throws { + // This will run before run() so set this here if debug { logger.logLevel = .debug } - // Version 0.2.x and below didn't require a project. Attempt to default this value if we can - if projectPath == nil { - projectPath = try findProjectPath() - } - - if !FileManager.default.fileExists(atPath: projectPath.filePath) { - throw ValidationError("Project doesn't exist at path: \(projectPath.filePath)") + if projectPath != nil { + logger.warning("--project-path has been deprecated and will go away in a future version. Please remove it from your invocation.") } // Version 0.2.x and below allowed the output folder to be any arbitrary folder. @@ -94,7 +90,6 @@ struct IREmitterCommand: ParsableCommand { mutating func run() throws { try run( - project: projectPath, log: logPath, archive: xcarchivePath, level: logger.logLevel, @@ -104,41 +99,26 @@ struct IREmitterCommand: ParsableCommand { } mutating func run( - project: URL, log: String, archive: URL, level: Logger.Level, dryRun: Bool, dumpDependencyGraph: Bool ) throws { + logger.logLevel = level let output = archive.appendingPathComponent("IR") - // let project = try ProjectParser(path: project, logLevel: level) - // var targets = Targets(for: project) let log = try logParser(for: log) try log.parse() // Find and parse the PIF cache let pifCache = try PIFCache(buildCache: log.buildCachePath) - - let buildCacheManipulator = try BuildCacheManipulator( - buildCachePath: log.buildCachePath, - buildSettings: log.settings, - archive: archive, - dryRun: dryRun - ) - let targets = Target.targets(from: pifCache.targets, with: log.targetCommands) - let runner = CompilerCommandRunner( - output: output, - buildCacheManipulator: buildCacheManipulator, - dryRun: dryRun + let builder = DependencyGraphBuilder( + provider: .init(targets: targets, cache: pifCache), + values: targets ) - try runner.run(targets: targets) - - let provider = PIFDependencyProvider(targets: targets, cache: pifCache) - let builder = DependencyGraphBuilder(provider: provider, values: targets) let graph = builder.graph if dumpDependencyGraph { @@ -153,6 +133,20 @@ struct IREmitterCommand: ParsableCommand { } } + let buildCacheManipulator = try BuildCacheManipulator( + buildCachePath: log.buildCachePath, + buildSettings: log.settings, + archive: archive, + dryRun: dryRun + ) + + let runner = CompilerCommandRunner( + output: output, + buildCacheManipulator: buildCacheManipulator, + dryRun: dryRun + ) + try runner.run(targets: targets) + let postprocessor = try OutputPostprocessor( archive: archive, output: output, @@ -206,34 +200,6 @@ struct IREmitterCommand: ParsableCommand { } } -extension IREmitterCommand { - /// Attempt to automatically determine the Xcode workspace or project path - /// - Returns: the path to the first xcworkspace or xcodeproj found in the current directory - private func findProjectPath() throws -> URL { - let cwd = FileManager.default.currentDirectoryPath.fileURL - // First, xcworkspace, then xcodeproj - let xcworkspace = try FileManager.default.directories(at: cwd, recursive: false) - .filter { $0.pathExtension == "xcworkspace" } - - if xcworkspace.count == 1 { - return xcworkspace[0] - } - - let xcodeproj = try FileManager.default.directories(at: cwd, recursive: false) - .filter { $0.pathExtension == "xcodeproj" } - - if xcodeproj.count == 1 { - return xcodeproj[0] - } - - throw ValidationError( - """ - Couldn't automatically determine path to xcodeproj or xcworkspace. Please use --project-path to provide it. - """ - ) - } -} - extension URL: ExpressibleByArgument { public init?(argument: String) { self = argument.fileURL.absoluteURL diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index acba0ac..964ea02 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -14,9 +14,6 @@ private typealias SizeAndCreation = (Int, Date) /// The `OutputPostprocessor` attempts to remedy this by using the `ProjectParser` to detect dependencies between products AND /// parsing the `xcarchive` to determine if something was statically linked, and if so, copies the IR for that product into the linker's IR folder. class OutputPostprocessor { - /// The to the output IR folders that will be processed - let output: URL - /// The archive path, this should be the parent path of `output` let archive: URL @@ -32,7 +29,6 @@ class OutputPostprocessor { private let manager: FileManager = .default init(archive: URL, output: URL, graph: DependencyGraph, targets: [Target]) throws { - self.output = output self.archive = archive self.graph = graph self.targets = targets @@ -65,19 +61,15 @@ class OutputPostprocessor { /// - Parameter targets: the targets to operate on func process() throws { // TODO: remove 'static' deps so we don't duplicate them in the submission? - _ = try targetsToPaths - .map { try process(target: $0.key, at: $0.value) } + _ = try targets + .map { try process(target: $0) } } /// Processes an individual target /// - Parameters: /// - target: the target to process - /// - path: the output path /// - Returns: - private func process( - target: Target, - at path: URL - ) throws -> Set { + private func process(target: Target) throws -> Set { // TODO: we need better handling of swift package products and targets in the dependency graph or we fail to move dependencies here let chain = graph.chain(for: target) diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index e24f0e2..449b4e5 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -6,6 +6,16 @@ class PIFCache { private let pifCachePath: URL private let workspace: PIF.Workspace + var projects: [PIF.Project] { + workspace.projects + } + + var targets: [PIF.BaseTarget] { + workspace + .projects + .flatMap { $0.targets } + } + enum Error: Swift.Error { case nonexistentCache(String) case pifError(Swift.Error) @@ -49,31 +59,6 @@ class PIFCache { } } - var projects: [PIF.Project] { - workspace.projects - } - - var targets: [PIF.BaseTarget] { - workspace - .projects - .flatMap { $0.targets } - } - - private lazy var namesToTargets: [String: PIF.BaseTarget] = { - targets - .reduce(into: [String: PIF.BaseTarget]()) { partial, target in - partial[target.name] = target - } - }() - - private lazy var productNamesToTargets: [String: PIF.BaseTarget] = { - targets - .compactMap { $0 as? PIF.Target } - .reduce(into: [String: PIF.BaseTarget]()) { partial, target in - partial[target.productName] = target - } - }() - private func fileReferences(for project: PIF.Project) -> [PIF.FileReference] { func resolveChildren(starting children: [PIF.Reference], result: inout [PIF.FileReference]) { for child in children { @@ -147,29 +132,31 @@ struct PIFDependencyProvider: DependencyProviding { let productName = String(packageGUID.dropFirst(productToken.count)) - // TODO: should this also use the framework build phase to determine a dependency? Currently not needed because Gen IR doesn't care about prebuilt frameworks - // but for the completeness of the graph this could be a nice to have... - let dependencies = product + let productTargetDependencies = product .baseTarget .dependencies .filter { $0.targetGUID.starts(with: targetToken) } - let packageTargetDependencies = dependencies + let productUnderlyingTargets = productTargetDependencies .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } - if packageTargetDependencies.isEmpty && !dependencies.isEmpty { + if productUnderlyingTargets.isEmpty && !productTargetDependencies.isEmpty { // We likely have a stub target here (i.e. a precompiled framework) // see https://github.com/apple/swift-package-manager/issues/6069 for more - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(dependencies)") + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(productTargetDependencies)") return nil - } else if packageTargetDependencies.isEmpty && dependencies.isEmpty { - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets.") + } else if productUnderlyingTargets.isEmpty && productTargetDependencies.isEmpty { + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Likely a prebuilt dependency") return nil } - precondition(packageTargetDependencies.count == 1, "Expecting one matching package target - found \(packageTargetDependencies.count): \(packageTargetDependencies). Returning first match") + guard productTargetDependencies.count == 1, let target = productTargetDependencies.first else { + logger.debug("Expecting one matching package target - found \(productTargetDependencies.count): \(productTargetDependencies). Returning first match if it exists") + return productTargetDependencies.first?.targetGUID + } - return packageTargetDependencies.first?.targetGUID ?? packageGUID + logger.debug("\(packageGUID) resolves to \(target.targetGUID)") + return target.targetGUID } func dependencies(for value: Target) -> [Target] { diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index acf034d..00497e3 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -15,7 +15,6 @@ final class OutputPostprocessorFileMoverTests: XCTestCase { var runner = IREmitterCommand() try runner.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index eba625a..cd9f969 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -40,7 +40,6 @@ final class UmbrellaTests: XCTestCase { var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -70,7 +69,6 @@ final class UmbrellaTests: XCTestCase { var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index a7844fb..b250b92 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -55,7 +55,6 @@ final class WorkspaceTests: XCTestCase { var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, From 8c687f003a535a0ba3b0d1d9e7085854c8410ce4 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 27 May 2024 14:40:47 +0200 Subject: [PATCH 42/61] Add support for CMake builds These have different PIF cache locations in the build cache --- Sources/GenIR/BuildCacheManipulator.swift | 1 + .../GenIR/Extensions/Process+Extension.swift | 13 +++- Sources/GenIR/PIFCache.swift | 25 +++----- Sources/GenIR/XcodeLogParser.swift | 26 +++++--- TestAssets/CMakeDiscoveryTest/CMakeLists.txt | 60 +++++++++++++++++++ .../CMakeDiscoveryTest/Source/App.swift | 10 ++++ Tests/GenIRTests/CMakeDiscoveryTests.swift | 54 +++++++++++++++++ Tests/GenIRTests/DependencyGraphTests.swift | 10 ++-- 8 files changed, 167 insertions(+), 32 deletions(-) create mode 100644 TestAssets/CMakeDiscoveryTest/CMakeLists.txt create mode 100644 TestAssets/CMakeDiscoveryTest/Source/App.swift create mode 100644 Tests/GenIRTests/CMakeDiscoveryTests.swift diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index b9a2e7a..43501db 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -38,6 +38,7 @@ struct BuildCacheManipulator { func manipulate() throws { if shouldDeploySkipInstallHack { let intermediatesPath = buildCachePath + .appendingPathComponent("Build") .appendingPathComponent("Intermediates.noindex") .appendingPathComponent("ArchiveIntermediates") diff --git a/Sources/GenIR/Extensions/Process+Extension.swift b/Sources/GenIR/Extensions/Process+Extension.swift index a5df895..7de2e80 100644 --- a/Sources/GenIR/Extensions/Process+Extension.swift +++ b/Sources/GenIR/Extensions/Process+Extension.swift @@ -54,7 +54,16 @@ extension Process { let process = Process() - let executable = command.replacingOccurrences(of: "\\", with: "") + let executable: String + let args: [String] + + if command.starts(with: ".") || command.starts(with: "/") { + executable = command.replacingOccurrences(of: "\\", with: "") + args = arguments + } else { + executable = "/usr/bin/env" + args = [command] + arguments + } if #available(macOS 10.13, *) { process.executableURL = executable.fileURL @@ -62,7 +71,7 @@ extension Process { process.launchPath = executable } - process.arguments = arguments.map { $0.replacingOccurrences(of: "\\", with: "") } + process.arguments = args.map { $0.replacingOccurrences(of: "\\", with: "") } process.standardOutput = stdoutPipe process.standardError = stderrPipe process.standardInput = FileHandle.nullDevice diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 449b4e5..3325c3a 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -2,7 +2,6 @@ import Foundation import PIFSupport class PIFCache { - private let buildCache: URL private let pifCachePath: URL private let workspace: PIF.Workspace @@ -22,7 +21,6 @@ class PIFCache { } init(buildCache: URL) throws { - self.buildCache = buildCache self.pifCachePath = try Self.pifCachePath(in: buildCache) do { @@ -34,13 +32,12 @@ class PIFCache { } private static func pifCachePath(in buildCache: URL) throws -> URL { - // TODO: test this variation, because I haven't seen this personally let cmakePIFCachePath = buildCache - .deletingLastPathComponent() .appendingPathComponent("XCBuildData") .appendingPathComponent("PIFCache") let regularPIFCachePath = buildCache + .appendingPathComponent("Build") .appendingPathComponent("Intermediates.noindex") .appendingPathComponent("XCBuildData") .appendingPathComponent("PIFCache") @@ -111,12 +108,10 @@ extension PIF.BaseTarget: Hashable { } struct PIFDependencyProvider: DependencyProviding { - private let targets: [Target] private let cache: PIFCache private var guidToTargets: [PIF.GUID: Target] init(targets: [Target], cache: PIFCache) { - self.targets = targets self.cache = cache self.guidToTargets = targets @@ -168,24 +163,20 @@ struct PIFDependencyProvider: DependencyProviding { .compactMap { resolveSwiftPackage($0) } // Framework build phase dependencies - let frameworkBuildPhases = value + // NOTE: Previously we just cast this - all of a sudden with pods this is broken + // Not the end of the world - just as quick to do a dictionary lookup + let frameworkGUIDs = value .baseTarget .buildPhases - .compactMap { $0 as? PIF.FrameworksBuildPhase } - - let referenceGUIDs = frameworkBuildPhases .flatMap { $0.buildFiles } + // .compactMap { $0 as? PIF.FrameworksBuildPhase } .compactMap { switch $0.reference { - case .file(let guid): return guid - case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency + case let .file(guid): return guid + case .target: return nil } } - - let frameworkGUIDs = referenceGUIDs - .compactMap { - cache.frameworks[$0]?.guid - } + .compactMap { cache.frameworks[$0]?.guid } let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index ced8b74..c3495a5 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -92,14 +92,24 @@ class XcodeLogParser { guard let startIndex = line.firstIndex(of: ":") else { continue } let stripped = line[line.index(after: startIndex).. Date: Mon, 27 May 2024 15:10:46 +0200 Subject: [PATCH 43/61] PIF Package: Add Logger and update test --- PIF/Package.resolved | 15 +++++++++++++++ PIF/Package.swift | 8 ++++++-- PIF/Sources/pif-parser/pif-parser.swift | 5 +++-- 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 PIF/Package.resolved diff --git a/PIF/Package.resolved b/PIF/Package.resolved new file mode 100644 index 0000000..d0d296f --- /dev/null +++ b/PIF/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "12c22d4bea7efd3c5084cc3bac01b276e5f01c1011991fc15f93790ff260cb4b", + "pins" : [ + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", + "version" : "1.5.4" + } + } + ], + "version" : 3 +} diff --git a/PIF/Package.swift b/PIF/Package.swift index 9391feb..ff4790d 100644 --- a/PIF/Package.swift +++ b/PIF/Package.swift @@ -13,13 +13,17 @@ let package = Package( targets: ["PIFSupport"] ) ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0") + ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. // Targets can depend on other targets in this package and products from dependencies. .target( name: "PIFSupport", - dependencies: [] + dependencies: [ + .product(name: "Logging", package: "swift-log") + ] ), .testTarget( name: "PIFSupportTests", diff --git a/PIF/Sources/pif-parser/pif-parser.swift b/PIF/Sources/pif-parser/pif-parser.swift index 78db443..92cf019 100644 --- a/PIF/Sources/pif-parser/pif-parser.swift +++ b/PIF/Sources/pif-parser/pif-parser.swift @@ -7,6 +7,7 @@ import Foundation import PIFSupport +import Logging @main struct PIFParser { @@ -17,8 +18,8 @@ struct PIFParser { } let cachePath = URL(fileURLWithPath: CommandLine.arguments[1]) - let parser = PIFSupport.PIFParser(cachePath: cachePath) - let workspace = try parser.parse() + let parser = try PIFSupport.PIFParser(cachePath: cachePath, logger: .init(label: "com.veracode.pif-parser")) + let workspace = parser.workspace print("workspace: \(workspace.guid):") print("projects: \(workspace.projects.map { $0.guid }.joined(separator: "\n"))\n") From eb096ed71edadb8edbf46f2a1e41c923c6c0f7b1 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 27 May 2024 15:10:58 +0200 Subject: [PATCH 44/61] Remove version files from tests --- Tests/GenIRTests/UmbrellaTests.swift | 8 ++++---- Tests/GenIRTests/WorkspaceTests.swift | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index cd9f969..13a40af 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -11,10 +11,10 @@ final class UmbrellaTests: XCTestCase { let scheme = "Umbrella" let targetsToFiles = [ - "Common.framework": ["Common_vers.bc", "Common-dummy.bc", "OrgModel.bc"].sorted(), - "Networking.framework": ["Networking_vers.bc", "Networking-dummy.bc", "Networking.bc"].sorted(), - "Pods_Umbrella.framework": ["Pods_Umbrella_vers.bc", "Pods-Umbrella-dummy.bc"].sorted(), - "Umbrella.framework": ["GetOrg.bc", "Umbrella_vers.bc"].sorted() + "Common.framework": ["OrgModel.bc"].sorted(), + "Networking.framework": ["Networking.bc"].sorted(), + "Pods_Umbrella.framework": [], + "Umbrella.framework": ["GetOrg.bc"].sorted() ] func testUmbrellaTargets() throws { diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index b250b92..d5abf84 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -11,8 +11,8 @@ final class WorkspaceTests: XCTestCase { let scheme = "App" static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] - static let commonIRFiles: Set = ["Common_vers.bc", "Model.bc"] - static let frameworkIRFiles: Set = ["Framework_vers.bc", "Framework.bc"] + static let commonIRFiles: Set = ["Model.bc"] + static let frameworkIRFiles: Set = ["Framework.bc"] static let sfSafeSymbolsIRFiles: Set = [ "NSImageExtension.bc", "SFSymbol+1.0.bc", @@ -94,9 +94,9 @@ final class WorkspaceTests: XCTestCase { let expectedSFSafeSymbolsIRFiles = Self.sfSafeSymbolsIRFiles .reduce(into: Set(), { $0.insert($1) }) - XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual") - XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual") - XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual") - XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual") + XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedAppIRFiles.symmetricDifference(appIRPathContents))") + XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual \(expectedFrameworkIRFiles.symmetricDifference(frameworkIRPathContents))") + XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual \(expectedCommonIRFiles.symmetricDifference(commonIRPathContents))") + XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))") } } From cba89fb7235adf5bfe782f68044c51783bdcaa37 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 28 May 2024 14:08:00 +0200 Subject: [PATCH 45/61] Add documentation, refactor the Graph ready for extraction --- .swiftlint.yml | 10 +-- PIF/Sources/PIFSupport/PIFSupport.swift | 57 +++++++------ PIF/Sources/pif-parser/pif-parser.swift | 4 +- Sources/GenIR/BuildCacheManipulator.swift | 37 ++++++--- Sources/GenIR/CompilerCommandRunner.swift | 10 ++- .../Dependency Graph/DependencyGraph.swift | 39 ++++----- .../DependencyGraphBuilder.swift | 23 +++++- Sources/GenIR/Dependency Graph/Edge.swift | 58 +++++++++----- Sources/GenIR/Dependency Graph/Node.swift | 48 ++++++----- .../GenIR/Extensions/Process+Extension.swift | 19 +++-- Sources/GenIR/GenIR.swift | 4 +- Sources/GenIR/OutputPostprocessor.swift | 35 +++++--- Sources/GenIR/PIFCache.swift | 79 +++++++++++++++---- Sources/GenIR/Target.swift | 17 ++++ Sources/GenIR/XcodeLogParser.swift | 1 + Tests/GenIRTests/TestContext.swift | 32 ++++++-- Tests/GenIRTests/WorkspaceTests.swift | 6 +- Tests/GenIRTests/gen_irTests.swift | 2 +- 18 files changed, 322 insertions(+), 159 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index d40d541..aec2552 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,6 +5,9 @@ excluded: - .build/ - TestAssets/ - PIF/.build/ + - '**/Package.swift' + - Package.swift + - PIF/Sources/pif-parser/pif-parser.swift disabled_rules: - todo @@ -16,11 +19,8 @@ line_length: missing_docs: warning: - - private - - open - - fileprivate - - public - - internal + - private + - public opt_in_rules: - missing_docs diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index dedfd15..7ca3b8e 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -1,17 +1,22 @@ import Foundation import Logging +/// Global logger for the module var logger: Logger! -public class PIFParser { +/// PIFCacheParser is responsible for discovering the files in a PIF Cache and decoding them. +public class PIFCacheParser { + /// The path to the PIF Cache (in Xcode's DerivedData this is often under Build/Intermediates.noindex/XCBuildData/PIFCache) private let cachePath: URL + /// The most recent workspace in the cache public let workspace: PIF.Workspace public enum Error: Swift.Error { + /// A PIF Workspace was not found in the cache case workspaceNotFound - case filesystemError(Swift.Error) } + /// Creates an instance initialized with the cache data at the given path. public init(cachePath: URL, logger log: Logger) throws { logger = log self.cachePath = cachePath @@ -20,39 +25,33 @@ public class PIFParser { workspace = try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) } + /// Finds the most recent workspace in the cache + /// - Throws: a `workspaceNotFound` error when a workspace is not found + /// - Returns: the path to the most recent workspace file private static func workspacePath(in cachePath: URL) throws -> URL { - let path = cachePath.appendingPathComponent("workspace") - - let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) - .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } - - guard !workspaces.isEmpty else { - throw Error.workspaceNotFound + let workspaces = try FileManager.default.contentsOfDirectory( + at: cachePath.appendingPathComponent("workspace"), + includingPropertiesForKeys: nil + ) + .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } + .map { + ( + workspace: $0, + modificationDate: (try? FileManager.default.attributesOfItem(atPath: $0.path)[.modificationDate] as? Date) ?? Date() + ) } - if workspaces.count == 1 { - return workspaces[0] + if workspaces.isEmpty { + throw Error.workspaceNotFound + } else if workspaces.count == 1, let workspace = workspaces.first?.workspace { + return workspace } // If multiple workspaces exist, it's because the something in the project changed between builds. Sort workspaces by the most recent. - func modificationDate(_ path: URL) -> Date { - (try? FileManager.default.attributesOfItem(atPath: path.path)[.modificationDate] as? Date) ?? Date() - } - - logger.debug("Found multiple workspaces, sorting by modification date and returning most recently modified workspace") - - let workspacesAndDates = workspaces - .map { - (modificationDate($0), $0) - } - - logger.debug("Comparing dates and workspaces: ") - workspacesAndDates.forEach { logger.debug("\($0) - \($1)") } - - return workspacesAndDates - .sorted { - $0.0 > $1.0 - }[0].1 + return workspaces + .sorted(using: KeyPathComparator(\.modificationDate)) + .first! + .workspace } } diff --git a/PIF/Sources/pif-parser/pif-parser.swift b/PIF/Sources/pif-parser/pif-parser.swift index 92cf019..b7b0c8b 100644 --- a/PIF/Sources/pif-parser/pif-parser.swift +++ b/PIF/Sources/pif-parser/pif-parser.swift @@ -10,7 +10,7 @@ import PIFSupport import Logging @main -struct PIFParser { +struct PIFCacheParser { static func main() throws { guard CommandLine.arguments.count == 2 else { print("USAGE: \(CommandLine.arguments.first!) [PIFCache path]") @@ -18,7 +18,7 @@ struct PIFParser { } let cachePath = URL(fileURLWithPath: CommandLine.arguments[1]) - let parser = try PIFSupport.PIFParser(cachePath: cachePath, logger: .init(label: "com.veracode.pif-parser")) + let parser = try PIFSupport.PIFCacheParser(cachePath: cachePath, logger: .init(label: "com.veracode.pif-parser")) let workspace = parser.workspace print("workspace: \(workspace.guid):") diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index 43501db..cc18684 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -8,6 +8,7 @@ struct BuildCacheManipulator { /// Build settings used as part of the build private let buildSettings: [String: String] + /// Run without doing any cache manipulation private let dryRun: Bool /// Should we run the SKIP_INSTALL hack? @@ -21,6 +22,12 @@ struct BuildCacheManipulator { case tooManyDirectories(String) } + /// Creates an instance of the cache manipulator + /// - Parameters: + /// - buildCachePath: the build cache to operate on + /// - buildSettings: the project build settings + /// - archive: the path to the xcarchive produced as part of the build + /// - dryRun: should be a dry run? init(buildCachePath: URL, buildSettings: [String: String], archive: URL, dryRun: Bool) throws { self.buildCachePath = buildCachePath self.buildSettings = buildSettings @@ -35,7 +42,10 @@ struct BuildCacheManipulator { } } + /// Start the build cache manipulator func manipulate() throws { + guard !dryRun else { return } + if shouldDeploySkipInstallHack { let intermediatesPath = buildCachePath .appendingPathComponent("Build") @@ -79,17 +89,19 @@ struct BuildCacheManipulator { } } - // This is a hack. Turn away now. - // - // When archiving frameworks with the SKIP_INSTALL=NO setting, frameworks will be evicted (see below) from the build cache. - // This means when we rerun commands to generate IR, the frameworks no longer exist on disk, and we fail with linker errors. - // - // This is how the build cache is (roughly) laid out: - // - // * Build/Intermediates.noindex/ArchiveIntermediates//BuildProductsPath/- - // * this contains a set of symlinks to elsewhere in the build cache. These links remain in place, but the items they point to are removed - // - // The idea here is simple, attempt to update the symlinks so they point to valid framework product. + /// This is a hack. Turn away now. + /// + /// When archiving frameworks with the SKIP_INSTALL=NO setting, frameworks will be evicted (see below) from the build cache. + /// This means when we rerun commands to generate IR, the frameworks no longer exist on disk, and we fail with linker errors. + /// + /// This is how the build cache is (roughly) laid out: + /// + /// * Build/Intermediates.noindex/ArchiveIntermediates//BuildProductsPath/- + /// * this contains a set of symlinks to elsewhere in the build cache. These links remain in place, but the items they point to are removed + /// + /// The idea here is simple, attempt to update the symlinks so they point to valid framework product. + /// + /// - Parameter archiveBuildProductsPath: build products path (see description) private func skipInstallHack(_ archiveBuildProductsPath: URL) throws { let symlinksToUpdate = FileManager.default.filteredContents(of: archiveBuildProductsPath) { $0.lastPathComponent.hasSuffix("framework") @@ -118,9 +130,10 @@ struct BuildCacheManipulator { } } + /// TODO: This could be more sensible and get the build configuration from the log and match that to a configuration in the PIF Cache /// Tries to find the xcode build configuration directory path inside the given path /// - Parameter path: the path to search - /// - Returns: + /// - Returns: the path to the build configuration directory, if found private static func findConfigurationDirectory(_ path: URL) -> URL? { let folders = (try? FileManager.default.directories(at: path, recursive: false)) ?? [] diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index e5586d5..b0c3657 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -20,8 +20,10 @@ struct CompilerCommandRunner { /// The directory to place the LLVM BC output private let output: URL + /// The cache manipulator, required to do fix ups on the build cache in very specific circumstances private let buildCacheManipulator: BuildCacheManipulator + /// Manager used to access the file system private let fileManager = FileManager.default enum Error: Swift.Error { @@ -29,11 +31,14 @@ struct CompilerCommandRunner { case failedToParse(String) } + /// Run without running the commands private let dryRun: Bool - /// Initializes a runner + /// Initializes the runner /// - Parameters: /// - output: The location to place the resulting LLVM IR + /// - buildCacheManipulator: the cache manipulator to perform fixups with + /// - dryRun: should run in dry run mode? init(output: URL, buildCacheManipulator: BuildCacheManipulator, dryRun: Bool) { self.output = output self.dryRun = dryRun @@ -41,6 +46,7 @@ struct CompilerCommandRunner { } /// Starts the runner + /// - Parameter targets: the targets holding the commands to run func run(targets: [Target]) throws { // Quick, do a hack! try buildCacheManipulator.manipulate() @@ -165,7 +171,7 @@ struct CompilerCommandRunner { /// Parses, and corrects, the executable name and arguments for a given command. /// - Parameter command: The command to parse and correct /// - Returns: A tuple of executable name and an array of arguments - private func parse(command: CompilerCommand) throws -> (String, [String]) { + private func parse(command: CompilerCommand) throws -> (executable: String, arguments: [String]) { let fixed = fixup(command: command.command) let (executable, arguments) = try split(command: fixed) let fixedArguments = fixup(arguments: arguments, for: command.compiler) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index e1cd9e6..f6107f3 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -10,32 +10,32 @@ import Foundation /// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) class DependencyGraph { /// All the nodes in the graph - private(set) var nodes = [String: Node]() + private(set) var nodes = [String: Node]() - /// Adds a value to the graph - /// - Parameter value: the value to add - /// - Returns: the node added - func addNode(value: Value) -> Node { + /// Adds a node for the associated value to the graph + /// - Parameter value: the value associated with the node + /// - Returns: the node for the associated value. If the node already existed it is returned + func addNode(for value: Value) -> Node { if let node = findNode(for: value) { return node } - let node = Node(value) + let node = Node(value) nodes[value.valueName] = node return node } - /// Finds a value's node in the graph + /// Finds the node associated with a value /// - Parameter value: the value to look for - /// - Returns: the node for the given value, if found - func findNode(for value: Value) -> Node? { + /// - Returns: the node for which the value is associated, if found + func findNode(for value: Value) -> Node? { nodes[value.valueName] } - /// Builds a dependency 'chain' for a value using a depth-first search - /// - Parameter value: the value to get a chain for - /// - Returns: the chain of nodes, starting - func chain(for value: Value) -> [Node] { + /// Returns the dependency 'chain' for the value associated with a node in the graph using a depth-first search + /// - Parameter value: the associated value for a node to start the search with + /// - Returns: the chain of nodes, starting with the 'bottom' of the dependency subgraph + func chain(for value: Value) -> [Node] { guard let node = findNode(for: value) else { logger.debug("Couldn't find node for value: \(value.valueName)") return [] @@ -47,15 +47,16 @@ class DependencyGraph { /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach - private func depthFirstSearch(startingAt node: Node) -> [Node] { + private func depthFirstSearch(startingAt node: Node) -> [Node] { logger.debug("----\nSearching for: \(node.value.valueName)") - var visited = Set>() - var chain = [Node]() + var visited = Set() + var chain = [Node]() - func depthFirst(node: Node) { + /// Visits node dependencies and adds them to the chain from the bottom up + /// - Parameter node: the node to search through + func depthFirst(node: Node) { logger.debug("inserting node: \(node.value.valueName)") visited.insert(node) - logger.debug("visited: \(visited)") for edge in node.edges where edge.relationship == .dependency { if visited.insert(edge.to).inserted { @@ -74,6 +75,8 @@ class DependencyGraph { return chain } + /// Writes a 'dot' graph file to disk + /// - Parameter path: the path to write the graph to func toDot(_ path: String) throws { var contents = "digraph DependencyGraph {\n" diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index 9e88715..429c0a2 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -5,17 +5,32 @@ // Created by Thomas Hedderwick on 28/08/2023. // +/// A type that provides dependency relationships between values protocol DependencyProviding { + /// A type that represents the value of a node associatedtype Value: NodeValue + + /// Returns the direct dependencies for a given value + /// - Parameter value: the value to get dependencies for + /// - Returns: a list of dependencies func dependencies(for value: Value) -> [Value] } +/// A builder for the DependencyGraph - you should _always_ use this class to build out the `DependencyGraph` class DependencyGraphBuilder where Value == Provider.Value { + /// The graph the builder will operate on + typealias Graph = DependencyGraph + + /// The dependency provider private let provider: Provider - let graph = DependencyGraph() + + /// The built graph + let graph = Graph() /// Inits the Graph Builder - /// - Parameter provider: the dependency provider for the values + /// - Parameters: + /// - provider: the dependency provider for the values + /// - values: the values to add to the graph init(provider: Provider, values: [Value]) { self.provider = provider values.forEach { add(value: $0) } @@ -25,7 +40,7 @@ class DependencyGraphBuilder wh /// - Parameters: /// - value: the value to add @discardableResult - private func add(value: Value) -> Node { + private func add(value: Value) -> Graph.Node { if let existingNode = graph.findNode(for: value) { return existingNode } @@ -33,7 +48,7 @@ class DependencyGraphBuilder wh logger.debug("Adding value: \(value.valueName) to graph") let dependencies = provider.dependencies(for: value) - let node = graph.addNode(value: value) + let node = graph.addNode(for: value) for dependency in dependencies { let dependencyNode = add(value: dependency) diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift index c78de34..c9d8d41 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -7,36 +7,52 @@ // swiftlint:disable identifier_name /// An edge describes the relationship between two Nodes in a graph -class Edge { - /// The source node - let to: Node - /// The destination node - let from: Node - /// The relationship between the two nodes - let relationship: Relationship - /// Description of the relationships between two nodes - enum Relationship { - /// From depends on To - case dependency - /// From is a depender of To - case depender - } +extension DependencyGraph { + class Edge { + /// The source node + let to: DependencyGraph.Node + /// The destination node + let from: DependencyGraph.Node + /// The relationship between the two nodes + let relationship: Relationship + + /// Description of the relationships between two nodes + enum Relationship { + /// From depends on To + case dependency + /// From is a depender of To + case depender + } - init(to: Node, from: Node, relationship: Relationship) { - self.to = to - self.from = from - self.relationship = relationship + /// Initializes an edge between two nodes + /// - Parameters: + /// - to: the node this edge is pointing to + /// - from: the node this edge is pointing from + /// - relationship: the type of relationship this edge represents + init(to: DependencyGraph.Node, from: DependencyGraph.Node, relationship: Relationship) { + self.to = to + self.from = from + self.relationship = relationship + } } } -extension Edge: Equatable { - static func == (_ lhs: Edge, rhs: Edge) -> Bool { +extension DependencyGraph.Edge: Equatable { + static func == (_ lhs: DependencyGraph.Edge, rhs: DependencyGraph.Edge) -> Bool { lhs.to == rhs.to && lhs.from == rhs.from && lhs.relationship == rhs.relationship } } -extension Edge: CustomStringConvertible { +extension DependencyGraph.Edge: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(to) + hasher.combine(from) + hasher.combine(relationship) + } +} + +extension DependencyGraph.Edge: CustomStringConvertible { var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} } // swiftlint:enable identifier_name diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index a2e9406..cf94ac8 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -12,36 +12,42 @@ protocol NodeValue: Hashable { var valueName: String { get } } -class Node { - /// The edges from and to this node - private(set) var edges = [Edge]() - /// The value this node represents - let value: Value - /// The name of this node - var valueName: String { - value.valueName - } +extension DependencyGraph { + class Node { + /// The edges from and to this node + private(set) var edges = [DependencyGraph.Edge]() - init(_ value: Value) { - self.value = value - } + /// The associated value of this node + let value: Value + + /// The name of this node + var valueName: String { + value.valueName + } + + /// Initializes a node with an associated value + /// - Parameter value: the value to associate with this node + init(_ value: Value) { + self.value = value + } - /// Adds an edge to this node - /// - Parameter edge: the edge to add - func add(edge: Edge) { - if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { - edges.append(edge) + /// Adds an edge to this node + /// - Parameter edge: the edge to add + func add(edge: DependencyGraph.Edge) { + if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { + edges.append(edge) + } } } } -extension Node: Equatable { - static func == (_ lhs: Node, rhs: Node) -> Bool { +extension DependencyGraph.Node: Equatable { + static func == (_ lhs: DependencyGraph.Node, rhs: DependencyGraph.Node) -> Bool { lhs.value == rhs.value && lhs.edges == rhs.edges } } -extension Node: CustomStringConvertible { +extension DependencyGraph.Node: CustomStringConvertible { var description: String { var description = "" @@ -55,7 +61,7 @@ extension Node: CustomStringConvertible { } } -extension Node: Hashable { +extension DependencyGraph.Node: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(valueName) hasher.combine(value) diff --git a/Sources/GenIR/Extensions/Process+Extension.swift b/Sources/GenIR/Extensions/Process+Extension.swift index 7de2e80..c1d2c8e 100644 --- a/Sources/GenIR/Extensions/Process+Extension.swift +++ b/Sources/GenIR/Extensions/Process+Extension.swift @@ -8,7 +8,7 @@ import Foundation extension Process { - /// Presents the result of a Process + /// Represents the result of a Process struct ReturnValue { /// The stdout output of the process, if there was any let stdout: String? @@ -17,17 +17,22 @@ extension Process { /// The return code of the process let code: Int32 + /// Initializes a ReturnValue + /// - Parameters: + /// - stdout: the standard output + /// - stderr: the standard error + /// - code: the exit code init(stdout: String?, stderr: String?, code: Int32) { - if let stdout, stdout.isEmpty { - self.stdout = nil + self.stdout = if let stdout, stdout.isEmpty { + nil } else { - self.stdout = stdout + stdout } - if let stderr, stderr.isEmpty { - self.stderr = nil + self.stderr = if let stderr, stderr.isEmpty { + nil } else { - self.stderr = stderr + stderr } self.code = code diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 61f8bb9..75e13d2 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -6,6 +6,7 @@ import PIFSupport /// Global logger object var logger = Logger(label: Bundle.main.bundleIdentifier ?? "com.veracode.gen-ir", factory: StdOutLogHandler.init) +/// The name of the program let programName = CommandLine.arguments.first! /// Command to emit LLVM IR from an Xcode build log @@ -150,8 +151,7 @@ struct IREmitterCommand: ParsableCommand { let postprocessor = try OutputPostprocessor( archive: archive, output: output, - graph: graph, - targets: targets + graph: graph ) try postprocessor.process() diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 964ea02..6c7f754 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -7,8 +7,6 @@ import Foundation -private typealias SizeAndCreation = (Int, Date) - /// The `OutputPostprocessor` is responsible for trying to match the IR output of the `CompilerCommandRunner` with the products in the `xcarchive`. /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. /// The `OutputPostprocessor` attempts to remedy this by using the `ProjectParser` to detect dependencies between products AND @@ -20,18 +18,23 @@ class OutputPostprocessor { /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk private let dynamicDependencyToPath: [String: URL] + /// A dependency graph containing the targets in the output archive private let graph: DependencyGraph + + /// The targets in this archive private let targets: [Target] - private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] + /// A mapping of targets to their path on disk private let targetsToPaths: [Target: URL] + /// The manager to use for file system access private let manager: FileManager = .default - init(archive: URL, output: URL, graph: DependencyGraph, targets: [Target]) throws { + /// Initializes the postprocessor + init(archive: URL, output: URL, graph: DependencyGraph) throws { self.archive = archive self.graph = graph - self.targets = targets + self.targets = graph.nodes.map { $0.value.value } let namesToTargets = targets .reduce(into: [String: Target]()) { partial, target in @@ -44,7 +47,7 @@ class OutputPostprocessor { if let target = namesToTargets[path.lastPathComponent] { partial[target] = path } else { - logger.error("Failed to look up target for path: \(path)") + logger.error("Path (\(path.lastPathComponent)) wasn't found in namesToTargets: \(namesToTargets)") } } @@ -122,10 +125,11 @@ class OutputPostprocessor { func copyContentsOfDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { let files = try manager.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) + /// Source and destination paths + typealias SourceAndDestination = (source: URL, destination: URL) // Get two arrays of file paths of the source and destination file where: // 1) destination already exists // 2) destination doesn't already exist - typealias SourceAndDestination = (source: URL, destination: URL) let (existing, nonexisting) = files .map { ( @@ -154,6 +158,12 @@ class OutputPostprocessor { } } + /// The size and creation date of a file system item + private typealias SizeAndCreation = (Int, Date) + + /// A cache of seen files and their associated metadata + private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] + /// Copies a file, uniquing the path if it conflicts, _if_ the files they conflict with aren't the same size /// - Parameters: /// - source: source file path @@ -174,11 +184,7 @@ class OutputPostprocessor { let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - if seenConflictingFiles[source] == nil { - seenConflictingFiles[source] = [(sourceSize, sourceCreatedDate)] - } - - for (size, date) in seenConflictingFiles[source]! where size == destinationSize && date == destinationCreatedDate { + for (size, date) in seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]] where size == destinationSize && date == destinationCreatedDate { return } @@ -215,6 +221,9 @@ private func baseSearchPath(startingAt path: URL) -> URL { let applicationsPath = productsPath.appendingPathComponent("Applications") let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") + /// Returns the first directory found at the given path + /// - Parameter path: the path to search for directories + /// - Returns: the first directory found in the path if one exists func firstDirectory(at path: URL) -> URL? { guard FileManager.default.directoryExists(at: path), @@ -225,7 +234,7 @@ private func baseSearchPath(startingAt path: URL) -> URL { } if contents.count > 1 { - logger.error("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") + logger.debug("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") } return contents.first! diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 3325c3a..8fb6760 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -1,14 +1,22 @@ import Foundation import PIFSupport +/// The PIFCache provides a wrapper around a `PIF.Workspace`. +/// It includes a set of helper functions to make operating on the PIF Cache structures easier. +/// This class is used in conjunction with `PIFDependencyProvider` to enable building dependency relationships between the various targets class PIFCache { + /// The path to the PIF Cache private let pifCachePath: URL + + /// The most recent `PIF.Workspace` in the cache private let workspace: PIF.Workspace + /// All projects contained by the workspace var projects: [PIF.Project] { workspace.projects } + /// All targets contained by the workspace var targets: [PIF.BaseTarget] { workspace .projects @@ -20,17 +28,23 @@ class PIFCache { case pifError(Swift.Error) } + /// Initializes the PIF Cache from a build cache + /// - Parameter buildCache: path to the Xcode DerivedData Build Cache init(buildCache: URL) throws { self.pifCachePath = try Self.pifCachePath(in: buildCache) do { - let cache = try PIFParser(cachePath: pifCachePath, logger: logger) + let cache = try PIFCacheParser(cachePath: pifCachePath, logger: logger) workspace = cache.workspace } catch { throw Error.pifError(error) } } + /// Finds the PIF Cache in the Xcode Build Cache. This can vary depending on the build system used. + /// - Parameter buildCache: the Xcode build cache + /// - Throws: if no cache was found + /// - Returns: the path to the PIF Cache private static func pifCachePath(in buildCache: URL) throws -> URL { let cmakePIFCachePath = buildCache .appendingPathComponent("XCBuildData") @@ -56,24 +70,33 @@ class PIFCache { } } + /// Recursively gets all file references inside a given project + /// This will not return Groups, it will attempt to resolve them to the underlying file references + /// - Parameter project: the project to find file references in + /// - Returns: a list of file references private func fileReferences(for project: PIF.Project) -> [PIF.FileReference] { - func resolveChildren(starting children: [PIF.Reference], result: inout [PIF.FileReference]) { - for child in children { - if let fileReference = child as? PIF.FileReference { - result.append(fileReference) - } else if let group = child as? PIF.Group { + var result = [PIF.FileReference]() + /// Recursively resolve references to file references + /// - Parameters: + /// - children: the starting list of references + func resolveChildren(starting children: [PIF.Reference]) { + children.forEach { child in + switch child { + case let file as PIF.FileReference: + result.append(file) + case let group as PIF.Group: resolveChildren(starting: group.children, result: &result) - } else { - print("Unhandled reference type: \(child)") + default: + logger.debug("Unhandled reference type: \(child)") } } } - var result = [PIF.FileReference]() - resolveChildren(starting: project.groupTree.children, result: &result) + resolveChildren(starting: project.groupTree.children) return result } + /// A mapping of Framework `PIF.GUID`s to the framework `PIF.Target` lazy var frameworks: [PIF.GUID: PIF.Target] = { // Here we have to get all the wrapper.framework references from the group, and then attempt to map them to targets let frameworkFileReferences = projects @@ -107,10 +130,20 @@ extension PIF.BaseTarget: Hashable { } } +// TODO: think if there is a better type enforced way of defining the relationships here - should this be in the Targets file (and called TargetDependencyProvider) + +/// The PIFDependencyProvider is responsible for calculating dependency relationships between targets in the cache struct PIFDependencyProvider: DependencyProviding { + /// The `PIFCache` to provide dependency relationships for private let cache: PIFCache + + /// A mapping of `PIF.GUID` to the `Target` they represent private var guidToTargets: [PIF.GUID: Target] + // / Initializes the PIFDependencyProvider + /// - Parameters: + /// - targets: the list of targets to provide dependency relationships for + /// - cache: the cache that contains the targets init(targets: [Target], cache: PIFCache) { self.cache = cache @@ -120,12 +153,26 @@ struct PIFDependencyProvider: DependencyProviding { } } - private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID? { + /// Attempts to resolve a Swift Package Product reference to it's Swift Package Target reference + /// When SPM generates PIF files relating to the package you (typically) end up with 4 PIF Targets: + /// - Package Product: + /// - This depends on the Package Target + /// - This has a dynamic target variant pointing to the Dynamic Package Product + /// - Dynamic Package Product + /// - This depends on the Package Target + /// - Package Target + /// - This is the object file that is the actual target we're looking for + /// - This has a dynamic target variant pointing to the Dynamic Package Target + /// - Dynamic Package Target + /// Typically, we want to take the Package Product and find the Package Target it depends on + /// - Parameter packageProductGUID: the swift product package guid + /// - Returns: the guid of the related Swift Package Target + private func resolveSwiftPackage(_ packageProductGUID: PIF.GUID) -> PIF.GUID? { let productToken = "PACKAGE-PRODUCT:" let targetToken = "PACKAGE-TARGET:" - guard packageGUID.starts(with: productToken), let product = guidToTargets[packageGUID] else { return packageGUID } + guard packageProductGUID.starts(with: productToken), let product = guidToTargets[packageProductGUID] else { return packageProductGUID } - let productName = String(packageGUID.dropFirst(productToken.count)) + let productName = String(packageProductGUID.dropFirst(productToken.count)) let productTargetDependencies = product .baseTarget @@ -138,10 +185,10 @@ struct PIFDependencyProvider: DependencyProviding { if productUnderlyingTargets.isEmpty && !productTargetDependencies.isEmpty { // We likely have a stub target here (i.e. a precompiled framework) // see https://github.com/apple/swift-package-manager/issues/6069 for more - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(productTargetDependencies)") + logger.debug("Resolving Swift Package (\(productName) - \(packageProductGUID)) resulted in no targets. Possible stub target in: \(productTargetDependencies)") return nil } else if productUnderlyingTargets.isEmpty && productTargetDependencies.isEmpty { - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Likely a prebuilt dependency") + logger.debug("Resolving Swift Package (\(productName) - \(packageProductGUID)) resulted in no targets. Likely a prebuilt dependency") return nil } @@ -150,7 +197,7 @@ struct PIFDependencyProvider: DependencyProviding { return productTargetDependencies.first?.targetGUID } - logger.debug("\(packageGUID) resolves to \(target.targetGUID)") + logger.debug("\(packageProductGUID) resolves to \(target.targetGUID)") return target.targetGUID } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index 0f26a8a..0453d1f 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -8,8 +8,13 @@ import Foundation import PIFSupport +/// A Target represents a product to build (app, framework, plugin, package) class Target { + /// The name of the target var name: String { baseTarget.name } + + /// The product name of the target, if one is available, otherwise the name + /// This can happen when the product is not directly buildable (such as a package product or aggregate) var productName: String { if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { return target.productName @@ -17,11 +22,18 @@ class Target { return baseTarget.name } + // TODO: we need to handle SPM's insane naming scheme for products here ^ including the potential of a dynamic variant + /// The `PIF.BaseTarget` structure that backs this target let baseTarget: PIF.BaseTarget + /// The `CompilerCommands` related to building this target let commands: [CompilerCommand] + /// Initializes a target with the given backing target and commands + /// - Parameters: + /// - baseTarget: the underlying `PIF.BaseTarget` + /// - commands: the commands related to this target init(baseTarget: PIF.BaseTarget, commands: [CompilerCommand]) { self.baseTarget = baseTarget self.commands = commands @@ -29,6 +41,11 @@ class Target { } extension Target { + /// Helper function to map targets and commands to an array of targets + /// - Parameters: + /// - targets: the `PIF.BaseTarget`s that will back the new targets + /// - targetsToCommands: a mapping of target names to the `CompilerCommands` that relate to them + /// - Returns: the newly created targets static func targets(from targets: [PIF.BaseTarget], with targetsToCommands: [String: [CompilerCommand]]) -> [Target] { targets .map { diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index c3495a5..3f07a34 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -17,6 +17,7 @@ class XcodeLogParser { private(set) var settings: [String: String] = [:] /// The path to the Xcode build cache private(set) var buildCachePath: URL! + /// A mapping of target names to the compiler commands that relate to them private(set) var targetCommands: [String: [CompilerCommand]] = [:] enum Error: Swift.Error { diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 8132ac5..a104aac 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -2,22 +2,29 @@ import Foundation @testable import gen_ir import XCTest +/// TestContext is a grouping of convenance functions and a context to ease the burden of testing Gen IR class TestContext { enum Error: Swift.Error { case commandFailed(Process.ReturnValue) case invalidArgument(String) - case notBuilt } - static let baseTestingPath: URL = { + /// The base folder path of the Gen IR project + static let baseProjectPath: URL = { // HACK: use the #file magic to get a path to the test case... Maybe there's a better way to do this? URL(fileURLWithPath: #file.replacingOccurrences(of: "Tests/GenIRTests/TestContext.swift", with: "")) }() + /// Path to the TestAssets folder static let testAssetPath: URL = { - baseTestingPath.appendingPathComponent("TestAssets") + baseProjectPath.appendingPathComponent("TestAssets") }() + /// Run xcodebuild's clean action + /// - Parameters: + /// - path: the path of the project to clean + /// - scheme: the name of the scheme to clean (required for workspaces) + /// - Returns: func clean(test path: URL, scheme: String) throws -> Process.ReturnValue { var arguments = ["clean"] @@ -38,8 +45,13 @@ class TestContext { ) } - @discardableResult - func build( + /// Run xcodebuild's archive action + /// - Parameters: + /// - path: the path of the project to build + /// - scheme: the name of the scheme to build + /// - additionalArguments: any additional arguments to passthrough to xcodebuild + /// - Returns: the result of running the action. + @discardableResult func build( test path: URL, scheme: String, additionalArguments: [String] = [] @@ -85,12 +97,18 @@ class TestContext { return process } + /// Path to the xcarchive (once built) let archive: URL + /// Path to the build log (once built) let buildLog: URL + /// Path to the temporary working directory for this context let temporaryDirectory: URL + /// Has the project been built? private(set) var built = false + /// Contents of the built build log private(set) var buildLogContents = [String]() + /// Initializes the test context init() { // swiftlint:disable force_try temporaryDirectory = try! FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") @@ -103,6 +121,7 @@ class TestContext { // swiftlint:enable force_try } + /// Generate the log parser for this context lazy var logParser: XcodeLogParser = { XCTAssertTrue(built, "Requests a log parser without building the project") let parser = XcodeLogParser(log: buildLogContents) @@ -114,6 +133,7 @@ class TestContext { return parser }() + /// Generate the PIF Cache for this context lazy var pifCache: PIFCache = { do { return try PIFCache(buildCache: logParser.buildCachePath) @@ -122,10 +142,12 @@ class TestContext { } }() + /// Generate the Targets for this context lazy var targets: [Target] = { Target.targets(from: pifCache.targets, with: logParser.targetCommands) }() + /// Generate the dependency graph for this context lazy var graph: DependencyGraph = { DependencyGraphBuilder(provider: PIFDependencyProvider(targets: targets, cache: pifCache), values: targets).graph }() diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index d5abf84..711e66f 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -97,6 +97,10 @@ final class WorkspaceTests: XCTestCase { XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedAppIRFiles.symmetricDifference(appIRPathContents))") XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual \(expectedFrameworkIRFiles.symmetricDifference(frameworkIRPathContents))") XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual \(expectedCommonIRFiles.symmetricDifference(commonIRPathContents))") - XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))") + XCTAssertEqual( + expectedSFSafeSymbolsIRFiles, + sfSafeSymbolsIRPathContents, + "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))" + ) } } diff --git a/Tests/GenIRTests/gen_irTests.swift b/Tests/GenIRTests/gen_irTests.swift index 7f55708..fcfc920 100644 --- a/Tests/GenIRTests/gen_irTests.swift +++ b/Tests/GenIRTests/gen_irTests.swift @@ -4,7 +4,7 @@ import XCTest final class GenIRTests: XCTestCase { func testManyTargetTestTargets() throws { let context = TestContext() - let projectPath = TestContext.baseTestingPath + let projectPath = TestContext.baseProjectPath .appendingPathComponent("TestAssets") .appendingPathComponent("ManyTargetTest") .appendingPathComponent("ManyTargetTest.xcodeproj") From c0ff81967d01b3ce908efb5f12fb78d95c54f692 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 28 May 2024 16:24:29 +0200 Subject: [PATCH 46/61] Extract the DependencyGraph & Logger to packages --- .swiftlint.yml | 1 - Package.swift | 22 ++- .../DependencyGraph.swift | 13 +- .../DependencyGraphBuilder.swift | 12 +- .../Edge.swift | 17 +-- .../Node.swift | 17 +-- Sources/GenIR/BuildCacheManipulator.swift | 5 +- Sources/GenIR/CompilerCommandRunner.swift | 2 +- Sources/GenIR/GenIR.swift | 12 +- Sources/GenIR/OutputPostprocessor.swift | 136 +++++++++--------- Sources/GenIR/PIFCache.swift | 4 +- Sources/GenIR/StdoutLogHandler.swift | 82 ----------- Sources/GenIR/Target.swift | 1 + Sources/GenIR/XcodeLogParser.swift | 2 +- Sources/LogHandlers/.gitignore | 8 ++ .../LogHandlers/StdoutLogHandler.swift | 101 +++++++++++++ Tests/GenIRTests/DependencyGraphTests.swift | 1 + Tests/GenIRTests/TestContext.swift | 1 + 18 files changed, 244 insertions(+), 193 deletions(-) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/DependencyGraph.swift (90%) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/DependencyGraphBuilder.swift (84%) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/Edge.swift (72%) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/Node.swift (74%) delete mode 100644 Sources/GenIR/StdoutLogHandler.swift create mode 100644 Sources/LogHandlers/.gitignore create mode 100644 Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index aec2552..6b98756 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -19,7 +19,6 @@ line_length: missing_docs: warning: - - private - public opt_in_rules: diff --git a/Package.swift b/Package.swift index ac47272..43e6f30 100644 --- a/Package.swift +++ b/Package.swift @@ -8,6 +8,11 @@ let package = Package( platforms: [ .macOS(.v12) ], + products: [ + .library(name: "DependencyGraph", targets: ["DependencyGraph"]), + .library(name: "LogHandlers", targets: ["LogHandlers"]), + .executable(name: "gen-ir", targets: ["gen-ir"]) + ], dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), @@ -19,12 +24,27 @@ let package = Package( targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "LogHandlers", + dependencies: [ + .product(name: "Logging", package: "swift-log") + ] + ), + .target( + name: "DependencyGraph", + dependencies: [ + .product(name: "Logging", package: "swift-log"), + .target(name: "LogHandlers") + ] + ), .executableTarget( name: "gen-ir", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), - .product(name: "PIFSupport", package: "PIF") + .product(name: "PIFSupport", package: "PIF"), + .target(name: "DependencyGraph"), + .target(name: "LogHandlers") ], path: "Sources/GenIR" ), diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/DependencyGraph/DependencyGraph.swift similarity index 90% rename from Sources/GenIR/Dependency Graph/DependencyGraph.swift rename to Sources/DependencyGraph/DependencyGraph.swift index f6107f3..25296fd 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/DependencyGraph/DependencyGraph.swift @@ -6,11 +6,12 @@ // import Foundation +import LogHandlers /// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) -class DependencyGraph { +public class DependencyGraph { /// All the nodes in the graph - private(set) var nodes = [String: Node]() + private(set) public var nodes = [String: Node]() /// Adds a node for the associated value to the graph /// - Parameter value: the value associated with the node @@ -28,14 +29,14 @@ class DependencyGraph { /// Finds the node associated with a value /// - Parameter value: the value to look for /// - Returns: the node for which the value is associated, if found - func findNode(for value: Value) -> Node? { + public func findNode(for value: Value) -> Node? { nodes[value.valueName] } /// Returns the dependency 'chain' for the value associated with a node in the graph using a depth-first search /// - Parameter value: the associated value for a node to start the search with /// - Returns: the chain of nodes, starting with the 'bottom' of the dependency subgraph - func chain(for value: Value) -> [Node] { + public func chain(for value: Value) -> [Node] { guard let node = findNode(for: value) else { logger.debug("Couldn't find node for value: \(value.valueName)") return [] @@ -77,7 +78,7 @@ class DependencyGraph { /// Writes a 'dot' graph file to disk /// - Parameter path: the path to write the graph to - func toDot(_ path: String) throws { + public func toDot(_ path: String) throws { var contents = "digraph DependencyGraph {\n" for node in nodes.values { @@ -92,7 +93,7 @@ class DependencyGraph { } extension DependencyGraph: CustomStringConvertible { - var description: String { + public var description: String { var description = "" nodes diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/DependencyGraph/DependencyGraphBuilder.swift similarity index 84% rename from Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift rename to Sources/DependencyGraph/DependencyGraphBuilder.swift index 429c0a2..74ee422 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/DependencyGraph/DependencyGraphBuilder.swift @@ -5,8 +5,10 @@ // Created by Thomas Hedderwick on 28/08/2023. // +import LogHandlers + /// A type that provides dependency relationships between values -protocol DependencyProviding { +public protocol DependencyProviding { /// A type that represents the value of a node associatedtype Value: NodeValue @@ -17,21 +19,21 @@ protocol DependencyProviding { } /// A builder for the DependencyGraph - you should _always_ use this class to build out the `DependencyGraph` -class DependencyGraphBuilder where Value == Provider.Value { +public class DependencyGraphBuilder where Value == Provider.Value { /// The graph the builder will operate on - typealias Graph = DependencyGraph + public typealias Graph = DependencyGraph /// The dependency provider private let provider: Provider /// The built graph - let graph = Graph() + public let graph = Graph() /// Inits the Graph Builder /// - Parameters: /// - provider: the dependency provider for the values /// - values: the values to add to the graph - init(provider: Provider, values: [Value]) { + public init(provider: Provider, values: [Value]) { self.provider = provider values.forEach { add(value: $0) } } diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/DependencyGraph/Edge.swift similarity index 72% rename from Sources/GenIR/Dependency Graph/Edge.swift rename to Sources/DependencyGraph/Edge.swift index c9d8d41..595f57d 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/DependencyGraph/Edge.swift @@ -9,16 +9,17 @@ /// An edge describes the relationship between two Nodes in a graph extension DependencyGraph { - class Edge { + /// An edge represents the connection between two nodes in the graph + public class Edge { /// The source node - let to: DependencyGraph.Node + public let to: DependencyGraph.Node /// The destination node - let from: DependencyGraph.Node + public let from: DependencyGraph.Node /// The relationship between the two nodes - let relationship: Relationship + public let relationship: Relationship /// Description of the relationships between two nodes - enum Relationship { + public enum Relationship { /// From depends on To case dependency /// From is a depender of To @@ -39,13 +40,13 @@ extension DependencyGraph { } extension DependencyGraph.Edge: Equatable { - static func == (_ lhs: DependencyGraph.Edge, rhs: DependencyGraph.Edge) -> Bool { + public static func == (_ lhs: DependencyGraph.Edge, rhs: DependencyGraph.Edge) -> Bool { lhs.to == rhs.to && lhs.from == rhs.from && lhs.relationship == rhs.relationship } } extension DependencyGraph.Edge: Hashable { - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(to) hasher.combine(from) hasher.combine(relationship) @@ -53,6 +54,6 @@ extension DependencyGraph.Edge: Hashable { } extension DependencyGraph.Edge: CustomStringConvertible { - var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} + public var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} } // swiftlint:enable identifier_name diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/DependencyGraph/Node.swift similarity index 74% rename from Sources/GenIR/Dependency Graph/Node.swift rename to Sources/DependencyGraph/Node.swift index cf94ac8..45f8456 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/DependencyGraph/Node.swift @@ -7,21 +7,22 @@ import Foundation -protocol NodeValue: Hashable { +public protocol NodeValue: Hashable { /// The name of this node, this should be unique var valueName: String { get } } extension DependencyGraph { - class Node { + /// A node holds an associated value and edges to other nodes in the graph + public class Node { /// The edges from and to this node - private(set) var edges = [DependencyGraph.Edge]() + private(set) public var edges = [DependencyGraph.Edge]() /// The associated value of this node - let value: Value + public let value: Value /// The name of this node - var valueName: String { + public var valueName: String { value.valueName } @@ -42,13 +43,13 @@ extension DependencyGraph { } extension DependencyGraph.Node: Equatable { - static func == (_ lhs: DependencyGraph.Node, rhs: DependencyGraph.Node) -> Bool { + public static func == (_ lhs: DependencyGraph.Node, rhs: DependencyGraph.Node) -> Bool { lhs.value == rhs.value && lhs.edges == rhs.edges } } extension DependencyGraph.Node: CustomStringConvertible { - var description: String { + public var description: String { var description = "" if !edges.isEmpty { @@ -62,7 +63,7 @@ extension DependencyGraph.Node: CustomStringConvertible { } extension DependencyGraph.Node: Hashable { - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(valueName) hasher.combine(value) } diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index cc18684..e8f8489 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -1,4 +1,5 @@ import Foundation +import LogHandlers /// Manipulates the build cache, if and when needed, to fix up potential invalidations struct BuildCacheManipulator { @@ -78,7 +79,7 @@ struct BuildCacheManipulator { .appendingPathComponent("BuildProductsPath") guard - let archivePath = Self.findConfigurationDirectory(intermediatesBuildPath) + let archivePath = findConfigurationDirectory(intermediatesBuildPath) else { throw Error.directoryNotFound( "Couldn't find or determine a build configuration directory (expected inside of: \(intermediatesBuildPath))" @@ -134,7 +135,7 @@ struct BuildCacheManipulator { /// Tries to find the xcode build configuration directory path inside the given path /// - Parameter path: the path to search /// - Returns: the path to the build configuration directory, if found - private static func findConfigurationDirectory(_ path: URL) -> URL? { + private func findConfigurationDirectory(_ path: URL) -> URL? { let folders = (try? FileManager.default.directories(at: path, recursive: false)) ?? [] guard folders.count != 0 else { diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index b0c3657..087affc 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -6,7 +6,7 @@ // import Foundation -import Logging +import LogHandlers /// A model of the contents of an output file map json typealias OutputFileMap = [String: [String: String]] diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 75e13d2..80cf6e6 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -2,16 +2,14 @@ import Foundation import ArgumentParser import Logging import PIFSupport - -/// Global logger object -var logger = Logger(label: Bundle.main.bundleIdentifier ?? "com.veracode.gen-ir", factory: StdOutLogHandler.init) +import DependencyGraph +import LogHandlers /// The name of the program let programName = CommandLine.arguments.first! /// Command to emit LLVM IR from an Xcode build log -@main -struct IREmitterCommand: ParsableCommand { +@main struct IREmitterCommand: ParsableCommand { static let configuration = CommandConfiguration( commandName: "", abstract: "Consumes an Xcode build log, and outputs LLVM IR, in the bitstream format, to the folder specified", @@ -175,8 +173,6 @@ struct IREmitterCommand: ParsableCommand { /// Reads stdin until an EOF is found /// - Returns: An array of Strings representing stdin split by lines private func readStdin() throws -> [String] { - logger.info("Collating input via pipe") - var results = [String]() while let line = readLine() { @@ -194,8 +190,6 @@ struct IREmitterCommand: ParsableCommand { print("\n\n") } - logger.info("Finished reading from pipe") - return results } } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 6c7f754..7a9d2b7 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -6,6 +6,8 @@ // import Foundation +import DependencyGraph +import LogHandlers /// The `OutputPostprocessor` is responsible for trying to match the IR output of the `CompilerCommandRunner` with the products in the `xcarchive`. /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. @@ -16,7 +18,9 @@ class OutputPostprocessor { let archive: URL /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk - private let dynamicDependencyToPath: [String: URL] + private lazy var dynamicDependencyToPath: [String: URL] = { + dynamicDependencies(in: archive) + }() /// A dependency graph containing the targets in the output archive private let graph: DependencyGraph @@ -25,23 +29,13 @@ class OutputPostprocessor { private let targets: [Target] /// A mapping of targets to their path on disk - private let targetsToPaths: [Target: URL] - - /// The manager to use for file system access - private let manager: FileManager = .default - - /// Initializes the postprocessor - init(archive: URL, output: URL, graph: DependencyGraph) throws { - self.archive = archive - self.graph = graph - self.targets = graph.nodes.map { $0.value.value } - + private lazy var targetsToPaths: [Target: URL] = { let namesToTargets = targets .reduce(into: [String: Target]()) { partial, target in partial[target.productName] = target } - targetsToPaths = try manager + return (try? manager .directories(at: output, recursive: false) .reduce(into: [Target: URL]()) { partial, path in if let target = namesToTargets[path.lastPathComponent] { @@ -49,15 +43,21 @@ class OutputPostprocessor { } else { logger.error("Path (\(path.lastPathComponent)) wasn't found in namesToTargets: \(namesToTargets)") } - } + }) ?? [:] + }() - let namedTargetSet = Set(namesToTargets.values) - let pathTargetSet = Set(targetsToPaths.keys) - let difference = namedTargetSet.symmetricDifference(pathTargetSet) + /// Path to the IR output folder + private let output: URL - logger.debug("target set difference: \(difference)") + /// The manager to use for file system access + private let manager: FileManager = .default - dynamicDependencyToPath = dynamicDependencies(in: self.archive) + /// Initializes the postprocessor + init(archive: URL, output: URL, graph: DependencyGraph) throws { + self.archive = archive + self.output = output + self.graph = graph + self.targets = graph.nodes.map { $0.value.value } } /// Starts the OutputPostprocessor @@ -188,64 +188,64 @@ class OutputPostprocessor { return } - seenConflictingFiles[source]!.append((destinationSize, destinationCreatedDate)) + seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]].append((destinationSize, destinationCreatedDate)) logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") try manager.copyItem(at: source, to: uniqueDestinationURL) } -} -/// Returns a map of dynamic objects in the provided path -/// - Parameter xcarchive: the path to search through -/// - Returns: a mapping of filename to filepath for dynamic objects in the provided path -private func dynamicDependencies(in xcarchive: URL) -> [String: URL] { - let searchPath = baseSearchPath(startingAt: xcarchive) - logger.debug("Using search path for dynamic dependencies: \(searchPath)") - - let dynamicDependencyExtensions = ["framework", "appex", "app"] - - return FileManager.default.filteredContents(of: searchPath, filter: { path in - return dynamicDependencyExtensions.contains(path.pathExtension) - }) - .reduce(into: [String: URL]()) { partialResult, path in - // HACK: For now, insert both with an without extension to avoid any potential issues - partialResult[path.deletingPathExtension().lastPathComponent] = path - partialResult[path.lastPathComponent] = path + /// Returns a map of dynamic objects in the provided path + /// - Parameter xcarchive: the path to search through + /// - Returns: a mapping of filename to filepath for dynamic objects in the provided path + private func dynamicDependencies(in xcarchive: URL) -> [String: URL] { + let searchPath = baseSearchPath(startingAt: xcarchive) + logger.debug("Using search path for dynamic dependencies: \(searchPath)") + + let dynamicDependencyExtensions = ["framework", "appex", "app"] + + return FileManager.default.filteredContents(of: searchPath, filter: { path in + return dynamicDependencyExtensions.contains(path.pathExtension) + }) + .reduce(into: [String: URL]()) { partialResult, path in + // HACK: For now, insert both with an without extension to avoid any potential issues + partialResult[path.deletingPathExtension().lastPathComponent] = path + partialResult[path.lastPathComponent] = path + } } -} -/// Returns the base URL to start searching inside an xcarchive -/// - Parameter path: the original path, should be an xcarchive -/// - Returns: the path to start a dependency search from -private func baseSearchPath(startingAt path: URL) -> URL { - let productsPath = path.appendingPathComponent("Products") - let applicationsPath = productsPath.appendingPathComponent("Applications") - let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") - - /// Returns the first directory found at the given path - /// - Parameter path: the path to search for directories - /// - Returns: the first directory found in the path if one exists - func firstDirectory(at path: URL) -> URL? { - guard - FileManager.default.directoryExists(at: path), - let contents = try? FileManager.default.directories(at: path, recursive: false), - contents.count < 0 - else { - return nil - } + /// Returns the base URL to start searching inside an xcarchive + /// - Parameter path: the original path, should be an xcarchive + /// - Returns: the path to start a dependency search from + private func baseSearchPath(startingAt path: URL) -> URL { + let productsPath = path.appendingPathComponent("Products") + let applicationsPath = productsPath.appendingPathComponent("Applications") + let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") + + /// Returns the first directory found at the given path + /// - Parameter path: the path to search for directories + /// - Returns: the first directory found in the path if one exists + func firstDirectory(at path: URL) -> URL? { + guard + FileManager.default.directoryExists(at: path), + let contents = try? FileManager.default.directories(at: path, recursive: false), + contents.count < 0 + else { + return nil + } - if contents.count > 1 { - logger.debug("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") - } + if contents.count > 1 { + logger.debug("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") + } - return contents.first! - } + return contents.first! + } - for path in [applicationsPath, frameworkPath] { - if let directory = firstDirectory(at: path) { - return directory + for path in [applicationsPath, frameworkPath] { + if let directory = firstDirectory(at: path) { + return directory + } } - } - logger.debug("Couldn't determine the base search path for the xcarchive, using: \(productsPath)") - return productsPath + logger.debug("Couldn't determine the base search path for the xcarchive, using: \(productsPath)") + return productsPath + } } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 8fb6760..9783a89 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -1,5 +1,7 @@ import Foundation import PIFSupport +import DependencyGraph +import LogHandlers /// The PIFCache provides a wrapper around a `PIF.Workspace`. /// It includes a set of helper functions to make operating on the PIF Cache structures easier. @@ -85,7 +87,7 @@ class PIFCache { case let file as PIF.FileReference: result.append(file) case let group as PIF.Group: - resolveChildren(starting: group.children, result: &result) + resolveChildren(starting: group.children) default: logger.debug("Unhandled reference type: \(child)") } diff --git a/Sources/GenIR/StdoutLogHandler.swift b/Sources/GenIR/StdoutLogHandler.swift deleted file mode 100644 index f95a688..0000000 --- a/Sources/GenIR/StdoutLogHandler.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// File.swift -// -// -// Created by Thomas Hedderwick on 30/08/2022. -// - -import Foundation -import Logging - -struct StdOutLogHandler: LogHandler { - subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? { - get { - metadata[key] - } - set(newValue) { - metadata[key] = newValue - } - } - - var metadata: Logging.Logger.Metadata = [:] - - var logLevel: Logging.Logger.Level = .info - - init(_: String) { } - - // swiftlint:disable function_parameter_count - // periphery:ignore:parameters source - func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt - ) { - let levelPrefix = prefix(for: level) - let timestamp = timestamp(for: level) - let lineInfo = lineInfo(for: level, file: file, function: function, line: line) - - print("\(timestamp)\(lineInfo)\(levelPrefix)\(message)") - } - // swiftlint:enable function_parameter_count - - private func prefix(for level: Logger.Level) -> String { - switch level { - case .trace: - return "[TRACE] " - case .debug: - return "[DEBUG] " - case .info: - return "" - case .notice: - return "[~] " - case .warning: - return "[~] " - case .error: - return "[!] " - case .critical: - return "[!!!] " - } - } - - private func timestamp(for level: Logger.Level) -> String { - switch level { - case .trace, .debug, .notice, .warning, .error, .critical: - return "\(Date.now) " - case .info: - return "" - } - } - - private func lineInfo(for level: Logger.Level, file: String, function: String, line: UInt) -> String { - switch level { - case .trace, .debug, .notice, .warning, .error, .critical: - return "[\(file):\(line) \(function)] " - case .info: - return "" - } - } -} diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index 0453d1f..ed018d6 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -7,6 +7,7 @@ import Foundation import PIFSupport +import DependencyGraph /// A Target represents a product to build (app, framework, plugin, package) class Target { diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index 3f07a34..4f26553 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -6,7 +6,7 @@ // import Foundation -import Logging +import LogHandlers /// An XcodeLogParser extracts targets and their compiler commands from a given Xcode build log class XcodeLogParser { diff --git a/Sources/LogHandlers/.gitignore b/Sources/LogHandlers/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/Sources/LogHandlers/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift b/Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift new file mode 100644 index 0000000..1cd36d3 --- /dev/null +++ b/Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift @@ -0,0 +1,101 @@ +// +// StdoutLogHandler.swift +// +// +// Created by Thomas Hedderwick on 30/08/2022. +// + +import Foundation +import Logging + +/// All module logger (yes because swift-log hasn't really solved for setting level globally without passing an instance around everywhere) +public var logger = Logger(label: "LogHandler", factory: StdIOStreamLogHandler.init) + +struct StdIOTextStream: TextOutputStream { + static let stdout = StdIOTextStream(file: Darwin.stdout) + static let stderr = StdIOTextStream(file: Darwin.stderr) + + let file: UnsafeMutablePointer + + func write(_ string: String) { + var string = string + string.makeContiguousUTF8() + string.utf8.withContiguousStorageIfAvailable { bytes in + flockfile(file) + defer { funlockfile(file) } + + fwrite(bytes.baseAddress!, 1, bytes.count, file) + + fflush(file) + } + } +} + +public struct StdIOStreamLogHandler: LogHandler { + internal typealias SendableTextOutputStream = TextOutputStream & Sendable + + private let stdout = StdIOTextStream.stdout + + public subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? { + get { metadata[key] } + set(newValue) { metadata[key] = newValue } + } + + public var metadata: Logging.Logger.Metadata = [:] + public var logLevel: Logging.Logger.Level = .info + + public init() {} + + public init(_: String) {} + + // periphery:ignore:parameters count + // swiftlint:disable:next function_parameter_count + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { + let lineInfo = lineInfo(for: level, file: file, function: function, line: line) + + stdout.write("\(timestamp)\(lineInfo)\(levelPrefix)\(message)\n") + } + + private var levelPrefix: String { + switch logLevel { + case .trace: + return "[TRACE] " + case .debug: + return "[DEBUG] " + case .info: + return "" + case .notice, .warning: + return "[~] " + case .error: + return "[!] " + case .critical: + return "[!!!] " + } + } + + private var timestamp: String { + switch logLevel { + case .trace, .debug, .notice, .warning, .error, .critical: + return "\(Date.now) " + case .info: + return "" + } + } + + private func lineInfo(for level: Logger.Level, file: String, function: String, line: UInt) -> String { + switch level { + case .trace, .debug, .notice, .warning, .error, .critical: + return "[\(file):\(line) \(function)] " + case .info: + return "" + } + } +} diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 2fee28d..d0d4ef2 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -1,5 +1,6 @@ import XCTest @testable import gen_ir +@testable import DependencyGraph final class DependencyGraphTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index a104aac..40f7230 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -1,5 +1,6 @@ import Foundation @testable import gen_ir +import DependencyGraph import XCTest /// TestContext is a grouping of convenance functions and a context to ease the burden of testing Gen IR From a942a8ed726c0014016fa298537d2b9d14b93fd5 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:17 -0400 Subject: [PATCH 47/61] Specialize the responsibilities of some Gen-IR constructs. This attempts to more accurately define what Gen-IR's idea of a target is, which is a bit different than the PIF representation. Not every target is considered buildable, so we also split out the storage of commands and pass them directly to CompilerCommandRunner. --- Sources/GenIR/CompilerCommandRunner.swift | 33 ++++------ Sources/GenIR/GenIR.swift | 12 ++-- Sources/GenIR/OutputPostprocessor.swift | 78 +++++++++++++++-------- Sources/GenIR/PIFCache.swift | 34 ++++++---- Sources/GenIR/Target.swift | 77 +++++++++++----------- 5 files changed, 133 insertions(+), 101 deletions(-) diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index 087affc..ae304ba 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -17,6 +17,11 @@ typealias OutputFileMap = [String: [String: String]] /// /// > clang will emit LLVM BC to the current working directory in a named file. In this case, the runner will move the files from temporary storage to the output location struct CompilerCommandRunner { + enum Error: Swift.Error { + /// Command runner failed to parse the command for the required information + case failedToParse(String) + } + /// The directory to place the LLVM BC output private let output: URL @@ -26,11 +31,6 @@ struct CompilerCommandRunner { /// Manager used to access the file system private let fileManager = FileManager.default - enum Error: Swift.Error { - /// Command runner failed to parse the command for the required information - case failedToParse(String) - } - /// Run without running the commands private let dryRun: Bool @@ -47,31 +47,26 @@ struct CompilerCommandRunner { /// Starts the runner /// - Parameter targets: the targets holding the commands to run - func run(targets: [Target]) throws { + func run(targets: [Target], commands: [String: [CompilerCommand]]) throws { // Quick, do a hack! try buildCacheManipulator.manipulate() - let tempDirectory = try fileManager.temporaryDirectory(named: "gen-ir-\(UUID().uuidString)") - defer { try? fileManager.removeItem(at: tempDirectory) } - logger.debug("Using temp directory as working directory: \(tempDirectory.filePath)") - - let totalCommands = targets - .map { $0.commands.count } - .reduce(0, +) - + let totalCommands = commands.reduce(0) { $0 + $1.value.count } logger.info("Total commands to run: \(totalCommands)") var totalModulesRun = 0 - for target in targets { + for target in targets.filter({ $0.containsBitcode }) { + guard let targetCommands = commands[target.name] else { + continue + } + logger.info("Operating on target: \(target.name). Total modules processed: \(totalModulesRun)") - totalModulesRun += try run(commands: target.commands, for: target.productName, at: tempDirectory) + totalModulesRun += try run(commands: targetCommands, for: target.productName, at: output) } - try fileManager.moveItemReplacingExisting(from: tempDirectory, to: output) - - let uniqueModules = try fileManager.files(at: output, withSuffix: ".bc").count + let uniqueModules = Set(try fileManager.files(at: output, withSuffix: ".bc")).count logger.info("Finished compiling all targets. Unique modules: \(uniqueModules)") } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 80cf6e6..0c2d6af 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -112,7 +112,7 @@ let programName = CommandLine.arguments.first! // Find and parse the PIF cache let pifCache = try PIFCache(buildCache: log.buildCachePath) - let targets = Target.targets(from: pifCache.targets, with: log.targetCommands) + let targets = pifCache.targets.map { Target(from: $0) } let builder = DependencyGraphBuilder( provider: .init(targets: targets, cache: pifCache), @@ -139,16 +139,20 @@ let programName = CommandLine.arguments.first! dryRun: dryRun ) + let tempDirectory = try FileManager.default.temporaryDirectory(named: "gen-ir-\(UUID().uuidString)") + defer { try? FileManager.default.removeItem(at: tempDirectory) } + logger.info("Using temp directory as working directory: \(tempDirectory)") + let runner = CompilerCommandRunner( - output: output, + output: tempDirectory, buildCacheManipulator: buildCacheManipulator, dryRun: dryRun ) - try runner.run(targets: targets) + try runner.run(targets: targets, commands: log.targetCommands) let postprocessor = try OutputPostprocessor( archive: archive, - output: output, + output:tempDirectory, graph: graph ) diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 7a9d2b7..a68ee15 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -28,6 +28,12 @@ class OutputPostprocessor { /// The targets in this archive private let targets: [Target] + private lazy var guidToTargets: [String: Target] = { + targets.reduce(into: [String: Target]()) { partial, target in + partial[target.guid] = target + } + }() + /// A mapping of targets to their path on disk private lazy var targetsToPaths: [Target: URL] = { let namesToTargets = targets @@ -63,58 +69,74 @@ class OutputPostprocessor { /// Starts the OutputPostprocessor /// - Parameter targets: the targets to operate on func process() throws { - // TODO: remove 'static' deps so we don't duplicate them in the submission? + var processed: Set = [] + _ = try targets - .map { try process(target: $0) } + .filter { $0.isPackageProduct } + .map { try process(target: $0, processed: &processed) } + + logger.info("Processed \(processed.count) top-level IR targets") + + try copyToArchive() } /// Processes an individual target /// - Parameters: /// - target: the target to process /// - Returns: - private func process(target: Target) throws -> Set { - // TODO: we need better handling of swift package products and targets in the dependency graph or we fail to move dependencies here + private func process(target: Target, processed: inout Set) throws { + guard processed.insert(target).inserted else { + return + } + + let targetDirectory = output.appendingPathComponent(target.productName) + + // This directory may already exist if the `CompilerCommandRunner` has already built an + // artifact at this location. + try? FileManager.default.createDirectory(at: targetDirectory, withIntermediateDirectories: false) + let chain = graph.chain(for: target) logger.debug("Chain for target: \(target.productName):\n\(chain.map { "\($0)\n" })") chain.forEach { logger.debug("\($0)") } - // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent - var processed = Set() - for node in chain { logger.debug("Processing Node: \(node.valueName)") // Ensure node is not a dynamic dependency guard dynamicDependencyToPath[node.value.productName] == nil else { continue } - // Only care about moving dependencies into dependers - check this node's edges to dependent relationships - let dependers = node.edges - .filter { $0.relationship == .depender } - .map { $0.to } + try process(target: node.value, processed: &processed) + + let nodeFolderPath = output.appendingPathComponent(node.value.productName) - // Move node's IR into depender's IR folder - guard let nodeFolderPath = targetsToPaths[node.value] else { - logger.debug("IR folder for node: \(node) is nil") - continue + do { + try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: targetDirectory) + } catch { + logger.debug("Copy error: \(error)") } + } + } - for depender in dependers { - guard let dependerFolderPath = targetsToPaths[depender.value] else { - logger.debug("IR folder for depender node \(depender) is nil") - continue - } + private func copyToArchive() throws { + let irDirectory = archive.appendingPathComponent("IR") + try? FileManager.default.createDirectory(at: irDirectory, withIntermediateDirectories: false) + + let nodes = targets.compactMap { graph.findNode(for: $0) } + + for node in nodes { + let dependers = node.edges.filter { $0.relationship == .depender } + let targetPath = output.appendingPathComponent(node.value.productName) + let archivePath = irDirectory.appendingPathComponent(node.value.productName) - // Move the dependency IR (the node) to the depender (the thing depending on this node) - do { - try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: dependerFolderPath) - } catch { - logger.debug("Copy error: \(error)") + if dynamicDependencyToPath[node.value.productName] != nil || (dependers.count == 0 && !node.value.isSwiftPackage) { + logger.debug("Copying \(node.value.productName) with name \(node.value.productName)") + if !FileManager.default.directoryExists(at: targetPath) { + logger.debug("No target found for target \(node.value.guid) with name \(node.value.productName)") + continue } - processed.insert(nodeFolderPath) + try FileManager.default.copyItem(at: targetPath, to: archivePath) } } - - return processed } // TODO: Write tests for this. diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 9783a89..b8e4c11 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -19,11 +19,10 @@ class PIFCache { } /// All targets contained by the workspace - var targets: [PIF.BaseTarget] { - workspace - .projects - .flatMap { $0.targets } - } + let targets: [PIF.BaseTarget] + + /// Maps GUIDs to their respective targets for easy lookup. + private let guidToTargets: [PIF.GUID: PIF.BaseTarget] enum Error: Swift.Error { case nonexistentCache(String) @@ -33,7 +32,7 @@ class PIFCache { /// Initializes the PIF Cache from a build cache /// - Parameter buildCache: path to the Xcode DerivedData Build Cache init(buildCache: URL) throws { - self.pifCachePath = try Self.pifCachePath(in: buildCache) + pifCachePath = try Self.pifCachePath(in: buildCache) do { let cache = try PIFCacheParser(cachePath: pifCachePath, logger: logger) @@ -41,6 +40,15 @@ class PIFCache { } catch { throw Error.pifError(error) } + + targets = workspace.projects.flatMap { $0.targets } + guidToTargets = targets.reduce(into: [PIF.GUID: PIF.BaseTarget]()) { partial, target in + partial[target.guid] = target + } + } + + func target(guid: PIF.GUID) -> PIF.BaseTarget? { + guidToTargets[guid] } /// Finds the PIF Cache in the Xcode Build Cache. This can vary depending on the build system used. @@ -151,7 +159,7 @@ struct PIFDependencyProvider: DependencyProviding { self.guidToTargets = targets .reduce(into: [PIF.GUID: Target]()) { partial, target in - partial[target.baseTarget.guid] = target + partial[target.guid] = target } } @@ -176,10 +184,10 @@ struct PIFDependencyProvider: DependencyProviding { let productName = String(packageProductGUID.dropFirst(productToken.count)) - let productTargetDependencies = product - .baseTarget + let productTargetDependencies = cache.target(guid: product.guid)? .dependencies .filter { $0.targetGUID.starts(with: targetToken) } + ?? [] let productUnderlyingTargets = productTargetDependencies .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } @@ -205,17 +213,16 @@ struct PIFDependencyProvider: DependencyProviding { func dependencies(for value: Target) -> [Target] { // Direct dependencies - let dependencyTargetGUIDs = value - .baseTarget + let dependencyTargetGUIDs = cache.target(guid: value.guid)? .dependencies .map { $0.targetGUID } .compactMap { resolveSwiftPackage($0) } + ?? [] // Framework build phase dependencies // NOTE: Previously we just cast this - all of a sudden with pods this is broken // Not the end of the world - just as quick to do a dictionary lookup - let frameworkGUIDs = value - .baseTarget + let frameworkGUIDs = cache.target(guid: value.guid)? .buildPhases .flatMap { $0.buildFiles } // .compactMap { $0 as? PIF.FrameworksBuildPhase } @@ -226,6 +233,7 @@ struct PIFDependencyProvider: DependencyProviding { } } .compactMap { cache.frameworks[$0]?.guid } + ?? [] let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index ed018d6..e9ce651 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -9,50 +9,53 @@ import Foundation import PIFSupport import DependencyGraph -/// A Target represents a product to build (app, framework, plugin, package) +/// Represents a product to build (app, framework, plugin, package). It contains the identifying +/// information about a target and its output when it is built or archived. In the future the +/// `PIF` package will likely be modified so that it is usable within the context of Gen-IR +/// directly. class Target { - /// The name of the target - var name: String { baseTarget.name } + /// Target identifier. + let guid: String + /// Name of the target. + let name: String + /// The product name refers to the output of this target when it is built or copied into an archive. + let productName: String - /// The product name of the target, if one is available, otherwise the name - /// This can happen when the product is not directly buildable (such as a package product or aggregate) - var productName: String { - if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { - return target.productName - } - - return baseTarget.name + /// Returns true if this target produces bitcode. This is used to determine if this target is buildable. + /// For packages we only use the `PACKAGE-TARGET` for the object file, since this is used by the + /// other targets. + var containsBitcode: Bool { + get { guid == "PACKAGE-TARGET:\(name)" || !guid.hasPrefix("PACKAGE-") } } - // TODO: we need to handle SPM's insane naming scheme for products here ^ including the potential of a dynamic variant - - /// The `PIF.BaseTarget` structure that backs this target - let baseTarget: PIF.BaseTarget - /// The `CompilerCommands` related to building this target - let commands: [CompilerCommand] - - /// Initializes a target with the given backing target and commands - /// - Parameters: - /// - baseTarget: the underlying `PIF.BaseTarget` - /// - commands: the commands related to this target - init(baseTarget: PIF.BaseTarget, commands: [CompilerCommand]) { - self.baseTarget = baseTarget - self.commands = commands + /// Returns true if this target produces a package product that will be included in the archive. + /// For simplicity we say this can be anything that is not a `PACKAGE-TARGET`, which will just be + /// an object file. The `dynamicTargetVariantGuid` of a `PACKAGE-TARGET` is technically a framework, + /// but we are using the `PACKAGE-PRODUCT` for that, which depends on it. + var isPackageProduct: Bool { + get { !guid.hasPrefix("PACKAGE-TARGET:") } } -} -extension Target { - /// Helper function to map targets and commands to an array of targets - /// - Parameters: - /// - targets: the `PIF.BaseTarget`s that will back the new targets - /// - targetsToCommands: a mapping of target names to the `CompilerCommands` that relate to them - /// - Returns: the newly created targets - static func targets(from targets: [PIF.BaseTarget], with targetsToCommands: [String: [CompilerCommand]]) -> [Target] { - targets - .map { - Target(baseTarget: $0, commands: targetsToCommands[$0.name] ?? []) - } + var isSwiftPackage: Bool { + get { guid.hasPrefix("PACKAGE-") } } + + init(from baseTarget: PIF.BaseTarget) { + guid = baseTarget.guid + name = baseTarget.name + productName = if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { + target.productName + } else if baseTarget.guid == "PACKAGE-PRODUCT:\(baseTarget.name)" { + // The `PACKAGE-PRODUCT` for a package does not have a product reference name so + // we do not have a proper extension. For now we assume it is a framework and add + // the extension. A better way may be to follow the `dynamicTargetVariantGuid` of + // the `PACKAGE-TARGET` as this appears to provide the correct name if available. + baseTarget.name.appending(".framework") + } else { + // Fallback to the target's name if we are unable to determine a proper product name. + baseTarget.name + } + } } extension Target: NodeValue { From e1c0f455ef48bd12e32a77da2c8ea400e14b7f8b Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:18 -0400 Subject: [PATCH 48/61] Attempt to resolve Objective-C dependencies. --- Sources/GenIR/PIFCache.swift | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index b8e4c11..1b84f7d 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -113,20 +113,29 @@ class PIFCache { .flatMap { fileReferences(for: $0) } .filter { $0.fileType == "wrapper.framework" } - // Now, stupidly, we have to do a name lookup on the path and use that to look up a target let frameworks = targets .compactMap { $0 as? PIF.Target } .filter { $0.productType == .framework } + + let frameworkGUIDs = frameworks .reduce(into: [String: PIF.Target]()) { partial, target in - let key = target.productName.isEmpty ? target.guid : target.productName - partial[key] = target + partial[target.guid] = target } - return frameworkFileReferences + // Map product names to targets + let frameworkProducts = frameworks + .reduce(into: [String: PIF.Target]()) { partial, target in + if !target.productName.isEmpty { + partial[target.productName] = target + } + } + + let frameworkReferenceTargets = frameworkFileReferences .reduce(into: [PIF.GUID: PIF.Target]()) { partial, fileReference in - // Use the _file reference_ GUID as the key here - we're looking up frameworks by their file reference and not target GUID! - partial[fileReference.guid] = frameworks[fileReference.path] + partial[fileReference.guid] = frameworkProducts[fileReference.path] } + + return frameworkGUIDs.merging(frameworkReferenceTargets) { _, new in new } }() } @@ -225,11 +234,10 @@ struct PIFDependencyProvider: DependencyProviding { let frameworkGUIDs = cache.target(guid: value.guid)? .buildPhases .flatMap { $0.buildFiles } - // .compactMap { $0 as? PIF.FrameworksBuildPhase } .compactMap { switch $0.reference { - case let .file(guid): return guid - case .target: return nil + case let .file(guid): return cache.frameworks[guid]?.guid + case .target(let guid): return guid } } .compactMap { cache.frameworks[$0]?.guid } From b6bccec2fbd0b78c8ccf53ee0ee14e5eb579e928 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:18 -0400 Subject: [PATCH 49/61] Update unit tests. --- Tests/GenIRTests/MultipleAppTests.swift | 4 +-- Tests/GenIRTests/TestContext.swift | 2 +- Tests/GenIRTests/UmbrellaTests.swift | 8 ++--- Tests/GenIRTests/WorkspaceTests.swift | 43 +++++-------------------- 4 files changed, 15 insertions(+), 42 deletions(-) diff --git a/Tests/GenIRTests/MultipleAppTests.swift b/Tests/GenIRTests/MultipleAppTests.swift index 9791fa0..90b0042 100644 --- a/Tests/GenIRTests/MultipleAppTests.swift +++ b/Tests/GenIRTests/MultipleAppTests.swift @@ -18,7 +18,7 @@ final class MultipleAppTests: XCTestCase { let app = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp" })) let copy = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp Copy" })) - XCTAssertEqual(app.commands.count, 3) - XCTAssertEqual(copy.commands.count, 3) + XCTAssertEqual(context.logParser.targetCommands[app.name]?.count, 3) + XCTAssertEqual(context.logParser.targetCommands[copy.name]?.count, 3) } } diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 40f7230..401fa92 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -145,7 +145,7 @@ class TestContext { /// Generate the Targets for this context lazy var targets: [Target] = { - Target.targets(from: pifCache.targets, with: logParser.targetCommands) + pifCache.targets.map { Target(from: $0) } }() /// Generate the dependency graph for this context diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index 13a40af..2b86381 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -11,10 +11,10 @@ final class UmbrellaTests: XCTestCase { let scheme = "Umbrella" let targetsToFiles = [ - "Common.framework": ["OrgModel.bc"].sorted(), - "Networking.framework": ["Networking.bc"].sorted(), - "Pods_Umbrella.framework": [], - "Umbrella.framework": ["GetOrg.bc"].sorted() + "Common.framework": ["Common-dummy.bc", "Common_vers.bc", "OrgModel.bc"].sorted(), + "Networking.framework": ["Networking-dummy.bc", "Networking.bc", "Networking_vers.bc"].sorted(), + "Pods_Umbrella.framework": ["Pods-Umbrella-dummy.bc", "Pods_Umbrella_vers.bc"], + "Umbrella.framework": ["GetOrg.bc", "Umbrella_vers.bc"].sorted() ] func testUmbrellaTargets() throws { diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 711e66f..d9f56d3 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -11,8 +11,8 @@ final class WorkspaceTests: XCTestCase { let scheme = "App" static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] - static let commonIRFiles: Set = ["Model.bc"] - static let frameworkIRFiles: Set = ["Framework.bc"] + static let commonIRFiles: Set = ["Model.bc", "Common_vers.bc"] + static let frameworkIRFiles: Set = ["Framework.bc", "Framework_vers.bc"] static let sfSafeSymbolsIRFiles: Set = [ "NSImageExtension.bc", "SFSymbol+1.0.bc", @@ -62,45 +62,18 @@ final class WorkspaceTests: XCTestCase { dumpDependencyGraph: false ) - // Check dependencies made it to the right place + // Check dependencies made it to the right place. All dependencies should be statically + // linked and appear under the .app directory. let appIRPath = context.archive.appending(path: "IR/App.app/") - let commonIRPath = context.archive.appending(path: "IR/Common.framework/") - let frameworkIRPath = context.archive.appending(path: "IR/Framework.framework/") - let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols.framework/") - let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - let commonIRPathContents = try FileManager.default.contentsOfDirectory(at: commonIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - let frameworkIRPathContents = try FileManager.default.contentsOfDirectory(at: frameworkIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - let sfSafeSymbolsIRPathContents = try FileManager.default.contentsOfDirectory(at: sfSafeSymbolsIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - - let expectedAppIRFiles = Self.appIRFiles + let expectedIRFiles = Self.appIRFiles .union(Self.commonIRFiles) .union(Self.frameworkIRFiles) .union(Self.sfSafeSymbolsIRFiles) - .reduce(into: Set(), { $0.insert($1) }) - let expectedFrameworkIRFiles = Self.frameworkIRFiles - .union(Self.commonIRFiles) - .union(Self.sfSafeSymbolsIRFiles) - .reduce(into: Set(), { $0.insert($1) }) - - let expectedCommonIRFiles = Self.commonIRFiles - .reduce(into: Set(), { $0.insert($1) }) - - let expectedSFSafeSymbolsIRFiles = Self.sfSafeSymbolsIRFiles - .reduce(into: Set(), { $0.insert($1) }) + let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) + .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedAppIRFiles.symmetricDifference(appIRPathContents))") - XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual \(expectedFrameworkIRFiles.symmetricDifference(frameworkIRPathContents))") - XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual \(expectedCommonIRFiles.symmetricDifference(commonIRPathContents))") - XCTAssertEqual( - expectedSFSafeSymbolsIRFiles, - sfSafeSymbolsIRPathContents, - "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))" - ) + XCTAssertEqual(expectedIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedIRFiles.symmetricDifference(appIRPathContents))") } } From f442e1c0ede9aa4f46c323d7ca3f4ac88c4b56e1 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:18 -0400 Subject: [PATCH 50/61] Cleanup some code and fix lint issues. --- Sources/GenIR/CompilerCommandRunner.swift | 6 ++- Sources/GenIR/GenIR.swift | 2 +- Sources/GenIR/PIFCache.swift | 2 +- Sources/GenIR/Target.swift | 64 +++++++++++------------ 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index ae304ba..6e25247 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -51,12 +51,14 @@ struct CompilerCommandRunner { // Quick, do a hack! try buildCacheManipulator.manipulate() - let totalCommands = commands.reduce(0) { $0 + $1.value.count } + let totalCommands = commands + .map { $0.value.count } + .reduce(0, +) logger.info("Total commands to run: \(totalCommands)") var totalModulesRun = 0 - for target in targets.filter({ $0.containsBitcode }) { + for target in targets.filter({ $0.isBuildable }) { guard let targetCommands = commands[target.name] else { continue } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 0c2d6af..d996497 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -152,7 +152,7 @@ let programName = CommandLine.arguments.first! let postprocessor = try OutputPostprocessor( archive: archive, - output:tempDirectory, + output: tempDirectory, graph: graph ) diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 1b84f7d..ad6500b 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -231,7 +231,7 @@ struct PIFDependencyProvider: DependencyProviding { // Framework build phase dependencies // NOTE: Previously we just cast this - all of a sudden with pods this is broken // Not the end of the world - just as quick to do a dictionary lookup - let frameworkGUIDs = cache.target(guid: value.guid)? + let frameworkGUIDs = cache.target(guid: value.guid)? .buildPhases .flatMap { $0.buildFiles } .compactMap { diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index e9ce651..e609d46 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -14,48 +14,44 @@ import DependencyGraph /// `PIF` package will likely be modified so that it is usable within the context of Gen-IR /// directly. class Target { - /// Target identifier. - let guid: String - /// Name of the target. - let name: String - /// The product name refers to the output of this target when it is built or copied into an archive. - let productName: String + /// Target identifier. + let guid: String + /// Name of the target. + let name: String + /// The product name refers to the output of this target when it is built or copied into an archive. + let productName: String - /// Returns true if this target produces bitcode. This is used to determine if this target is buildable. - /// For packages we only use the `PACKAGE-TARGET` for the object file, since this is used by the - /// other targets. - var containsBitcode: Bool { - get { guid == "PACKAGE-TARGET:\(name)" || !guid.hasPrefix("PACKAGE-") } - } + /// Returns true if this target is buildable. For packages we only use the `PACKAGE-TARGET` + /// for the object file, since this is used by the other targets. + let isBuildable: Bool /// Returns true if this target produces a package product that will be included in the archive. /// For simplicity we say this can be anything that is not a `PACKAGE-TARGET`, which will just be /// an object file. The `dynamicTargetVariantGuid` of a `PACKAGE-TARGET` is technically a framework, /// but we are using the `PACKAGE-PRODUCT` for that, which depends on it. - var isPackageProduct: Bool { - get { !guid.hasPrefix("PACKAGE-TARGET:") } - } + let isPackageProduct: Bool - var isSwiftPackage: Bool { - get { guid.hasPrefix("PACKAGE-") } - } + let isSwiftPackage: Bool - init(from baseTarget: PIF.BaseTarget) { - guid = baseTarget.guid - name = baseTarget.name - productName = if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { - target.productName - } else if baseTarget.guid == "PACKAGE-PRODUCT:\(baseTarget.name)" { - // The `PACKAGE-PRODUCT` for a package does not have a product reference name so - // we do not have a proper extension. For now we assume it is a framework and add - // the extension. A better way may be to follow the `dynamicTargetVariantGuid` of - // the `PACKAGE-TARGET` as this appears to provide the correct name if available. - baseTarget.name.appending(".framework") - } else { - // Fallback to the target's name if we are unable to determine a proper product name. - baseTarget.name - } - } + init(from baseTarget: PIF.BaseTarget) { + guid = baseTarget.guid + name = baseTarget.name + productName = if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { + target.productName + } else if baseTarget.guid == "PACKAGE-PRODUCT:\(baseTarget.name)" { + // The `PACKAGE-PRODUCT` for a package does not have a product reference name so + // we do not have a proper extension. For now we assume it is a framework and add + // the extension. A better way may be to follow the `dynamicTargetVariantGuid` of + // the `PACKAGE-TARGET` as this appears to provide the correct name if available. + baseTarget.name.appending(".framework") + } else { + // Fallback to the target's name if we are unable to determine a proper product name. + baseTarget.name + } + isBuildable = guid == "PACKAGE-TARGET:\(name)" || !guid.hasPrefix("PACKAGE-") + isPackageProduct = !guid.hasPrefix("PACKAGE-TARGET:") + isSwiftPackage = guid.hasPrefix("PACKAGE-") + } } extension Target: NodeValue { From 8f7ae61eb372a1f5397fb9ecb886f17190ec4a6a Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Thu, 11 Jul 2024 13:39:03 -0400 Subject: [PATCH 51/61] Fix an issue where duplicate files were being generated. --- Sources/GenIR/GenIR.swift | 2 +- Sources/GenIR/OutputPostprocessor.swift | 199 +++++++----------------- 2 files changed, 58 insertions(+), 143 deletions(-) diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index d996497..50047cb 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -152,7 +152,7 @@ let programName = CommandLine.arguments.first! let postprocessor = try OutputPostprocessor( archive: archive, - output: tempDirectory, + build: tempDirectory, graph: graph ) diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index a68ee15..790c840 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -28,118 +28,84 @@ class OutputPostprocessor { /// The targets in this archive private let targets: [Target] - private lazy var guidToTargets: [String: Target] = { - targets.reduce(into: [String: Target]()) { partial, target in - partial[target.guid] = target - } - }() - - /// A mapping of targets to their path on disk - private lazy var targetsToPaths: [Target: URL] = { - let namesToTargets = targets - .reduce(into: [String: Target]()) { partial, target in - partial[target.productName] = target - } - - return (try? manager - .directories(at: output, recursive: false) - .reduce(into: [Target: URL]()) { partial, path in - if let target = namesToTargets[path.lastPathComponent] { - partial[target] = path - } else { - logger.error("Path (\(path.lastPathComponent)) wasn't found in namesToTargets: \(namesToTargets)") - } - }) ?? [:] - }() - - /// Path to the IR output folder - private let output: URL + /// Path to the folder containing build artifacts + private let build: URL /// The manager to use for file system access private let manager: FileManager = .default + /// Cache of all files that have been copied to the IR output folder. + /// This maps a destination to a set of source URLs. This is used to determine if a file has already + /// been copied without having to waste cycles comparing file contents. If multiple source files + /// map to the same destination, then they will be given unique file names when copied to the IR folder. + private var copiedFiles: [URL: Set] = [:] + /// Initializes the postprocessor - init(archive: URL, output: URL, graph: DependencyGraph) throws { + init(archive: URL, build: URL, graph: DependencyGraph) throws { self.archive = archive - self.output = output + self.build = build self.graph = graph self.targets = graph.nodes.map { $0.value.value } } /// Starts the OutputPostprocessor - /// - Parameter targets: the targets to operate on func process() throws { - var processed: Set = [] + let nodes = targets.compactMap { graph.findNode(for: $0) } + let output = archive.appendingPathComponent("IR") - _ = try targets - .filter { $0.isPackageProduct } - .map { try process(target: $0, processed: &processed) } + try manager.createDirectory(at: output, withIntermediateDirectories: false) - logger.info("Processed \(processed.count) top-level IR targets") + for node in nodes { + let dependers = node.edges.filter { $0.relationship == .depender } - try copyToArchive() - } + guard dynamicDependencyToPath[node.value.productName] != nil || (dependers.count == 0 && !node.value.isSwiftPackage) else { + continue + } - /// Processes an individual target - /// - Parameters: - /// - target: the target to process - /// - Returns: - private func process(target: Target, processed: inout Set) throws { - guard processed.insert(target).inserted else { - return - } + let irDirectory = output.appendingPathComponent(node.value.productName) + let buildDirectory = build.appendingPathComponent(node.value.productName) - let targetDirectory = output.appendingPathComponent(target.productName) + logger.debug("Copying \(node.value.guid) with name \(node.value.productName)") - // This directory may already exist if the `CompilerCommandRunner` has already built an - // artifact at this location. - try? FileManager.default.createDirectory(at: targetDirectory, withIntermediateDirectories: false) + // If there is a build directory for this target then copy the artifacts over to the IR + // folder. Otherwise we will create an empty directory and that will contain the artifacts + // of the dependency chain. + if manager.directoryExists(at: buildDirectory) { + try manager.copyItem(at: buildDirectory, to: irDirectory) + } else { + try manager.createDirectory(at: irDirectory, withIntermediateDirectories: false) + } - let chain = graph.chain(for: target) + // Copy over this target's static dependencies + var processed: Set = [] + try copyDependencies(for: node.value, to: irDirectory, processed: &processed) + } + } - logger.debug("Chain for target: \(target.productName):\n\(chain.map { "\($0)\n" })") - chain.forEach { logger.debug("\($0)") } + private func copyDependencies(for target: Target, to irDirectory: URL, processed: inout Set) throws { + guard processed.insert(target).inserted else { + return + } - for node in chain { + for node in graph.chain(for: target) { logger.debug("Processing Node: \(node.valueName)") - // Ensure node is not a dynamic dependency - guard dynamicDependencyToPath[node.value.productName] == nil else { continue } - try process(target: node.value, processed: &processed) - - let nodeFolderPath = output.appendingPathComponent(node.value.productName) - - do { - try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: targetDirectory) - } catch { - logger.debug("Copy error: \(error)") - } - } - } + // Do not copy dynamic dependencies + guard dynamicDependencyToPath[node.value.productName] == nil else { continue } - private func copyToArchive() throws { - let irDirectory = archive.appendingPathComponent("IR") - try? FileManager.default.createDirectory(at: irDirectory, withIntermediateDirectories: false) + try copyDependencies(for: node.value, to: irDirectory, processed: &processed) - let nodes = targets.compactMap { graph.findNode(for: $0) } - - for node in nodes { - let dependers = node.edges.filter { $0.relationship == .depender } - let targetPath = output.appendingPathComponent(node.value.productName) - let archivePath = irDirectory.appendingPathComponent(node.value.productName) - - if dynamicDependencyToPath[node.value.productName] != nil || (dependers.count == 0 && !node.value.isSwiftPackage) { - logger.debug("Copying \(node.value.productName) with name \(node.value.productName)") - if !FileManager.default.directoryExists(at: targetPath) { - logger.debug("No target found for target \(node.value.guid) with name \(node.value.productName)") - continue + let buildDirectory = build.appendingPathComponent(node.value.productName) + if manager.directoryExists(at: buildDirectory) { + do { + try copyContentsOfDirectoryMergingDifferingFiles(at: buildDirectory, to: irDirectory) + } catch { + logger.debug("Copy error: \(error)") } - try FileManager.default.copyItem(at: targetPath, to: archivePath) } } } - // TODO: Write tests for this. /// Copies the contents of a directory from source to destination, merging files that share the same name but differ in attributes /// - Parameters: /// - source: the source directory to copy the contents of @@ -147,72 +113,21 @@ class OutputPostprocessor { func copyContentsOfDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { let files = try manager.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) - /// Source and destination paths - typealias SourceAndDestination = (source: URL, destination: URL) - // Get two arrays of file paths of the source and destination file where: - // 1) destination already exists - // 2) destination doesn't already exist - let (existing, nonexisting) = files + let sourceAndDestinations = files .map { ( source: source.appendingPathComponent($0.lastPathComponent), destination: destination.appendingPathComponent($0.lastPathComponent) ) } - .reduce(into: (existing: [SourceAndDestination](), nonexisting: [SourceAndDestination]())) { (partialResult, sourceAndDestination) in - if manager.fileExists(atPath: sourceAndDestination.destination.filePath) { - partialResult.existing.append(sourceAndDestination) - } else { - partialResult.nonexisting.append(sourceAndDestination) - } - } - // Nonexisting files are easy - just move them - try nonexisting - .forEach { (source, destination) in - try manager.copyItem(at: source, to: destination) - } + for (source, destination) in sourceAndDestinations where copiedFiles[destination, default: []].insert(source).inserted { + // Avoid overwriting existing files with the same name. + let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - // Existing files require some additional checks and renaming - try existing - .forEach { - try copyFileUniquingConflictingFiles(source: $0.source, destination: $0.destination) - } - } - - /// The size and creation date of a file system item - private typealias SizeAndCreation = (Int, Date) - - /// A cache of seen files and their associated metadata - private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] - - /// Copies a file, uniquing the path if it conflicts, _if_ the files they conflict with aren't the same size - /// - Parameters: - /// - source: source file path - /// - destination: destination file path - private func copyFileUniquingConflictingFiles(source: URL, destination: URL) throws { - let destinationAttributes = try manager.attributesOfItem(atPath: destination.filePath) - let sourceAttributes = try manager.attributesOfItem(atPath: source.filePath) - - guard - let destinationSize = destinationAttributes[.size] as? Int, - let sourceSize = sourceAttributes[.size] as? Int, - let destinationCreatedDate = destinationAttributes[.creationDate] as? Date, - let sourceCreatedDate = sourceAttributes[.creationDate] as? Date - else { - logger.debug("Failed to get attributes for source: \(source) & destination: \(destination)") - return - } - - let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - - for (size, date) in seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]] where size == destinationSize && date == destinationCreatedDate { - return + logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") + try manager.copyItem(at: source, to: uniqueDestinationURL) } - - seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]].append((destinationSize, destinationCreatedDate)) - logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") - try manager.copyItem(at: source, to: uniqueDestinationURL) } /// Returns a map of dynamic objects in the provided path @@ -224,7 +139,7 @@ class OutputPostprocessor { let dynamicDependencyExtensions = ["framework", "appex", "app"] - return FileManager.default.filteredContents(of: searchPath, filter: { path in + return manager.filteredContents(of: searchPath, filter: { path in return dynamicDependencyExtensions.contains(path.pathExtension) }) .reduce(into: [String: URL]()) { partialResult, path in @@ -247,8 +162,8 @@ class OutputPostprocessor { /// - Returns: the first directory found in the path if one exists func firstDirectory(at path: URL) -> URL? { guard - FileManager.default.directoryExists(at: path), - let contents = try? FileManager.default.directories(at: path, recursive: false), + manager.directoryExists(at: path), + let contents = try? manager.directories(at: path, recursive: false), contents.count < 0 else { return nil From d2e9513ae072c7f476cb3707050e2f9193d34350 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Tue, 23 Jul 2024 10:17:49 -0400 Subject: [PATCH 52/61] Update version string to 0.5.0-beta --- Sources/GenIR/Versions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/GenIR/Versions.swift b/Sources/GenIR/Versions.swift index 29575ac..40e39d8 100644 --- a/Sources/GenIR/Versions.swift +++ b/Sources/GenIR/Versions.swift @@ -8,5 +8,5 @@ import Foundation enum Versions { - static let version = "0.4.3" + static let version = "0.5.0-beta" } From df49584c7816f1a06c456fe69c5c9311b14ef0c5 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:52:24 -0400 Subject: [PATCH 53/61] PIF workspace and project paths may not be present --- PIF/Sources/PIFSupport/PIF.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index 48d8145..14d5c4d 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -78,7 +78,7 @@ public enum PIF { public final class Workspace: Decodable { public let guid: GUID public let name: String - public let path: URL + public let path: URL? public let projects: [Project] private enum CodingKeys: CodingKey { @@ -94,7 +94,7 @@ public enum PIF { guid = try container.decode(GUID.self, forKey: .guid) name = try container.decode(String.self, forKey: .name) - path = try container.decode(URL.self, forKey: .path) + path = try container.decodeIfPresent(URL.self, forKey: .path) let projectPaths = try container.decode([String].self, forKey: .projects) .map { @@ -124,7 +124,7 @@ public enum PIF { public final class Project: Decodable { public let guid: GUID public let projectName: String? - public let path: URL + public let path: URL? public let projectDirectory: URL public let developmentRegion: String? public let buildConfigurations: [BuildConfiguration] @@ -143,7 +143,7 @@ public enum PIF { guid = try container.decode(GUID.self, forKey: .guid) projectName = try container.decodeIfPresent(String.self, forKey: .projectName) - path = try container.decode(URL.self, forKey: .path) + path = try container.decodeIfPresent(URL.self, forKey: .path) projectDirectory = try container.decode(URL.self, forKey: .projectDirectory) developmentRegion = try container.decodeIfPresent(String.self, forKey: .developmentRegion) buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) From 0c334e549c15c6b824ad4df6dbcf8abbd1c768c9 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:16:52 -0400 Subject: [PATCH 54/61] Add watchOS product types --- PIF/Sources/PIFSupport/PIF.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index 14d5c4d..dc6dce5 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -435,6 +435,8 @@ public enum PIF { case tool = "com.apple.product-type.tool" case hostBuild = "com.apple.product-type.tool.host-build" case xpcService = "com.apple.product-type.xpc-service" + case watchApp2 = "com.apple.product-type.application.watchapp2" + case watchKit2Extension = "com.apple.product-type.watchkit2-extension" } public let productName: String From 6000603296c5eabcbaa0419cedbf79e3568593fa Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 9 Sep 2024 09:57:54 +0200 Subject: [PATCH 55/61] Swap out SwiftLint action cirruslabs is more likely to keep theirs updated with issues --- .github/workflows/SwiftLint.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/SwiftLint.yml b/.github/workflows/SwiftLint.yml index 12300cf..d6fce06 100644 --- a/.github/workflows/SwiftLint.yml +++ b/.github/workflows/SwiftLint.yml @@ -2,17 +2,14 @@ name: SwiftLint on: pull_request: - paths: - - '.github/workflows/swiftlint.yml' - - '.swiftlint.yml' - - '**/*.swift' + branches: ['main'] jobs: + # Runs swiftlint on any pull request to main (note: this runs for _all_ files, not only the changed files in the PR!) SwiftLint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Lint changed files - uses: norio-nomura/action-swiftlint@3.2.1 - env: - DIFF_BASE: ${{ github.base_ref }} \ No newline at end of file + - uses: actions/checkout@v4 + - uses: cirruslabs/swiftlint-action@v1 + with: + version: latest \ No newline at end of file From 59fffc78b0e370035a3c60583648aab44cb22821 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 9 Sep 2024 11:20:07 +0200 Subject: [PATCH 56/61] Annotate skipped swiftlint violations --- PIF/Sources/PIFSupport/PIF.swift | 4 ++-- Sources/GenIR/CompilerCommandRunner.swift | 1 + Sources/GenIR/Extensions/Process+Extension.swift | 4 ++-- Sources/GenIR/XcodeLogParser.swift | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index dc6dce5..7222b40 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -15,7 +15,7 @@ - adjust types to remove external dependencies on SPM */ -// swiftlint:disable file_length type_body_length +// swiftlint:disable file_length type_body_length static_over_final_class import Foundation /// The Project Interchange Format (PIF) is a structured representation of the @@ -900,4 +900,4 @@ private struct UntypedTarget: Decodable { } let contents: TargetContents } -// swiftlint:enable file_length type_body_length +// swiftlint:enable file_length type_body_length static_over_final_class diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index 6e25247..2d1e6f3 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -78,6 +78,7 @@ struct CompilerCommandRunner { /// - name: The name this command relates to, used to create the product folder /// - directory: The directory to run these commands in /// - Returns: The total amount of modules produced for this target + // swiftlint:disable:next function_body_length private func run(commands: [CompilerCommand], for name: String, at directory: URL) throws -> Int { let targetDirectory = directory.appendingPathComponent(name) diff --git a/Sources/GenIR/Extensions/Process+Extension.swift b/Sources/GenIR/Extensions/Process+Extension.swift index c1d2c8e..425ec99 100644 --- a/Sources/GenIR/Extensions/Process+Extension.swift +++ b/Sources/GenIR/Extensions/Process+Extension.swift @@ -118,8 +118,8 @@ extension Process { try? stderrHandle.close() return .init( - stdout: String(data: stdout, encoding: .utf8), - stderr: String(data: stderr, encoding: .utf8), + stdout: String(decoding: stdout, as: UTF8.self), + stderr: String(decoding: stderr, as: UTF8.self), code: process.terminationStatus ) } diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index 4f26553..8b81dcb 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -70,6 +70,7 @@ class XcodeLogParser { /// Parses an array representing the contents of an Xcode build log /// - Parameters: /// - lines: contents of the Xcode build log lines + // swiftlint:disable:next cyclomatic_complexity private func parseBuildLog(_ lines: [String]) { var currentTarget: String? var seenTargets = Set() From e0dfada645c901fe2bdaa04b4920aedd9e18669a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 10 Sep 2024 09:36:29 +0200 Subject: [PATCH 57/61] Update pipelines - Change SwiftLint job to run on pull requests that change swift files - Update build job to use new checkout action --- .github/workflows/SwiftLint.yml | 3 ++- .github/workflows/build.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/SwiftLint.yml b/.github/workflows/SwiftLint.yml index d6fce06..4015e7f 100644 --- a/.github/workflows/SwiftLint.yml +++ b/.github/workflows/SwiftLint.yml @@ -2,7 +2,8 @@ name: SwiftLint on: pull_request: - branches: ['main'] + paths: + - '**.swift' jobs: # Runs swiftlint on any pull request to main (note: this runs for _all_ files, not only the changed files in the PR!) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0ee536..7708ad2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ jobs: build: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: πŸ”¨ Build run: | From d7f826234dea5d4c0a108ae5a42d99758bc33a1f Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 10 Sep 2024 09:52:17 +0200 Subject: [PATCH 58/61] Add --strict to SwiftLint --- .github/workflows/SwiftLint.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/SwiftLint.yml b/.github/workflows/SwiftLint.yml index 4015e7f..469c525 100644 --- a/.github/workflows/SwiftLint.yml +++ b/.github/workflows/SwiftLint.yml @@ -13,4 +13,5 @@ jobs: - uses: actions/checkout@v4 - uses: cirruslabs/swiftlint-action@v1 with: - version: latest \ No newline at end of file + version: latest + args: "--strict" \ No newline at end of file From 4775e85ec856cf85d4446a3acef49e3c84240393 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:15:56 -0400 Subject: [PATCH 59/61] Add missing watchapp2-container product type --- PIF/Sources/PIFSupport/PIF.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index 7222b40..2af5f4a 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -436,6 +436,7 @@ public enum PIF { case hostBuild = "com.apple.product-type.tool.host-build" case xpcService = "com.apple.product-type.xpc-service" case watchApp2 = "com.apple.product-type.application.watchapp2" + case watchApp2Container = "com.apple.product-type.application.watchapp2-container" case watchKit2Extension = "com.apple.product-type.watchkit2-extension" } From 9973999ea3742717566be1f4daee6fdf4a2c238b Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Thu, 5 Sep 2024 20:16:33 -0400 Subject: [PATCH 60/61] XcodeLogParser performance improvements --- .../GenIR/Extensions/Process+Extension.swift | 4 +- .../GenIR/Extensions/String+Extension.swift | 7 + Sources/GenIR/XcodeLogParser.swift | 211 +++++++++--------- 3 files changed, 119 insertions(+), 103 deletions(-) diff --git a/Sources/GenIR/Extensions/Process+Extension.swift b/Sources/GenIR/Extensions/Process+Extension.swift index 425ec99..c1d2c8e 100644 --- a/Sources/GenIR/Extensions/Process+Extension.swift +++ b/Sources/GenIR/Extensions/Process+Extension.swift @@ -118,8 +118,8 @@ extension Process { try? stderrHandle.close() return .init( - stdout: String(decoding: stdout, as: UTF8.self), - stderr: String(decoding: stderr, as: UTF8.self), + stdout: String(data: stdout, encoding: .utf8), + stderr: String(data: stderr, encoding: .utf8), code: process.terminationStatus ) } diff --git a/Sources/GenIR/Extensions/String+Extension.swift b/Sources/GenIR/Extensions/String+Extension.swift index 4f6df5b..bbd51b3 100644 --- a/Sources/GenIR/Extensions/String+Extension.swift +++ b/Sources/GenIR/Extensions/String+Extension.swift @@ -118,3 +118,10 @@ extension [String] { return nil } } + +extension StringProtocol { + /// Trims leading and trailing whitespace characters + func trimmed() -> String { + return trimmingCharacters(in: .whitespacesAndNewlines) + } +} diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index 8b81dcb..5da9d26 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -10,10 +10,11 @@ import LogHandlers /// An XcodeLogParser extracts targets and their compiler commands from a given Xcode build log class XcodeLogParser { - /// The Xcode build log contents private let log: [String] - /// Any CLI Settings found in the build log + /// The current line offset in the log + private var offset: Int = 0 + /// Any CLI settings found in the build log private(set) var settings: [String: String] = [:] /// The path to the Xcode build cache private(set) var buildCachePath: URL! @@ -33,9 +34,8 @@ class XcodeLogParser { } /// Start parsing the build log - /// - Parameter targets: The global list of targets func parse() throws { - parseBuildLog(log) + parseBuildLog() if targetCommands.isEmpty { logger.debug("Found no targets in log") @@ -67,106 +67,137 @@ class XcodeLogParser { } } - /// Parses an array representing the contents of an Xcode build log - /// - Parameters: - /// - lines: contents of the Xcode build log lines - // swiftlint:disable:next cyclomatic_complexity - private func parseBuildLog(_ lines: [String]) { - var currentTarget: String? + /// Parse the lines from the build log + func parseBuildLog() { var seenTargets = Set() - for (index, line) in lines.enumerated() { - let line = line.trimmingCharacters(in: .whitespacesAndNewlines) - - if line.contains("Build settings from command line") { - // Every line until an empty line will contain a build setting from the CLI arguments - guard let nextEmptyLine = lines.nextIndex(of: "", after: index) else { continue } - - settings = lines[index.advanced(by: 1).. String? { + guard offset + 1 < log.endIndex else { return nil } - currentTarget = target - } + defer { offset += 1 } + return log[offset] + } - guard let currentTarget else { - continue + /// Parse build settings key-value pairs + private func parseBuildSettings() -> [String: String] { + var settings = [String: String]() + + // Build settings end with a blank line + while let line = consumeLine()?.trimmed(), !line.isEmpty { + let pair = line.split(separator: "=", maxSplits: 1).map { $0.trimmed() } + if pair.count < 2 { + settings[pair[0]] = "" + } else { + settings[pair[0]] = pair[1] } + } - guard - let compilerCommand = compilerCommand(from: line), - isPartOfCompilerCommand(lines, index) - else { - continue - } + return settings + } + + /// Parse the build description path from the provided line + /// - Parameter from: the line that should contain the build description path + private func buildDescriptionPath(from line: String) -> URL? { + guard line.hasPrefix("Build description path:"), let startIndex = line.firstIndex(of: ":") else { + return nil + } + + var cachePath = String(line[line.index(after: startIndex).. Bool { - var result = false - var offset = lines.index(index, offsetBy: -2) - - // Check the line starts with either 'CompileC', 'SwiftDriver', or 'CompileSwiftSources' to ensure we only pick up compilation commands - while lines.indices.contains(offset) { - let previousLine = lines[offset].trimmingCharacters(in: .whitespacesAndNewlines) - offset -= 1 - - if previousLine.isEmpty { - // hit the top of the block, exit loop + /// Parsecompiler commands from the current block + private func parseCompilerCommands() -> [CompilerCommand] { + var commands: [CompilerCommand] = [] + + while let line = consumeLine() { + // Assume we have reached the end of this build task's block when we encounter an unindented line. + guard line.hasPrefix(" ") else { break } - if previousLine.starts(with: "CompileC") - || previousLine.starts(with: "SwiftDriver") - || previousLine.starts(with: "CompileSwiftSources") { - result = true - break + guard let compilerCommand = parseCompilerCommand(from: line) else { + continue } + + commands.append(compilerCommand) + } + + return commands + } + + /// Parses a `CompilerCommand` from the given line if one exists + /// - Parameter from: the line which may contain a compiler command + private func parseCompilerCommand(from line: String) -> CompilerCommand? { + var commandLine = line + + if let index = line.firstIndexWithEscapes(of: "/"), index != line.startIndex { + commandLine = String(line[index.. CompilerCommand? { - var stripped = line - if let index = stripped.firstIndexWithEscapes(of: "/"), index != stripped.startIndex { - stripped = String(stripped[index.. Date: Tue, 10 Sep 2024 16:19:09 -0400 Subject: [PATCH 61/61] Update release version to 0.5.0 --- README.md | 12 ++++++------ Sources/GenIR/Versions.swift | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4241377..733fac8 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,13 @@ All installed! You can now use `gen-ir` on your system - be sure to run `gen-ir ```bash # Path to build log (you can export from inside of Xcode too) xcodebuild clean && \ -xcodebuild build -project TestProject.xcodeproj -scheme TestProject -configuration Debug -destination generic/platform=iOS -archivePath TestProject.xcarchive > build_log.txt +xcodebuild archive -project TestProject.xcodeproj -scheme TestProject -configuration Debug -destination generic/platform=iOS -archivePath TestProject.xcarchive > build_log.txt -gen-ir build_log.txt TestProject.xcarchive --project-path TestProject.xcodeproj +gen-ir build_log.txt TestProject.xcarchive # Stdin (you may need to redirect stderr to stdout here, Xcode is weird about writing to it sometimes) xcodebuild clean && \ -xcodebuild build -project TestProject.xcodeproj -scheme TestProject -configuration Debug -destination generic/platform=iOS -archivePath TestProject.xcarchive | gen-ir - TestProject.xcarchive --project-path TestProject.xcodeproj +xcodebuild archive -project TestProject.xcodeproj -scheme TestProject -configuration Debug -destination generic/platform=iOS -archivePath TestProject.xcarchive | gen-ir - TestProject.xcarchive ``` ## Building @@ -86,6 +86,6 @@ swift build -c release If you previously installed the test version during early access testing, run the following commands to remove the test version from your system before installing: ```sh - brew uninstall gen-ir && - brew untap NinjaLikesCheez/tap - ``` +brew uninstall gen-ir && +brew untap NinjaLikesCheez/tap +``` diff --git a/Sources/GenIR/Versions.swift b/Sources/GenIR/Versions.swift index 40e39d8..485d724 100644 --- a/Sources/GenIR/Versions.swift +++ b/Sources/GenIR/Versions.swift @@ -8,5 +8,5 @@ import Foundation enum Versions { - static let version = "0.5.0-beta" + static let version = "0.5.0" }