2010-03-10 22 views
8

Jak odnotowano w the documentation, Gradle używa skierowanego wykresu acyklicznego (DAG) do zbudowania wykresu zależności. Z mojego rozumienia, posiadanie oddzielnych cykli do oceny i wykonania jest główną cechą narzędzia do budowania. na przykład Gradle doc stwierdza, że ​​umożliwia to niektóre funkcje, które w innym przypadku byłyby niemożliwe.Jakie są rzeczywiste przykłady wykresu zależności Gradle'a?

Interesują mnie rzeczywiste przykłady ilustrujące moc tej funkcji. Jakie są przypadki użycia, dla których wykres zależności jest ważny? Szczególnie interesują mnie osobiste historie z pola, czy to z Gradle, czy z podobnie wyposażonym narzędziem.

Zrobiłem to "wiki społeczności" od samego początku, ponieważ trudno będzie ocenić "poprawną" odpowiedź.

Odpowiedz

5

To prowokacyjne pytanie dostarczyło motywacji do ostatecznego spojrzenia na Gradle. Wciąż go nie używałem, więc mogę zaoferować analizę zanotowaną tylko podczas przeglądania dokumentów, a nie osobistych historii.

Moje pierwsze pytanie dotyczyło tego, dlaczego wykres zależności zadań od Gradle ma być acykliczny. Nie znalazłem na to odpowiedzi, ale przeciwnie, sprawa jest prosta, więc zakładam, że wykrywanie cyklu jest sprawdzaniem poprawności, które jest uruchamiane, gdy wykres jest zbudowany, a kompilacja kończy się niepowodzeniem przed wykonaniem pierwszego zadania, jeśli istnieją nielegalne zależności cykliczne. Bez wcześniejszego zbudowania wykresu ten warunek niepowodzenia może nie zostać wykryty, dopóki kompilacja nie zostanie ukończona. Dodatkowo, procedura wykrywania musiała być uruchamiana po wykonaniu każdego zadania, co byłoby bardzo nieefektywne (o ile wykres był budowany przyrostowo i dostępny globalnie, pierwsze wyszukiwanie w głębi było wymagane tylko w celu znalezienia punktu początkowego, a następnie ewaluacja cyklu wymagałaby minimalnej pracy, ale całkowita praca byłaby większa niż wykonanie jednorazowej redukcji całego zestawu relacji na początku). Przypisuję wczesne wykrywanie jako główną korzyść.

Zależność od zadania może być leniwą (patrz: 4.3 Zależności zadań i powiązany przykład w 13.14). Leniwe zależności zadań nie mogą zostać poprawnie ocenione, dopóki cały wykres nie zostanie zbudowany. To samo dotyczy przechodniego (niezwiązanego z zadaniem) rozwiązywania zależności, które może powodować niezliczone problemy i wymagać powtórnych ponownych kompilacji, ponieważ dodatkowe zależności są odkrywane i rozwiązywane (również wymagające ponawiania żądań do repozytorium). Funkcja reguł zadań (13,8) również nie byłaby możliwa.Te problemy, i prawdopodobnie wiele innych, można uogólnić, biorąc pod uwagę, że Gradle używa dynamicznego języka i może dynamicznie dodawać i modyfikować zadania, więc przed oceną pierwszego przejścia wyniki mogą być niedeterministyczne, ponieważ ścieżka wykonania jest zbudowana i zmodyfikowane w czasie działania, a zatem różne sekwencje oceny mogą dawać dowolnie różne wyniki, jeśli istnieją zależności lub dyrektywy behawioralne, które są nieznane dopiero później, ponieważ nie zostały jeszcze utworzone. (Może to być warte zbadania konkretnymi przykładami, a jeśli to prawda, to nawet dwie przepustki nie zawsze będą wystarczające.Jeśli A -> B, B -> C, gdzie C zmienia zachowanie A, aby nie było już zależy to od B. Mam nadzieję, że istnieją pewne najlepsze praktyki dotyczące ograniczania metaprogramowania o zasięgu nielokalnym, aby nie dopuścić do tego w arbitralnych zadaniach. Przykładem może być symulacja paradoksu w czasie podróży, w którym wnuczka zabija swojego dziadka lub poślubia swoją babcię, żywo ilustrując pewne praktyczne zasady etyczne!)

