Quais são os exemplos do mundo real do gráfico de dependência de Gradle?

StackOverflow https://stackoverflow.com/questions/2414281

  •  19-09-2019
  •  | 
  •  

Pergunta

Como observado em a documentação, Gradle usa um gráfico acíclico direcionado (DAG) para criar um gráfico de dependência. Pelo meu entendimento, ter ciclos separados para avaliação e execução é um recurso importante para uma ferramenta de construção. por exemplo, o Gradle doc afirma que isso permite alguns recursos que, de outra forma, seriam impossíveis.

Estou interessado em exemplos do mundo real que ilustram o poder desse recurso. Quais são alguns casos de uso para os quais um gráfico de dependência é importante? Estou especialmente interessado em histórias pessoais do campo, seja com Gradle ou uma ferramenta igualmente equipada.

Estou fazendo esse 'wiki da comunidade' desde o início, pois será difícil avaliar uma resposta 'correta'.

Foi útil?

Solução

Essa pergunta provocativa forneceu motivação para finalmente analisar a gradle. Ainda não o usei, então só posso oferecer análises observadas ao navegar nos documentos, não histórias pessoais.

Minha primeira pergunta foi por que um gráfico de dependência de tarefas gradle é garantido como acíclico. Não encontrei a resposta para isso, mas um caso contrário é facilmente construído, então presumirei que a detecção de ciclo é uma validação executada quando o gráfico é construído, e a construção falha antes da execução da primeira tarefa se Existem dependências cíclicas ilegais. Sem primeiro construir o gráfico, essa condição de falha pode não ser descoberta até que a construção esteja quase completa. Além disso, a rotina de detecção teria que executar depois que todas as tarefas foram executadas, o que seria muito ineficiente (desde que o gráfico fosse construído de forma incremental e disponível globalmente, uma pesquisa de profundidade seria necessária apenas para encontrar um ponto de partida e subsequente As avaliações do ciclo exigiriam trabalho mínimo, mas o trabalho total ainda seria maior do que fazer uma única redução em todo o conjunto de relações desde o início). Eu gritaria a detecção precoce como um grande benefício.

Uma dependência de tarefas pode ser preguiçosa (consulte: 4.3 Dependências de tarefas e um exemplo relacionado em 13.14). As dependências de tarefas preguiçosas não puderam ser avaliadas corretamente até que todo o gráfico seja construído. O mesmo se aplica à resolução de dependência transitiva (não tarefa), que pode causar inúmeros problemas, e exigir recompilações repetidas à medida que dependências adicionais são descobertas e resolvidas (também exigindo solicitações repetidas para um repositório). O recurso Regras de Tarefa (13.8) também não seria possível. Esses problemas, e provavelmente muitos outros, podem ser generalizados considerando que Gradle usa uma linguagem dinâmica e pode adicionar e modificar dinamicamente tarefas; portanto, antes de uma avaliação de primeira passagem, os resultados podem ser não determinísticos, pois o caminho de execução é construído e Modificado durante o tempo de execução, portanto, diferentes seqüências de avaliação podem produzir resultados arbitrariamente diferentes se houver dependências ou diretrizes comportamentais que sejam desconhecidas até mais tarde, porque ainda não foram criadas. (Isso pode ser digno de investigar com alguns exemplos concretos. Se for verdadeiro, até dois passes nem sempre seriam suficientes. Se A -> B, B -> C, onde C muda o comportamento de A para que não seja mais Depende de B, então você tem um problema. Espero que haja algumas práticas recomendadas para restringir a metaprogramação com o escopo não local, para não permitir isso em tarefas arbitrárias. Um exemplo divertido seria uma simulação de um paradoxo de viagem no tempo, onde um neto Mata seu avô ou se casa com sua avó, ilustrando vividamente alguns princípios éticos práticos!)

