Gnu Makefile - Обработка зависимостей
Какой подход используют программисты C++ на платформе Unix для создания и управления файлами Makefile?
Я использовал созданные вручную Make-файлы для своих проектов, но они не обрабатывают изменения файла заголовка и другие зависимости. Я погуглил и нашел здесь хорошее решение.
Но я столкнулся с проблемой здесь, в команде sed -
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
Проблема с 3-м выражением "-e 's/ *\$$//'. Это не работает. Предполагается, что он должен убирать конечные обратные слеши. Я понимаю, что там должен быть двойной доллар, так как это часть Makefile. Может кто-нибудь сказать мне, что здесь не так?
Вот полный Makefile -
CC=g++
CFLAGS=-g -Wall
LIBS=-lpthread
OBJS=file1.o file2.o
TARGET=testProg
$(TARGET) : $(OBJS)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
%.o : %.cpp
$(CC) -MMD -c -o $@ $< $(CFLAGS)
@cp $*.d $*.P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
rm -f $*.d
-include $(OBJS:%.o=%.P)
clean :
rm -f $(TARGET) $(OBJS)
all : $(TARGET)
Помимо решения этой проблемы, я хотел бы также несколько советов / указателей на мой первый вопрос.
12 ответов
- Я тоже использую этот подход и не могу похвалить его достаточно высоко. И я пишу свои make-файлы вручную и многократно использую их в новых проектах.
- . Выражение "s/ *\\$//" будет работать вне контекста Make. В make-файле это не работает, потому что Make пытается интерпретировать "$/" перед передачей результата в оболочку. Таким образом, вы должны использовать "s/ *\\$$//" (обратите внимание на дополнительный $) внутри make-файла, но это не будет работать вне контекста Make (так что тестирование - это небольшая боль).
РЕДАКТИРОВАТЬ:
Я пробовал твой make-файл, и это утверждение sed, похоже, прекрасно удаляет завершающие обратные слеши. Попробуйте что-нибудь попроще, например так:
обратная косая черта: @echo " \\" > $@ test: обратная косая черта @echo без sed: @cat обратная косая черта @echo с помощью sed: @sed -e 's/ *\\$$//' <обратная косая черта
РЕДАКТИРОВАТЬ: Хорошо, теперь я подсел. Не могли бы вы попробовать эти эксперименты и рассказать нам результаты?
Измените последний символ на "z": s/.$/z/ Измените обратную косую черту на 'z': s/\\$/z/ Измените обратную косую черту на 'z': sm\\$mzm Удалить обратную косую черту: s / \\ $ // Удалить пробелы и обратную косую черту: s/ *\\$// Попробуйте все это внутри и снаружи Make, используя '$' и '$$'.
gcc/g++ может генерировать зависимости для вас с помощью -M
Семья вариантов. Следующее работает, определяя, как генерировать .depends
файлы даны исходный файл. При выполнении -include $(DEPS)
$(DEPS) распознается как цель и будет построен / перестроен при изменении исходных файлов.
CXX = g++
CXXFLAGS = -Wall -O3
LDFLAGS =
TARGET = testcpp
SRCS = main.cc x.cc foo.cc
OBJS = $(SRCS:.cc=.o)
DEPS = $(SRCS:.cc=.depends)
.PHONY: clean all
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)
.cc.o:
$(CXX) $(CXXFLAGS) -c $< -o $@
%.depends: %.cc
$(CXX) -M $(CXXFLAGS) $< > $@
clean:
rm -f $(OBJS) $(DEPS) $(TARGET)
-include $(DEPS)
В файле make все, что вы перечисляете в строке зависимостей, является файлами заголовков зависимостей или другими включенными файлами.
Учебник BSD по make Примечание: вы можете автоматически генерировать информацию о зависимости заголовка с помощью ключа -MM GCC.
Я должен что-то упустить. Почему генерация файлов зависимостей не работает для вас?
Я предпочитаю использовать CMake, хотя это не совсем решение вашей проблемы.
Это язык описания проекта, который сгенерирует ваши Makefiles, Visual Studio Project, Eclipse Project, KDevelop и т. Д. Все зависимости сделаны для вас:
CMakeLists.txt
add_executable(my_exe file1.c file2.c)
target_link_libraries(my_exe my_library)
add_subdirectory(lib)
В lib/CMakeLists.txt
add_library(my_library file3.c file4.c)
Это создает файл my_exe из file1.c file2.c, связанный с my_library. Я нахожу это намного проще. У этого также есть вещи как обнаружение пакета:
find_package(Qt4)
Утилита makedepend устанавливается во многих системах и может быть весьма полезна для генерации информации о зависимостях.
Вот пример Makefile, который использует include
директива (плюс немного магии Perl) для включения вывода из makedepend:
# the name of the executable that we'll build
TARGET = foo_prog
# our .cc source files
SRCS = foo.cc main.cc
# the .o versions of our source files
OBJS := $(patsubst %.cc, %.o, $(filter %.cc, $(SRCS)))
# some flags for compiling
CXXFLAGS = -Wall -Werror
# In order to build $(TARGET), we first build each of the $(OBJS).
# Then we use the given command to link those $(OBJS) into our
# $(TARGET) executable. $^ is a shortcut for $(OBJS). $@ is a
# shortcut for $(TARGET).
#
# The default compile rule will compile each of the $(OBJS) for us.
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $^ -o $@
# Use "make clean" to remove all of the support files.
clean:
rm -f $(OBJS) $(TARGET) Makefile.depend *~
# This automatically uses the 'makedepend' utility to add any
# dependencies that our source files have, namely .h files. This way,
# if the .h files change, the code will be re-compiled.
include Makefile.depend
Makefile.depend: $(SRCS)
makedepend -f- -Y $(SRCS) 2> /dev/null | \
perl -p -e "s/(^.*?:)/Makefile.depend \1/" > Makefile.depend
Если оба foo.cc
а также main.cc
зависит от foo.h
, то содержимое Makefile.depend
было бы:
Makefile.depend foo.o: foo.h
Makefile.depend main.o: foo.h
Конечным результатом является то, что информация о зависимости от makedepend
вводится в Makefile как ряд правил. Это похоже на подход с использованием .d
файл для каждого .cc
файл, но хранит информацию о зависимостях в одном файле, а не разбросаны повсюду.
В системе сборки Mozilla мы используем ключ -MD GCC для генерации файлов зависимостей: http://mxr.mozilla.org/mozilla-central/source/configure.in#7134 а затем мы используем скрипт mddepend.pl для проверки для удаленных файлов заголовков, например, удаление заголовка просто вызывает перестроение, а не ошибку: http://mxr.mozilla.org/mozilla-central/source/config/rules.mk#2066 http: //mxr.mozilla. орг / Mozilla-центральный / источник / сборки / Unix / mddepend.pl
Этот скрипт генерирует файл.all.pp, содержащий все зависимости, с дополнительным foo.o: FORCE
зависимости застряли для отсутствующих заголовочных файлов. Затем мы просто включаем файл.all.pp в rules.mk прямо там.
Я использую BSD make (pmake?), Которая делает для меня много работы (мой язык - C, но я думаю, что здесь нет никакой разницы). Это мой обычный 'local.prog.mk', я никогда не меняю его:
.PHONY: tags .depend
# .depend depends only on $(SRCS) in bsd.dep.mk, so we can't track changes of
# header's own dependencies properly. so .depend is .PHONY target here.
CSTD ?=c99
WARNS ?=9
.if !empty(PC_LIST)
PC_CF !=pkg-config --cflags $(PC_LIST)
PC_LD !=pkg-config --libs $(PC_LIST)
.endif
CFLAGS +=$(PC_CF) -fgnu89-inline
.if !defined(NO_DEBUG)
CFLAGS +=-O0 -ggdb3
.endif
LDFLAGS +=$(PC_LD)
CTAGS =exctags
NO_MAN=
NO_OBJ=
CLEANFILES+=$(PROG).core
.include <bsd.prog.mk>
$(PROG): $(SUBDIR)
build: clean cleandepend depend all
run: $(PROG)
./$(PROG)
Обратите внимание на включение "bsd.prog.mk" - оно обрабатывает все, создает, зависит, очищает цели. Проект конкретных BSDmakefile
с просты:
.SILENT:
PROG =hello
SRCS =hello.c world.c
PC_LIST =gtk+-2.0 gnet-2.0
.include "../local.prog.mk"
proto:
cproto -siv `pkg-config --cflags $(PC_LIST)` $(SRCS) > prototypes
CLEANFILES+=prototypes
Я просто устанавливаю зависимость каждый раз, когда вставляю / удаляю любые директивы #include.
В более современной версии GCC вы можете добавить флаг -MP, чтобы GCC генерировал пустые правила для самих заголовков.
Вместо сценариев sed используйте опцию -cc для gcc, чтобы изменить цель сгенерированных правил зависимости. Этот блог имеет больше информации.
Вы можете использовать qmake для генерации Makefiles для проекта, даже если этот проект не использует Qt.
Верхний совет, который я нашел полезным при построении файлов зависимостей, - это включить файл зависимостей в качестве цели в сгенерированное правило:
file.d file.o : file.c header.h header2.h ...
таким образом make
восстановит зависимости в случае изменения источника или любого из заголовков. Включая фальшивые цели для заголовков (GCC -MP
) должен позволять стабильные сборки при удалении заголовков - отсутствие требуемого заголовка остается ошибкой компиляции, а не ошибкой построения зависимости.
Предполагая, что файлы зависимостей создаются в том же каталоге, что и объектные файлы, для GCC в Unix должно работать следующее:
-include $(OBJ:.o=.d)
$(OBJDIR)/%d : $(SRCDIR)/%.cpp
mkdir -p $(@D)
echo -n "$@ " > $@.tmp
$(CXX) $(CPPFLAGS) -MM -MP -MT $(@:.d=.o) $< >> $@.tmp
mv $@.tmp $@
(из памяти)