Лучшая практика для зависимостей от #defines? - PullRequest
7 голосов
/ 28 июля 2011

Есть ли лучшая практика для поддержки зависимостей от флагов препроцессора C / C ++, таких как -DCOMPILE_WITHOUT_FOO? Вот моя проблема:

> setenv COMPILE_WITHOUT_FOO
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO>
  <Compiles nothing, since no source file has changed>

Я хотел бы, чтобы все файлы, которые основаны на операторах #ifdef, перекомпилировались:

> setenv COMPILE_WITHOUT_FOO
> make
  g++ FileWithIfdefFoo.cpp

Чего я не хочу, так это перекомпилировать все, если значение COMPILE_WITHOUT_FOO не изменилось.

У меня работает примитивный скрипт Python (см. Ниже), который в основном записывает заголовочный файл FooDefines.h и затем проверяет его, чтобы увидеть, если что-то отличается. Если это так, он заменяет FooDefines.h, а затем вступает в действие обычная зависимость от исходного файла. Определением является , а не , передаваемое в командной строке с -D. Недостатком является то, что теперь я должен включать FooDefines.h в любой исходный файл, который использует #ifdef, а также у меня есть новый, динамически генерируемый заголовочный файл для каждого #ifdef. Если есть инструмент для этого или способ избежать использования препроцессора, я весь в ушах.

import os, sys
def makeDefineFile(filename, text):
    tmpDefineFile = "/tmp/%s%s"%(os.getenv("USER"),filename) #Use os.tempnam?
    existingDefineFile = filename

    output = open(tmpDefineFile,'w')
    output.write(text)
    output.close()

    status = os.system("diff -q %s %s"%(tmpDefineFile, existingDefineFile))

    def checkStatus(status):
        failed = False
        if os.WIFEXITED(status):
            #Check return code
            returnCode = os.WEXITSTATUS(status)
            failed = returnCode != 0
        else:
            #Caught a signal, coredump, etc.
            failed = True
        return failed,status

    #If we failed for any reason (file didn't exist, different, etc.)
    if checkStatus(status)[0]:
        #Copy our tmp into the new file
        status = os.system("cp %s %s"%(tmpDefineFile, existingDefineFile))
        failed,status  = checkStatus(status)
        print failed, status
        if failed:
            print "ERROR: Could not update define in makeDefine.py"
            sys.exit(status)

Ответы [ 6 ]

3 голосов
/ 28 июля 2011

Это, конечно, не самый хороший подход, но он будет работать:

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch

Это просмотрит ваш исходный код для макроса COMPILE_WITHOUT_FOO и «дотронется» до каждого файла, что обновит отметку времени. Затем, когда вы запустите make, эти файлы будут перекомпилированы.

Если у вас установлено ack, вы можете упростить эту команду:

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch
2 голосов
/ 28 июля 2011

Я не верю, что это можно определить автоматически. Директивы препроцессора не скомпилированы ни во что. Вообще говоря, я рассчитываю на полную перекомпиляцию, если я зависим от определения. Отладка - знакомый пример.

Я не думаю, что есть правильный способ сделать это. Если вы не можете сделать это правильно, тогда, возможно, самый тупой путь - ваш лучший вариант. Текстовый поиск COMPILE_WITH_FOO и создание зависимостей таким образом. Я бы классифицировал это как шенаниган, и если вы пишете общий код, я бы порекомендовал искать довольно значительный бай-ин у ваших коллег.

CMake имеет некоторые возможности, которые могут сделать это проще. Вы должны создать собственную цель для этого. Здесь вы можете торговать проблемами, сохраняя список файлов, которые зависят от вашего символа. Ваш текстовый поиск может создать этот файл, если он изменился. Я использовал аналогичные методы, проверяя, нужно ли мне перестраивать статические хранилища данных на основе временных меток wget.

Гепард - еще один полезный инструмент.

Если бы это был я, думаю, я бы сделал полную перестройку.

1 голос
/ 29 июля 2011

Один из способов сделать это - сохранить предыдущее значение каждого # define в файле и использовать условные выражения в вашем make-файле, чтобы принудительно обновить этот файл всякий раз, когда текущее значение не соответствует предыдущему. Любые файлы, которые зависят от этого макроса, будут включать этот файл в качестве зависимости.

