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)
và $(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))))
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
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