Предположим, у меня есть исходный файл C++, который выглядит так:
int main() {
...
#ifdef flag
// do something
#endif
...
#ifndef flag
// do something different
#endif
...
}
Я знаю, что для gcc я могу использовать -D'flag'
в качестве директивы для препроцессора, чтобы определить flag
и, таким образом, определить, какая часть приведенного выше кода действительно компилируется, и что это можно использовать в Makefile.
Но как мне написать Makefile, чтобы исходный файл перестраивался в зависимости от того, хочу ли я определить flag
или нет (без каких-либо других изменений в каком-либо файле, включая сам Makefile)?
Я знаю, что могу, например (и решение не обязательно должно следовать этому, если есть лучший способ!), определить переменную, когда я запускаю make, например. с make flag=1 all
, но это не вызовет перестроение, если файл, содержащий main
(в соответствии с приведенным выше примером исходного кода), не был изменен с тех пор.
то есть, начиная с чистого каталога и запуская make all
, будет запущена сборка, но затем, если я сразу запущу make flag=1 all
, я бы хотел, чтобы файл, содержащий main
(скажем, main.cpp
), был правильно перестроен без необходимости touch main.cpp
сначала.
«Память» о том, использовала ли последняя сборка флаг, должна где-то храниться. Make может записать файл для записи этой информации. Такой подход приемлем?
@Beta было бы интересно посмотреть, как это работает, спасибо
Вы можете создать зависимость от Makefile для своего кода. Это вызовет компиляцию при изменении Makefile.
Или используйте cmake, тогда ваша IDE сделает/должна сделать это за вас...
Я не буду вносить изменения в Makefile. Я бы хотел, чтобы перестроение запускалось на основе аргументов, которые я передаю make
в командной строке. Я отредактировал свой вопрос, чтобы уточнить это. Спасибо за совет по cmake, сейчас я только изучаю make с намерением изучить cmake дальше!
Хммм, тогда вы можете создать специальную цель в вашем Makefile, которая использует «касание» для запуска определенных действий.
Прежде всего make
должен знать, что флаг изменился. Это означает, что вы должны хранить флаг «где-то», который обычно является каким-то файлом. Далее вы должны сказать make
, какой файл зависит от вашего флага. Нет необходимости компилировать все и вся, кроме зависимых файлов. Это означает, что вы должны указать, какой источник зависит от того, какой флаг или, в данном случае, от того, в каком файле хранится этот флаг.
Хорошо, все вместе ваш Makefile может быть примерно таким:
# define a first rule, that we want, if no target is given, we want to
# compile our program named "go"
all: go
# read the file, where our last given flag was stored.
# the minus in front tells make, that if the file can't be read, simple ignore it!
-include stored_flag.mk
# after that, we compare the old flag and the new one which was given on command
# line or from environment. If it differs or was not present before,
# write the new flag to the file to make it reusable for the next call
ifneq ($(OLDFLAG),$(FLAG))
$(file > stored_flag.mk, OLDFLAG:=$(FLAG))
endif
# now we forward the environment variable to the compiler
# if it is present
ifneq ($(FLAG),)
CXXFLAGS=-DFLAG=$(FLAG)
endif
# as said: we need to make the source files dependent on the flag file,
# if the flag is used in that source. If ot, no need to comoile even if flag has
# changed
main.o: stored_flag.mk
# standard rule:
main.o: main.cpp
# build our program "go"
go: main.o
g++ $< -o go
Пример источника (main.cpp):
#include <iostream>
int main()
{
#ifdef FLAG
std::cout << "Flag is " << FLAG << std::endl;
#else
std::cout << "No flag" << std::endl;
#endif
}
Выполнить с помощью:
make FLAG=1
make FLAG=1
make FLAG=2
...
Удачного тестирования!
Если у вас есть несколько флагов и вы хотите определить зависимости, независимые для каждого флага, вы можете использовать несколько файлов для хранения флагов.
Добавить:
В: «Майк знает, что это единственный способ инициировать перестройку, сравнивая временные метки зависимостей с целью?»
A: Это основная идея make! Сравните временные метки в файлах, чтобы определить необходимость выполнения каких-либо действий. НО: как вы можете видеть, make может работать с «ifdef», и поэтому вы можете определить правила внутри такого блока, которые делают компиляцию зависимой от некоторых условий без сравнения временных параметров или определения зависимостей.
Это очень полезно, спасибо. Я полагаю, что более фундаментальный вопрос был бы таким: «Единственный способ, которым Make знает, чтобы запустить перестроение, сравнивая временные метки зависимостей с целью?» Похоже, ответ «да», и в этом случае я теперь понимаю, почему вы должны написать предыдущий флаг, используемый для файла, и включить этот файл в качестве зависимости! Однако этот ответ не полностью решает мою проблему; Я должен использовать #if FLAG==1
и #if FLAG==0
в исходном коде вместо #ifdef FLAG
и #ifndef FLAG
, и поэтому я не могу просто запустить make
сам по себе (т.е. оставить FLAG
неопределенным)
@ogb119: Вместо «очень полезно» мы обычно голосуем здесь...
@ogb119 добавьте ответ на свой вопрос
@ ogb119 А теперь также обновите вариант использования «с изменением или включением флага, указанного в командной строке»
Обновленный ответ - это именно то, что я искал, спасибо
аналогичным образом можно автоматически перестроить файл на основе любых зависимостей или файлов заголовков. make этого тоже не делает, но в случае компилятора, связанного с GNU, он может вывести файл "зависимостей", который будет включен в makefile, который, по сути, является целевым кодом для файла .ccp, в котором упоминаются все заголовки. Но временные метки — это не единственное, что можно использовать.
Я бы предложил использовать cmake. Он перестраивает цель, если вы запускаете cmake с измененными флагами.