TL; DR : ใช้error
ฟังก์ชั่น :
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
โปรดทราบว่าบรรทัดจะต้องไม่เยื้อง แม่นยำยิ่งขึ้นไม่มีแท็บใดที่ต้องนำหน้าบรรทัดเหล่านี้
วิธีแก้ปัญหาทั่วไป
ในกรณีที่คุณจะทดสอบตัวแปรหลายตัวมันก็คุ้มค่าที่จะกำหนดฟังก์ชั่นเสริมสำหรับสิ่งนั้น:
# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
# 1. Variable name(s) to test.
# 2. (optional) Error message to print.
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))))
และนี่คือวิธีการใช้งาน:
$(call check_defined, MY_FLAG)
$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
LIB_INCLUDE_DIR \
LIB_SOURCE_DIR, \
library path)
สิ่งนี้จะทำให้เกิดข้อผิดพลาดเช่นนี้:
Makefile:17: *** Undefined OUT_DIR (build directory). Stop.
หมายเหตุ:
การตรวจสอบจริงเสร็จแล้วที่นี่:
$(if $(value $1),,$(error ...))
สิ่งนี้สะท้อนถึงพฤติกรรมของifndef
เงื่อนไขดังนั้นตัวแปรที่กำหนดให้กับค่าว่างจะถูกพิจารณาว่าเป็น "undefined" แต่นี่เป็นจริงสำหรับตัวแปรอย่างง่ายและตัวแปรแบบเรียกซ้ำที่ว่างเปล่าเท่านั้น:
# ifndef and check_defined consider these UNDEFINED:
explicitly_empty =
simple_empty := $(explicitly_empty)
# ifndef and check_defined consider it OK (defined):
recursive_empty = $(explicitly_empty)
ตามที่แนะนำโดย @VictorSergienko ในความคิดเห็นอาจมีพฤติกรรมที่แตกต่างออกไปเล็กน้อย:
$(if $(value $1)
ทดสอบว่าค่าไม่ว่างเปล่า บางครั้งก็ตกลงถ้าตัวแปรถูกกำหนดด้วยค่าว่าง ฉันจะใช้$(if $(filter undefined,$(origin $1)) ...
และ:
นอกจากนี้ถ้าหากมันเป็นไดเรกทอรีและมันต้องมีอยู่$(if $(wildcard $1))
เมื่อการันฉันต้องการใช้ แต่จะเป็นฟังก์ชั่นอื่น
การตรวจสอบเฉพาะเป้าหมาย
นอกจากนี้ยังเป็นไปได้ที่จะขยายโซลูชันเพื่อให้สามารถต้องการตัวแปรเฉพาะเมื่อเรียกใช้เป้าหมายที่แน่นอน
$(call check_defined, ...)
จากภายในสูตร
เพียงแค่ย้ายเช็คไปที่สูตร:
foo :
@:$(call check_defined, BAR, baz value)
ชั้นนำ@
ผลัดกันลงชื่อสะท้อนคำสั่งและ:
เป็นคำสั่งที่เกิดขึ้นจริงเปลือกไม่มี-op ต้นขั้ว
แสดงชื่อเป้าหมาย
check_defined
ฟังก์ชั่นได้ดีขึ้นนอกจากนี้ยังส่งออกชื่อเป้าหมาย (ให้ผ่าน$@
ตัวแปร):
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))$(if $(value @), \
required by target `$@')))
ดังนั้นตอนนี้การตรวจสอบที่ล้มเหลวจะสร้างผลลัพธ์ที่ได้รับการจัดรูปแบบอย่างดี:
Makefile:7: *** Undefined BAR (baz value) required by target `foo'. Stop.
check-defined-MY_FLAG
เป้าหมายพิเศษ
โดยส่วนตัวแล้วฉันจะใช้วิธีที่ง่ายและตรงไปตรงมาข้างต้น อย่างไรก็ตามตัวอย่างเช่นคำตอบนี้แนะนำให้ใช้เป้าหมายพิเศษเพื่อทำการตรวจสอบจริง ใคร ๆ ก็สามารถพยายามที่จะพูดคุยเรื่องนั้นและกำหนดเป้าหมายเป็นกฎรูปแบบโดยนัย:
# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
# %: The name of the variable to test.
#
check-defined-% : __check_defined_FORCE
@:$(call check_defined, $*, target-specific)
# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :
การใช้งาน:
foo :|check-defined-BAR
โปรดสังเกตว่าสิ่งที่check-defined-BAR
แสดงรายการเป็นสิ่งที่จำเป็นต้องมีเพื่อคำสั่งซื้ออย่างเดียว ( |...
)
ข้อดี:
- (เนื้อหา) ไวยากรณ์ที่สะอาดมากขึ้น
จุดด้อย:
- ไม่มีใครสามารถระบุข้อความแสดงข้อผิดพลาดที่กำหนดเองได้
- การเรียกใช้
make -t
(ดูแทนการใช้สูตรอาหาร ) จะทำให้ไดเรกทอรีรากของคุณสกปรกด้วยcheck-defined-...
ไฟล์จำนวนมาก นี่คืออุปสรรคที่น่าเศร้าของความจริงที่ว่ากฎรูปแบบที่ไม่สามารถประกาศ.PHONY
ฉันเชื่อว่าข้อ จำกัด เหล่านี้สามารถเอาชนะได้โดยใช้eval
เวทมนตร์และแฮ็กการขยายตัวรองแม้ว่าฉันไม่แน่ใจว่ามันคุ้มค่า