Перестроение триггера Makefile для директив препроцессора C/C++

Предположим, у меня есть исходный файл 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 сначала.

Я бы предложил использовать cmake. Он перестраивает цель, если вы запускаете cmake с измененными флагами.

273K 11.12.2020 15:58

«Память» о том, использовала ли последняя сборка флаг, должна где-то храниться. Make может записать файл для записи этой информации. Такой подход приемлем?

Beta 11.12.2020 16:19

@Beta было бы интересно посмотреть, как это работает, спасибо

ogb119 11.12.2020 16:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
534
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете создать зависимость от Makefile для своего кода. Это вызовет компиляцию при изменении Makefile.

Или используйте cmake, тогда ваша IDE сделает/должна сделать это за вас...

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

ogb119 11.12.2020 16:14

Хммм, тогда вы можете создать специальную цель в вашем Makefile, которая использует «касание» для запуска определенных действий.

U. W. 15.12.2020 09:24
Ответ принят как подходящий

Прежде всего 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 12.12.2020 00:03

@ogb119: Вместо «очень полезно» мы обычно голосуем здесь...

Klaus 12.12.2020 09:41

@ogb119 добавьте ответ на свой вопрос

Klaus 12.12.2020 09:45

@ ogb119 А теперь также обновите вариант использования «с изменением или включением флага, указанного в командной строке»

Klaus 12.12.2020 09:50

Обновленный ответ - это именно то, что я искал, спасибо

ogb119 12.12.2020 11:13

аналогичным образом можно автоматически перестроить файл на основе любых зависимостей или файлов заголовков. make этого тоже не делает, но в случае компилятора, связанного с GNU, он может вывести файл "зависимостей", который будет включен в makefile, который, по сути, является целевым кодом для файла .ccp, в котором упоминаются все заголовки. Но временные метки — это не единственное, что можно использовать.

Swift - Friday Pie 12.12.2020 12:27

Другие вопросы по теме