Переменные в нерекурсивных файлах Makefile - PullRequest
0 голосов
/ 13 февраля 2019

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

Например, если у меня есть два файла Makefile для модулей libA и libB

libA Makefile.inc:
src_dir := libA/src
inc_dir := libA/inc

libB Makefile.inc:
src_dir := libB/src
inc_dir := libB/inc

Затем, когда я включаю вышеупомянутые Make-файлы в основной Make-файл

include libA/Makefile.inc
include libB/Makefile.inc

Значения в переменных src_dir и inc_dir перезаписываются самым последним Makefile, который был включен.Хорошо, это, вероятно, ожидаемо, поскольку здесь переменные имеют глобальный охват, и это приводит к путанице в командах компоновки, использующих эти переменные, то есть команда компоновки для libA находит значения переменных для libB.

Способ обойти это - создать уникальные переменные.для каждого Makefile

libA Makefile.inc:
src_dir_libA := libA/src
inc_dir_libA := libA/inc

libB Makefile.inc:
src_dir_libB := libB/src
inc_dir_libB := libB/inc

Это решает проблему, но это немного неудобно в использовании, поскольку каждая переменная должна быть переименована.Кто-нибудь знает, есть ли лучший способ решить эту проблему, например, если GNU Make имеет какое-то понятие области видимости или пространства имен?Я посмотрел на документацию, но не могу найти ничего подобного.Существуют переменные, специфичные для цели, но они, похоже, имеют неприятные побочные эффекты.

ОК, просто чтобы быть очень конкретным, ниже приведен пример Makefile

dep_all :=
all:

# Begin Makefile.inc for project1
# ------------------------------------------------------------------------------
$(foreach var,$(filter local_%,$(.VARIABLES)),$(eval $(var) := ))
local_src_dir := project1/src
local_obj_dir := project1/obj
local_src := $(local_src_dir)/fileA.c $(local_src_dir)/fileB.c
local_obj := $(patsubst $(local_src_dir)/%.c,$(local_obj_dir)/%.o,$(local_src))

dep_all += $(local_src)
dep_all += $(local_obj)

$(info local_obj="$(local_obj)")
$(local_obj): $(local_obj_dir)/%.o: $(local_src_dir)/%.c
    gcc -L$(local_obj_dir) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project1


# Begin Makefile.inc for project2
# ------------------------------------------------------------------------------
$(foreach var,$(filter local_%,$(.VARIABLES)),$(eval $(var) := ))
local_src_dir := project2/src
local_obj_dir := project2/obj
local_src := $(local_src_dir)/fileX.c $(local_src_dir)/fileY.c
local_obj := $(patsubst $(local_src_dir)/%.c,$(local_obj_dir)/%.o,$(local_src))

dep_all += $(local_src)
dep_all += $(local_obj)

$(info local_obj="$(local_obj)")
$(local_obj): $(local_obj_dir)/%.o: $(local_src_dir)/%.c
    gcc -L$(local_obj_dir) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project2

$(info dep_all="$(dep_all)")

.PHONY: all
all: $(dep_all)

