Makefile'dan bir program olup olmadığını kontrol edin


115

Bir programın Makefile'dan çağrılabilir olup olmadığını nasıl kontrol edebilirim?

(Yani, program yolda bulunmalı veya başka şekilde çağrılabilir olmalıdır.)

Örneğin, hangi derleyicinin kurulu olduğunu kontrol etmek için kullanılabilir.

Örneğin bu soru gibi bir şey , ancak temeldeki kabuğun POSIX uyumlu olduğunu varsaymadan.


1
POSIX uyumlu bir kabuk çağırabilir misiniz ?
reinierpost

Muhtemelen hayır, sanırım birinin orada olmasını talep edebilirim, ama mecbur olmasaydım çok daha kolay olurdu.
Prof. Falken

Bu arada, projeye ilk inşa edilen ve tek amacı diğer programı kontrol etmek olan bir program ekleyerek
çözdüm

2
Geleneksel çözüm, automakeçeşitli ön koşulları kontrol eden ve uygun bir komut dosyası yazan bir komut dosyasına sahip olmaktır Makefile.
üçlü

Yanıtlar:


84

Bazen farklı hedef işletim sistemlerinde çalışabilmek için bir Makefile'a ihtiyaç duyarsınız ve gerekli bir yürütülebilir dosya PATHbaşarısız olmadan önce muhtemelen uzun bir süre çalıştırmak yerine içinde değilse derlemenin erken başarısız olmasını istersiniz .

Tarafından sağlanan mükemmel çözüm engineerchuan bir yapım gerektirir hedef . Bununla birlikte, test edilecek çok sayıda yürütülebilir dosyanız varsa ve Makefile'ınızın her biri testleri gerektiren birçok bağımsız hedefi varsa, her hedef bağımlılık olarak test hedefini gerektirir. Bu, bir seferde birden fazla hedef yaptığınızda fazladan yazmanın yanı sıra işlem süresi sağlar.

0xf tarafından sağlanan çözüm , bir hedef belirlemeden yürütülebilir bir dosyayı test edebilir. Bu, ayrı ayrı veya birlikte oluşturulabilen birden fazla hedef olduğunda çok fazla yazma ve yürütme süresinden tasarruf sağlar.

