Jak uzyskać aktualny katalog względny swojego pliku Makefile?

202

Mam kilka plików Makefiles w katalogach specyficznych dla aplikacji, takich jak to:

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

Każdy plik Makefile zawiera plik .inc w tej ścieżce o jeden poziom wyżej:

/project1/apps/app_rules.inc

Wewnątrz app_rules.inc Ustawiam miejsce docelowe, w którym chcę umieścić pliki binarne po zbudowaniu. Chcę, aby wszystkie pliki binarne były na odpowiedniej app_typeścieżce:

/project1/bin/app_typeA/

Próbowałem użyć$(CURDIR) , w ten sposób:

OUTPUT_PATH = /project1/bin/$(CURDIR)

ale zamiast tego mam pliki binarne zakopane w całej nazwie ścieżki w ten sposób: (zauważ nadmiarowość)

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

Co mogę zrobić, aby uzyskać „bieżący katalog” wykonania, aby wiedzieć tylko app_typeX, jak umieścić pliki binarne w folderze odpowiednich typów?

boltup_im_coding
źródło

Odpowiedzi:

271

Funkcja powłoki.

Można użyć shellfunkcji: current_dir = $(shell pwd). Lub shellw połączeniu z notdir, jeśli nie musisz ścieżki bezwzględnej: current_dir = $(notdir $(shell pwd)).

Aktualizacja.

Podane rozwiązanie działa tylko w przypadku uruchamiania makez bieżącego katalogu Makefile.
Jak zauważył @Flimm:

Zauważ, że zwraca bieżący katalog roboczy, a nie katalog nadrzędny pliku Makefile.
Na przykład, jeśli uruchomisz cd /; make -f /home/username/project/Makefile, current_dirzmienna będzie /, a nie /home/username/project/.

