Как разместить объектные файлы в отдельном подкаталоге - PullRequest
54 голосов
/ 03 марта 2011

У меня проблемы с попыткой использовать make для помещения объектных файлов в отдельный подкаталог, вероятно, очень простой метод.Я пытался использовать информацию на этой странице: http://www.gnu.org/software/hello/manual/make/Prerequisite-Types.html#Prerequisite-Types

Я получаю следующий вывод от make:

make: *** No rule to make target `ku.h', needed by `obj/kumain.o'.  Stop.

Однако ku.h является зависимостью, а не целью (хотя этоочевидно, # включена в исходные файлы c).Когда я не пытаюсь использовать подкаталог для объектных файлов (то есть пропускаю части OBJDIR), он работает нормально.Почему заставляет думать, что ku.h является целью?

Мой make-файл такой: (стиль после чтения различных источников информации)

.SUFFIXES:
.SUFFIXES: .c .o

CC=gcc 
CPPFLAGS=-Wall
LDLIBS=-lhpdf
VPATH=%.c src
VPATH=%.h src
VPATH=%.o obj
OBJDIR=obj

objects= $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o \
  kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o \
  kushapes.o )

ku : $(objects)
  $(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS)

$(objects) : ku.h kudefines.h kuglobals.h kufns.h | $(OBJDIR)

$(OBJDIR):
  mkdir $(OBJDIR)

.PHONY: clean
clean :
  rm $(objects)

Редактировать: я применил изменения, чтобы использоватьдиректива vpath.Моя версия была плохой смесью VPATH = xxx и vpath% .c xxx.Однако теперь я получаю другую проблему (которая была исходной проблемой до того, как я добавил неправильный vpath).Теперь это вывод:

    gcc  -o ku -lhpdf obj/kumain.o obj/kudlx.o obj/kusolvesk.o ..etc
    gcc: obj/kumain.o: No such file or directory
    gcc: obj/kudlx.o: No such file or directory
    gcc: obj/kusolvesk.o: No such file or directory
    gcc: obj/kugetpuz.o: No such file or directory
    gcc: obj/kuutils.o: No such file or directory
    gcc: obj/kurand.o: No such file or directory
    gcc: obj/kuASCboard.o: No such file or directory
    gcc: obj/kuPDFs.o: No such file or directory
    gcc: obj/kupuzstrings.o: No such file or directory
    gcc: obj/kugensud.o: No such file or directory
    gcc: obj/kushapes.o: No such file or directory
    make: *** [ku] Error 1

Похоже, что make не применяет неявное правило для объектного файла, хотя в руководстве говорится: «Неявные правила говорят make, как использовать обычные методы, так что вам не нужноукажите их подробно, когда вы хотите их использовать. Например, существует неявное правило для компиляции C. Имена файлов определяют, какие неявные правила запускаются. Например, компиляция C обычно берет файл .c и создает файл .o.Так что make применяет неявное правило для компиляции C, когда видит эту комбинацию окончаний имени файла. "а также "Поиск по каталогам, указанным в VPATH или с помощью vpath, также происходит во время рассмотрения неявных правил (см. Использование неявных правил)."

Снова здесь "Например, когда файл foo.o не имеет явногоrule, make рассматривает неявные правила, такие как встроенное правило для компиляции foo.c, если этот файл существует. Если такого файла нет в текущем каталоге, его ищут в соответствующих каталогах. Если существует foo.c (илиупоминается в make-файле) в любом из каталогов, применяется неявное правило для компиляции Си. "

Любая помощь в получении неявных правил для работы с моим make-файлом будет принята с благодарностью.

Редактирование № 2: Благодаря Джеку Келли я сделал явное правило для компиляции файлов .c, так как я не мог нигде пытаться использовать неявные правила.Также спасибо al_miro за информацию о vpath.

Вот рабочий макфайл:

.SUFFIXES:
.SUFFIXES: .c .o

CC=gcc 
CPPFLAGS=-Wall
LDLIBS=-lhpdf
OBJDIR=obj
vpath %.c src
vpath %.h src

objects = $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o \
  kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o \
  kushapes.o )

ku : $(objects)
  $(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS)

$(OBJDIR) obj/%.o : %.c ku.h kudefines.h kuglobals.h kufns.h 
  $(CC) -c $(CPPFLAGS) $< -o $@

.PHONY : clean
clean :
  rm $(objects)

Ответы [ 6 ]

61 голосов
/ 04 марта 2011

Поскольку вы используете GNUmake, используйте шаблонное правило для компиляции объектных файлов:

$(OBJDIR)/%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
47 голосов
/ 06 января 2015

Это make-файл, который я использую для большинства своих проектов,

. Он позволяет помещать исходные файлы, заголовки и встроенные файлы в подпапки, подпапки подпапок и т. Д., И автоматически генерировать файл зависимостей для каждого объекта Это означает, что изменение заголовков и встроенных файлов приведет к повторной компиляции зависимых файлов.

Исходные файлы обнаруживаются с помощью команды поиска оболочки, поэтому нет необходимости явно указыватьукажите, просто сохраняйте кодировку для вашего сердца.

Он также скопирует все файлы из папки 'resources' в папку bin при компиляции проекта, что я считаю удобным в большинстве случаев.

Чтобы предоставить кредит там, где это необходимо, функция автозависимостей была основана в основном на странице Скотта Макпика, которую можно найти ЗДЕСЬ , с некоторыми дополнительными изменениями / настройками для моих нужд.

Пример Makefile

#Compiler and Linker
CC          := g++-mp-4.7

#The Target Binary Program
TARGET      := program

