2017-11-27 26 views
6

Tôi muốn có một tệp makefile để biên soạn một dự án và tôi cần một số tệp (binaries) để tải xuống nếu chúng bị thiếu, vì đó không phải là ý tưởng hay để theo dõi chúng với git.Tải xuống danh sách các tệp từ danh sách URL làm điều kiện tiên quyết trong makefile

Tôi có danh sách URL và danh sách tên đã cho (và tôi muốn có thể chọn tên để lưu chúng, vì tôi không thể kiểm soát tên URL và chủ yếu là tên khá khủng khiếp và không - mô tả). Hãy nói rằng các tập tin sẽ luôn luôn giống nhau hoặc chúng tôi không quan tâm nếu chúng thay đổi trong máy chủ (chúng tôi không cần phải tải chúng lại).

Vì vậy, ý tưởng của tôi là viết một cái gì đó như thế này trong makefile của tôi:

files = one two three four 

main : $(files) 
    command to do when files are present 

nhưng tôi muốn mỗi người trong số họ sẽ được tải xuống nếu họ không phải là đã có mặt, và mỗi một từ URL riêng của mình, vì vậy tôi 'd cần một cái gì đó giống như một iteration trên các yếu tố của $ (tập tin).

Tôi sẽ viết ý tưởng tôi có trong tâm trí của tôi trộn mã python và những gì tôi biết từ makefiles. Tôi biết nó là rác rưởi, nhưng tôi không biết cách nào khác để làm điều đó (câu hỏi là làm thế nào để viết nó tốt, về cơ bản) và tôi nghĩ nó khá dễ hiểu.

urls = url1 url2 url3 url4 

for i in len(files): 
    $(files)[i] : 
     curl -Lo $(files)[i] $(urls)[i] 

Vì vậy, câu hỏi đặt ra là: làm thế nào tôi nên làm điều này?

Tôi đã tìm kiếm tài liệu trong một thời gian và tôi không thể tìm ra cách thích hợp để thực hiện nó mà không cần viết tên tệp nhiều lần (mà tôi nghĩ nên tránh và các biến nên được sử dụng thay thế). Có lẽ canned recipes là cách, nhưng tôi không thể thấy như thế nào.

+0

Thay đổi tệp (trên máy chủ) và bạn có cần tìm nạp lại chúng không? Trong trường hợp đó, bạn có thể nghĩ về một loại quản lý phụ thuộc (tức là Apache Ivy) hoặc có thể thay thế các tệp makefile của bạn bằng các kịch bản lệnh gradle. – pitseeker

+0

Không, họ không thay đổi (hoặc tôi sẽ không quan tâm nếu họ đã làm). Tôi sẽ chỉnh sửa câu hỏi để phản xạ điều đó, cảm ơn! – josealberto4444

Trả lời

1
FILES := file1 file2 file3 

main : $(FILES) 
    command to do when files are present 

file1: firstuglyurl 
file2: seconduglyurl 
file3: thirduglyurl 

$(FILES): 
    curl -Lo [email protected] $< 

[EDIT] RẰNG là một giải pháp rất ngu ngốc, tôi không biết những gì tôi đã suy nghĩ. Hãy thử điều này:

FILES := file1 file2 file3 

main : $(FILES) 
    command to do when files are present 

file1: URL:=firstuglyurl 
file2: URL:=seconduglyurl 
file3: URL:=thirduglyurl 

$(FILES): 
    curl -Lo [email protected] $(URL) 

Nếu bản đồ của bạn (URL => filename) là trong một tập tin và bạn không muốn để duy trì nó trong makefile bằng tay, bạn có thể có Make nhập khẩu nó mà không có quá nhiều rắc rối, chỉ cần cho chúng tôi biết định dạng. Lưu ý rằng điều này không kiểm tra các tệp hiện tại nhưng đã lỗi thời, tức là phiên bản mới hơn của tệp tồn tại tại URL (có thể có thể được thực hiện, nhưng nó phức tạp ...).

+0

Tôi đã nghĩ rằng điều này sẽ phàn nàn về các yêu cầu bị thiếu trước đó. Tức là, không có quy tắc để xây dựng firstuglyurl. – lockcmpxchg8b

+0

Ahh .. nhưng thực hiện .PHONY phụ thuộc vào các URL sửa chữa nó. – lockcmpxchg8b