.PHONY: clean
clean:
    rm -f project1/obj/* project2/obj/*

Если я запустил его, тоопция -L<object_path>, переданная в gcc, содержит значение из последнего включенного Makefile, т.е. при сборке project1 он запускает gcc с -Lproject2/obj, что не является правильным путем к объекту для этого проекта.Это проблема, которую я пытаюсь решить.

mkdir -p project1/{src,obj} project2/{src,obj}
touch project1/src/{fileA.c,fileB.c} project2/src/{fileX.c,fileY.c}

$ make
local_obj="project1/obj/fileA.o project1/obj/fileB.o"
local_obj="project2/obj/fileX.o project2/obj/fileY.o"
dep_all=" project1/src/fileA.c project1/src/fileB.c project1/obj/fileA.o project1/obj/fileB.o project2/src/fileX.c project2/src/fileY.c project2/obj/fileX.o project2/obj/fileY.o"
gcc -Lproject2/obj -c -o project1/obj/fileA.o project1/src/fileA.c
gcc -Lproject2/obj -c -o project1/obj/fileB.o project1/src/fileB.c
gcc -Lproject2/obj -c -o project2/obj/fileX.o project2/src/fileX.c
gcc -Lproject2/obj -c -o project2/obj/fileY.o project2/src/fileY.c

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Думаю, я только что нашел решение.Я читал «Книгу GNU Make Book», в которой говорится о побочном эффекте переменных, специфичных для цели

Переменные, специфичные для цели, применяются не только к цели, но и ко всем предпосылкам этой цели, а такжевсе их предпосылки и тд.Область действия целевой переменной - это целое дерево целей, начиная с цели, для которой была определена переменная.

Это не то поведение, которое мне нужно, однако начиная с GNU Make 3.82 есть поддержкадля частных целевых переменных

Целевая переменная обычно определяется для цели и всех ее предпосылок.Но если переменная, относящаяся к цели, имеет префикс с ключевым словом private, она определяется только для этой цели, а не для ее предварительных условий.

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

dep_all :=
all:

# Begin Makefile.inc for project1
# ------------------------------------------------------------------------------
inc_dir := project1/inc
src_dir := project1/src
obj_dir := project1/obj
src := $(src_dir)/fileA.c $(src_dir)/fileB.c
obj := $(patsubst $(src_dir)/%.c,$(obj_dir)/%.o,$(src))

# These flags will be overwritten by another Makefile
CFLAGS      := -I$(inc_dir)

# These flags will be private and not be overwritten by another Makefile
CFLAGS_priv := -I$(inc_dir) -L$(obj_dir)

dep_all += $(src)
dep_all += $(obj)

# Private target specific variables
$(obj): private CFLAGS_priv:=$(CFLAGS_priv)

$(obj): $(obj_dir)/%.o: $(src_dir)/%.c
    gcc $(CFLAGS) $(CFLAGS_priv) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project1


# Begin Makefile.inc for project2
# ------------------------------------------------------------------------------
inc_dir := project2/inc
src_dir := project2/src
obj_dir := project2/obj
src := $(src_dir)/fileX.c $(src_dir)/fileY.c
obj := $(patsubst $(src_dir)/%.c,$(obj_dir)/%.o,$(src))

# These flags will be overwritten by another Makefile
CFLAGS      := -I$(inc_dir)

# These flags will be private and not be overwritten by another Makefile
CFLAGS_priv := -I$(inc_dir) -L$(obj_dir)

dep_all += $(src)
dep_all += $(obj)

# Private target specific variables
$(obj): private CFLAGS_priv:=$(CFLAGS_priv)

$(obj): $(obj_dir)/%.o: $(src_dir)/%.c
    gcc $(CFLAGS) $(CFLAGS_priv) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project2


.PHONY: all
all: $(dep_all)

.PHONY: clean
clean:
    rm -f project1/obj/* project2/obj/*

Вывод - это то, что я хотел, так как теперь для каждого проекта я могу определить личную переменную CFLAGS_priv, которая устанавливает пути -I<dir>, -L<dir>, и она не будет перезаписываться другими файлами Makefile.

$ make
gcc -Iproject2/inc -Iproject1/inc -Lproject1/obj -c -o project1/obj/fileA.o project1/src/fileA.c
gcc -Iproject2/inc -Iproject1/inc -Lproject1/obj -c -o project1/obj/fileB.o project1/src/fileB.c
gcc -Iproject2/inc -Iproject2/inc -Lproject2/obj -c -o project2/obj/fileX.o project2/src/fileX.c
gcc -Iproject2/inc -Iproject2/inc -Lproject2/obj -c -o project2/obj/fileY.o project2/src/fileY.c

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

0 голосов
/ 14 февраля 2019

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

Затем в начале каждого субмаке-файла те переменные, которые имеют префикс this_, могут быть очищены следующим образом:

$(foreach var,$(filter this_%,$(.VARIABLES)),$(eval $(var) := ))

PS.Я использую этот подход в моей реализации нерекурсивных makefile s, очищая описанные здесь переменные: https://github.com/igagis/prorab/blob/master/wiki/TutorialBasicConcepts.md#defining-several-builds-in-one-makefile

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