Xcode 11 se recompila demasiado

12

Xcode 11 está recompilando (¿casi?) Todo mi proyecto, incluso si solo cambio una variable privada local, o cambio el valor de una constante en el ámbito local, a veces incluso en el ámbito de la función privada local. En algún momento puedo obtener 2 o 3 cambios con compilaciones rápidas como se esperaba, pero muy pronto decide recompilar todo nuevamente (lo que lleva demasiado tiempo).

¿Alguna idea de lo que podría estar pasando? Si Xcode no puede determinar qué ha cambiado, ¿por qué recompila tantas otras cosas (incluso otros módulos)?

Cualquier consejo es muy apreciado, gracias!

Nikolay Suvandzhiev
fuente
2
Aconsejaría: asegúrese de realizar compilaciones de depuración con compilación incremental, no optimización de módulo completo. Salga y limpie DerivedData. Y actualice a Xcode 11.4, a veces se compila tan rápido que ni siquiera veo que suceda.
mate
1
Este hilo podría responder a su pregunta: stackoverflow.com/questions/25537614/…
Endanke
Es muy dependiente del proyecto, necesita analizar el registro de compilación de lo que está sucediendo. No observo ese comportamiento con Xcode 11.2+, aunque tengo proyectos muy grandes. ¿Proporcionaría acceso a las fuentes de su proyecto de alguna manera, de lo contrario, todos los consejos no tienen sentido?
Asperi
Verifique la propiedad del sistema de compilación heredado, debe desmarcarse si no está modificando submódulos
BrunoLoops

Respuestas:

8

Tuvimos el mismo problema y lo solucionamos. Dos veces.

Construcción incremental (misma máquina de construcción):

antes: ~ 10m después: ~ 35s

¿CÓMO?

Comencemos con nuestra experiencia primero. Teníamos un proyecto masivo Swift / Obj-C y esa era la principal preocupación: los tiempos de construcción eran lentos y había que crear un nuevo proyecto para implementar una nueva característica (literalmente). Puntos de bonificación por resaltado de sintaxis que nunca funciona.

Teoría

Para solucionar esto realmente, debe comprender realmente cómo funciona el sistema de compilación. Por ejemplo, intentemos este fragmento de código:

import FacebookSDK
import RxSwift
import PinLayout

e imagine que usa todas estas importaciones en su archivo. Y también este archivo depende de otro archivo, que depende de otras bibliotecas, que a su vez usa otras bibliotecas, etc.

Entonces, para compilar su archivo, Xcode tiene que compilar cada biblioteca que mencionó y cada archivo del que depende, por lo que si cambia uno de los archivos "centrales", Xcode tiene que reconstruir literalmente todo el proyecto.

Árbol de dependencia

La construcción de Xcode es multiproceso , pero consta de muchos árboles de subproceso único .

Entonces, en el primer paso de cada compilación incremental, Xcode decide qué archivos deben volver a compilarse y crea un árbol AST . Si cambia un archivo que actúa como " confiable " en otros archivos, entonces todos los demás archivos que actúan como " dependientes " tienen que volver a compilarse.

Acoplamiento

Entonces, el primer consejo es reducir el acoplamiento . Las partes de su proyecto deben ser independientes entre sí.

Obj-C / Puente Swift

Problema con esos árboles si está utilizando un puente Obj-C / Swift, Xcode tiene que pasar por más fases de lo habitual:

Mundo perfecto:

  1. Construye código Obj-C
  2. Construir código Swift

Puente Swift / Obj-C

Obj-C / Puente Swift:

  1. [PASO REPETIBLE] Cree el código Swift, que es necesario para compilar el código Obj-C
  2. [PASO REPETIBLE] Compila el código Obj-C, que es necesario para compilar el código Swift
  3. Repita 1 y 2 hasta que solo le quede un código Swift y Obj-C no confiable
  4. Construir código Obj-C
  5. Construir código Swift

Obj-C / Puente Swift

Entonces, si cambia algo del paso 1 o 2, básicamente está en problemas. La mejor solución es minimizar Obj-C / Swift Bridge (y eliminarlo de su proyecto).

Si no tiene un puente Obj-C / Swift, es increíble y puede continuar con el siguiente paso:

Swift Package Manager

Es hora de pasar a SwiftPM (o al menos configurar mejor sus Cocoapods).

La cuestión es que la mayoría de los marcos con configuración predeterminada de Cocoapods arrastran consigo muchas cosas que no necesita.

Para probar esto, cree un proyecto vacío con una sola dependencia como PinLayout, por ejemplo, e intente escribir este código con Cocoapods (configuración predeterminada) y SwiftPM.

import PinLayout

final class TestViewController: UIViewController {

}

Spoiler: Cocoapods compilará este código, porque Cocoapods importará CADA IMPORTACIÓN de PinLayout (incluido UIKit) y SwiftPM no porque SwiftPM importa marcos atómicamente.

Truco sucio

¿Recuerdas que la construcción de Xcode es multihilo?

Bueno, puede abusar de él si puede dividir su proyecto en muchas partes independientes e importarlas todas como marcos independientes para su proyecto. Reduce el acoplamiento y esa fue en realidad la primera solución que usamos, pero de hecho no fue muy efectiva, porque solo pudimos reducir el tiempo de construcción incremental a ~ 4-5m, lo cual no es NADA en comparación con el primer método.

x0 z1
fuente
Buena suerte compañero. Comparta su experiencia sobre cómo redujo el acoplamiento en su proyecto. ¡Adiós!
x0 z1
3

No hay una bala de oro aquí, pero hay muchas cosas que verificar:

  • Asegúrese de estar utilizando la configuración de depuración en su esquemaXcode Scheme Editor usando la configuración de depuración

  • Vea a continuación cómo asegurarse de que está utilizando compilaciones incrementales en comparación con el módulo completo según los consejos de Matt. También asegúrese de que su Nivel de optimización para las compilaciones de depuración no sea ninguno. Configuración de compilación Xcode que muestra compilaciones incrementales

  • Si está utilizando marcos pesados ​​de inferencia de tipos como RxSwift, agregar anotaciones de tipo explícitas puede acelerar los tiempos de construcción.

  • Si el proyecto es muy grande, podría considerar refactorizar grupos lógicos de archivos fuente en marcos, pero eso puede ser un cambio demasiado drástico de lo que preferiría

Podría ayudar si proporcionara más detalles sobre el proyecto: ¿está vinculando estáticamente alguna biblioteca? ¿Es un marco o objetivo de aplicación? ¿Qué tan grande y qué versión rápida estás usando? ¿Tiene alguna Fase de construcción personalizada como linters o generación de código que a veces se puede omitir?

nteissler
fuente