İkinci çözüme yönelik geliştirmem , yeni bir değişken tanımlamak yerine , her yürütülebilir dosyada doğrudan GNU Make yönergesinde bir seçeneğe güvenmek yerine whichyürütülebilir dosyayı ( whereWindows'ta) kullanmak ve GNU Make'i kullanmaktır. gerekli bir yürütülebilir dosya yoksa derlemeyi durdurmak için işlev . Örneğin, yürütülebilir dosyayı test etmek için :--versionifeqerror${PATH}lzop

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

Kontrol etmeniz gereken birkaç çalıştırılabilir dosyanız varsa foreach, whichçalıştırılabilir dosyayla bir işlev kullanmak isteyebilirsiniz :

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

:=RHS ifadesinin anında değerlendirilmesini zorunlu kılmak için gerekli olan atama operatörünün kullanımına dikkat edin . Makefile dosyanız değişiyorsa, PATHyukarıdaki son satır yerine şunlara ihtiyacınız olacaktır:

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

Bu size şuna benzer bir çıktı vermelidir:

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

2
EXECUTABLES'ı tüm değişkenleri (yani LS CC LD) yaparsanız ve $ ($ (exec)) kullanırsanız, bunları ortamdan veya diğer makefile dosyalarından sorunsuz bir şekilde yapmak için geçirebilirsiniz. Çapraz derleme yaparken kullanışlıdır.
rickfoosusa

1
"," Çalıştırırken "ifeq (, $ (shell that lzop))" = /
mentatkgs

2
Windows'ta where my_exe 2>NULyerine kullanın which.
Aaron Campbell

2
Kural sözdizimi olan ifeq ile TABS girintilerine dikkat edin! TAB OLMADAN olmalıdır. Bununla zor zamanlar geçirdim. stackoverflow.com/a/21226973/2118777
Pipo

2
Eğer kullanıyorsanız Bash, harici bir arama yapmanıza gerek yoktur which. Yerleşik olanı command -vkullanın. Daha fazla bilgi .
kurczynski

48

Çözümleri @kenorb ve @ 0xF'den karıştırdım ve şunu aldım:

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 

Güzel çalışıyor çünkü "command -v" çalıştırılabilir mevcut değilse hiçbir şey yazdırmıyor, bu nedenle DOT değişkeni hiçbir zaman tanımlanmaz ve kodunuzda istediğiniz zaman kontrol edebilirsiniz. Bu örnekte bir hata veriyorum, ancak isterseniz daha yararlı bir şey yapabilirsiniz.

Değişken mevcutsa, "komut -v", DOT değişkenini tanımlayarak komut yolunu yazdırmak için pahalı olmayan bir işlem gerçekleştirir.


5
Bazı sistemlerde (yani Ubuntu 14.04) En azından $(shell command -v dot)başarısız olur make: command: Command not found. Bunu düzeltmek için stderr çıktısını / dev / null: 'a yönlendirin $(shell command -v dot 2> /dev/null). Açıklama
J0HN

3
commandbir bash yerleşik, daha fazla taşınabilirlik için kullanmayı düşünün which?
Julien Palard

10
Sana inanıyorum @JulienPalard sizsiniz, yanlış: commandyarar gerektirdiği posix (dahil -vÖte yandan seçeneği) whichhiçbir bahaviour (Bildiğim kadarıyla) standardize etmiştir ve bazen düpedüz olan kullanışsız
Gyom

3
command -vPOSIX benzeri bir kabuk ortamı gerektirir. Bu, cygwin veya benzeri öykünme olmadan Windows'ta çalışmayacaktır.
dolmen

10
Nedeni commandçoğu zaman çalışır olduğunu çünkü onun yokluğunda , fakat a özgü optimizasyonu GNU Make yaptığı: Bir komut "yeterince basit bir" ise o olacak kabuk baypas; bu maalesef yerleşiklerin bazen komutu biraz karıştırmadığınız sürece çalışmayacağı anlamına gelir.
Rufflewind

33

Yaptığın bu mu?

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

iş arkadaşıma kredi.


Hayır, programı bir hedefte derledim ve başka bir hedefte çalıştırdım.
Prof. Falken

21

shellProgramınızı standart çıktıya bir şeyler yazdıracak şekilde çağırmak için işlevi kullanın . Örneğin, geç --version.

GNU Make, iletilen komutun çıkış durumunu yok sayar shell. Olası "komut bulunamadı" mesajını önlemek için standart hatayı adresine yeniden yönlendirin /dev/null.

Sonra kullanarak sonucunu kontrol edebilir ifdef, ifndef, $(if)vb

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

Bonus olarak çıktı (program sürümü gibi) Makefile dosyanızın diğer bölümlerinde yararlı olabilir.


Bu hata veriyor *** missing separator. Stop.sonra tüm hatları kısmındaki sekmeleri eklerseniz all:ben hata alıyorummake: ifdef: Command not found
Matthias 009

ayrıca: mevcut olmasa ifdefbile doğru olarak değerlendirilir gnu.org/software/make/manual/make.html#Conditional-Syntaxyour_program
Matthias 009

1
Sekmeleri eklemeyin ifdef, else, endifhatlar. @echoSatırların sekmelerle başladığından emin olun .
0xF

Çözümümü Windows ve Linux üzerinde test ettim. Bağladığınız dokümantasyon, ifdefboş olmayan değişken değerini kontrol ettiğini belirtir . Değişkeniniz boş değilse, neyi ifade eder?
0xF

1
@ Matthias009 GNU Make v4.2.1'i test ettiğimde, ifdefveya kullanarak ifndef(kabul edilen cevapta olduğu gibi) sadece değerlendirilen değişken :=tembel set ( =) yerine hemen set ( ) ise çalışır . Bununla birlikte, hemen ayarlanmış değişkenleri kullanmanın bir dezavantajı, bunların bildirim zamanında değerlendirilmeleridir, oysa tembel ayarlanmış değişkenler çağrıldıklarında değerlendirilir. Bu :=, Make yalnızca bu değişkenleri asla kullanmayan kuralları çalıştırdığında bile değişkenler için komut çalıştıracağınız anlamına gelir ! Bir =ile kullanarak bunu önleyebilirsinizifneq ($(MY_PROG),)
Dennis

9

Buradaki mevcut çözümlerden bazılarını temizledim ...

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)`)))

Daha $(info ...)sessiz olmasını istiyorsanız dışlayabilirsiniz.

Bu hızla başarısız olur . Hedef gerekmez.


8

Çözümüm , gerekli tüm komutlar mevcutsa bir bayrak dosyası yerleştiren küçük bir yardımcı komut dosyası 1 içeriyor. Bunun avantajı, gerekli komutların kontrolünün her makeçağrıda değil yalnızca bir kez yapılmasıdır .

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

1command -v Teknik hakkında daha fazla bilgiyi burada bulabilirsiniz .


2
Sadece test ettim ve işe yarıyor ve sizin için neyin çalışmadığı (bash betiği veya Makefile kodu) veya herhangi bir hata mesajı hakkında hiçbir ipucu sağlamadığınız için sorunu bulmanıza yardımcı olamıyorum.
Flow

İlk ififadede "beklenmeyen dosya sonu" mesajı alıyorum .
Adam Grant

6

Benim için yukarıdaki cevapların tümü linux'a dayanıyor ve Windows ile çalışmıyor. Yapmak için yeniyim, bu yüzden yaklaşımım ideal olmayabilir. Ama benim için hem linux hem de windows üzerinde çalışan tam örnek şudur:

# 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

isteğe bağlı olarak daha fazla araç bulmam gerektiğinde kullanabileceğim:

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

Korkunç cmd.exe ... "kabuk" ile birinin GNU Make kullanacağını hiç düşünmemiştim.
Johan Boulé

4

Aşağıdaki gibi type fooveya gibi bash oluşturulmuş komutları kullanabilirsiniz command -v foo:

SHELL := /bin/bash
all: check

check:
        @type foo

fooProgramınız / komutunuz nerede ? > /dev/nullSessiz olmasını istiyorsanız adresine yönlendirin .


Evet, ama mesele şu ki, sadece GNU make'e sahipken ama hiçbir bash kurulmamışken çalışmasını istiyordum.
Prof. Falken

3

Farklı hedefleriniz ve geliştiricileriniz olduğunu varsayın, her biri başka bir takım araçlar gerektirir. Bu tür araçların bir listesini oluşturun ve bunları, kullanılabilirliklerini kontrol etmeye zorlama hedefi olarak değerlendirin

Örneğin:

make_tools := gcc md5sum gzip

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

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

3

Kişisel requireolarak diğerlerinden önce koşan bir hedef tanımlıyorum . Bu hedef, tüm gereksinimlerin sürüm komutlarını teker teker çalıştırır ve komut geçersizse uygun hata mesajlarını yazdırır.

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.

Aşağıdaki betiğin çıktısı

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

Tek amacı aradığım çalışma zamanı öğelerini kontrol etmek olan başka bir makefile hedefinde özel bir küçük program derleyerek çözüldü.

Daha sonra bu programı başka bir makefile hedefinde çağırdım.

Doğru hatırlıyorsam şöyle bir şeydi:

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

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

Teşekkür ederim. Ama bu iptal olur, değil mi? Make'in iptal edilmesini önlemek mümkün mü? Gerekli araç mevcut değilse, bir oluşturma adımını atlamaya çalışıyorum ..
Marc-Christian Schulze

@ coding.mof düzenlendi - daha çok böyleydi. Program çalışma zamanında bir şeyi kontrol etti ve derlemenin geri kalanına uygun şekilde bilgi verdi.
Prof. Falken

1

Denetlenirken çözümler STDERRçıkışında --versionkendi sürümünü yazdırmak programları için çalışmaz STDOUTyerine STDERR. STDERRVeya çıkışlarını kontrol etmek yerine STDOUTprogram dönüş kodunu kontrol edin. Program mevcut değilse, çıkış kodu her zaman sıfır olmayacaktır.

#!/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}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.