#The Directories, Source, Includes, Objects, Binary and Resources
SRCDIR      := src
INCDIR      := inc
BUILDDIR    := obj
TARGETDIR   := bin
RESDIR      := res
SRCEXT      := cpp
DEPEXT      := d
OBJEXT      := o

#Flags, Libraries and Includes
CFLAGS      := -fopenmp -Wall -O3 -g
LIB         := -fopenmp -lm -larmadillo
INC         := -I$(INCDIR) -I/usr/local/include
INCDEP      := -I$(INCDIR)

#---------------------------------------------------------------------------------
#DO NOT EDIT BELOW THIS LINE
#---------------------------------------------------------------------------------
SOURCES     := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS     := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))

#Defauilt Make
all: resources $(TARGET)

#Remake
remake: cleaner all

#Copy Resources from Resources Directory to Target Directory
resources: directories
    @cp $(RESDIR)/* $(TARGETDIR)/

#Make the Directories
directories:
    @mkdir -p $(TARGETDIR)
    @mkdir -p $(BUILDDIR)

#Clean only Objecst
clean:
    @$(RM) -rf $(BUILDDIR)

#Full Clean, Objects and Binaries
cleaner: clean
    @$(RM) -rf $(TARGETDIR)

#Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))

#Link
$(TARGET): $(OBJECTS)
    $(CC) -o $(TARGETDIR)/$(TARGET) $^ $(LIB)

#Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
    @mkdir -p $(dir $@)
    $(CC) $(CFLAGS) $(INC) -c -o $@ $<
    @$(CC) $(CFLAGS) $(INCDEP) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
    @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
    @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
    @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
    @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp

#Non-File Targets
.PHONY: all remake clean cleaner resources
23 голосов
/ 03 марта 2011

Неправильные строки VPATH, они должны быть

vpath %.c  src
vpath %.h  src

т.е. не капитал и без =. Как и сейчас, он не находит файл .h и думает, что это цель, которую нужно сделать.

5 голосов
/ 22 мая 2014

В общем, вы должны либо указать $(OBJDIR) слева от всех правил, которые помещают файлы в $(OBJDIR), либо вы можете запустить make из $(OBJDIR). VPATH для источников, а не для объектов.

Взгляните на эти две ссылки для более подробного объяснения и "умного" обходного пути.

2 голосов
/ 06 октября 2014

Следующее решение, на мой взгляд, нехорошо, поскольку мне действительно нравятся встроенные правила. Однако GNU make не поддерживает что-то вроде vpath для выходных каталогов. И встроенные правила не могут совпадать, так как % в %.o будет соответствовать obj/foo из obj/foo.o, оставляя make с поиском в vpath %.c src/ для таких вещей, как src/obj/foo.c, но не src/foo.c.

Но это настолько близко к встроенным правилам, насколько вы можете получить, и поэтому, насколько мне известно, самое хорошее решение, которое доступно.

$(OBJDIR)/%.o: %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

Объяснение: $(COMPILE.c) $(OUTPUT_OPTION) $< на самом деле это то, как .c.o реализовано, см. http://git.savannah.gnu.org/cgit/make.git/tree/default.c (и даже упомянуто в руководстве)

Кроме того, если бы $(OBJDIR) содержал только автоматически сгенерированные файлы, вы могли бы создать его на лету с предварительным условием только для заказа, сделав правило очистки немного проще:

$(OBJDIR):
        mkdir -p $(OBJDIR)

$(OBJDIR)/%.o: %.c | $(OBJDIR)
        $(COMPILE.c) $(OUTPUT_OPTION) $<

.PHONY: clean
clean:
        $(RM) -r $(OBJDIR)

Для этого требуется, чтобы была доступна функция только для заказа , которую можно проверить с помощью $(filter order-only, $(.FETAURES)). Я проверил на Kubuntu 14.04 GNU make 3.81 и OpenSUSE 13.1 GNU make 3.82. Оба были построены с включенным только для заказа , и теперь я озадачен, почему Kubuntu 14.04 поставляется с более старой версией GNU make, чем OpenSUSE 13.1. В любом случае, собираюсь скачать make 4.1 сейчас:)

1 голос
/ 21 сентября 2016

Для всех, кто работает с неявными правилами (и GNU MAKE).Вот простой make-файл, который поддерживает разные каталоги:

#Start of the makefile

VPATH = ./src:./header:./objects

OUTPUT_OPTION = -o objects/$@

CXXFLAGS += -Wall -g -I./header

Target = $(notdir $(CURDIR)).exe

Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)))



all: $(Target)

$(Target): $(Objects)
     $(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects))


#Beware of -f. It skips any confirmation/errors (e.g. file does not exist)

.PHONY: clean
clean:
     rm -f $(addprefix objects/,$(Objects)) $(Target)

Давайте посмотрим поближе (я буду ссылаться на текущий каталог с помощью curdir):

Эта строка используется для получения спискаиз используемых .o файлов, которые находятся в curdir / src.

Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)))
#expands to "foo.o myfoo.o otherfoo.o"

Через переменную вывод устанавливается в другой каталог (curdir / objects).

OUTPUT_OPTION = -o objects/$@
#OUTPUT_OPTION will insert the -o flag into the implicit rules

Чтобы убедиться, чтокомпилятор находит объекты в новой папке объектов, к имени файла добавляется путь.

$(Target): $(Objects)
     $(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects))
#                                    ^^^^^^^^^^^^^^^^^^^^    

Это подразумевается в качестве примера и определенно есть место для улучшения.

За дополнительной информацией обращайтесь: Составьте документацию.См. Главу 10.2

Или: Oracle: Руководство по программному обеспечению

...