Цветная подсветка предупреждений и ошибок Makefile

Я написал Makefile, который отлично работает; Я просто размещаю его часть:

BUILD_PRINT = @echo -e "\e[1;34mBuilding $<\e[0m"
COMPILE_cpp = $(CXX) $(CFLAGS) -o [email protected] -c $< $(MAKEDEP) $(INCLUDES)
%.o : %.cpp
    $(BUILD_PRINT)
    $(COMPILE_cpp)
.SUFFIXES: .o .cpp

Я хотел бы выделить предупреждения и ошибки, предоставленные компилятором, без использования внешних инструментов (таких как colorgcc или CMake); Я подумал, что хороший способ взломать его можно с помощью "bash script трюков". Глядя на решение, опубликованное в Как я могу выделить строки предупреждений и ошибок в выходном файле? Я пробовал следующее:

pathpat="(/[^/]*)+:[0-9]+"
ccred=$(echo -e "\033[0;31m")
ccyellow=$(echo -e "\033[0;33m")
ccend=$(echo -e "\033[0m")

BUILD_PRINT = @echo -e "\e[1;34mBuilding $<\e[0m"
COMPILE_cpp = $(CXX) $(CFLAGS) -o [email protected] -c $< $(MAKEDEP) $(INCLUDES)
%.o : %.cpp
    $(BUILD_PRINT)
    $(COMPILE_cpp) 2>&1 | sed -e "/[Ee]rror[: ]/ s%$pathpat%$ccred&$ccend%g" -e "/[Ww]arning[: ]/ s%$pathpat%$ccyellow&$ccend%g" echo "${PIPESTATUS[0]}"
.SUFFIXES: .o .cpp

но он не работает. Я получаю следующий вывод

Building main.cpp
g++ -o main.o -c main.cpp  2>&1 | sed -e "/[Ee]rror[: ]/ s%athpat%cred&cend%g" -e "/[Ww]arning[: ]/ s%athpat%cyellow&cend%g" echo ""
sed: can't read echo: No such file or directory
sed: can't read : No such file or directory

Спасибо заранее!

Ответ 1

Сработало.

Прежде всего спасибо @EtanReisner и @rici. Здесь код:

BUILD_PRINT = \e[1;34mBuilding $<\e[0m

COMPILE_cpp = $(CXX) $(CFLAGS) -o [email protected] -c $< $(MAKEDEP) $(INCLUDES)
COMPILE_cpp_OUT=$$($(COMPILE_cpp) 2>&1 | sed -e 's/error/\\\e[1;31merror\\\e[0m/g' -e s/warning/\\\e[1;33mwarning\\\e[0m/g')

%.o : %.cpp
    @echo -e "$(BUILD_PRINT)\n$(COMPILE_cpp)\n$(COMPILE_cpp_OUT)"
.SUFFIXES: .o .cpp

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

$(BUILD_PRINT) просто печатает путь к файлу, который в настоящее время строится.

$(COMPILE_cpp) выводит строку компилятора, чтобы я мог видеть команду со всеми флагами/зависимостями/etc...

$(COMPILE_cpp_OUT) хранит выходные данные компилятора и изменяет соответствующий цвет слов с помощью команды sed.

Ответ 2

В контексте make следующие строки не ведут себя так, как они были бы в оболочке:

ccred=$(echo -e "\033[0;31m")
ccyellow=$(echo -e "\033[0;33m")
ccend=$(echo -e "\033[0m")

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

Эти строки должны быть

  • ccred=$(shell echo -e "\033[0;31m") для запуска команд через оболочку и сохранения вывода, а затем используется как $(ccred) в теле
  • ccred=\033[0;31m, чтобы сохранить строку в переменной, а затем использоваться как $$(echo -e '$(ccred)') в теле
  • ccred=echo -e "\033[0;31m", чтобы сохранить команду в переменной, а затем использоваться как $$($(ccred) в теле

Любой из первых или вторых вариантов, скорее всего, прав. (Используйте := вместо = в первом параметре, чтобы иметь возможность запускать только команду echo один раз, при выполнении времени синтаксического анализа вместо каждого использования ccred).

make и переменные оболочки совместно используют префикс sigil $. Таким образом, для использования переменных оболочки в контекстах make требуется экранирование $ в контексте оболочки, удвоив его до $$. Как таковой s%$pathpat%$ccred&$ccend%g должен быть s%$$pathpat%$$ccred&$$ccend%g и т.д.

Каждая строка тела правила make выполняется как отдельная команда оболочки, поэтому команды не могут взаимодействовать друг с другом. Поэтому для использования конструкций, подобных echo "${PIPESTATUS[0]}", поэтому требуется, чтобы они находились в одной командной строке в make. Таким образом, строка компиляции в этом правиле шаблона должна быть $(COMPILE_cpp) 2>&1 | sed ...; echo "${PIPESTATUS[0]}".

Однако даже это не будет делать то, что вам нужно, поскольку вам не нужно эхо-статус выхода из компиляции, с которой вам нужно выйти, поэтому вы, вероятно, захотите ; exit "${PIPESTATUS[0]}" там.