ตรวจสอบว่ามีโปรแกรมจาก Makefile หรือไม่


115

ฉันจะตรวจสอบได้อย่างไรว่าโปรแกรมสามารถเรียกใช้จาก Makefile ได้หรือไม่?

(นั่นคือโปรแกรมควรมีอยู่ในเส้นทางหรือมิฉะนั้นสามารถเรียกใช้ได้)

สามารถใช้เพื่อตรวจสอบว่ามีการติดตั้งคอมไพเลอร์ใดบ้าง

เช่นคำถามนี้แต่ไม่ถือว่าเชลล์ต้นแบบเข้ากันได้กับ POSIX


1
คุณสามารถเรียกใช้เชลล์ที่เข้ากันได้กับ POSIX ได้หรือไม่?
Reinierpost

อาจจะไม่ฉันเดาว่าฉันสามารถเรียกร้องให้มีใครอยู่ที่นั่นได้ แต่มันจะง่ายกว่ามากถ้าฉันไม่ต้องทำ
ศ. Falken

ในระหว่างนี้ฉันแก้ไขโดยเพิ่มโปรแกรมลงในโปรเจ็กต์ซึ่งสร้างขึ้นก่อนและมีจุดประสงค์เพียงอย่างเดียวเพื่อตรวจสอบโปรแกรมอื่นนั้น ... :-)
ศ. Falken

2
วิธีแก้ปัญหาแบบดั้งเดิมคือการมีสคริปต์ที่จะตรวจสอบสิ่งที่จำเป็นต่างๆและเขียนออกมาเป็นที่เหมาะสมautomake Makefile
tripleee

คำตอบ:


84

บางครั้งคุณต้องใช้ Makefile เพื่อให้สามารถรันบน OS เป้าหมายที่แตกต่างกันได้และคุณต้องการให้บิลด์ล้มเหลวในช่วงต้นหากไฟล์ปฏิบัติการที่ต้องการไม่อยู่ในPATHแทนที่จะรันเป็นเวลานานก่อนที่จะล้มเหลว

วิธีการแก้ปัญหาที่ดีเยี่ยมให้โดยengineerchuanต้องทำเป้าหมาย อย่างไรก็ตามหากคุณมีไฟล์ปฏิบัติการจำนวนมากที่จะทดสอบและ Makefile ของคุณมีเป้าหมายอิสระจำนวนมากซึ่งแต่ละเป้าหมายต้องมีการทดสอบดังนั้นแต่ละเป้าหมายต้องการเป้าหมายการทดสอบเป็นการพึ่งพา ซึ่งทำให้ต้องพิมพ์เพิ่มมากขึ้นรวมถึงเวลาในการประมวลผลเมื่อคุณสร้างเป้าหมายได้มากกว่าหนึ่งเป้าหมายในแต่ละครั้ง

โซลูชันที่จัดทำโดย0xfสามารถทดสอบปฏิบัติการได้โดยไม่ต้องกำหนดเป้าหมาย ซึ่งช่วยประหยัดเวลาในการพิมพ์และดำเนินการได้มากเมื่อมีหลายเป้าหมายที่สามารถสร้างแยกกันหรือร่วมกัน

การปรับปรุงวิธีแก้ปัญหาหลังของฉันคือการใช้whichปฏิบัติการ ( whereใน Windows) แทนที่จะพึ่งพาการมี--versionตัวเลือกในแต่ละปฏิบัติการโดยตรงในifeqคำสั่งGNU Make แทนที่จะกำหนดตัวแปรใหม่และใช้ GNU Make ฟังก์ชั่นที่จะหยุดการสร้างถ้าปฏิบัติการต้องไม่ได้อยู่ในerror ${PATH}ตัวอย่างเช่นในการทดสอบlzopปฏิบัติการ:

 ifeq (, $(shell which lzop))
 $(error "No lzop in $(PATH), consider doing apt-get install lzop")
 endif

หากคุณมีไฟล์ปฏิบัติการที่ต้องตรวจสอบหลายรายการคุณอาจต้องการใช้foreachฟังก์ชันกับwhichไฟล์ปฏิบัติการ:

