Como obter o diretório relativo atual do seu Makefile?

202

Eu tenho vários Makefiles em diretórios específicos de aplicativos como este:

/project1/apps/app_typeA/Makefile
/project1/apps/app_typeB/Makefile
/project1/apps/app_typeC/Makefile

Cada Makefile inclui um arquivo .inc neste caminho um nível acima:

/project1/apps/app_rules.inc

Dentro de app_rules.inc, estou definindo o destino de onde quero que os binários sejam colocados quando criados. Eu quero que todos os binários estejam no respectivo app_typecaminho:

/project1/bin/app_typeA/

Eu tentei usar$(CURDIR) , assim:

OUTPUT_PATH = /project1/bin/$(CURDIR)

mas, em vez disso, enterrei os binários no nome do caminho inteiro assim: (observe a redundância)

/project1/bin/projects/users/bob/project1/apps/app_typeA

O que posso fazer para obter o "diretório atual" de execução, para que eu possa saber exatamente o que é app_typeX, para colocar os binários em suas respectivas pastas de tipos?

boltup_im_coding
fonte

Respostas:

272

A função shell.

Você pode usar shella função: current_dir = $(shell pwd). Ou shellem combinação com notdir, se você não precisa caminho absoluto: current_dir = $(notdir $(shell pwd)).

Atualizar.

A solução fornecida só funciona quando você está executando makeno diretório atual do Makefile.
Como @Flimm observou:

Observe que isso retorna o diretório de trabalho atual, não o diretório pai do Makefile.
Por exemplo, se você executar cd /; make -f /home/username/project/Makefile, a current_dirvariável será /, não /home/username/project/.

