Makefiles з вихідними файлами в різних каталогах


135

У мене є проект, де структура каталогу виглядає так:

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

Як мені написати файл makefile, який би був частиною / src (або там, де реально), який міг би частково доповнити / посилатись на вихідні файли c / c ++? / Src?

Чи можу я зробити щось на кшталт -I $ projectroot / part1 / src -I $ projectroot / part1 / inc -I $ projectroot / part2 / src ...

Якщо це допоможе, чи є простіший спосіб зробити це. Я бачив проекти, де в кожній відповідній частині є makefile? папки. [у цій публікації я використав знак питання, як у синтаксисі bash]



1
В оригінальному посібнику з gnu ( gnu.org/software/make/manual/html_node/Phony-Targets.html ) у програмі Phony Targets є зразок recursive invocation, який може бути досить елегантним.
Френк Нокк

Який інструмент ви використали для створення цієї графічної тексту?
Халид Хуссей

Відповіді:


114

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

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

Оскільки кожен рядок у цілі створення виконується у власній оболонці, не потрібно турбуватися про перехід резервного копіювання до каталогу каталогів чи до інших каталогів.

Я пропоную поглянути на GNU зробити посібник, розділ 5.7 ; це дуже корисно.


26
Це має бути +$(MAKE) -C part1і т. Д. Це дозволяє контролю роботи Make працювати в підкаталогах.
ефемія

26
Це класичний підхід і широко застосовується, але він є неоптимальним кількома способами, які погіршуються в міру зростання проекту. У Дейва Хінтона є вказівник, який слід виконувати.
dmckee --- кошеня колишнього модератора

89

Якщо у вас є код в одному підкаталозі, залежний від коду, в іншому підкаталозі вам, ймовірно, краще з одним makefile на верхньому рівні.

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

Наведене вище посилання, здається, недоступне. Цей же документ доступний тут:


3
Дякую, про це не знали. Дуже корисно знати «правильний спосіб» робити речі замість способів, які «просто працюють» або приймаються як стандартні.
tjklemz

3
Рекурсивне виготовлення вважалося шкідливим, ще тоді, коли воно справді мало працювало. Сьогодні це не вважається шкідливим, насправді це те, як автоінструменти / автоматики керують більшими проектами.
Едвін Бак

36

Можливо, стане в нагоді опція 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 розбитий на підкаталоги. Що ви описуєте, як уже говорили інші, вам, мабуть, краще буде скласти файл для кожної частини, особливо якщо кожна частина може стояти окремо.


2
Не можу повірити, що ця проста ідеальна відповідь не отримала більше голосів. Це +1 від мене.
Ніколас Гамільтон

2
У мене були декілька загальних вихідних файлів у вищому каталозі для декількох різних проектів у папках, які VPATH=..працювали на мене!
EkriirkE

23

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

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

ДЖЕРЕЛ = cppTest.cpp
ДЖЕРЕЛ = $ (OTHERDIR) /file.cpp

## Визначення кінцевих джерел
INCLUDE = -I./ $ (AN_INCLUDE_DIR)  
ВКЛЮЧИТИ = -I. $ (OTHERDIR) /../ вкл
## end more включає

VPATH = $ (OTHERDIR)
OBJ = $ (приєднатися $ (addsuffix ../obj/, $ (dir $ (ДЖЕРЕЛО))), $ (notdir $ (ДЖЕРЕЛ: .cpp = .o))) 

## Виправити призначення залежності, яке має бути ../.dep щодо src dir
DEPENDS = $ (приєднатися $ (addsuffix ../.dep/, $ (dir $ (ДЖЕРЕЛО))), $ (notdir $ (ДЖЕРЕЛ: .cpp = .d)))

## Правило за замовчуванням виконано
всього: $ (ТАРГЕТ)
        @true

## Чисте правило
чисто:
        @ -rm -f $ (TARGET) $ (OBJ) $ (DEPENDS)


## Правило для досягнення фактичної мети
$ (TARGET): $ (OBJ)
        @echo "============="
        @echo "Зв'язування цільового $ @"
        @echo "============="
        @ $ (CC) $ (CFLAGS) -o $ @ $ ^ $ (LIBS)
        @echo - Посилання закінчено -

## Загальне правило компіляції
% .o:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo "Компіляція $ <"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @


## Правила для об'єктних файлів із файлів cpp
## Файл об'єктів для кожного файлу поміщається в каталог obj
## на один рівень вгору від фактичного каталогу джерел.
../obj/%.o:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo "Компіляція $ <"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