Pode permitir um melhor status e relatórios de progresso em uma compilação atualmente em execução. Um TaskexecutionListener fornece ganchos antes/depois do processamento de cada tarefa, mas sem saber o número de tarefas restantes, não há muito que possa dizer sobre o status diferente de "6 tarefas concluídas. Prestes a executar a tarefa Foo". Em vez disso, você pode inicializar um taskexecutionListener com o número de tarefas no gradle.Taskgraph.WhenReady e, em seguida, conectá -lo ao TaskexecutionGraph. Agora ele poderia fornecer informações para permitir detalhes do relatório como "6 de 72 tarefas concluídas. Agora, executando a tarefa Foo. Tempo estimado restante: 2H 38m". Isso seria útil para exibir em um console para um servidor de integração contínuo ou se o gradle estivesse sendo usado para orquestrar uma grande compilação com vários projetos e estimativas de tempo eram cruciais.

Conforme apontado pelo Jerry Bullard, a parte de avaliação do ciclo de vida é fundamental para determinar o plano de execução, que fornece informações sobre o ambiente, uma vez que o ambiente é parcialmente determinado pelo contexto de execução (Exemplo 4.15 na configuração da seção DAG). Além disso, eu pude ver que isso é útil para a otimização de execução. Subpaths independentes podem ser entregues com segurança a diferentes encadeamentos. Os algoritmos de caminhada para execução podem ser menos intensivos na memória se não forem ingênuos (minha intuição diz que sempre caminham pelo caminho com mais subspates levará a uma pilha maior do que sempre preferindo os caminhos com os menos subspatórios).

Um uso interessante disso pode ser uma situação em que muitos componentes de um sistema são inicialmente elaborados para apoiar demos e desenvolvimento incremental. Então, durante o desenvolvimento, em vez de atualizar a configuração de compilação à medida que cada componente é implementado, a própria construção pode determinar se um subprojeto está pronto para a inclusão ainda (talvez tente pegar o código, compilá-lo e executar um conjunto de testes pré-determinado) . Se for, o estágio de avaliação revelaria isso e as tarefas apropriadas seriam incluídas; caso contrário, seleciona as tarefas para os stubs. Talvez haja uma dependência de um banco de dados Oracle que ainda não esteja disponível e você esteja usando um banco de dados incorporado enquanto isso. Você pode deixar a construção verificar a disponibilidade, alternar de forma transparente quando puder e dizer que ele alterou os bancos de dados, em vez de você dizer. Pode haver muitos usos criativos nesse sentido.

Gradle parece incrível. Obrigado por provocar algumas pesquisas!

Outras dicas

Um Exemplo da mesma documentação ilustra o poder dessa abordagem:

Como descrevemos em detalhes mais tarde (consulte o capítulo 30, o ciclo de vida da construção) Gradle possui uma fase de configuração e uma fase de execução. Após a fase de configuração, o Gradle conhece todas as tarefas que devem ser executadas. Gradle oferece um gancho para usar essas informações. Um caso de uso para isso seria verificar se a tarefa de liberação faz parte das tarefas a serem executadas. Dependendo disso, você pode atribuir valores diferentes a algumas variáveis.

Em outras palavras, você pode conectar -se ao processo de construção mais cedo, para que você possa alterar o curso conforme necessário. Se algum trabalho de construção real já tivesse sido realizado, pode ser tarde demais para mudar.

Estou avaliando diferentes sistemas de construção agora e com o gradle, consegui adicionar código feio que enumera todas as tarefas do tipo 'jar' e as altera para que cada jar manifesto inclua o atributo 'Build-Number' (que é usado posteriormente para compor final nomes de arquivos):

gradle.taskGraph.whenReady {
    taskGraph ->
    taskGraph.getAllTasks().findAll {
        it instanceof org.gradle.api.tasks.bundling.Jar
    }.each {
        it.getManifest().getAttributes().put('Build-Number', project.buildVersion.buildNumber)
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top