Embora eu possa codificar, ainda não tenho nenhuma experiência em trabalhar em grandes projetos. O que eu fiz até agora foi codificar pequenos programas que são compilados em questão de segundos (vários exercícios de c / c ++ como algoritmos, princípios de programação, idéias, paradigmas ou apenas experimentar APIs ...) ou trabalhar em alguns projetos menores que estavam feito em uma (s) linguagem (s) de script (python, php, js) em que não é necessária nenhuma compilação.
O problema é que, ao codificar em uma linguagem de script, sempre que eu quero tentar se algo funciona - eu apenas executo o script e vejo o que acontece. Se as coisas não funcionarem, eu posso simplesmente mudar o código e testá-lo novamente, executando o script novamente e continuar fazendo isso até obter o resultado desejado. Meu ponto é que você não precisa esperar pelo qualquer coisa para compilar e, por isso, é muito fácil usar uma grande base de códigos, modificá-la, adicionar algo a ela ou simplesmente brincar com ela - você pode ver as alterações instantaneamente.
Como exemplo, tomarei o Wordpress. É muito fácil tentar descobrir como criar um plugin para ele. Primeiro, você começa criando um plugin simples "Hello World", depois cria uma interface simples para o painel de administração para se familiarizar com a API, depois a cria e torna algo mais complexo, enquanto isso muda a aparência de algumas vezes .. A idéia de ter que recompilar algo tão grande quanto o WP repetidamente, após cada pequena alteração, para tentar "se funciona" e "como funciona / sente", parece ineficiente, lenta e errada.
Agora, como eu poderia fazer isso com um projeto escrito em uma linguagem compilada? Eu gostaria de contribuir com alguns projetos de código aberto e essa pergunta continua me incomodando. A situação provavelmente difere de projeto para projeto, onde alguns deles que foram pensados com sabedoria serão "modulares" de alguma forma, enquanto outros serão apenas um grande blob que precisa ser recompilado repetidamente.
Gostaria de saber mais sobre como isso é feito corretamente. Quais são algumas práticas comuns, abordagens e desenhos de projetos (padrões?) Para lidar com isso? Como essa "modularidade" é chamada no mundo dos programadores e para que devo pesquisar no Google para saber mais sobre isso? É frequente os projetos crescerem de suas proporções de primeiro pensamento que se tornam problemáticas depois de um tempo? Existe alguma maneira de evitar a longa compilação de projetos não tão bem projetados? Uma maneira de modulá-los de alguma forma (talvez excluindo partes não vitais do programa durante o desenvolvimento (alguma outra idéia?))?
Obrigado.
fonte
Respostas:
Assim como foi dito, você nunca recompila todo o projeto cada vez que faz uma pequena alteração. Em vez disso, você apenas recompila a parte do código que foi alterada, bem como todo o código, dependendo dela.
No C / C ++, a compilação é bastante direta. Você compila a conversão de cada arquivo de origem em código de máquina (nós os chamamos de arquivos de objeto * .o) e depois vincula todos os seus arquivos de objeto em um grande executável.
Assim como o MainMa mencionou, algumas bibliotecas são construídas em arquivos separados, que serão vinculados dinamicamente em tempo de execução ao executável. Essas bibliotecas são chamadas de Objetos Compartilhados (* .so) no Unix e nas Bibliotecas Dinamicamente Vinculadas (DLL) no Windows. As bibliotecas dinâmicas têm muitas vantagens, uma das quais você não precisa compilá-las / vinculá-las, a menos que seu código-fonte mude efetivamente.
Existem ferramentas de automação de construção que ajudam você a:
Os mais famosos (make, ant, maven, ...) podem detectar automaticamente quais partes do código foram alteradas desde a última compilação e exatamente qual objeto / binário precisa ser atualizado.
No entanto, esse é o custo (relativamente pequeno) de ter que escrever um "script de construção". É um arquivo que contém todas as informações sobre sua compilação, como definir os destinos e suas dependências, definir qual compilador você deseja e quais opções usar, definir seu ambiente de compilação, seus caminhos de biblioteca, ... Você talvez tenha ouvido falar sobre Makefiles (muito comum no mundo Unix) ou build.xml (muito popular no mundo Java). Isto é o que eles fazem.
fonte
build.xml
arquivoVocê não recompila todo o projeto todas as vezes. Por exemplo, se for um aplicativo C / C ++, há chances de ele ser separado em bibliotecas (DLLs no Windows), cada biblioteca sendo compilada separadamente.
O projeto em si geralmente é compilado diariamente em um servidor dedicado: essas são construções noturnas. Esse processo pode demorar bastante, pois inclui não apenas o tempo de compilação, mas também o tempo gasto na execução de testes de unidade, outros testes e outros processos.
fonte
Eu acho que todas as respostas até agora foram aludidas também, é que grandes projetos de software são quase sempre divididos em partes muito menores. Cada peça é normalmente armazenada em seu próprio arquivo.
Essas peças são compiladas individualmente para criar objetos. Os objetos são então vinculados para formar o produto final. [De certa forma, é como construir coisas com Legos. Você não tenta moldar a coisa final com um grande pedaço de plástico, mas combina vários pedaços menores para fazê-lo.]
Quebrar o projeto em partes que são compiladas individualmente permite que algumas coisas legais aconteçam.
Edifício Incremental
Primeiro, quando você muda uma peça, geralmente não precisa recompilar todas as peças. De um modo geral, desde que você não altere a maneira como as outras peças interagem com a peça, as outras não precisam ser recompiladas.
Isso dá origem à ideia de construção incremental . Ao fazer uma construção incremental, apenas as partes afetadas pela alteração são recompiladas. Isso acelera bastante o tempo de desenvolvimento. É verdade que talvez você ainda precise esperar que tudo seja vinculado novamente, mas isso ainda poupa muito em ter que recompilar e vincular tudo novamente. (BTW: Alguns sistemas / idiomas oferecem suporte à vinculação incremental, de modo que apenas as coisas que foram alteradas precisam ser vinculadas novamente. O custo para isso geralmente está no desempenho e tamanho do código ruins.)
Teste de Unidade
A segunda coisa que ter peças pequenas permite fazer é testar individualmente as peças antes de serem combinadas. Isso é conhecido como teste de unidade . Nos testes de unidade, cada unidade é testada individualmente antes de ser integrada (combinada) com o restante do sistema. Os testes de unidade são normalmente escritos para que possam ser executados rapidamente sem envolver o restante do sistema.
O caso limitante da aplicação de testes é visto em Test Driven Development (TDD). Nesse modelo de desenvolvimento, nenhum código é gravado / modificado, a menos que seja para corrigir um teste com falha.
Tornando mais fácil
Portanto, dividir as coisas parece bom, mas também parece ser necessário muito trabalho para construir o projeto: você precisa descobrir quais peças foram alteradas e o que depende dessas peças, compilar cada peça e vincular tudo.
Felizmente, os programadores são preguiçosos *, então inventam muitas ferramentas para facilitar seus trabalhos. Para esse fim, muitas ferramentas foram escritas para automatizar a tarefa acima. Os mais famosos já foram mencionados (marca, formiga, maven). Essas ferramentas permitem definir quais peças precisam ser reunidas para criar seu projeto final e como as peças dependem umas das outras (ou seja, se você alterar isso, será necessário recompilar). O resultado é que emitir apenas um comando descobre o que precisa ser recompilado, compila e vincula tudo.
Mas ainda resta descobrir como as coisas se relacionam. Isso dá muito trabalho e, como eu disse antes, os programadores são preguiçosos. Então eles criaram outra classe de ferramentas. Essas ferramentas foram escritas para determinar as dependências para você! Freqüentemente, as ferramentas fazem parte de Ambientes de Desenvolvimento Integrado (IDEs), como Eclipse e Visual Studio, mas também existem alguns autônomos usados para aplicativos genéricos e específicos (makedep, programas QMake for Qt).
* Na verdade, os programadores não são realmente preguiçosos, eles gostam de gastar seu tempo trabalhando em problemas, não realizando tarefas repetitivas que podem ser automatizadas por um programa.
fonte
Aqui está minha lista de itens que você pode tentar acelerar as compilações em C / C ++:
fonte
Executar algo interpretado também é muito ineficiente e lento, e (sem dúvida) errado. Você está reclamando dos requisitos de tempo no PC do desenvolvedor, mas não compilar causa requisitos de tempo no PC do usuário , o que é provavelmente muito pior.
Mais importante, os sistemas modernos podem fazer reconstruções incrementais bastante avançadas e não é comum recompilar tudo para pequenas alterações - os sistemas compilados podem incluir componentes de script, especialmente comuns para coisas como a interface do usuário.
fonte
Se o projeto implementar o DAG de dependência de compilação adequado, você poderá apenas recompilar os arquivos de objeto que sua alteração afeta.
Assumindo também um DAG de dependência de compilação adequado, você pode compilar usando vários processos. Um trabalho por núcleo / CPU é a norma.
Você pode criar vários executáveis para teste que vinculam apenas arquivos de objetos específicos.
fonte
Além da resposta da MainMa, também acabamos de atualizar as máquinas em que trabalhamos. Uma das melhores compras que fizemos foi um SSD para o qual você não pode deixar de recompilar todo o projeto.
Outra sugestão seria tentar um compilador diferente. Naquela época, passamos do compilador Java para o Jikes e agora passamos a usar o compilador fornecido com o Eclipse (não sei se ele tem um nome), que tira melhor proveito dos processadores multicore.
Nosso projeto de 37.000 arquivos levou cerca de 15 minutos para compilar do zero antes de fazermos essas alterações. Após as alterações, foi reduzido para 2-3 minutos.
Claro, vale a pena mencionar o argumento da MainMa novamente. Não recompile o projeto inteiro toda vez que quiser ver uma alteração.
fonte