2013-06-25 25 views
11

Tôi đang thử nghiệm bằng cách sử dụng Shake để xây dựng mã Java và có một chút khó khăn do tính chất bất thường của trình biên dịch javac. Nói chung cho mỗi mô-đun của một dự án lớn, trình biên dịch được gọi với tất cả của các tệp nguồn cho mô-đun đó làm đầu vào và tạo tất cả các tệp đầu ra trong một lần truyền. Sau đó, chúng tôi thường lấy các tệp .class do trình biên dịch tạo và lắp ráp chúng thành một JAR (về cơ bản chỉ là một ZIP).Trình biên dịch nhiều đầu vào, nhiều đầu ra với Shake

Ví dụ, một dự án module Java điển hình được bố trí như sau:

  • một thư mục src có chứa nhiều file java, một số trong số họ lồng nhau nhiều cấp độ sâu trong một cái cây.
  • một thư mục bin chứa đầu ra từ trình biên dịch. Thông thường, đầu ra này theo cùng cấu trúc thư mục và tên tệp, với .class được thay thế cho mỗi tệp .java, nhưng ánh xạ là không phải là nhất thiết là một: một tệp .java có thể tạo không cho nhiều tệp .class! do đó

Các quy tắc tôi muốn xác định trong Shake như sau:

1) Nếu bất kỳ tập tin dưới src mới hơn bất kỳ tập tin dưới bin sau đó xóa tất cả nội dung của bin và tái tạo với:

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

Tôi biết quy tắc này có vẻ quá mức nhưng không yêu cầu trình biên dịch e không thể biết mức độ thay đổi trong đầu ra do thậm chí là một thay đổi nhỏ trong một tệp đầu vào đơn lẻ.

2) nếu bất kỳ tập tin dưới bin mới hơn module.jar sau đó tái module.jar với:

jar cf module.jar -C bin .

Rất cám ơn!

PS Câu trả lời trong tĩnh mạch "chỉ cần sử dụng Ant/Maven/Gradle /" sẽ không được đánh giá cao! Tôi biết những công cụ này cung cấp Java compilation out-of-the-box, nhưng chúng khó hơn nhiều để soạn và tổng hợp. Đây là lý do tại sao tôi muốn thử nghiệm với một công cụ dựa trên Haskell/Shake.

Trả lời

8

Viết quy tắc tạo ra nhiều kết quả đầu ra có tên không thể xác định tĩnh có thể hơi phức tạp một chút. Cách tiếp cận thông thường là tìm một đầu ra có tên được biết đến tĩnh và luôn luôn là need, hoặc nếu không tồn tại, tạo một tệp giả để sử dụng làm đầu ra tĩnh (theo ghc-make, the .result file). Trong trường hợp của bạn, bạn có module.jar như đầu ra cuối cùng, vì vậy tôi sẽ viết:

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

Không có lợi thế để tách nó ra thành hai quy tắc, vì bạn không bao giờ phụ thuộc vào .class file (và không thể thực sự, vì chúng không thể đoán trước được trong tên) và nếu bất kỳ tệp nguồn nào thay đổi thì bạn sẽ luôn luôn xây dựng lại module.jar.Quy tắc này có tất cả các phụ thuộc mà bạn đề cập, cộng với nếu bạn thêm/đổi tên/xóa bất kỳ tệp .java hoặc .class thì nó sẽ tự động biên dịch lại, vì cuộc gọi getDirectoryFiles được theo dõi.

+0

Tuyệt vời, cảm ơn Neil! Điều này về cơ bản hoạt động nhưng một vài thay đổi nhỏ là bắt buộc. Ví dụ chúng ta cần 'cần' đường dẫn đầy đủ của các tệp .java và .class bao gồm tiền tố src/bin. Ngoài ra chúng ta cần tạo thư mục 'bin' một cách rõ ràng trước khi gọi javac. Tôi có nên chỉnh sửa câu trả lời của bạn không? –

+0

Có, vui lòng! Tôi nghi ngờ nếu bạn thay đổi getDirectoryFiles thành "" ["src // *. Java"] sẽ dễ dàng hơn so với src trước sau đó, mặc dù cả hai sẽ hoạt động giống hệt nhau (và chỉ hiệu quả). Để tạo thư mục, bạn sẽ cần liftIO $ createDirectory - thường không cần thiết vì shake tạo ra tất cả các thư mục cho các kết quả đầu ra mà nó biết, nhưng ở đây nó không biết về bin. –

+0

Tôi đang làm một cái gì đó tương tự, nhưng tôi dường như nhận được vào "thread bị chặn vô thời hạn trong một hoạt động MVar" khi tôi 'cần' FilePaths được tạo động. – user239558

Các vấn đề liên quan