Описание рецепта distdir

Argument list too long, Как собирать monodevelop
test commad of sed
Как написать цикл
Создание директорий по списку файлов

Как работает исходный код

distdir: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
          dist_files=`for file in $$list; do echo $$file; done | \
          sed -e "s|^$$srcdirstrip/||;t" \
              -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
        case $$dist_files in \
          */*) $(MKDIR_P) `echo "$$dist_files" | \
                           sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
                           sort -u` ;; \
        esac; \
        for file in $$dist_files; do \
          if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
          if test -d $$d/$$file; then \
            dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
            if test -d "$(distdir)/$$file"; then \
              find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
            fi; \
            if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
              cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
              find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
            fi; \
            cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
          else \
            test -f "$(distdir)/$$file" \
            || cp -p $$d/$$file "$(distdir)/$$file" \
            || exit 1; \
          fi; \
        done
@ перед длинной bash-командой, много строк которой соединены символами \ + конец строки
означает, что не надо распечатывать текст этой команды.
It means "don't echo this command on the output." So this rule is saying "execute the shell command and don't echo the output.

srcdir
The distinction between ./ (the build directory) and $(srcdir)/ (the source directory) is important because users can build in a separate directory using the ‘--srcdir’ option to configure.
    это из файла https://www.gnu.org/prep/standards/html_node/Makefile-Basics.html
top_srcdir
— Variable: top_srcdir
    The name of the top-level source code directory for the package. In the top-level directory, this is the same as srcdir.
    цитата из файла https://www.gnu.org/software/autoconf/manual/autoconf-2.65/html_node/Preset-Output-Variables.html#index-top_005fsrcdir-134

Зачем используется команда t в sed?
t label
    If a s/// has done a successful substitution since the last input line was read and since the last t or T command, then branch to label; if label is omitted, branch to end of script.

nothing changes without that last t that's why their comment in line 150 is wrong

sed 's/[].[^$$\\*]/\\\\&/g'
вставляет перед любым из символов ] . [ ^ $ \ * два обратных слеша
в строке sed \\ заменяется на \ самим sed (неясно почему, вероятно из-за потребностей синтаксиса регэкспов sed)
$$ - передаётся в sed как один $ (это фишка gmake)
what this command from gmake Makefile replaces? sed 's/[].[^$$\\*]/\\\\&/g'
It (likely) inserts "\\" in front of any of the characters: ] . [ ^ $ \ *
# echo 'foo ^ $ bar.[] baz' | sed 's/[].[^$$\\*]/\\\\&/g'
foo \\^ \\$ bar\\.\\[\\] baz
I don't understood, why character set is not closed by first ]
it is a special case, "]" does not terminate class specification if it is the first character
& - denotes the substitution

как в bash работает case/esac ?
http://ss64.com/bash/case.html

Зачем он здесь?
the variable $dist_files contain list of files and directories. case checks if it contain at least one directory, and if it does, then mkdir is called
whatever dist_files contains, case treats it as a single string, and runs the $(MKDIR_P) if that string contains at least one / character

Зачем здесь два цикла for, почему нельзя было обойтись одним общим?
ну, сначала создаются директории, а потом туда копируются файлы
почему нельзя делать в один проход пока непонятно.

sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,'
sed '/\//!d' - удалить строки в которых нет /
d = Delete pattern space. Start next cycle.
s|^|$(distdir)/|
в каждое начало строки дописывается значение переменной $(distdir)/, которое передано из gmake в bash уже в виде заменённого текста
s,/[^/]*$$,,
эта команда, заменяет имена файлов (то, что не оканчивается на символ '/') на пустые строки, то есть остаются только имена директорий (дублирующиеся от разных файлов)
/path/to/file/filename1
/path/to/file/filename1
здесь заменяется "/filename1" на пустую строку и "/filename2" на пустую строку
и остаётся два имени /path/to/file /path/to/file

man sort
-u, --unique
    with -c, check for strict ordering; without -c, output only the first of an equal run

Конструкция if test condition-true является точным эквивалентом конструкции if [ condition-true ], где левая квадратная скобка [ выполняет те же действия, что и команда test. Закрывающая правая квадратная скобка ] не является абсолютно необходимой, однако, более новые версии Bash требуют ее наличие.
Команда test -- это встроенная команда Bash, которая выполняет проверки файлов и производит сравнение строк. Таким образом, в Bash-скриптах, команда test не вызывает внешнюю (/usr/bin/test) утилиту, которая является частью пакета sh-utils. Аналогично, [ не производит вызов утилиты /usr/bin/[, которая является символической ссылкой на /usr/bin/test.
-d FILE
    FILE exists and is a directory
-f FILE
    FILE exists and is a regular file

|| exit 1;
это хитрая проверка типа || die, мол если команда cp выполнилась с ошибкой, то надо выйти из цикла и завершить работу

переменные srcdir и top_srcdir являются переменными bash, так как они записаны внутри makefile target.
Каждый рецепт bash запускается независимо.
Из-за того, что это переменные bash - в оригинальном коде нельзя разбить один recipe на две части убрав \ после esac.
В случае фрагментации рецепта на два фрагмента, переменная dist_dir во втором фрагменте будет неопределена и код не сработает.

ну вот вроде бы всё понятно, что здесь происходит за исключением некоторых моментов (например неясно как работает $(MKDIR_P) )