EXECUTABLES = ls dd dudu lxop
K := $(foreach exec,$(EXECUTABLES),\
        $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))

สังเกตการใช้ตัว:=ดำเนินการกำหนดที่จำเป็นเพื่อบังคับให้มีการประเมินนิพจน์ RHS ทันที หาก Makefile ของคุณเปลี่ยนไปPATHแทนที่จะเป็นบรรทัดสุดท้ายด้านบนคุณจะต้อง:

        $(if $(shell PATH=$(PATH) which $(exec)),some string,$(error "No $(exec) in PATH")))

สิ่งนี้ควรให้ผลลัพธ์คล้ายกับ:

ads$ make
Makefile:5: *** "No dudu in PATH.  Stop.

2
หากคุณสร้างตัวแปรทั้งหมด (เช่น LS CC LD) และใช้ $ ($ (exec)) คุณสามารถส่งผ่านตัวแปรเหล่านี้เพื่อสร้างจากสภาพแวดล้อมหรือ makefiles อื่น ๆ ได้อย่างราบรื่น มีประโยชน์เมื่อคอมไพล์ข้าม
rickfoosusa

1
โช้คของฉันที่ "," เมื่อทำงาน "ifeq (, $ (shell which lzop))" = /
mentatkgs

2
บน Windows ให้ใช้where my_exe 2>NULแทนwhichไฟล์.
Aaron Campbell

2
ระวังการเยื้อง TABS ด้วย ifeq ซึ่งเป็นไวยากรณ์ของกฎ! จะต้องไม่มีแท็บ มีช่วงเวลาที่ยากลำบากกับสิ่งนั้น stackoverflow.com/a/21226973/2118777
Pipo

2
หากคุณกำลังใช้มีไม่จำเป็นต้องโทรออกภายนอกBash whichใช้บิวท์อินcommand -vแทน ข้อมูลเพิ่มเติม
kurczynski

48

ฉันผสมโซลูชันจาก @kenorb และ @ 0xF แล้วได้สิ่งนี้:

DOT := $(shell command -v dot 2> /dev/null)

all:
ifndef DOT
    $(error "dot is not available please install graphviz")
endif
    dot -Tpdf -o pres.pdf pres.dot 

มันทำงานได้อย่างสวยงามเพราะ "command -v" ไม่พิมพ์อะไรเลยหากไม่สามารถใช้งานได้ดังนั้นตัวแปร DOT จะไม่ถูกกำหนดและคุณสามารถตรวจสอบได้ทุกเมื่อที่คุณต้องการในโค้ดของคุณ ในตัวอย่างนี้ฉันแสดงข้อผิดพลาด แต่คุณสามารถทำสิ่งที่มีประโยชน์มากกว่านี้ได้หากต้องการ

หากตัวแปรพร้อมใช้งาน "command -v" จะดำเนินการที่ไม่แพงในการพิมพ์เส้นทางคำสั่งโดยกำหนดตัวแปร DOT


5
อย่างน้อยในบางระบบ (เช่น Ubuntu 14.04) $(shell command -v dot)ล้มเหลวด้วยmake: command: Command not found. เพื่อแก้ไขปัญหานี้เอาท์พุท stderr เปลี่ยนเส้นทางไปยัง / dev / $(shell command -v dot 2> /dev/null)null: คำอธิบาย
J0HN

3
commandเป็นแบชในตัวเพื่อความสะดวกในการพกพามากขึ้นลองใช้which?
Julien Palard

10
@JulienPalard ฉันเชื่อว่าคุณเข้าใจผิด: posixcommandจำเป็นต้องใช้ยูทิลิตี้(รวมถึงตัวเลือก) ในทางกลับกันไม่มี bahaviour ที่เป็นมาตรฐาน (ที่ฉันรู้จัก) และบางครั้งก็ใช้ไม่ได้ทันที-vwhich
Gyom

3
command -vต้องการสภาพแวดล้อมเชลล์เหมือน POSIX สิ่งนี้จะไม่ทำงานบน Windows หากไม่มี cygwin หรือการจำลองที่คล้ายกัน
dolmen

