Makefile с исходными файлами в разных каталогах

У меня есть проект, в котором структура каталогов выглядит следующим образом:

                         $projectroot
                              |
              +---------------+----------------+
              |               |                |
            part1/          part2/           part3/
              |               |                |
       +------+-----+     +---+----+       +---+-----+
       |      |     |     |        |       |         |
     data/   src/  inc/  src/     inc/   src/       inc/

Как мне написать файл makefile, который был бы частично /src (или где бы он был на самом деле), который мог бы завершить/связать исходные файлы c/С++?/src?

Могу ли я сделать что-то вроде -I $projectroot/part1/src -I $projectroot/part1/inc -I $projectroot/part2/src...

Если это сработает, есть ли более простой способ сделать это. Я видел проекты, в которых есть makefile в каждой из соответствующих частей? папки. [в этом посте я использовал знак вопроса, как в синтаксисе bash)

Ответ 1

Традиционным способом является наличие Makefile в каждом из подкаталогов (part1, part2 и т.д.), что позволяет вам создавать их самостоятельно. Кроме того, в корневом каталоге проекта есть Makefile, который строит все. "Root" Makefile будет выглядеть примерно так:

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

Так как каждая строка в make-мишени запускается в своей собственной оболочке, нет необходимости беспокоиться о перемещении резервной копии дерева каталогов или других каталогов.

Я предлагаю взглянуть на GNU make manual section 5.7; это очень полезно.

Ответ 2

Если у вас есть код в одном подкаталоге, зависящем от кода в другом подкаталоге, вам, вероятно, лучше сделать один make файл на верхнем уровне.

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

Ссылка выше, похоже, недоступна. Этот же документ доступен здесь:

Ответ 3

Вы можете добавить правила в свой корневой файл Makefile, чтобы скомпилировать необходимые файлы cpp в других каталогах. Приведенный ниже пример Makefile должен стать хорошим началом для вас, когда вы хотите быть.

CC=g++
TARGET=cppTest
OTHERDIR=../../someotherpath/in/project/src

SOURCE = cppTest.cpp
SOURCE = $(OTHERDIR)/file.cpp

## End sources definition
INCLUDE = -I./ $(AN_INCLUDE_DIR)  
INCLUDE = -I.$(OTHERDIR)/../inc
## end more includes

VPATH=$(OTHERDIR)
OBJ=$(join $(addsuffix ../obj/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.o))) 

## Fix dependency destination to be ../.dep relative to the src dir
DEPENDS=$(join $(addsuffix ../.dep/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.d)))

## Default rule executed
all: $(TARGET)
        @true

## Clean Rule
clean:
        @-rm -f $(TARGET) $(OBJ) $(DEPENDS)


## Rule for making the actual target
$(TARGET): $(OBJ)
        @echo "============="
        @echo "Linking the target [email protected]"
        @echo "============="
        @$(CC) $(CFLAGS) -o [email protected] $^ $(LIBS)
        @echo -- Link finished --

## Generic compilation rule
%.o : %.cpp
        @mkdir -p $(dir [email protected])
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o [email protected]


## Rules for object files from cpp files
## Object file for each file is put in obj directory
## one level up from the actual source directory.
../obj/%.o : %.cpp
        @mkdir -p $(dir [email protected])
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o [email protected]

# Rule for "other directory"  You will need one per "other" dir
$(OTHERDIR)/../obj/%.o : %.cpp
        @mkdir -p $(dir [email protected])
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o [email protected]

## Make dependancy rules
../.dep/%.d: %.cpp
        @mkdir -p $(dir [email protected])
        @echo "============="
        @echo Building dependencies file for $*.o
        @$(SHELL) -ec '$(CC) -M $(CFLAGS) $< | sed "s^$*.o^../obj/$*.o^" > [email protected]'

## Dependency rule for "other" directory
$(OTHERDIR)/../.dep/%.d: %.cpp
        @mkdir -p $(dir [email protected])
        @echo "============="
        @echo Building dependencies file for $*.o
        @$(SHELL) -ec '$(CC) -M $(CFLAGS) $< | sed "s^$*.o^$(OTHERDIR)/../obj/$*.o^" > [email protected]'

