Как сделать GNU/Сделать стоп-реферирование символических ссылок на каталоги

GNU/Make manual §5.7 гласит следующее:

5.7 Рекурсивное использование make

Рекурсивное использование make означает использование make в качестве команды в make файле. Этот метод полезен, когда вам нужны отдельные make файлы для различных подсистем, которые составляют более крупную систему. Например, предположим, что у вас есть subdirectory subdir, у которого есть свой собственный make файл, и вы хотите, чтобы содержащийся в make файле каталог запускал make в подкаталоге. Вы можете сделать это, написав это:

 subsystem:
         cd subdir && $(MAKE) or, equivalently, this (see Summary of Options):

 subsystem:
         $(MAKE) -C subdir

Итак, в основном это означает, что cd subdir && $(MAKE) совпадает с $(MAKE) -C subdir.

Однако оказывается, что это не эквивалент. При использовании -C путь к каталогу, если он является символической ссылкой, всегда -C, поэтому нет способа получить "логический" (как в pwd -L) имя каталога. Рассмотрим следующий пример. Имея следующий Makefile в каталоге /tmp/b, который является символической /tmp/a/ каталог /tmp/a/:

foo:
    @echo PWDL=$(shell pwd -L)
    @echo PWDP=$(shell pwd -P)
    @echo CURDIR=$(CURDIR)

Теперь пусть invoke make иначе:

$ pwd
/tmp
$ (cd b && make foo)
PWDL=/tmp/b
PWDP=/tmp/a
CURDIR=/tmp/a
$ make -C b foo
make: Entering directory '/tmp/a'
PWDL=/tmp/a
PWDP=/tmp/a
CURDIR=/tmp/a
make: Leaving directory '/tmp/a'
$ make --directory=b foo
make: Entering directory '/tmp/a'
PWDL=/tmp/a
PWDP=/tmp/a
CURDIR=/tmp/a
make: Leaving directory '/tmp/a'
$ 

Как вы можете видеть, pwd -P и $(CURDIR) всегда показывают символическую ссылку с ссылкой на ссылку. Но pwd -L работает только при смене каталога перед запуском make, что доказывает, что опция -C для GNU/Make всегда делает ее -C ссылки на путь к каталогу, без уважительной причины. Я пытался найти какое-либо объяснение этого поведения в документации, но не смог. Я также не могу придумать обходной путь для этой проблемы, не меняя директорию с использованием cd перед запуском make (или с очень плохими крючками через LD_PRELOAD).

Вопрос в том, кто-нибудь еще сталкивался с этой проблемой раньше, имеет объяснение или обходное решение? Благодарю!

ОБНОВИТЬ:

Пытаясь разобраться в этом, я загрузил исходный код для make и не нашел никакой специальной обработки каталогов, только звонки в chdir. Поэтому я написал небольшую программу для проведения эксперимента, и вот что я нашел.

Когда вы находитесь в символическом каталоге (/tmp/b) и пытаетесь сменить каталог на то же, операция не имеет никакого эффекта, и текущий рабочий каталог продолжает указывать на символическую ссылку.

Когда вы вызываете chdir() и указываете полный или относительный путь, он отменяет ссылки на него перед сменой каталога. Вот доказательство:

$ cat cd.cpp 
#include <stdio.h>
#include <unistd.h>

int main ()
{
    chdir ("/tmp/b/");
    printf ("Current dir: %s\n",
        get_current_dir_name ()); /* Forgive my memory leak. */
}

$ gcc -o test ./cd.cpp 
$ pwd
/tmp/b
$ ./test 
Current dir: /tmp/b
$ cd ../
$ ./b/test 
Current dir: /tmp/a
$ cd /
$ /tmp/b/test 
Current dir: /tmp/a
$ 

Кажется, что либо libc либо Linux играют на меня трюки, которые мне на самом деле не нравились раньше. И вдобавок к этому, bash s cd 'работает как-то иначе.

Как ни странно, Linux chdir() человек страницы ничего об этом не говорится, но есть записка о нем в Borland Builder (! SIC) документации, здесь. Так интересно, что bash делает в этой связи.

Ответ 1

С помощью эфемерной помощи я смог понять это. Итак, есть несколько вещей, которые происходят:

  1. Linux не поддерживает наличие текущего рабочего каталога, установленного в символическую ссылку. chdir() всегда устанавливает его в реальный каталог.
  2. Существует переменная, называемая PWD которая представляет собой текущий рабочий каталог, по соглашению.
  3. Некоторые из функций/системных вызовов выполняют функцию переменной PWD а некоторые нет.
  4. Bash поддерживает "поддельный" путь, построенный с использованием символических имен, и соответственно устанавливает переменную PWD.

Это объясняет поведение gmake. Когда gmake вызывается sh, PWD обновляется до gmake процесса gmake. Таким образом, PWD настроен на "символический" путь, и функции, выполняющие его, продолжают работать нормально. В противном случае gmake вызывает chdir(), который меняет рабочий каталог и устанавливает PWD без трюков. Таким образом, все функции, включая те, кто выполняет PWD начинают возвращать "реальный" путь.

В целом, я бы сказал, что в зависимости от символического пути это плохая идея, и все может легко развалиться. Например, поскольку системный вызов getcwd() не заботится о PWD. Вызов cd перед запуском make работает как краткосрочное решение.