Umożliwia lepsze raportowanie stanu i postępu w aktualnie wykonywanej kompilacji. TaskExecutionListener zapewnia przed/po hooks do przetwarzania każdego zadania, ale bez znajomości liczby pozostałych zadań, nie ma wiele może powiedzieć o statusie innym niż "6 zadań zakończonych." O wykonanie zadania foo. " Zamiast tego można zainicjować TaskExecutionListener z liczbą zadań w gradle.taskGraph.whenReady, a następnie dołączyć go do TaskExecutionGraph. Teraz może dostarczyć informacji, aby włączyć szczegóły raportu, takie jak "Wykonanie 6 z 72 zadań Teraz wykonanie zadania foo, Szacowany pozostały czas: 2h 38m." Byłoby to przydatne do wyświetlania na konsoli dla ciągłego serwera integracyjnego lub jeśli Gradle był używany do aranżowania dużej kompilacji wieloprojektowej i oszacowania czasu były kluczowe.

Jak zauważył Jerry Bullard, część oceny cyklu życia ma kluczowe znaczenie dla określenia planu wykonania, który dostarcza informacji o środowisku, ponieważ środowisko jest częściowo określone przez kontekst wykonania (Przykład 4.15 w Konfiguracji wg DAG Sekcja). Dodatkowo mogłem zobaczyć, że jest to przydatne do optymalizacji wykonania. Niezależne ścieżki podrzędne można bezpiecznie przekazywać różnym wątkom. Algorytmy chodzenia do wykonania mogą być mniej intensywne w pamięci, jeśli nie są naiwne (moja intuicja mówi, że zawsze chodzenie ścieżką z większością ścieżek podrzędnych prowadzi do większego stosu niż zawsze preferuje ścieżki z najmniejszą ścieżką podrzędną).

Ciekawym zastosowaniem może być sytuacja, w której wiele elementów systemu jest początkowo zgaszonych w celu wspierania prezentacji i stopniowego rozwoju. Następnie, w trakcie rozwoju, zamiast aktualizować konfigurację kompilacji, gdy każdy komponent zostanie zaimplementowany, sama kompilacja może określić, czy podprojekt jest gotowy do włączenia (może próbuje pobrać kod, skompilować go i uruchomić z góry ustalony pakiet testowy) . Jeśli tak, etap ewaluacji to ujawniłby, a odpowiednie zadania zostałyby uwzględnione, w przeciwnym razie wybiera zadania dla kodów pośredniczących. Być może istnieje zależność od bazy danych Oracle, która nie jest jeszcze dostępna, a w międzyczasie korzystasz z wbudowanej bazy danych. Możesz pozwolić, aby kompilacja sprawdzała dostępność, przeźroczysto przełączaj się, kiedy to możliwe, i powiedz, że przełączała bazy danych, zamiast je wymieniać. Może być wiele twórczych zastosowań wzdłuż tych linii.

Gradle wygląda niesamowicie. Dzięki za prowokowanie niektórych badań!

3

example from the same documentation ilustruje potęgę tego podejścia:

Jak opisać w szczegółach później (patrz rozdział 30, Lifecycle Build) Gradle ma fazę konfiguracji i fazę realizacji. Po fazie konfiguracji Gradle zna wszystkie zadania, które powinny zostać wykonane. Gradle oferuje hak do korzystania z tej informacji . Przypadkiem przydatnym do tego byłoby , aby sprawdzić, czy zadanie zwolnienia to część zadań do wykonania. W zależności od tego można przypisać różne wartości różnym wartościom .

Innymi słowy, można wcześnie włączyć proces budowania, aby w razie potrzeby można było zmienić jego kurs. Jeśli niektóre rzeczywiste prace budowlane zostały już wykonane, może być za późno na zmianę.

+0

Dzięki za odpowiedź! Jednak znałem ten przykład. Szukam czegoś z odrobiną więcej mięsa. –

1

Oceniam różne systemy kompilacji teraz iz gradle udało mi się dodać brzydki kod, który wylicza wszystkie zadania typu "jar" i zmienia je tak, że każdy manifest słoika zawiera atrybut "Numer kompilacji" (który jest używany później, aby skomponować ostateczne nazwy plików):

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) 
    } 
}