2013-06-25 39 views
11

我正在嘗試使用Shake來構建Java代碼,並且由於javac編譯器的特殊性質而有點卡住了。一般而言,對於大型項目的每個模塊,編譯器都會以該模塊的源文件的全部作爲輸入來調用,並在一次傳遞中生成所有輸出文件。隨後,我們通常採用由編譯器生成的.class文件,並將它們組裝成一個JAR(基本上只是一個ZIP)。帶有Shake的多輸入多輸出編譯器

例如,一個典型的Java模塊項目安排如下:

  • 包含多個.java文件一個src目錄,他們中的一些深嵌套在一個樹多層次。
  • a bin包含編譯器輸出的目錄。通常這個輸出遵循相同的目錄結構和文件名,用.class代替每個.java文件,但映射是而不是必然是一對一的:一個.java文件可以生成零到多個.class文件!因此

我想在搖定義的規則如下:

1)如果src下任何文件比更新bin下任何文件然後擦除bin所有內容並重新創建搭配:

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

我知道這個規則似乎過高,但沒有調用編譯器w^e無法知道單個輸入文件中的小變更所導致的輸出變化程度。

2)如果bin下的任何文件比module.jar更新,然後重新創建module.jar有:

jar cf module.jar -C bin .

非常感謝!

PS對靜脈「只使用Ant/Maven/Gradle /」的反應將不會感激!我知道這些工具開箱即用地提供了Java編譯,但是它們更難以編寫和彙總。這就是爲什麼我想試驗一個基於Haskell/Shake的工具。

回答

8

產生多個輸出的名稱不能被靜態確定的寫規則可能有點棘手。通常的方法是找到名稱爲靜態已知的輸出,並且始終爲need,或者如果不存在,則創建一個假文件用作靜態輸出(按照ghc-make, the .result file)。你的情況,你有module.jar作爲最終的輸出,所以我會寫:

"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 ." 

沒有優勢,它分裂成兩個規則,因爲你永遠不取決於.class文件(並不能真正,因爲它們的名稱是不可預知的),並且如果有任何源文件發生更改,則總是會重建module.jar。此規則具有您提及的所有依賴關係,如果您添加/重命名/刪除任何.java.class文件,則會自動重新編譯,因爲getDirectoryFiles調用被跟蹤。

+0

非常感謝尼爾!這基本上工作,但需要一些小的更改。例如,我們需要「需要」.java和.class文件的完整路徑,包括src/bin前綴。我們還需要在調用javac之前明確創建'bin' dir。我應該編輯你的答案嗎? –

+0

是的請!我懷疑,如果將getDirectoryFiles更改爲「[」src // *。java「],比之前預先設置src更容易,儘管兩者的行爲應該完全相同(並且效率同樣高)。爲了創建目錄,你需要liftIO $ createDirectory - 通常這不是必要的,因爲shake會創建所有知道輸出的目錄,但是在這裏它不知道bin。 –

+0

我正在做類似的事情,但是當我'需要'動態生成的FilePath時,我似乎進入了「在MVar操作中無限期地被阻塞的線程」。 – user239558