# Правило для "іншого каталогу" Вам знадобиться один за один "інший"
$ (OTHERDIR) /../ obj /%. O:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo "Компіляція $ <"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

## Складіть правила залежності
../.dep/%.d:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo Файл залежності побудови для $ *. o
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ .. / obj / $ *. o ^"> $ @ '

## Правило залежності для каталогу "other"
$ (OTHERDIR) /../. Dep /%. D:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo Файл залежності побудови для $ *. o
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ $ (OTHERDIR) /../ obj / $ *. o ^"> $ @ '

## Додайте файли залежності
-включити $ (DEPENDS)


7
Я знаю, що це вже досить давно, але хіба ДЖЕРЕЛО ТА ВКЛЮЧЕННЯ переписані іншим завданням на один рядок пізніше?
skelliam

@skelliam так, це так.
наброян

1
Цей підхід порушує принцип DRY. Дуже дублювати код для кожного "іншого каталогу"
Jesus H

20

Якщо джерела поширюються у багатьох папках, і є сенс мати окремі Makefiles, то, як було запропоновано раніше, рекурсивний 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)))

це чудово підходить для менших проектів, де ви просто хочете додати деякі власні бібліотеки та мати їх у окремій папці. Дуже дякую
zitroneneis

6

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

Це чудовий інструмент, але його пряме використання слід вважати застарілим у 2010 році.

Якщо, звичайно, ви працюєте в спеціальному середовищі, тобто зі спадковим проектом тощо.

Використовуйте IDE, CMake або, якщо у вас жорстке серцевина, Autotools .

(відредаговано через низовини, введіть Honza для вказівки)


1
Було б непогано пояснити знищення. Раніше я теж робив свої Makefiles.
IlDan

2
Я виступаю за пропозицію "не робіть цього" - KDevelop має дуже графічний інтерфейс для налаштування Make та інших систем побудови, і поки я сам пишу Makefiles, KDevelop - це те, що я дарую своїм однокласникам - але я не думаю, що посилання Autotools не допомагає. Щоб зрозуміти Autotools, вам потрібно зрозуміти m4, libtool, automake, autoconf, shell, make і в основному весь стек.
ефемія

7
Очевидно, що ви відповідаєте на власне запитання. Питання полягало не в тому, варто використовувати чи Makefiles чи ні. Йшлося про те, як їх писати.
Хонза

8
Було б непогано, якщо ви могли б, у кількох реченнях пояснити, як сучасні інструменти полегшують життя, ніж написання Makefile. Чи забезпечують вони абстракцію більш високого рівня? або що?
Абхішек Ананд

1
xterm, vi, bash, makefile == керувати світом, cmake - це для людей, які не можуть зламати код.
μολὼν.λαβέ

2

Повідомлення RC було надзвичайно корисним. Я ніколи не замислювався над використанням функції $ (dir $ @), але робив саме те, що мені потрібно для цього.

У parentDir майте купу каталогів із вихідними файлами в них: dirA, dirB, dirC. Різні файли залежать від об'єктних файлів в інших каталогах, тому я хотів мати можливість зробити один файл з одного каталогу, і зробити це залежністю, викликавши makefile, пов'язаний із цією залежністю.

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

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

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

Щоразу, коли мені потрібно було зробити файл, я використовував (по суті) це правило, щоб рекурсивно робити будь-які / всі залежності. Ідеально!

ПРИМІТКА: є утиліта під назвою "makepp", яка, здається, робить цю задачу ще більш інтуїтивно, але заради портативності та, не залежно від іншого інструменту, я вирішила зробити це саме так.

Сподіваюся, це допомагає!


2

Рекурсивне використання марки

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

Це дозволяє makeрозділити на робочі місця і використовувати кілька ядер


2
Як це краще, ніж робити щось подібне make -j4?
девін

1
@devin, як я бачу, це не краще , він просто дозволяє make контролювати роботу взагалі. Під час виконання окремого процесу виготовлення він не підлягає контролю роботи.
Михайло Паньков

1

Я шукав щось подібне, і після деяких спроб і падінь я створюю свій власний makefile, я знаю, що це не "ідіоматичний шлях", але це починає розуміти, і це працює для мене, можливо, ви могли б спробувати у своєму проекті.

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


-1

Я пропоную використовувати autotools:

//## Помістіть створені об’єктні файли (.o) в ту саму директорію, що і їх вихідні файли, щоб уникнути зіткнень, коли використовується нерекурсивна марка.

AUTOMAKE_OPTIONS = subdir-objects

просто включити його Makefile.amдо інших досить простих речей.

Ось підручник .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.