Xcode 11 rekompiluje za dużo

12

Xcode 11 rekompiluje (prawie?) Cały mój projekt, nawet jeśli po prostu zmienię lokalną zmienną prywatną lub zmienię wartość stałej w lokalnym zasięgu, czasem nawet w lokalnym lokalnym zakresie funkcji. Czasami mogę uzyskać 2 lub 3 zmiany z szybkimi kompilacjami zgodnie z oczekiwaniami, ale wkrótce postanawia ponownie wszystko skompilować (co zajmuje zbyt dużo czasu).

Jakieś pomysły, co się dzieje? Czy Xcode nie jest w stanie określić, co się zmieniło, dlaczego rekompiluje tyle innych rzeczy (nawet innych modułów).

Wszelkie porady są mile widziane, dziękuję!

Nikolay Suvandzhiev
źródło
2
Radzę: Upewnij się, że wykonujesz kompilacje debugowania z przyrostowym budowaniem, a nie optymalizacją całego modułu. Zamknij i wyczyść DerivedData. I zaktualizuj do Xcode 11.4, czasami kompiluje się tak szybko, że nawet tego nie widzę.
mat
1
Ten wątek może odpowiedzieć na twoje pytanie: stackoverflow.com/questions/25537614/…
Endanke
Jest bardzo zależny od projektu, musi analizować dziennik kompilacji na temat tego, co się dzieje. Nie obserwuję takiego zachowania w Xcode 11.2+, chociaż mam bardzo duże projekty. Czy zapewniłbyś w jakiś sposób dostęp do źródeł projektu, w przeciwnym razie wszystkie porady są bezsensowne?
Asperi
Sprawdź właściwość starszego systemu kompilacji, należy ją odznaczyć, jeśli nie modyfikujesz submodułów
BrunoLoops

Odpowiedzi:

8

Mieliśmy ten sam problem i naprawiliśmy go. Dwa razy.

Kompilacja przyrostowa (ta sama maszyna do kompilacji):

przed: ~ 10 m po: ~ 35s

W JAKI SPOSÓB?

Zacznijmy od naszego doświadczenia. Mieliśmy ogromny projekt Swift / Obj-C i to był główny problem: czasy kompilacji były powolne i trzeba było stworzyć nowy projekt, aby wdrożyć nową funkcję (dosłownie). Punkty bonusowe za niedziałające podświetlanie składni.

Teoria

Aby naprawdę to naprawić, musisz naprawdę zrozumieć, jak działa system kompilacji. Na przykład spróbujmy tego fragmentu kodu:

import FacebookSDK
import RxSwift
import PinLayout

i wyobraź sobie, że używasz wszystkich tych importów w swoim pliku. A także ten plik zależy od innego pliku, który zależy od innych bibliotek, które z kolei korzystają z innych bibliotek itp.

Aby skompilować plik, Xcode musi skompilować każdą wymienioną bibliotekę i każdy plik , od którego zależy, więc jeśli zmienisz jeden z „podstawowych” plików, Xcode musi odbudować dosłownie cały projekt.

Drzewo zależności

Kompilacja Xcode jest wielowątkowa , ale składa się z wielu jednowątkowych drzew .

Tak więc na pierwszym etapie każdej przyrostowej kompilacji Xcode decyduje, które pliki należy ponownie skompilować i buduje drzewo AST . Jeśli zmienisz plik, który działa jako „ niezawodny ” w stosunku do innych plików, więc każdy inny plik, który działa jako „ zależny ”, musi zostać ponownie skompilowany.

Sprzęganie

Pierwszą radą jest więc obniżenie sprzęgła . Części twojego projektu muszą być od siebie niezależne.

Most Obj-C / Swift

Problem z tymi drzewami, jeśli używasz mostu Obj-C / Swift, Xcode musi przejść więcej faz niż zwykle:

Doskonały świat:

  1. Buduje kod Obj-C
  2. Zbuduj kod Swift

