Как выполнить make во всех подкаталогах

Вопрос или проблема

У меня есть несколько каталогов, в которых находится Makefile.

Я написал этот скрипт, чтобы запустить эти Makefile, если они существуют:

find . -name "Makefile" -exec sh -c 'make re -C "$1" $(dirname "$0")' {} \;

Но он, похоже, не работает, я получаю эту ошибку:

make: the `-C' option requires a non-empty string argument
Usage: make [options] [target] ...
Options:
  -b, -m                      Ignored for compatibility.
  -B, --always-make           Unconditionally make all targets.
  -C DIRECTORY, --directory=DIRECTORY
[...]

Но когда я заменяю make re -C на echo или printf, он работает нормально…

find . -name "Makefile" -exec sh -c 'printf "%s\n" "$1" $(dirname "$0")' {} \;
# или
find . -name "Makefile" -exec sh -c 'echo "$1" $(dirname "$0")' {} \;

Ваш оригинальный код использует два аргумента, $0 и $1, но вы всегда передаете что-то только в $0, оставляя $1 пустым. Это вызывает ошибку, которую вы показываете.

Вы можете исправить это, просто убрав "$1" из вашей команды (и поставив кавычки вокруг замены команды dirname), если хотите, предполагая, что ваша make не против смешения операндов и опций в порядке, но я дам вам еще несколько идей.


Следующие две команды находят все обычные файлы с именем Makefile в текущем каталоге или ниже и затем используют make -C, чтобы создать цель re в каждом каталоге, где такой файл был найден.

find . -name Makefile -type f -exec sh -c 'make -C "${1%/*}" re' sh {} \;

или

find . -name Makefile -type f -exec sh -c '
  for pathname do
      make -C "${pathname%/*}" re
  done' sh {} +

Во второй команде я изменил скрипт sh -c, чтобы сделать цикл по как можно большему числу найденных путей, а не только по одному имени.

Я заменил dirname в обеих командах стандартным расширением параметра, которое удаляет последний элемент пути из $pathname. Мы знаем, что в имени пути есть как минимум одна косая черта, даже если файл Makefile был найден в текущем каталоге, что делает это безопасным; это в противном случае – пограничный случай, когда вы не хотите заменять dirname на более быструю замену.


Еще более быстрая версия, которая не использует make с опцией -C, заключается в запуске make с (нестандартным) предикатом -execdir от find:

find . -name Makefile -type f -execdir make re \;

Это заставляет find менять рабочий каталог для make на директорию, где был найден файл Makefile. Преимущество этого варианта заключается в том, что он выполняет make напрямую, не прибегая к скрипту sh -c.

Вместо использования make -C проще просто cd зайти в каталог.

Эта реализация работает хорошо:

find . -name "Makefile" -exec sh -c 'cd "$(dirname "$0")" && make re' {} \;

Использование -exec или -execdir работает хорошо, пока не перестанет, потому что find будет тихо игнорировать любые ошибки. Вы можете сделать следующее в bash, используя флаг -e, чтобы корректно завершить выполнение при любой ошибке:

set -e
find . -name "Makefile" | while read makefile; do
    dir=$(dirname "$makefile")
    (       
        set -e 
        cd "$dir"
        make
    )
done

Ответ или решение

Для того чтобы корректно запускать команду make во всех поддиректориях, содержащих файл Makefile, необходимо учитывать несколько ключевых моментов, чтобы избежать ошибок и обеспечить эффективное выполнение задач. Рассмотрим, как можно решить данную задачу правильно и эффективно.

Теория

Makefile является инструментом автоматизации сборки программ, который описывает, какие цели (например, компиляция или установка) должны быть достигнуты и как они зависят друг от друга. Основной командой для работы с Makefiles является make, которая по умолчанию ищет Makefile в текущем каталоге. Однако, если у вас несколько подкаталогов с Makefile, необходимо обрабатывать каждый из них отдельно.

Команда find в Unix-подобных системах позволяет находить файлы и выполнять команды для каждого файла. В нашем контексте потребуется найти все файлы Makefile и выполнить команду make в каждом из директорий, в которых они находятся.

Пример

Рассмотрим несколько подходов к решению этой задачи:

  1. Использование команды -exec с find:

    find . -name "Makefile" -type f -exec sh -c 'make -C "${1%/*}" re' _ {} \;

    Здесь команда find ищет все файлы с именем Makefile, а для каждого найденного файла выполняется оболочка sh -c, которая запускает make с опцией -C, указывающей директорию для выполнения.

  2. Использование команды -execdir с find:

    find . -name "Makefile" -type f -execdir make re \;

    Опция -execdir выполняет команду make в директории, где найден файл Makefile. Это удобный способ, поскольку он автоматически меняет текущую директорию, избавляя от необходимости вручную вычислять директорию файла.

  3. Использование цикла в Bash для надежности:

    Иногда команды, выполненные через find -exec, могут не отображать ошибки корректно. Для более надежной обработки ошибок можно использовать скрипт на Bash:

    set -e
    find . -name "Makefile" | while read makefile; do
       dir=$(dirname "$makefile")
       (
           set -e 
           cd "$dir"
           make re
       )
    done

    В этом скрипте команда find выводит все пути к Makefile, а цикл while читает каждый путь по одному, переходит в соответствующий каталог и выполняет make. Использование подзадачи в скобках создает новый контекст выполнения, что изолирует команды и предотвращает неправильное изменение текущей директории для внешних команд. Это особенно важно, если ваш сценарий имеет сложные зависимости.

Применение

Каждый из подходов имеет свои достоинства. Опция -exec с find достаточно проста и эффективна для большинства случаев. Когда вы уверены, что ошибки выполнения make должны прерывать весь процесс, подход с использованием Bash-сценария более подходящий, так как с помощью set -e обеспечивается выход в случае любой ошибки.

Важно учитывать, что инструменты, подобные find, могут различаться в зависимости от операционной системы, поэтому всегда следует проверять их поведение в ваших специфических условиях. Опция -execdir, хотя и очень удобна, может быть доступна не во всех системах.

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

В заключение, важно выбрать такой подход, который обеспечит не только корректность выполнения, но и простоту отладки и возможность масштабирования решения.

Оцените материал
Добавить комментарий

Капча загружается...