Вот пример. Он обновит file.o, если либо file.c изменилось, либо переменная COMPILE_WITHOUT_FOO отличается от прошлого раза. Он использует $(shell ) для сравнения текущего значения со значением, сохраненным в файле envvars/COMPILE_WITHOUT_FOO. Если они отличаются, то для этого файла создается команда, которая зависит от force, которая всегда обновляется.

file.o: file.c envvars/COMPILE_WITHOUT_FOO
    gcc -DCOMPILE_WITHOUT_FOO=$(COMPILE_WITHOUT_FOO) $< -o $@

ifneq ($(strip $(shell cat envvars/COMPILE_WITHOUT_FOO 2> /dev/null)), $(strip $(COMPILE_WITHOUT_FOO)))
force: ;
envvars/COMPILE_WITHOUT_FOO: force
    echo "$(COMPILE_WITHOUT_FOO)" > envvars/COMPILE_WITHOUT_FOO
endif

Если вы хотите, чтобы макросы не были определены, вам нужно будет использовать условные выражения ifdef или ifndef и указать в файле, что значение было неопределенным при последнем запуске.

1 голос
/ 28 июля 2011

Ваша проблема кажется специально разработанной, чтобы обработать ее с помощью autoconf и autoheader, записав значения переменных в файл config.h.Если это невозможно, попробуйте прочитать директивы "-D" из файла и записать флаги в этот файл.

При любых обстоятельствах вам следует избегать сборок, которые зависят только от переменных среды.Вы не можете сказать, когда изменилась среда.Существует определенная необходимость хранить переменные в файле, самым чистым способом будет использование autoconf, autoheader и исходного кода и нескольких деревьев сборки;второй наиболее чистый способ - повторное указание 1005 * для каждого переключателя контекста компиляции;и третий самый чистый способ - файл, содержащий все изменяемые переключатели компилятора, от которых зависят все объекты, зависящие от этих переключателей.

Когда вы решите реализовать третий способ, не забудьте не обновлять этот файл без необходимости, например, путем созданияво временную папку и условно скопируйте его в diff, а затем создайте правила, которые смогут условно перестроить ваши файлы в зависимости от флагов.

0 голосов
/ 28 июля 2011

Джей отметил, что «создает триггеры для отметок даты и времени в файлах».

Теоретически, вы можете иметь свой основной make-файл, называемый его m1, включающий переменные из второго make-файла с именем m2.m2 будет содержать список всех флагов препроцессора.

У вас может быть правило make для вашей программы, которое зависит от актуальности m2.

правило для m2 будет импортировать все переменные среды (и, следовательно, #includeдирективы).

хитрость в том, что правило для создания m2 будет определять наличие различий из предыдущей версии.Если это так, это позволит включить переменную, которая заставит «сделать все» и / или очистить для основной цели.в противном случае он просто обновит метку времени на m2 и не вызовет полный римейк.

наконец, правило для обычной цели (make all) будет исходить из директив препроцессора из m2 и применять их по мере необходимости.

в теории это звучит просто / возможно, но на практике GNU Make намного сложнее заставить работать этот тип вещей.Я уверен, что это может быть сделано, хотя.

0 голосов
/ 28 июля 2011

делает триггеры на отметках даты и времени в файлах. Зависимый файл, который новее, чем тот, который от него зависит, вызывает его перекомпиляцию. Вы должны будете поместить свое определение для каждой опции в отдельный файл .h и убедиться, что эти зависимости представлены в make-файле. Затем, если вы измените параметр, зависимые от него файлы будут автоматически перекомпилированы.

Если он учитывает включаемые файлы, включающие файлы, вам не придется изменять структуру источника. Вы можете включить файл «BuildSettings.h», который включает все отдельные файлы настроек.

Единственная сложная проблема будет, если вы сделаете его достаточно умным для анализа включающих охранников . Я видел проблемы с компиляцией из-за коллизий имен включаемых файлов и порядка поиска по каталогам включений.

Теперь, когда вы упомянули об этом, я должен проверить, достаточно ли у меня интегрированная среда разработки, чтобы автоматически создавать эти зависимости для меня. Звучит как отличная вещь для добавления в IDE.

...