Makefiles с исходными файлами в разных каталогах - PullRequest
118 голосов
/ 16 июля 2009

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

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

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

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

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

Ответы [ 10 ]

95 голосов
/ 16 июля 2009

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

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

Поскольку каждая строка в целевом объекте запускается в своей собственной оболочке, нет необходимости беспокоиться о обратном копировании дерева каталогов или других каталогов.

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

83 голосов
/ 16 июля 2009

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

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

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

30 голосов
/ 16 июля 2009

Может пригодиться опция VPATH, которая указывает make для каких каталогов искать исходный код. Вам все равно понадобится опция -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-файл для каждой части, особенно если каждая часть может стоять отдельно.

23 голосов
/ 16 июля 2009

Вы можете добавить правила в ваш корневой 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 $@"
        @echo "============="
        @$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
        @echo -- Link finished --

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


## 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 $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

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

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

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

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

18 голосов
/ 16 мая 2012

Если исходные файлы распределены по многим папкам, и имеет смысл иметь отдельные 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 $@ ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<

И построить вывод еще проще:

# 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)))
9 голосов
/ 16 июля 2009

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

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

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

Используйте IDE, CMake или, если у вас жесткая сердцевина, Автоинструмент .

(отредактировано из-за отрицательных голосов, Ты Хонза указал)

2 голосов
/ 05 апреля 2012

Рекурсивное использование Make

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

Это позволяет make разделяться на рабочие места и использовать несколько ядер

2 голосов
/ 15 февраля 2012

Пост RC был СУПЕР полезен. Я никогда не думал об использовании функции $ (dir $ @), но она сделала именно то, что мне было нужно.

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

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

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

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

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

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

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

1 голос
/ 23 апреля 2019

Я искал что-то вроде этого, и после некоторых попыток и неудач я создаю свой собственный make-файл, я знаю, что это не «идиоматический способ», но это начало понимать, что делать, и это работает для меня, возможно, вы могли бы попробовать в своем проект.

PROJ_NAME=mono

CPP_FILES=$(shell find . -name "*.cpp")

S_OBJ=$(patsubst %.cpp, %.o, $(CPP_FILES))

CXXFLAGS=-c \
         -g \
        -Wall

all: $(PROJ_NAME)
    @echo Running application
    @echo
    @./$(PROJ_NAME)

$(PROJ_NAME): $(S_OBJ)
    @echo Linking objects...
    @g++ -o $@ $^

%.o: %.cpp %.h
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS) -o $@

main.o: main.cpp
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS)

clean:
    @echo Removing secondary things
    @rm -r -f objects $(S_OBJ) $(PROJ_NAME)
    @echo Done!

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

Я принимаю предложения: D

0 голосов
/ 10 сентября 2013

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

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

AUTOMAKE_OPTIONS = subdir-objects

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

Вот учебник .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...