Цель Makefile запускает параллельные цели и ждет их завершения, прежде чем делать что-то еще

Я пытаюсь иметь «родительскую цель», ожидающую завершения «параллельных дочерних целей», прежде чем что-то делать в «теле» «родительской цели».

В следующем примере сообщение @echo "Print this only after *ALL* the previous PARALLEL tasks are done!" должно быть напечатано только после, все целевые foo-task1, foo-task2 и foo-task3 завершены.

Если я удалю амперсанд (&) в конце этих «дочерних целей» затем они становятся серийными, но это не ожидаемое поведение. Они должны вести себя аналогично «пулу потоков» или «пулу процессов». Я не знаю заранее, какая из этих целей завершится последней, поэтому я не могу удалить амперсанд (&) из цели узкого места.

Эти цели представляют собой HTTP-вызовы удаленного REST API, для выполнения которых может потребоваться разное количество времени. Я хотел бы дождаться этих «параллельных вызовов REST», чтобы увидеть, удались ли они, прежде чем печатать последнее сообщение в foo-parallel.

Эти «параллельные цели» возвращают stdout / stderr, поэтому я хочу, чтобы родительская цель могла дождаться их и выйти из строя в случае каких-либо ошибок.

Как я мог это сделать?

foo-task1:
    @bash -c 'sleep 1 && echo "task1" &'

foo-task2:
    @bash -c 'sleep 1 && echo "task2" &'

foo-task3:
    @bash -c 'sleep 2 && echo "task3" &'

foo-parallel: foo-task3 foo-task2 foo-task1
    @echo "Print this only after *ALL* the previous PARALLEL tasks are done!"

Версия команды make:

$ make --version
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

редактировать:

Это немного другое определение цели (я менял sleep раз, чтобы убедиться, что сообщения печатаются в отложенном порядке, а не в порядке в коде):

foo-parallel:
    @bash -c 'sleep 5 && echo "task1" & \
                        sleep 1 && echo "task2" & \
                        sleep 2 && echo "task3" & \
                        wait; \
                        echo "done here!"'

печатает:

$ make foo-parallel 
task2
task3
task1
done here!

но мне нужно поместить логику всех этих task1, task2 и task3 в одну цель. Я хотел бы разделить эти задачи на разные цели.

Почему бы вам не использовать опцию -j? Я считаю, что это лучший метод распараллеливания в make (см. gnu.org/software/make/manual/make.html#Parallel).

uzsolt 24.10.2018 18:40

Не знал об этом варианте J. Спасибо

TPPZ 24.10.2018 18:45

Пожалуйста! Я тоже написал подробный ответ.

uzsolt 24.10.2018 18:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
1 682
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Думаю, вы хотите использовать опцию -j:

foo-task1:
    @sleep 1
    @echo "task1"

foo-task2:
    @sleep 1
    @echo "task2"

foo-task3:
    @sleep 2
    @echo "task3"

foo-parallel: foo-task3 foo-task2 foo-task1
    @echo "Print this only after *ALL* the previous PARALLEL tasks are done!"

И запустите make -j3 foo-parallel - make будет работать параллельно с правильной зависимостью. См. Больше раздел Параллельное выполнение:

GNU make knows how to execute several recipes at once. Normally, make will execute only one recipe at a time, waiting for it to finish before executing the next. However, the ‘-j’ or ‘--jobs’ option tells make to execute many recipes simultaneously.

Спасибо! Что, если есть цель all, которая определена как all: foo-first-serial foo-3-parallel-targets foo-last-serial, если я вызываю ее как make -j3 all, будет ли она учитывать последовательную цепочку foo-first-serial, затем 3 параллельные цели, затем foo-last-serial?

TPPZ 24.10.2018 18:53

@TPPZ: Я вижу, что MadScientist ответил вам под своим ответом.

uzsolt 25.10.2018 08:55
Ответ принят как подходящий

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

foo-parallel: foo-task3 foo-task2 foo-task1
        @echo "Print this only after *ALL* the previous PARALLEL tasks are done!"

foo-task1:
        @sleep 1 && echo "task1"

foo-task2:
        @sleep 1 && echo "task2"

foo-task3:
        @sleep 2 && echo "task3"

Затем запустите: make -j. Или, если вы хотите ограничить количество параллельных сборок, используйте make -j2 или сколько угодно.

Если вы ДЕЙСТВИТЕЛЬНО не хотите вводить -j, вы можете использовать рекурсивную команду make:

foo-parallel:
        @$(MAKE) -j foo-task1 foo-task2 foo-task3
        @echo "Print this only after *ALL* the previous PARALLEL tasks are done!"

foo-task1:
        @sleep 1 && echo "task1"

foo-task2:
        @sleep 1 && echo "task2"

foo-task3:
        @sleep 2 && echo "task3"

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

Спасибо! Что, если есть цель all, которая определена как all: foo-first-serial foo-3-parallel-targets foo-last-serial, если я вызываю ее как make -j3 all, будет ли она учитывать последовательную цепочку foo-first-serial, затем 3 параллельные цели, затем foo-last-serial?

TPPZ 24.10.2018 18:53

Хорошо, я попробовал второй вариант, который вы предложили из командной строки, например make all, и он работает, как ожидалось (то есть foo-first-serial, затем 3 параллельных цели, затем foo-last-serial), но я вижу странное предупреждение, когда вызываю его как make -j2 aaa, поэтому я угадайте, что это так, нет более чистого способа обеспечить какой-то поток, например последовательный> параллельный> последовательный?

TPPZ 24.10.2018 19:03

Вам нужно будет указать необходимые предварительные условия: поэтому создайте foo-first-serial: | foo-3-parallel, чтобы обеспечить этот порядок, и foo-3-parallel: | foo-last-serial, чтобы обеспечить этот порядок. Здесь я использую предварительные условия только для заказа, но вы могли бы просто использовать вместо них обычные, если это работает лучше. Все make-файлы всегда должны явно объявлять свой граф зависимостей; не делать этого приводит к душевной боли и боли.

MadScientist 24.10.2018 19:04

Плохая идея полагаться на порядок, в котором цели появляются в списках предварительных требований, чтобы обеспечить соблюдение порядка сборки. Если вам нужно заставить вещи строиться в определенном порядке, вы должны использовать операторы target / prerequisite, чтобы сообщить make об этом.

MadScientist 24.10.2018 19:05

Хм, чтобы обеспечить выполнение: foo-first-serial, затем трех параллельных целей foo-3-parallel, затем последней последовательной цели foo-last-serial, мне пришлось определить предварительные условия только для заказа (штука |) по-другому, это: foo-3-parallel: | foo-first-serial и foo-last-serial: | foo-3-parallel. Затем из командной строки мне пришлось назвать это так: time make foo-last (time должен измерить необходимое время, чтобы проверить, параллельна ли параллельная вещь). Вот довольно непонятное объяснение труб: gnu.org/software/make/manual/make.html#Prerequisite-Types

TPPZ 25.10.2018 11:42

Упс получил это задом наперед. Но да, в make вы ВСЕГДА указываете конечную цель, которую хотите построить, в командной строке, а не первую.

MadScientist 25.10.2018 15:06

Другие вопросы по теме