+0

Cũng có thể muốn thêm '-R/- remote-time' vào lời gọi curl, chỉ trong trường hợp chúng ta đang kéo từ một nguồn có ngày ... nếu bạn có thể tin tưởng rằng các đồng hồ tương đối tốt trong đồng bộ, Là. – lockcmpxchg8b

3

Có một số cách để thực hiện những gì bạn muốn. Cảnh báo: họ là một chút khó khăn vì họ sử dụng các tính năng tiên tiến của GNU thực hiện:

Giải pháp # 1:

files := one two three four 
urls := url1 url2 url3 url4 

main: $(files) 
    @echo 'command to do when files are present' 

# $(1): file name 
# $(2): url 
define DOWNLOAD_rule 
$(1): 
    @echo 'curl -Lo $(1) $(2)' 
endef 
$(foreach f,$(files),\ 
    $(eval $(call DOWNLOAD_rule,$(f),$(firstword $(urls))))\ 
    $(eval urls := $(wordlist 2,$(words $(urls)),$(urls)))\ 
) 

Tôi thay thế các công thức nấu ăn bởi echos để thử nghiệm dễ dàng hơn:

$ make -j 
curl -Lo one url1 
curl -Lo four url4 
curl -Lo three url3 
curl -Lo two url2 
command to do when files are present 

Giải thích:

DOWNLOAD_rule nhiều dòng và va riable là quy tắc mẫu cho thao tác tải xuống trong đó $(1) đại diện cho tên tệp và $(2) đại diện cho URL tương ứng.

Bạn có thể thực hiện việc thay thế $(1)$(2) trong DOWNLOAD_rule, và nhanh chóng kết quả như thực hiện cú pháp với:

$(eval $(call DOWNLOAD_rule,one,url1)) 

này cũng giống như nếu bạn đã viết:

one: 
    @echo 'curl -Lo one url1' 

Các foreach chức năng cho phép lặp qua danh sách các từ. Vì vậy:

$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f),url1))) 

sẽ khởi tạo quy tắc cho tất cả các tệp ... ngoại trừ URL sẽ giống nhau (url1) cho tất cả. Không phải những gì bạn muốn.

Để có được URL tương ứng với mỗi tập tin, chúng ta có thể đặt hai cuộc gọi khác nhau cho eval chức năng trong foreach vòng lặp của chúng tôi:

  • một đầu tiên mà instantiates quy tắc với tên tập tin hiện tại và URL đầu tiên trong các số $(urls),
  • số thứ hai xóa từ đầu tiên khỏi số $(urls) và gán lại kết quả cho urls.

Lưu ý rằng bài tập := là cần thiết. Mặc định = (mở rộng đệ quy) sẽ không hoạt động ở đây.

$(wordlist 2,$(words $(urls)),$(urls)) có thể trông phức tạp nhưng nó không phải là:

  • $(wordlist s,e,l) mở rộng như từ số s-e trong danh sách l (từ được đánh số từ 1 đến chiều dài của l)
  • $(words l) mở rộng khi số lượng từ trong danh sách l

Vì vậy, nếu $(urls) là, e. g, url2 url3 url4:.

  • $(words $(urls)) mở rộng khi 3
  • $(wordlist 2,$(words $(urls)),$(urls)) mở rộng khi url3 url4 vì nó cũng giống như $(wordlist 2,3,url2 url3 url4)

Giải pháp # 2:

Nó cũng có thể đóng gói số eval thứ hai trong biến số DOWNLOAD_rule, nhưng có một khía cạnh khác cần xem xét: cipe được mở rộng bằng cách thực hiện ngay trước khi được chuyển tới shell.Một biến mục tiêu cụ thể (url), được mở rộng trong thời gian đầu tiên vượt qua phân tích, giải quyết này:

files := one two three four 
urls := url1 url2 url3 url4 

main: $(files) 
    @echo 'command to do when files are present' 

# $(1): file name 
define DOWNLOAD_rule 
$(1): url := $$(firstword $$(urls)) 
$(1): 
    @echo 'curl -Lo $(1) $$(url)' 
urls := $$(wordlist 2,$$(words $$(urls)),$$(urls)) 
endef 
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f)))) 