Most Swift / Obj-C

Most Obj-C / Swift:

  1. [POWTARZALNY KROK] Zbuduj kod Swift, który jest potrzebny do kompilacji kodu Obj-C
  2. [POWTARZALNY KROK] Zbuduj kod Obj-C, który jest potrzebny do skompilowania kodu Swift
  3. Powtarzaj 1 i 2, aż pozostanie ci tylko niezawodny kod Swift i Obj-C
  4. Zbuduj kod Obj-C
  5. Zbuduj kod Swift

Most Obj-C / Swift

Więc jeśli zmienisz coś od kroku 1 lub 2, zasadniczo masz kłopoty. Najlepszym rozwiązaniem jest zminimalizowanie Obj-C / Swift Bridge (i usunięcie go z projektu).

Jeśli nie masz mostu Obj-C / Swift, to jest niesamowite i możesz przejść do następnego kroku:

Menedżer pakietów szybkich

Czas przejść do SwiftPM (lub przynajmniej lepiej skonfigurować Cocoapody).

Rzecz w tym, że większość frameworków z domyślną konfiguracją Cocoapods przeciąga wraz z sobą wiele rzeczy, których nie potrzebujesz.

Aby to przetestować, utwórz pusty projekt z tylko jedną zależnością, na przykład PinLayout, i spróbuj napisać ten kod za pomocą Cocoapods (konfiguracja domyślna) i SwiftPM.

import PinLayout

final class TestViewController: UIViewController {

}

Spoiler: Cocoapods skompilują ten kod, ponieważ Cocoapods zaimportują KAŻDY IMPORT PinLayout (w tym UIKit), a SwiftPM nie, ponieważ SwiftPM importuje struktury atomowo.

Brudny hack

Czy pamiętasz, że Xcode jest wielowątkowy?

Cóż, możesz go nadużyć, jeśli jesteś w stanie podzielić swój projekt na wiele niezależnych elementów i zaimportować je wszystkie jako niezależne ramy do projektu. Zmniejsza to sprzężenie i było to właściwie pierwsze zastosowane przez nas rozwiązanie, ale w rzeczywistości nie było bardzo skuteczne, ponieważ mogliśmy jedynie skrócić czas kompilacji do ~ 4-5 m, co nie jest NIC w porównaniu z pierwszą metodą.

x0 z1
źródło
Powodzenia, kolego. Podziel się swoim doświadczeniem, jak obniżyłeś sprzężenie w swoim projekcie. PA!
x0 z1
3

Nie ma tutaj złotej kuli, ale mnóstwo rzeczy do sprawdzenia:

  • Upewnij się, że faktycznie używasz konfiguracji debugowania w swoim schemacieEdytor schematów Xcode przy użyciu konfiguracji debugowania

  • Zobacz poniżej, jak upewnić się, że używasz przyrostowych kompilacji w porównaniu do całego modułu zgodnie z poradami Matta. Upewnij się również, że Twój poziom optymalizacji kompilacji debugowania jest zerowy. Ustawienia kompilacji Xcode pokazujące kompilacje przyrostowe

  • Jeśli używasz ciężkich struktur wnioskowania typu, takich jak RxSwift, dodanie wyraźnych adnotacji typu może przyspieszyć czas kompilacji.

  • Jeśli projekt jest bardzo duży, możesz rozważyć przekształcenie logicznych grup plików źródłowych w ramy, ale może to być zbyt drastyczna zmiana, niż wolisz

Może to pomóc, jeśli podasz więcej szczegółów na temat projektu: czy statycznie łączysz jakieś biblioteki? Czy jest to docelowy framework lub aplikacja? Jak dużej i jakiej szybkiej wersji używasz? Czy masz jakieś niestandardowe fazy kompilacji, takie jak kłaczki lub generowanie kodu, które można czasem pomijać?

nteissler
źródło