Poniższy kod będzie działał dla plików Makefile wywoływanych z dowolnego katalogu:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
Nikołaj Popow
źródło
5
To nie działa. To ten sam problem. pwd Drukuje pełna nazwa ścieżki, po prostu chcę nazwę rzeczywistej bieżącym folderze jestem w.
boltup_im_coding
6
Należy natychmiast zwiększyć wartość, aby użyć operatora: =. Tak jak w mkfile_path: = i current_dir = (w przeciwnym razie wartości po prawej stronie mogą być oszacowane później, gdy MAKEFILE_LIST zmieni się po uwzględnieniu innych plików Makefile
Tom Gruner
10
Wiem, że to stare pytanie, ale natknąłem się i pomyślałem, że to może być przydatne. To również nie zadziała, gdy na ścieżce względnej makefile znajduje się spacja. W szczególności $(lastword "$(MAKEFILE_LIST)")makefile na ścieżce "Sub Directory/Makefile"da ci "Directory/Makefile". Nie sądzę, żeby można to obejść, ponieważ $(MAKEFILE_LIST)z dwoma makefile daje ci jeden ciąg znaków "Makefile One Makefile Two, który nie obsługuje spacji
mrosales 24.01.2015
2
To current_dirmi nie działa. $(PWD)robi i jest o wiele prostsze.
CaTx,
3
"solution only works when you are running make from the Makefile's current directory"to łagodny sposób na powiedzenie „nie działa”. Chciałbym umieścić najnowszy artykuł na początku odpowiedzi.
Victor Sergienko
138

Jak zaczerpnięte stąd ;

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

Pokazuje się jako;

$ cd /home/user/

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

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

Mam nadzieję że to pomoże

senny
źródło
3
IMHO lepiej użyć wewnętrznego make-function dirzamiast shell dirnametego, że dostaje ścieżkę z końcowym /znakiem.
Serge Roussak,
3
z powodu zmniejszenia zależności od narzędzi zewnętrznych. To znaczy, jeśli będzie jakiś pseudonim polecenia dirname w jakimś systemie? ..
Serge Roussak
6
To prawie dla mnie zadziałało, ale DIR miał wiodącą białą spację, więc drobna zmiana sprawiła, że ​​działało idealnie:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz
9
Aby odwołać się do bieżącego pliku makefile, użycie $MAKEFILE_LISTmusi poprzedzać wszelkie includeoperacje ( dokumenty ).
nobar
2
Należy używać cudzysłowów - przynajmniej wokół argumentu dirname- w przypadku, gdy na ścieżce znajdują się spacje.
nobar
46

Jeśli używasz GNU make, $ (CURDIR) jest tak naprawdę zmienną wbudowaną. Jest to lokalizacja, w której Makefile znajduje się w bieżącym katalogu roboczym, prawdopodobnie tam, gdzie jest Makefile, ale nie zawsze .

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

Zobacz Dodatek A Krótki przegląd w http://www.gnu.org/software/make/manual/make.html

Tzunghsing David Wong
źródło
18
Z podręcznika GNU Make (strona 51): „kiedy GNU make uruchamia się (po przetworzeniu dowolnych opcji -C), ustawia zmienną CURDIR na nazwę ścieżki bieżącego katalogu roboczego.” Nie lokalizacja, w której znajduje się plik Makefile - chociaż mogą być takie same.
Simon Peverett
4
Zagłosowano, ponieważ odpowiedź nie jest technicznie poprawna, jak zauważył Simon Peverett w poprzednim komentarzu.
Subfuzion
5
@ SimonPeverett: Wyrażenie w nawiasach oznacza „bieżący katalog roboczy po zastosowaniu opcji -C”. Mianowicie $ (CURDIR) daje dokładnie takie same wyniki dla „make -C xxx” i „cd xxx; make”. Usuwa również końcowe /. Próbowałem związany z gmakewersją 3.11. Ale masz rację, jeśli makenazywa się to as make -f xxx/Makefile.
TrueY
CURDIRdziała dla mnie tam, gdzie PWDnie. Zrobiłem mkdir -p a/s/dto w każdym folderze, który miał makefile wydrukowany PWDi CURDIRprzed +$(MAKE) <subdir>.
Mark K Cowan
27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
momo
źródło
12
... chyba że masz spacje na dowolnych ścieżkach.
Craig Ringer
9
... chyba że w poprzednim wierszu
umieściłeś inny plik
1
@robal Yep. Właśnie to spotkałem. Działa to świetnie, jeśli masz tylko dwa Makefile (główny plus dołączony).
sherrellbc
6

Próbowałem wielu z tych odpowiedzi, ale na moim systemie AIX z GNU make 3.80 musiałem zrobić pewne rzeczy starej szkoły.

Okazuje się, że lastword, abspathi realpathnie dodawano aż do 3,81. :(

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

Jak powiedzieli inni, nie jest to najbardziej elegancki, ponieważ wywołuje dwukrotnie powłokę i nadal ma problemy ze spacjami.

Ale ponieważ nie mam spacji na ścieżkach, działa dla mnie niezależnie od tego, jak zacząłem make:

  • make -f ../wherever/makefile
  • make -C ../wherever
  • make -C ~ / gdziekolwiek
  • cd ../wherever; robić

Wszystko mi dać whereverza current_diri bezwzględna ścieżka whereverdo mkfile_dir.

Jesse Chisholm
źródło
Ja, na przykład, skompilowałem gnu-make-3.82 na aix (5-6-7) bez problemów.
Zsigmond Lőrinczy
Tak, do 3,81 zaimplementowano potrzebne funkcje wcześniejszych rozwiązań. Moja odpowiedź dotyczy starszych systemów z wersją 3.80 lub wcześniejszą. Niestety w pracy nie wolno mi dodawać ani aktualizować narzędzi do systemu AIX. :(
Jesse Chisholm
5

Podoba mi się wybrana odpowiedź, ale myślę, że bardziej pomocne byłoby pokazanie jej działania niż wyjaśnienie.

/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

Wynik:

+ 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

UWAGA: Ta funkcja $(patsubst %/,%,[path/goes/here/])służy do usuwania końcowego ukośnika.

Bruno Bronosky
źródło
2

Oto jedna linijka, aby uzyskać bezwzględną ścieżkę do Makefilepliku przy użyciu składni powłoki:

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

A oto wersja bez powłoki oparta na odpowiedzi @ 0xff :

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

Przetestuj go, drukując:

cwd:
        @echo $(CWD)
kenorb
źródło
1
Krytycznie ważne! Drugi powyższy przykład wymaga: = operator przypisania, w przeciwnym razie przy złożonych
plikach
1
Pierwszy to powtarzający się błąd i nie działa w przypadku kompilacji spoza drzewa.
Johan Boulé,
2

Rozwiązanie znalezione tutaj: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

Rozwiązaniem jest : $ (CURDIR)

Możesz użyć tego w ten sposób:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder
Soma Bini
źródło
3
Witamy w Stack Overflow. Czy możesz dodać więcej szczegółów do swojej odpowiedzi? Pytanie brzmi: $(CURDIR)próbowano już bezskutecznie.
Sean
4
OSTRZEŻENIE, osoby szybko przeglądające: To nie jest katalog, w którym znajduje się plik makefile, ale bieżący katalog roboczy (w którym makezostał uruchomiony). Rzeczywiście tego chciał OP, ale tytuł pytania jest fałszywy, więc samo pytanie jest niespójne. To jest poprawna odpowiedź na niepoprawne pytanie. ;)
Sz.
Jest to całkowicie błędne i zbędna błędna odpowiedź
UpAndAdam
0

O ile mi wiadomo, jest to jedyna odpowiedź, która działa poprawnie ze spacjami:

space:= 
space+=

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

To zasadniczo działa poprzez ucieczkę spacje przez podstawienie ' 'za '\ 'który pozwala wprowadzić do analizowania go prawidłowo, a następnie usuwa nazwę pliku makefile w MAKEFILE_LISTwykonując kolejne podstawienie więc jesteś w lewo z katalogu, który jest w makefile. Niezupełnie najbardziej kompaktowych coś na świecie, ale to działa.

Skończysz z czymś takim, w którym wszystkie przestrzenie są unikane:

$(info CURRENT_PATH = $(CURRENT_PATH))

CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/
Peter Frost
źródło
-2

Przykład w celach informacyjnych, jak poniżej:

Struktura folderów może wyglądać następująco:

wprowadź opis zdjęcia tutaj

Gdzie są dwa Makefile, każdy jak poniżej;

sample/Makefile
test/Makefile

Zobaczmy teraz zawartość plików Makefiles.

sample / Makefile

export ROOT_DIR=${PWD}

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

test / Makefile

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

Teraz uruchom sample / Makefile, as;

cd sample
make

WYNIK:

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'

Wyjaśnienie byłoby takie, że katalog macierzysty / domowy może być przechowywany w flagi środowiska i może być eksportowany, aby można go było używać we wszystkich plikach makef podkatalogów.

parasrish
źródło
2
Pobiera bieżący katalog roboczy, a nie katalog domowy makefile.
Jesse Chisholm
To są linie w Makefile. Teraz, gdy polecenie Makefile zostanie wykonane, uzyska ścieżkę, w której znajduje się plik Makefile. Rozumiem, że „katalog domowy makefile” odnosi się do tego samego, tzn. Do ścieżki katalogu, w której działa Makefile.
parasrish
@Jesse Chisholm proszę odwiedzić ponownie. Wyjaśniłem przy użyciu.
parasrish
Powtarzający się błąd: nie działa w przypadku kompilacji spoza drzewa. Ponadto terminal gnome zapewnia prosty sposób kopiowania tekstu: wystarczy go wybrać. Plotka głosi, że Imgur jest brankrupt.
Johan Boulé,
-2

aktualizacja 2018/03/05 finnaly Używam tego:


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

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

Można go wykonać poprawnie w ścieżce względnej lub ścieżce bezwzględnej. Wykonano poprawne wywołane przez crontab. Wykonano poprawnie w innej powłoce.

pokaż przykład, własną ścieżkę drukowania a.sh.

[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 /]# 

stare: używam tego:

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

Może wyświetlać się poprawnie, jeśli zostanie wykonany w innej powłoce i innym katalogu.

zhukunqian
źródło
1
„Może wyświetlać się poprawnie, jeśli zostanie wykonany, jeśli inna powłoka i inny katalog” nie ma sensu w języku angielskim. Czy możesz spróbować wyjaśnić ponownie?
Keith M
przepraszam za mój słaby angielski
Zhukunqian
Dzięki za edycję, widzę, że miałeś na myśli IN. Czy możesz wyjaśnić, co to robi, zanim spróbuję? Nie jestem pewien jak go używać. Jak rozumiesz przez „inną powłokę”
Keith M
1
Ta odpowiedź zawiera tak wiele złych rzeczy, że nie można tego naprawić. Proponuję proste usunięcie.
Johan Boulé,
-2

Jedna linia w pliku Makefile powinna wystarczyć:

DIR := $(notdir $(CURDIR))

Thach Van
źródło