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ń!
Dzięki za odpowiedź! Jednak znałem ten przykład. Szukam czegoś z odrobiną więcej mięsa. –