Lưu ý $$ trong định nghĩa của DOWNLOAD_rule, họ là cần thiết vì eval mở rộng tham số của nó và thực hiện sẽ mở rộng nó lần thứ hai khi phân tích kết quả như cú pháp thực hiện thường xuyên. Các $$ là một cách để bảo vệ tài liệu tham khảo của chúng tôi biến từ việc mở rộng đầu tiên, chẳng hạn những gì được khởi tạo bởi eval trong bốn lần lặp của foreach là:

one: url := $(firstword $(urls)) 
one: 
    @echo 'curl -Lo one $(url)' 
urls := $(wordlist 2,$(words $(urls)),$(urls)) 

two: url := $(firstword $(urls)) 
two: 
    @echo 'curl -Lo two $(url)' 
urls := $(wordlist 2,$(words $(urls)),$(urls)) 

three: url := $(firstword $(urls)) 
three: 
    @echo 'curl -Lo three $(url)' 
urls := $(wordlist 2,$(words $(urls)),$(urls)) 

four: url := $(firstword $(urls)) 
four: 
    @echo 'curl -Lo four $(url)' 
urls := $(wordlist 2,$(words $(urls)),$(urls)) 

mà sẽ làm chính xác những gì chúng ta muốn. Trong khi nếu không có sự $$ nó sẽ là:

one: url := url1 
one: 
    @echo 'curl -Lo one ' 
urls := url2 url3 url4 

two: url := url1 
two: 
    @echo 'curl -Lo two ' 
urls := url2 url3 url4 

three: url := url1 
three: 
    @echo 'curl -Lo three ' 
urls := url2 url3 url4 

four: url := url1 
four: 
    @echo 'curl -Lo four ' 
urls := url2 url3 url4 

Hãy nhớ điều này: khi sử dụng eval có hai mở rộng và $$ thường cần thiết để thoát khỏi cái đầu tiên.

Giải pháp # 3:

Chúng tôi cũng có thể khai báo một biến cho mỗi tập tin (<file>-url) để lưu trữ các địa chỉ URL, và một macro để thiết lập các biến này và danh sách các file (files):

# Set file's URL and update files' list 
# Syntax: $(call set_url,<file>,<url>) 
set_url = $(eval $(1)-url := $(2))$(eval files := $$(files) $(1)) 

$(call set_url,one,url1) 
$(call set_url,two,url2) 
$(call set_url,three,url3) 
$(call set_url,four,url4) 

main: $(files) 
    @echo 'command to do when files are present' 

# $(1): file name 
define DOWNLOAD_rule 
$(1): 
    @echo 'curl -Lo $(1) $$($(1)-url)' 
endef 
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f)))) 

giải pháp # 4:

Cuối cùng, chúng ta có thể sử dụng tuyệt vời GNU Make Standard Library (gmsl) bởi John Graham-Cumming và các mảng kết hợp của nó để làm tương tự như trong giải pháp # 3. Ví dụ: chúng tôi có thể xác định mảng kết hợp có tên là urls với tên tệp là khóa và URL làm giá trị:

include gmsl 

$(call set,urls,one,url1) 
$(call set,urls,two,url2) 
$(call set,urls,three,url3) 
$(call set,urls,four,url4) 

files := $(call keys,urls) 

main: $(files) 
    @echo 'command to do when files are present' 

# $(1): file name 
define DOWNLOAD_rule 
$(1): 
    @echo 'curl -Lo $(1) $$(call get,urls,$(1))' 
endef 
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f)))) 
+0

Tôi không thể tìm ra cách định dạng mã chính xác trong các nhận xét ... Bạn có thể thay thế hai '$ (eval)' đầu tiên trong '$ (foreach)' bằng một url: = $ (từ danh sách 2, x, $ (url)) 'trong phần nội dung của' DOWNLOAD_rule'. (với x> số từ trong url và các tập tin – VannTen

+0

@VannTen Bạn nói đúng, nhưng tôi không thích chưa được xác minh, cứng có dây, upper-bounds ('x') của bạn. Khi chưa được định hướng, chúng có xu hướng gây ra các lỗi cực kỳ khó tìm. –

+0

@VannTen Hơn nữa, 'eval' đầu tiên là cần thiết để trích xuất từ ​​đầu tiên từ' $ (url) '. –

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