## Include the dependency files
-include $(DEPENDS)

Ответ 4

Опция VPATH может пригодиться, что говорит о том, какие каталоги искать в исходном коде. Однако вам все равно нужна опция -I для каждого пути включения. Пример:

CXXFLAGS=-Ipart1/inc -Ipart2/inc -Ipart3/inc
VPATH=part1/src:part2/src:part3/src

OutputExecutable: part1api.o part2api.o part3api.o

Это автоматически найдет соответствующие файлы partXapi.cpp в любом из указанных каталогов VPATH и скомпилирует их. Однако это более полезно, когда ваш каталог src разбит на подкаталоги. Для того, что вы описали, как говорили другие, вам, вероятно, будет лучше с make файлом для каждой части, особенно если каждая часть может стоять одна.

Ответ 5

Если источники распространены во многих папках, и имеет смысл иметь индивидуальные Make файлы, то, как было предложено ранее, рекурсивный make является хорошим подходом, но для небольших проектов мне легче перечислить все исходные файлы в Makefile с помощью их относительный путь к Makefile:

# common sources
COMMON_SRC := ./main.cpp \
              ../src1/somefile.cpp \
              ../src1/somefile2.cpp \
              ../src2/somefile3.cpp \

Затем я могу установить VPATH следующим образом:

VPATH := ../src1:../src2

Затем я создаю объекты:

COMMON_OBJS := $(patsubst %.cpp, $(ObjDir)/%$(ARCH)$(DEBUG).o, $(notdir $(COMMON_SRC)))

Теперь правило прост:

# the "common" object files
$(ObjDir)/%$(ARCH)$(DEBUG).o : %.cpp Makefile
    @echo creating [email protected] ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o [email protected] $<

И создание выхода еще проще:

# This will make the cbsdk shared library
$(BinDir)/$(OUTPUTBIN): $(COMMON_OBJS)
    @echo building output ...
    $(CXX) -o $(BinDir)/$(OUTPUTBIN) $(COMMON_OBJS) $(LFLAGS)

Можно даже создать генерацию VPATH с помощью:

VPATH := $(dir $(COMMON_SRC))

Или используя тот факт, что sort удаляет дубликаты (хотя это не имеет значения):

VPATH := $(sort  $(dir $(COMMON_SRC)))

Ответ 6

Я думаю, что лучше указать, что использование Make (рекурсивное или нет) - это то, чего обычно вы можете избежать, потому что по сравнению с сегодняшними инструментами его трудно изучать, поддерживать и масштабировать.

Это замечательный инструмент, но его прямое использование должно считаться устаревшим в 2010 году.

Если, конечно, вы не работаете в специальной среде, то есть с унаследованным проектом и т.д.

Используйте IDE, CMake, или, если вы сильно вывернуты, Autotools.

(отредактирован из-за downvotes, ty Honza для указания)

Ответ 7

Сообщение RC было SUPER полезным. Я никогда не думал об использовании функции $(dir [email protected]), но он сделал именно то, что мне было нужно.

В parentDir у вас есть куча каталогов с исходными файлами в них: dirA, dirB, dirC. Различные файлы зависят от объектных файлов в других каталогах, поэтому я хотел иметь возможность сделать один файл из одного каталога и сделать его зависимым, вызвав make файл, связанный с этой зависимостью.

По существу, я сделал один Makefile в parentDir, который (среди многих других) использовал общее правило, подобное RC:

%.o : %.cpp
        @mkdir -p $(dir [email protected])
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o [email protected]

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

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

ПРИМЕЧАНИЕ: есть утилита под названием "makepp", которая, похоже, делает эту задачу еще более интуитивно, но ради мобильности и не зависит от другого инструмента, я решил сделать это таким образом.

Надеюсь, это поможет!

Ответ 9

Я предлагаю использовать autotools:

//## Поместите созданные объектные файлы (.o) в тот же каталог, что и их исходные файлы, чтобы избежать коллизий при использовании нерекурсивного make.

AUTOMAKE_OPTIONS = subdir-objects

просто включив его в Makefile.am с другим довольно простым материалом.

Вот учебник.