Gnu Makefile - Обработка зависимостей
Какой подход используют программисты на С++ на платформе Unix для создания и управления Make файлами?
Я использовал Makefile для рук для своих проектов, но они не обрабатывают изменения файла заголовка и другие зависимости. Я googled вокруг и нашел хорошее решение здесь.
Но я столкнулся с проблемой здесь, в команде sed -
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
Проблема заключается в третьем выражении: -e 's/*\$$//'.
Это не работает. Предполагалось, что он удаляет задние косые обратные косые черты. Я понимаю, что там должен быть двойной доллар, так как это часть Makefile. Может кто-нибудь сказать мне, что здесь не так?
Здесь полный Makefile -
CC=g++
CFLAGS=-g -Wall
LIBS=-lpthread
OBJS=file1.o file2.o
TARGET=testProg
$(TARGET) : $(OBJS)
$(CC) -o [email protected] $^ $(CFLAGS) $(LIBS)
%.o : %.cpp
$(CC) -MMD -c -o [email protected] $< $(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)
Помимо решения этой проблемы, мне также нужны некоторые советы/указатели на мой первый вопрос.
Ответы
Ответ 1
- Я использую тот подход и не могу хвалить его достаточно высоко. И я пишу свои make файлы вручную и многократно использую их для новых проектов.
- . Выражение "s/*\\$//" будет работать вне контекста Make. В make файле он не работает, потому что Make пытается интерпретировать "$/" перед передачей результата оболочке. Таким образом, вы должны использовать "s/*\\$$//" (обратите внимание на дополнительный $) в make файле, но это не будет работать вне контекста Make (поэтому тестирование немного боль).
EDIT:
Я пробовал ваш make файл, и это выражение sed, похоже, просто удаляет обратные косые черты. Попробуйте что-то более простое, например:
backslash:
@echo " \\" > [email protected]
test: backslash
@echo without sed:
@cat backslash
@echo with sed:
@sed -e 's/ *\\$$//' < backslash
EDIT:
Хорошо, теперь я зацепился. Не могли бы вы попробовать эти эксперименты и рассказать нам о результатах?
Change the last character to 'z' : s/.$/z/
Change a trailing backslash to 'z' : s/\\$/z/
Change a trailing backslash to 'z' : sm\\$mzm
Delete a trailing backslash : s/\\$//
Delete spaces and a trailing backslash: s/ *\\$//
Try all of these inside and outside of Make, with '$' and '$$'.
Ответ 2
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 [email protected]
%.depends: %.cc
$(CXX) -M $(CXXFLAGS) $< > [email protected]
clean:
rm -f $(OBJS) $(DEPS) $(TARGET)
-include $(DEPS)
Ответ 3
В файле make все, что вы указываете в строке зависимостей, - это файлы заголовков зависимостей или другие файлы.
Учебное пособие BSD по make Примечание: вы можете автоматически генерировать информацию о зависимостях заголовка с ключом -MM GCC.
Ответ 4
Мне что-то не хватает. Почему вы не создаете файлы зависимостей?
Ответ 5
Я предпочитаю использовать 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)
Ответ 6
Утилита 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). [email protected] is a
# shortcut for $(TARGET).
#
# The default compile rule will compile each of the $(OBJS) for us.
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $^ -o [email protected]
# 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
, но сохраняет информацию о зависимостях в одном файле вместо разброса повсюду.
Ответ 7
В системе сборки Mozilla мы используем ключ GCC -MD для генерации файлов зависимостей:
http://mxr.mozilla.org/mozilla-central/source/configure.in#7134
и затем мы используем script, называемый mddepend.pl, для проверки удаленных файлов заголовков, так что
удаление заголовка просто вызывает перестройку, а не ошибку:
http://mxr.mozilla.org/mozilla-central/source/config/rules.mk#2066
http://mxr.mozilla.org/mozilla-central/source/build/unix/mddepend.pl
Это script создает файл .all.pp, содержащий все зависимости, с дополнительными зависимостями foo.o: FORCE
, застрявшими в отсутствующих файлах заголовков. Затем мы просто включаем файл .all.pp в rules.mk прямо под ним.
Ответ 8
Вы можете использовать qmake для создания Makefile для проекта, даже если этот проект не использует Qt.
Ответ 9
Я использую BSD make (pmake?), который много работает для меня (мой lang - C, но я не думаю, что здесь никакой разницы). Это мой общий "local.prog.mk", я никогда не меняю его:
.PHONY: tags .depend
# .depend depends only on $(SRCS) in bsd.dep.mk, so we can't track changes of
# header 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.
Ответ 10
Вместо сценариев sed используйте параметр gcc -MT, чтобы изменить цель сгенерированных правил зависимостей. В этом сообщении в блоге есть дополнительная информация.
Ответ 11
С более современной версией GCC вы можете добавить флаг -MP, чтобы GCC генерировал пустые правила для самих заголовков.
Ответ 12
Я верю, что я нашел полезным при создании файлов зависимостей, чтобы включить файл зависимостей в качестве цели в сгенерированном правиле:
file.d file.o : file.c header.h header2.h ...
Таким образом, make
будет восстанавливать зависимости, если изменяется исходный или любой из заголовков. Включение фальшивых целей для заголовков (GCC -MP
) должно затем обеспечивать стабильную сборку при удалении заголовков - отсутствие требуемого заголовка остается ошибкой компиляции, а не ошибкой make make.
Предполагая, что файлы зависимостей сгенерированы в тот же каталог, что и файлы объектов, для GCC в Unix должно работать следующее:
-include $(OBJ:.o=.d)
$(OBJDIR)/%d : $(SRCDIR)/%.cpp
mkdir -p $(@D)
echo -n "[email protected] " > [email protected]
$(CXX) $(CPPFLAGS) -MM -MP -MT $(@:.d=.o) $< >> [email protected]
mv [email protected] [email protected]
(из памяти)