O código abaixo funcionará para Makefiles chamados de qualquer diretório:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
Nikolai Popov
fonte
5
Isso não funciona. É o mesmo problema. pwd imprime o nome completo do caminho, eu só quero o nome do atual pasta em que estou.
boltup_im_coding
6
Ele precisa ter o valor expandido imediatamente para que o operador: = seja usado. Como em mkfile_path: = e = current_dir (caso contrário, os valores do lado direito pode ser avaliadas mais tarde, quando o MAKEFILE_LIST mudou após outro Makefiles foram incluídos
Tom Gruner
10
Sei que essa é uma pergunta antiga, mas acabei encontrando e achei que isso poderia ser útil. Isso também não funcionará quando houver um espaço no caminho relativo do makefile. Especificamente, $(lastword "$(MAKEFILE_LIST)")um makefile no caminho "Sub Directory/Makefile"fornecerá a você "Directory/Makefile". Eu não acho que há alguma maneira de contornar isso desde que $(MAKEFILE_LIST)com dois makefiles dá-lhe uma única corda "Makefile One Makefile Two, que não pode lidar com espaços
mrosales
2
Isso current_dirnão funciona para mim. $(PWD)faz e é muito mais simples.
CaTx 19/09/18
3
"solution only works when you are running make from the Makefile's current directory"é uma maneira moderada de dizer "realmente não está funcionando". Eu colocaria a última peça no topo da resposta.
Victor Sergienko
138

Como tirado daqui ;

ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

Aparece como;

$ cd /home/user/

$ make -f test/Makefile 
/home/user/test

$ cd test; make Makefile 
/home/user/test

Espero que isto ajude

sonolento
fonte
3
IMHO, seria melhor usar a função make interna, em dirvez de shell dirnameobter o nome do caminho com /caracteres finais .
Serge Roussak
3
por causa da redução de dependências em ferramentas externas. Ou seja, e se houver apelido de comando dirname em algum sistema? ..
Serge Roussak
6
Isso quase funcionou para mim, mas o DIR tinha um espaço em branco líder; portanto, uma pequena alteração fez com que funcionasse perfeitamente:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz
9
Para fazer referência ao makefile atual , o uso de $MAKEFILE_LISTdeve preceder qualquer includeoperação ( docs ).
Nobar
2
Deve usar aspas - pelo menos ao redor do argumento dirname- no caso de haver espaços no caminho.
Nobar
46

Se você estiver usando o GNU make, $ (CURDIR) é na verdade uma variável interna. É o local em que o Makefile reside no diretório de trabalho atual, que provavelmente é onde está o Makefile, mas nem sempre .

OUTPUT_PATH = /project1/bin/$(notdir $(CURDIR))

Consulte o Apêndice A, Referência rápida em http://www.gnu.org/software/make/manual/make.html

Tzunghsing David Wong
fonte
18
No manual GNU Make (página 51): "quando o GNU make inicia (depois de processar qualquer opção -C), define a variável CURDIR como o nome do caminho do diretório de trabalho atual." Não é o local onde o Makefile está localizado - embora eles possam ser os mesmos.
Simon Peverett
4
Votado porque a resposta não é tecnicamente correta, como observado por Simon Peverett no comentário anterior.
Subfuzion
5
@ SimonPeverett: A expressão entre colchetes significa o "diretório de trabalho atual APÓS a opção C aplicada". Ou seja, $ (CURDIR) fornece exatamente os mesmos resultados para "make -C xxx" e "cd xxx; make". Também remove a trilha /. Eu tentei amarrado com gmakev3.81. Mas você está certo se makefor chamado como make -f xxx/Makefile.
TrueY 01/04/2015
CURDIRfunciona para mim onde PWDnão. Eu fiz mkdir -p a/s/dentão em cada pasta tinha um makefile que imprimia PWDe CURDIRantes +$(MAKE) <subdir>.
Mark K Cowan
27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
momo
fonte
12
... a menos que você tenha espaços em qualquer caminho.
Craig Ringer
9
... a menos que você tenha incluído algum outro makefile na linha anterior
robal
1
@robal Yep. Foi isso que acabei de encontrar. Isso funciona muito bem se você tiver apenas dois Makefiles (o principal mais o incluído).
precisa saber é o seguinte
6

Eu tentei muitas dessas respostas, mas no meu sistema AIX com o gnu make 3.80 eu precisava fazer algumas coisas da velha escola.

Acontece que lastword, abspathe realpathnão foram adicionados até 3,81. :(

mkfile_path := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
mkfile_dir:=$(shell cd $(shell dirname $(mkfile_path)); pwd)
current_dir:=$(notdir $(mkfile_dir))

Como outros já disseram, não é o mais elegante, pois invoca uma concha duas vezes e ainda apresenta problemas de espaço.

Mas como não tenho espaços em meus caminhos, funciona para mim, independentemente de como comecei make:

  • make -f ../wherever/makefile
  • make -C ../weverever
  • make -C ~ / wherewhere
  • cd ../weverever; faço

Todos me dar whereverpara current_dire o caminho absoluto para whereverpara mkfile_dir.

Jesse Chisholm
fonte
Eu, por exemplo, compilei o gnu-make-3.82 no aix (5-6-7) sem problemas.
Zsigmond Lőrinczy
Sim, por 3,81 as funções necessárias para as soluções anteriores foram implementadas. Minha resposta é para sistemas mais antigos com 3.80 ou anterior. Infelizmente, no trabalho, não tenho permissão para adicionar ou atualizar ferramentas no sistema AIX. :(
Jesse Chisholm
5

Gosto da resposta escolhida, mas acho que seria mais útil mostrá-la funcionando do que explicá-la.

/tmp/makefile_path_test.sh

#!/bin/bash -eu

# Create a testing dir
temp_dir=/tmp/makefile_path_test
proj_dir=$temp_dir/dir1/dir2/dir3
mkdir -p $proj_dir

# Create the Makefile in $proj_dir
# (Because of this, $proj_dir is what $(path) should evaluate to.)
cat > $proj_dir/Makefile <<'EOF'
path := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
cwd  := $(shell pwd)

all:
    @echo "MAKEFILE_LIST: $(MAKEFILE_LIST)"
    @echo "         path: $(path)"
    @echo "          cwd: $(cwd)"
    @echo ""
EOF

# See/debug each command
set -x

# Test using the Makefile in the current directory
cd $proj_dir
make

# Test passing a Makefile
cd $temp_dir
make -f $proj_dir/Makefile

# Cleanup
rm -rf $temp_dir

Resultado:

+ cd /tmp/makefile_path_test/dir1/dir2/dir3
+ make
MAKEFILE_LIST:  Makefile
         path: /private/tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test/dir1/dir2/dir3

+ cd /tmp/makefile_path_test
+ make -f /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
MAKEFILE_LIST:  /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
         path: /tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test

+ rm -rf /tmp/makefile_path_test

NOTA: A função $(patsubst %/,%,[path/goes/here/])é usada para remover a barra final.

Bruno Bronosky
fonte
3

Solução encontrada aqui: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

A solução é : $ (CURDIR)

Você pode usá-lo assim:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder
Soma Bini
fonte
3
Bem-vindo ao Stack Overflow. Você pode adicionar mais detalhes à sua resposta? A pergunta afirma que $(CURDIR)já foi tentada sem êxito.
18719 Sean
4
WARNING, quick-googlers: Este não é o diretório, onde está o makefile, mas o diretório de trabalho atual (onde makefoi iniciado). É, de fato, o que o OP realmente queria, mas o título da pergunta é falso, então a pergunta em si é inconsistente. Portanto, esta é uma resposta correta para uma pergunta incorreta. ;)
Sz.
Isso é
totalmente
2

Aqui está uma linha para obter o caminho absoluto para o seu Makefilearquivo usando a sintaxe do shell:

SHELL := /bin/bash
CWD := $(shell cd -P -- '$(shell dirname -- "$0")' && pwd -P)

E aqui está a versão sem shell com base na resposta @ 0xff :

CWD := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))

