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/ *\\$$//' &lt 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]

(из памяти)