Xcode 11 recompila demais

12

O Xcode 11 está recompilando (quase?) Todo o meu projeto, mesmo que eu mude apenas uma variável privada local ou mude o valor de uma constante no escopo local, às vezes até no escopo da função privada local. Em algum momento, eu posso obter 2 ou 3 alterações com compilações rápidas, conforme o esperado, mas logo decide recompilar tudo novamente (o que leva muito tempo).

Alguma idéia do que poderia estar acontecendo? O Xcode não é capaz de determinar o que mudou, por que recompilar tantas outras coisas (até outros módulos).

Qualquer conselho é muito apreciado, obrigado!

Nikolay Suvandzhiev
fonte
2
Eu recomendaria: Verifique se você está fazendo depurações com construção incremental, não otimização de módulo inteiro. Saia e limpe o DerivedData. E atualize para o Xcode 11.4, às vezes compila tão rápido que nem vejo isso acontecer.
matt
11
Esta discussão pode responder à sua pergunta: stackoverflow.com/questions/25537614/…
Endanke
É muito dependente do projeto, precisa analisar o log de compilação do que está acontecendo. Não observo esse comportamento com o Xcode 11.2+, enquanto tenho projetos muito grandes. Você forneceria acesso às fontes do seu projeto de alguma forma, caso contrário, todos os conselhos não farão sentido?
Asperi 01/04
Verifique a propriedade Legacy Build System, que deve ser desmarcada se você não estiver modificando submodules
BrunoLoops

Respostas:

8

Tivemos o mesmo problema e o corrigimos. Duas vezes.

Compilação incremental (mesma máquina de compilação):

antes: ~ 10m depois: ~ 35s

COMO?

Vamos começar com a nossa experiência primeiro. Tivemos um grande projeto Swift / Obj-C e essa era a principal preocupação: os tempos de construção eram lentos e você tinha que criar um novo projeto para implementar um novo recurso (literalmente). Pontos de bônus por destacar a sintaxe que nunca funciona.

Teoria

Para realmente corrigir isso, você precisa entender realmente como o sistema de compilação funciona. Por exemplo, vamos tentar este trecho de código:

import FacebookSDK
import RxSwift
import PinLayout

e imagine que você usa todas essas importações em seu arquivo. E também esse arquivo depende de outro arquivo, que depende de outras bibliotecas, que por sua vez usa outras bibliotecas etc.

Portanto, para compilar seu arquivo, o Xcode precisa compilar todas as bibliotecas mencionadas e todos os arquivos dos quais depende, portanto, se você alterar um dos arquivos "principais", o Xcode precisará reconstruir literalmente todo o projeto.

Árvore de dependência

A construção do Xcode é multithread , mas consiste em muitas árvores de thread único .

Portanto, na primeira etapa de cada compilação incremental, o Xcode decide quais arquivos devem ser recompilados e cria uma árvore AST . Se você alterar um arquivo que está atuando como " confiável " em outros arquivos, todos os outros arquivos que atuam como " dependentes " deverão ser recompilados.

Acoplamento

Portanto, o primeiro conselho é diminuir o acoplamento . As partes do seu projeto precisam ser independentes uma da outra.

Ponte Obj-C / Swift

Problema com essas árvores, se você estiver usando uma ponte Obj-C / Swift, o Xcode precisará passar por mais fases do que o habitual:

Mundo perfeito:

  1. Cria código Obj-C
  2. Criar código Swift

Ponte Swift / Obj-C

Ponte Obj-C / Swift:

  1. [REPEATABLE STEP] Crie código Swift, necessário para compilar o código Obj-C
  2. [REPEATABLE STEP] Crie código Obj-C, necessário para compilar o código Swift
  3. Repita 1 e 2 até que você tenha apenas o código Swift e Obj-C não confiável restante
  4. Criar código Obj-C
  5. Criar código Swift

Ponte Obj-C / Swift

Portanto, se você alterar algo da etapa 1 ou 2, estará basicamente com problemas. A melhor solução é minimizar a Obj-C / Swift Bridge (e removê-la do seu projeto).

Se você não possui uma Obj-C / Swift Bridge, isso é incrível e você pode ir para a próxima etapa:

Gerenciador de Pacotes Swift

Hora de mudar para o SwiftPM (ou pelo menos configurar melhor seus Cocoapods).

O fato é que a maioria das estruturas com a configuração padrão do Cocoapods arrasta consigo muitas coisas que você não precisa.

Para testar isso, crie um projeto vazio com apenas uma dependência como PinLayout, por exemplo, e tente escrever esse código com Cocoapods (configuração padrão) e SwiftPM.

import PinLayout

final class TestViewController: UIViewController {

}

Spoiler: o Cocoapods compilará esse código, porque o Cocoapods importará TODAS AS IMPORTAÇÕES do PinLayout (incluindo UIKit) e o SwiftPM não, porque o SwiftPM importa estruturas atomicamente.

Corte sujo

Você se lembra da construção do Xcode com vários threads?

Bem, você pode abusar, se conseguir dividir seu projeto em várias partes independentes e importar todas elas como estruturas independentes para o seu projeto. Ele reduz o acoplamento e essa foi realmente a primeira solução que usamos, mas na verdade não foi muito eficaz, porque só conseguimos reduzir o tempo de construção incremental para ~ 4-5m, o que não é nada comparado ao primeiro método.

x0 z1
fonte
Boa sorte, meu caro. Compartilhe sua experiência como você reduziu o acoplamento em seu projeto. Tchau!
x0 z1
3

Não há bala de ouro aqui, mas há muitas coisas para verificar:

  • Verifique se você está realmente usando a configuração Debug no seu esquemaEditor de esquema Xcode usando a configuração de depuração

  • Veja abaixo como garantir que você está usando compilações incrementais versus o módulo inteiro, de acordo com as recomendações de matt. Verifique também se o seu Nível de otimização para compilação é nenhum. Configurações do Xcode Build mostrando Incremental Builds

  • Se você estiver usando estruturas pesadas de inferência de tipo como o RxSwift, adicionar anotações de tipo explícitas pode acelerar o tempo de criação.

  • Se o projeto for muito grande, considere refatorar grupos lógicos de arquivos de origem em estruturas, mas isso pode ser uma mudança muito drástica do que você preferir

Pode ser útil se você forneceu mais detalhes específicos sobre o projeto: você está vinculando estaticamente alguma biblioteca? É uma estrutura ou destino de aplicativo? Qual o tamanho e qual versão rápida você está usando? Você tem fases de compilação personalizadas, como linters ou geração de código, que podem ser ignoradas algumas vezes?

nteissler
fonte