Makefiles: Как они определяют цели сборки? - PullRequest
0 голосов
/ 08 февраля 2019

Я сталкиваюсь с интересным поведением в унаследованном make-файле, который пытаюсь развернуть для моего текущего проекта.

LIBS := lib/precompiled1.a lib/precompiled2.a lib/mylib1.a lib/mylib2.a

mylib1-src := (source directory)
mylib1-obj := $(patsubst $(mylib1-src)/%.c, obj/LIB1/%.o, $(wildcard $(mylib1-src)/*.c))

mylib2-src := (source directory)
mylib2-obj := $(patsubst $(mylib2-src)/%.cpp, obj/LIB2/%.o, $(wildcard $(mylib2-src)/*.cpp))

default: all

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

all: exe/my_out

exe/my_out: $(my_out-obj)
    g++ $^ $(COMMON_FLAGS) $(LIBS) $(MY_FLAGS)

Когда я запускаю make, я сталкиваюсь с ошибкой, которая не можетссылка на lib / mylib1.a, и она ломается.Круто, мне просто нужно убедиться, что цель .a строится.Наивно возиться с make-файлом примерно так:

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

default: all

Теперь он прекрасно компилирует mylib1.a, но не компилирует ни mylib2.a, ни 'all'.

Вот где я застрял,Если я сделаю следующее:

default: all

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

all: lib/mylib1.a exe/my_out

, то компилятор пожалуется на то, что lib / mylib1.a не является исполняемым файлом.Что это не так, здорово, но оставляет меня в странной ситуации.Я могу скомпилировать .a файлы по отдельности.Я могу связать их с исполняемым файлом.Я не могу сделать все эти вещи одновременно, только возиться с make-файлом, что несколько противоречит сути.

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

Спасибо!

Ответы [ 3 ]

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

Поскольку Энди предлагает принудительно установить правило по умолчанию all:, выполнив это (обычно в верхней части файла makefile):

.DEFAULT_GOAL = all

Затем определите все как:

LIB_DEPS = lib/mylib1.a lib/mylib2.a
all: exe/my_out $(LIB_DEPS)

# Also to ensure that the libs are built first (for example with parallel builds):
exe/my_out: $(my_out-obj) $(LIB_DEPS)
    ... recipe here...

Таким образом, если вы делаете или делаете -j, ваш порядок сборки гарантирован.

Примечание Я не видел правила для $(my_out-obj) - вы пропустилиэто или просто не напечатано?

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

Порядок целей в make-файле не имеет значения, за одним исключением: первая - это цель по умолчанию, то есть та, над которой работали, когда вы вызываете make без цели.Это поведение можно изменить в GNU make с помощью .DEFAULT_GOAL := ...

Порядок зависимостей

Что имеет значение в последовательной сборке (make -j1), так это порядок зависимостей в определении цели, например

T: A B C

или

T: A B
T: C
    rule

В последовательной сборке make сначала выполнит «A», затем «B», затем «C».Если «C» имеет отсутствующую зависимость, которая выполняется во время построения «A», то сборка будет успешной.Измените make-файл на

T: C A B

... и последовательная сборка начнет давать сбой.

В параллельной сборке (make -jN) порядок зависимости не имеет значения, потому что make попытается построить "A", "B" и "C" параллельно, если это разрешено.

Неполные зависимости

Если обязательная зависимость "D" неЕсли указан список для цели "T", то make позволит построить цель "T" после выполнения перечисленных зависимостей.Но он не будет ждать завершения "D".Поэтому успех сборки зависит от неопределенного поведения: либо

  1. "D" уже существовал до начала сборки и не затрагивался текущей сборкой, либо
  2. target "D"выполняется до запуска цели "T".

Наихудший сценарий для параллельной сборки: если правило для "T" не дает сбой при чтении "D" и записи "D" вв то же время вы получите успешную сборку, но недействительные артефакты сборки.

Обычные индикаторы для неполных зависимостей:

  • сборка начинает (произвольно) давать сбой при переключении сПоследовательное в параллельное,
  • сборка начинает случайным образом завершаться неудачей при переходе на другую сборочную машину, или
  • сборка артефактов из успешной сборки со странными ошибками теста.

Решение

Всегда указывайте полные зависимости для всех целей.В вашем случае это будет как минимум:

# Add internal dependencies included in $(LIBS)
exe/my_out: $(my_out-obj) $(filter lib/%.a, $(LIBS))
    g++ $^ $(COMMON_FLAGS) $(LIBS) $(MY_FLAGS)
0 голосов
/ 08 февраля 2019

Целью по умолчанию является первая цель в Makefile, цель с именем 'default' не имеет особого значения.

Для последних версий Make цель по умолчанию может быть указана явно следующим образом:

DEFAULT_GOAL: = все

Библиотеки должны быть добавлены в качестве предварительных условий для исполняемого файла, а не ко всей цели, например:

exe/my_out: $(my_out-obj) lib/mylib1.a lib/mylib2.a
    g++ $^ $(COMMON_FLAGS) $(LIBS) $(MY_FLAGS)

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^
...