10
สาเหตุที่commandมักใช้ไม่ได้ไม่ใช่เพราะไม่มีแต่เป็นเพราะการเพิ่มประสิทธิภาพที่แปลกประหลาดซึ่ง GNU Make ทำ: ถ้าคำสั่ง "ง่ายพอ" คำสั่งจะข้ามเชลล์ สิ่งนี้น่าเสียดายที่บางครั้งบิวด์อินจะใช้งานไม่ได้เว้นแต่คุณจะขัดคำสั่งเล็กน้อย
Rufflewind

33

นี่คือสิ่งที่คุณทำ?

check: PYTHON-exists
PYTHON-exists: ; @which python > /dev/null
mytarget: check
.PHONY: check PYTHON-exists

ให้เครดิตเพื่อนร่วมงานของฉัน


ไม่ฉันรวบรวมโปรแกรมในเป้าหมายหนึ่งและเรียกใช้ในอีกเป้าหมายหนึ่ง
ศ. Falken

21

ใช้shellฟังก์ชันเพื่อเรียกโปรแกรมของคุณในลักษณะที่พิมพ์บางอย่างไปยังเอาต์พุตมาตรฐาน --versionยกตัวอย่างเช่นผ่าน

GNU shellยี่ห้อละเว้นออกจากสถานะของคำสั่งผ่านไป เพื่อหลีกเลี่ยงการที่มีศักยภาพ "คำสั่งไม่พบข้อความ" /dev/nullเปลี่ยนเส้นทางไปยังข้อผิดพลาดมาตรฐาน

จากนั้นคุณสามารถตรวจสอบผลการใช้ifdef, ifndef, $(if)ฯลฯ

YOUR_PROGRAM_VERSION := $(shell your_program --version 2>/dev/null)

all:
ifdef YOUR_PROGRAM_VERSION
    @echo "Found version $(YOUR_PROGRAM_VERSION)"
else
    @echo Not found
endif

เพื่อเป็นโบนัสผลลัพธ์ (เช่นเวอร์ชันโปรแกรม) อาจมีประโยชน์ในส่วนอื่น ๆ ของ Makefile ของคุณ


สิ่งนี้ทำให้เกิดข้อผิดพลาด*** missing separator. Stop.หากฉันเพิ่มแท็บทุกบรรทัดหลังจากที่all:ฉันได้รับข้อผิดพลาดmake: ifdef: Command not found
Matthias 009

ด้วย: ifdefจะประเมินค่าจริงแม้ว่าyour_programจะไม่มีอยู่ก็ตาม gnu.org/software/make/manual/make.html#Conditional-Syntax
Matthias 009

1
ไม่ต้องเพิ่มแท็บไปifdef, else, endifเส้น ตรวจสอบให้แน่ใจว่า@echoบรรทัดเริ่มต้นด้วยแท็บ
0xF

ฉันทดสอบโซลูชันของฉันบน Windows และ Linux เอกสารที่คุณเชื่อมโยงระบุว่าifdefตรวจสอบค่าตัวแปรที่ไม่ว่างเปล่า หากตัวแปรของคุณไม่ว่างเปล่ามันจะประเมินค่าอะไร?
0xF

1
@ Matthias009 เมื่อฉันทดสอบ GNU Make v4.2.1 การใช้ifdefหรือifndef(เช่นเดียวกับคำตอบที่ยอมรับ) จะใช้ได้ก็ต่อเมื่อตัวแปรที่กำลังประเมินถูกตั้งค่าทันที ( :=) แทนการตั้งค่าอย่างเกียจคร้าน ( =) อย่างไรก็ตามข้อเสียของการใช้ตัวแปร set ทันทีคือพวกมันได้รับการประเมิน ณ เวลาที่ประกาศในขณะที่ตัวแปรที่ตั้งค่าอย่างเฉื่อยชาจะถูกประเมินเมื่อมีการเรียกใช้ ซึ่งหมายความว่าคุณจะเรียกใช้คำสั่งสำหรับตัวแปร:=แม้ว่า Make จะรันกฎที่ไม่เคยใช้ตัวแปรเหล่านั้นก็ตาม คุณสามารถหลีกเลี่ยงปัญหานี้โดยใช้=กับifneq ($(MY_PROG),)
เดนนิส

