2013-06-25 16 views
11

Eksperymentuję z wykorzystaniem Shake do budowania kodu Java i jestem nieco zablokowany z powodu niezwykłej natury kompilatora javac. Ogólnie dla każdego modułu dużego projektu kompilator jest wywoływany z all plików źródłowych dla tego modułu jako danych wejściowych i tworzy wszystkie pliki wyjściowe w jednym przebiegu. Następnie zwykle bierzemy pliki .class produkowane przez kompilator i łączymy je w plik JAR (w zasadzie po prostu ZIP).Kompilatory z wieloma wejściami i wieloma wyjściami z Shake

Na przykład, typowy projekt Java moduł umieszczony jest w następujący sposób:

  • src katalogu, który zawiera wiele plików .java, niektóre z nich zagnieżdżone wiele poziomów głęboko w drzewie.
  • a bin katalog zawierający dane wyjściowe kompilatora. Zwykle dane wyjściowe mają tę samą strukturę katalogów i nazwy plików, a każdy plik .java zastępuje .class, ale mapowanie to: , a nie niekoniecznie jeden do jednego: pojedynczy plik .java może wygenerować zero do wielu plików .class!

Zasady chciałbym zdefiniować w wstrząsnąć są zatem następujące:

1) Jeżeli każdy plik pod src jest nowszy niż każdy plik pod bin następnie usunąć całą zawartość z bin i odtworzyć z:

javac -d bin <recursive list of .java files under src>

wiem zasada ta wydaje się zbędne, ale bez wywoływania kompilator wagowo e nie może znać zakresu zmian danych wyjściowych wynikających nawet z niewielkiej zmiany w jednym pliku wejściowym.

2) jeżeli każdy plik pod bin jest nowszy niż module.jar następnie odtworzyć module.jar z:

jar cf module.jar -C bin .

Many thanks!

PS Odpowiedzi w żyle "po prostu użyj Ant/Maven/Gradle /" nie zostaną docenione! Wiem, że te narzędzia oferują kompilację Java, ale są znacznie trudniejsze w komponowaniu i agregowaniu. Właśnie dlatego chcę eksperymentować z narzędziem opartym na Haskell/Shake.

Odpowiedz

8

Zapisywanie reguł, które dają wiele wyników, których nazwy nie można określić statycznie, może być nieco trudne. Najczęstszym podejściem jest znalezienie wyjścia, którego nazwa jest statycznie znana i zawsze need, lub jeśli nie istnieje, utworzyć fałszywy plik do użycia jako statyczny wynik (jak na ghc-make, the .result file). W twoim przypadku masz module.jar jako ostateczne wyjście, więc chciałbym napisać:

"module.jar" *> \out -> do 
    javas <- getDirectoryFiles "" ["src//*.java"] 
    need javas 
    liftIO $ removeFiles "" ["bin//*"] 
    liftIO $ createDirectory "bin" 
    () <- cmd "javac -d bin" javas 
    classes <- getDirectoryFiles "" ["bin//*.class"] 
    need classes 
    cmd "jar cf" [out] "-C bin ." 

Nie ma przewagę do dzielenia go na dwóch zasadach, ponieważ nigdy nie zależą od .class plików (i naprawdę nie mogę, ponieważ są one nieprzewidywalne w nazwie), a jeśli jakikolwiek plik źródłowy się zmieni, to i tak zawsze odbudujesz module.jar.Ta reguła ma wszystkie zależności, o których wspomniałeś, a także jeśli dodasz/zmienisz nazwę/usuniesz dowolny plik .java lub .class, to zostanie on automatycznie przekompilowany, ponieważ śledzone jest wywołanie getDirectoryFiles.

+0

Świetnie, dzięki Neil! Zasadniczo działa, ale wymagane są kilka drobnych zmian. Na przykład potrzebujemy "potrzebować" pełnej ścieżki plików .java i .class, w tym prefiksu src/bin. Musimy również jawnie utworzyć katalog 'bin' przed wywołaniem javac. Czy powinienem edytować odpowiedź? –

+0

Tak, proszę! Podejrzewam, że jeśli zmienisz getDirectoryFiles na "" ["src // *. Java"], będzie to łatwiejsze niż wcześniejsze src, chociaż oba powinny zachowywać się identycznie (i równie skutecznie). Do stworzenia katalogu potrzebny będzie liftIO $ createDirectory - zwykle nie jest to konieczne, ponieważ shake tworzy wszystkie katalogi dla wyjść, o których wie, ale tutaj nie wie o bin. –

+0

Robię coś podobnego, ale wydaje mi się, że wchodzę w "wątek blokowany w nieskończoność w operacji MVar", kiedy potrzebuję 'dynamicznie generowanych ścieżek FilePath. – user239558