Teste-o imprimindo, como:

cwd:
        @echo $(CWD)
kenorb
fonte
1
Importância crítica! O segundo exemplo acima necessidades: = operador de atribuição ou então algumas coisas muito estranhas e irritados pode acontecer com makefiles complexos (confie em mim)
Emon
1
O primeiro é um erro repetido e não funciona para construções fora da árvore.
Johan Boulé
0

Tanto quanto sei, esta é a única resposta aqui que funciona corretamente com espaços:

space:= 
space+=

CURRENT_PATH := $(subst $(lastword $(notdir $(MAKEFILE_LIST))),,$(subst $(space),\$(space),$(shell realpath '$(strip $(MAKEFILE_LIST))')))

Ele funciona essencialmente por escapar caracteres de espaço, substituindo ' 'para '\ 'que permita fazer para analisá-lo corretamente, e, em seguida, ele remove o nome do arquivo do makefile em MAKEFILE_LISTfazendo outra substituição assim que você é deixado com o diretório que makefile está dentro. Não exatamente o mais compacto coisa no mundo, mas funciona.

Você vai acabar com algo assim, onde todos os espaços são escapados:

$(info CURRENT_PATH = $(CURRENT_PATH))

CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/
Peter Frost
fonte
-2

Exemplo para sua referência, como abaixo:

A estrutura da pasta pode ser como:

insira a descrição da imagem aqui

Onde existem dois Makefiles, cada um como abaixo;

sample/Makefile
test/Makefile

Agora, vamos ver o conteúdo dos Makefiles.

amostra / Makefile

export ROOT_DIR=${PWD}

all:
    echo ${ROOT_DIR}
    $(MAKE) -C test

teste / Makefile

all:
    echo ${ROOT_DIR}
    echo "make test ends here !"

Agora, execute a amostra / Makefile, como;

cd sample
make

RESULTADO:

echo /home/symphony/sample
/home/symphony/sample
make -C test
make[1]: Entering directory `/home/symphony/sample/test'
echo /home/symphony/sample
/home/symphony/sample
echo "make test ends here !"
make test ends here !
make[1]: Leaving directory `/home/symphony/sample/test'

Explicação: o diretório pai / home pode ser armazenado no sinalizador do ambiente e exportado, para que possa ser usado em todos os makefiles do subdiretório.

parasita
fonte
2
Isso obtém o diretório de trabalho atual, não o diretório inicial do makefile.
Jesse Chisholm
Estas são as linhas no Makefile. Agora, à medida que o comando Makefile é executado, isso obtém o caminho no qual o Makefile está. Entendo que "diretório inicial do makefile" se refere ao mesmo, ou seja, o caminho do diretório no qual o Makefile está executando.
parasrish
@Jesse Chisholm, por favor, revisite. Eu expliquei com o uso.
parasrish
Um erro repetido: não funciona para construções fora da árvore. Além disso, seu terminal gnome fornece uma maneira simples de copiar texto: basta selecioná-lo. O boato de que Imgur está falido.
Johan Boulé
-2

atualização 2018/03/05 finnaly eu uso isso:


shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

Pode ser executado corretamente no caminho relativo ou no caminho absoluto. Executado corretamente invocado pelo crontab. Executado correto em outro shell.

mostre o exemplo, a.sh imprime o caminho próprio.

[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/test/a.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

echo $shellPath
[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/b.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

$shellPath/test/a.sh
[root@izbp1a7wyzv7b5hitowq2yz /]# ~/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# cd ~
[root@izbp1a7wyzv7b5hitowq2yz ~]# ./b.sh
/root/./test
[root@izbp1a7wyzv7b5hitowq2yz ~]# test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz ~]# cd test
[root@izbp1a7wyzv7b5hitowq2yz test]# ./a.sh
/root/test/.
[root@izbp1a7wyzv7b5hitowq2yz test]# cd /
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# 

old: eu uso isso:

MAKEFILE_PATH := $(PWD)/$({0%/*})

Pode mostrar correto se executado em outro shell e outro diretório.

zhukunqian
fonte
1
"Pode mostrar correto se executado se outro shell e outro diretório" não fizer sentido em inglês. Você pode tentar explicar de novo?
Keith M
desculpe pelo meu mau Inglês
zhukunqian
Obrigado pela edição, vejo que você quis dizer IN agora. Você pode explicar o que isso faz antes de eu tentar? Não sei bem como usá-lo. Como o que você quer dizer com "outra concha" #
Keith M
1
Esta resposta tem tantas coisas erradas que não pode ser corrigida. Eu sugiro uma remoção simples.
Johan Boulé
-2

Uma linha no Makefile deve ser suficiente:

DIR := $(notdir $(CURDIR))

Thach Van
fonte