9

ทำความสะอาดโซลูชันที่มีอยู่บางส่วนที่นี่ ...

REQUIRED_BINS := composer npm node php npm-shrinkwrap
$(foreach bin,$(REQUIRED_BINS),\
    $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`)))

$(info ...)คุณสามารถยกเว้นถ้าคุณต้องการนี้จะเป็นที่เงียบสงบ

นี้จะล้มเหลวอย่างรวดเร็ว ไม่จำเป็นต้องมีเป้าหมาย


8

โซลูชันของฉันเกี่ยวข้องกับสคริปต์ตัวช่วยเล็ก ๆ1ที่วางไฟล์แฟล็กหากมีคำสั่งที่จำเป็นทั้งหมด สิ่งนี้มาพร้อมกับข้อได้เปรียบที่การตรวจสอบคำสั่งที่ต้องการนั้นทำได้เพียงครั้งเดียวและไม่ใช่ทุกครั้งที่makeเรียกใช้

check_cmds.sh

#!/bin/bash

NEEDED_COMMANDS="jlex byaccj ant javac"

for cmd in ${NEEDED_COMMANDS} ; do
    if ! command -v ${cmd} &> /dev/null ; then
        echo Please install ${cmd}!
        exit 1
    fi
done

touch .cmd_ok

Makefile

.cmd_ok:
    ./check_cmds.sh

build: .cmd_ok target1 target2

1เพิ่มเติมเกี่ยวกับcommand -vเทคนิคที่สามารถพบได้ที่นี่


2
ฉันเพิ่งทดสอบและใช้งานได้และเนื่องจากคุณไม่ได้ให้คำแนะนำเกี่ยวกับสิ่งที่ไม่ทำงานสำหรับคุณ (สคริปต์ทุบตีหรือรหัส Makefile) หรือข้อความแสดงข้อผิดพลาดใด ๆ ที่ฉันไม่สามารถช่วยคุณค้นหาปัญหาได้
ไหล

ฉันได้รับ "จุดสิ้นสุดของไฟล์ที่ไม่คาดคิด" ในifคำสั่งแรก
Adam Grant

6

สำหรับฉันคำตอบข้างต้นทั้งหมดขึ้นอยู่กับ linux และไม่ทำงานกับ windows ฉันยังใหม่เพื่อให้แนวทางของฉันอาจไม่เหมาะ แต่ตัวอย่างที่สมบูรณ์ที่เหมาะกับฉันทั้งบน linux และ windows คือ:

# detect what shell is used
ifeq ($(findstring cmd.exe,$(SHELL)),cmd.exe)
$(info "shell Windows cmd.exe")
DEVNUL := NUL
WHICH := where
else
$(info "shell Bash")
DEVNUL := /dev/null
WHICH := which
endif

# detect platform independently if gcc is installed
ifeq ($(shell ${WHICH} gcc 2>${DEVNUL}),)
$(error "gcc is not in your system PATH")
else
$(info "gcc found")
endif

เป็นทางเลือกเมื่อฉันต้องการตรวจหาเครื่องมือเพิ่มเติมที่ฉันสามารถใช้ได้:

EXECUTABLES = ls dd 
K := $(foreach myTestCommand,$(EXECUTABLES),\
        $(if $(shell ${WHICH} $(myTestCommand) 2>${DEVNUL} ),\
            $(myTestCommand) found,\
            $(error "No $(myTestCommand) in PATH)))
$(info ${K})        

ฉันไม่เคยคิดว่าจะมีใครใช้ GNU Make กับ cmd.exe ที่หวั่น ๆ ... "shell"
Johan Boulé

4

คุณสามารถใช้คำสั่ง bash built เช่นtype fooหรือcommand -v fooดังต่อไปนี้:

SHELL := /bin/bash
all: check

check:
        @type foo

fooโปรแกรม / คำสั่งของคุณอยู่ที่ไหน เปลี่ยนเส้นทางไปที่> /dev/nullหากคุณต้องการให้เงียบ


ใช่ แต่สิ่งนี้คือฉันต้องการให้มันใช้งานได้เมื่อฉันมีเพียง GNU make แต่ไม่มีการติดตั้ง bash
ศ. Falken

3

สมมติว่าคุณมีเป้าหมายและผู้สร้างที่แตกต่างกันแต่ละคนต้องใช้ชุดเครื่องมืออื่น กำหนดรายการเครื่องมือดังกล่าวและพิจารณาว่าเป็นเป้าหมายเพื่อบังคับให้ตรวจสอบความพร้อมใช้งาน

ตัวอย่างเช่น:

make_tools := gcc md5sum gzip

$(make_tools):  
    @which $@ > /dev/null

file.txt.gz: file.txt gzip
    gzip -c file.txt > file.txt.gz 

3

ฉันเป็นคนกำหนดrequireเป้าหมายเองที่วิ่งก่อนคนอื่น ๆ ทั้งหมด เป้าหมายนี้เพียงแค่รันคำสั่งเวอร์ชันของข้อกำหนดทั้งหมดทีละรายการและพิมพ์ข้อความแสดงข้อผิดพลาดที่เหมาะสมหากคำสั่งไม่ถูกต้อง

all: require validate test etc

require:
    @echo "Checking the programs required for the build are installed..."
    @shellcheck --version >/dev/null 2>&1 || (echo "ERROR: shellcheck is required."; exit 1)
    @derplerp --version >/dev/null 2>&1 || (echo "ERROR: derplerp is required."; exit 1) 

# And the rest of your makefile below.

ผลลัพธ์ของสคริปต์ด้านล่างคือ

Checking the programs required for the build are installed...
ERROR: derplerp is required.
makefile:X: recipe for target 'prerequisites' failed
make: *** [prerequisites] Error 1

1

แก้ไขได้โดยการรวบรวมโปรแกรมเล็ก ๆ พิเศษในเป้าหมาย makefile อื่นซึ่งมีจุดประสงค์เพียงอย่างเดียวเพื่อตรวจสอบสิ่งที่รันไทม์ที่ฉันกำลังมองหา

จากนั้นฉันเรียกโปรแกรมนี้ใน makefile เป้าหมายอื่น

มันเป็นอย่างนี้ถ้าฉันจำได้อย่างถูกต้อง:

real: checker real.c
    cc -o real real.c `./checker`

checker: checker.c
    cc -o checker checker.c

ขอบคุณ. แต่นี่จะทำให้แท้งใช่มั้ย? สามารถป้องกันไม่ให้ทำแท้งได้หรือไม่? ฉันกำลังพยายามข้ามขั้นตอนการสร้างหากไม่มีเครื่องมือที่จำเป็น ..
Marc-Christian Schulze

@ coding.mof แก้ไข - มันเป็นแบบนั้นมากกว่า โปรแกรมตรวจสอบบางสิ่งในรันไทม์และให้ข้อมูลตามส่วนที่เหลือของคอมไพล์
ศ. Falken

1

การแก้ปัญหาการตรวจสอบSTDERRการส่งออกของ--versionไม่ทำงานสำหรับโปรแกรมที่พิมพ์รุ่นของพวกเขาไปแทนSTDOUT STDERRแทนที่จะตรวจสอบผลลัพธ์ไปยังSTDERRหรือSTDOUTตรวจสอบรหัสส่งคืนโปรแกรม หากไม่มีโปรแกรมนี้รหัสออกจะไม่เป็นศูนย์เสมอ

#!/usr/bin/make -f
# /programming/7123241/makefile-as-an-executable-script-with-shebang
ECHOCMD:=/bin/echo -e
SHELL := /bin/bash

RESULT := $(shell python --version >/dev/null 2>&1 || (echo "Your command failed with $$?"))

ifeq (,${RESULT})
    EXISTS := true
else
    EXISTS := false
endif

all:
    echo EXISTS: ${EXISTS}
    echo RESULT: ${RESULT}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.