Я помню, как узнал о Makefiles от своего наставника. Он указал, что волшебство действительно происходит из-за целей и их связи с реальными файлами. Со временем написание эффективных файлов Makefile, позволяющих избежать повторения работы, стало более естественным. В то же время я обнаружил, что мои файлы Makefile стали более сложными и трудными для понимания. Проблема обычно связана с объявлением зависимостей при попытке сохранить некоторый уровень удобства использования. Дело в том, что неявные зависимости трудно надежно определить, поэтому вы застряли с явным определением зависимости, несмотря ни на что.

Я не хочу выделять make здесь. Bazel — еще один пример инструмента сборки, который ставит зависимости на первое место и в центр. Например, при использовании Bazel с Go вы, скорее всего, будете использовать инструмент под названием Газель. Gazelle проверит ваши зависимости в вашем проекте Go и обеспечит наличие необходимых bazel.build файлов во всех ваших зависимых пакетах, включая вложенные пакеты. Чтобы представить это в перспективе, представьте, что вы размещаете сгенерированный Makefile во всех репозиториях, поставляемых для вашего приложения, и поддерживаете их в актуальном состоянии, и вы сможете понять, что происходит на высоком уровне.

Это не столько критика, сколько свидетельство того, что зависимости действительно усложняются с течением времени. Универсальный инструмент, такой как Make, удобен, потому что он предоставляет простые средства через файловую систему для пересечения границ разных платформ. Если вы не хотите перестраивать свой контейнер Docker, если некоторые файлы не изменятся, вы можете это сделать.

target/my-docker-container: build/* config/*
    docker build -t my-docker-container .
.PHONY=build-my-docker-container
build-my-docker-container: target/my-docker-container

Очевидная (надеюсь) проблема заключается в том, что вам приходится вручную извлекать большинство зависимостей от соответствующей платформы. В этом случае build/* и config/*, скорее всего, являются путями, включенными в ваш Dockerfile, которые вам нужно было явно определить в Makefile. Вероятно, есть способы обойти это с помощью небольшого сценария, который находит COPY и ADD в Dockerfile, но это больше кода, который необходимо понимать при определении целей. Если бы вы увидели что-то вроде следующего, вы бы поняли, что оно сделало?

target/my-docker-container: $(shell bin/find-docker-dependencies .)

Это не отражает таких вещей, как когда кто-то использует скрипт в Dockerfile, который также может использовать зависимые файлы или должен учитываться при перестроении контейнера. Мы даже не учли, когда изменился базовый контейнер или что-то делает сетевые запросы.

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

У меня нет хорошего решения проблемы. Я думаю, что есть место для полезных инструментов, которые можно использовать с make. Я уверен, что другие решили проблему, используя другие инструменты, такие как cmake и Bazel. Что я точно знаю, так это то, что если мы хотим улучшить нашу разработку и создать быстрые циклы обратной связи, нам нужно эффективно справляться с этими зависимостями сборки. Хотя это может показаться чрезмерным проектированием, со временем внедрение инструментов для поддержки выбранного вами инструмента сборки или добавление кода в вашу систему сборки со временем принесет дивиденды с точки зрения производительности разработчика, даже если это станет сложным и трудным для понимания. .