make перекомпилирует неизмененные файлы

make перекомпилирует файлы исходного кода, даже если они не изменились. Чтобы воспроизвести это поведение, мне нужно сделать:

  1. сделать чистым, чтобы остались только файлы *.90
  2. делать
  3. сохраните один из файлов *.f90, например. "сенсорные полосы.f90"
  4. make: bands.f90 перекомпилирован, что правильно, так как он был изменен
  5. make: bands.f90 перекомпилирован заново, что не правильно, так как bands.f90 не трогали

В этом примере после 4. файл bands.f90 будет перекомпилироваться при каждом вызове make. Если я дополнительно изменю какой-либо другой файл исходного кода, поведение также будет применено к этому файлу. Поэтому через некоторое время я перекомпилирую весь свой исходный код. Я могу частично исправить это, вызвав make clean, который сбрасывает цикл. Но это не постоянное исправление.

Ниже вы видите результат для пяти шагов, описанных выше:

1: find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +

2: [triggered by changes in const.f90]
   mpifort -c const.f90

   [triggered by changes in system.f90 const.mod]
   mpifort -c system.f90

   [triggered by changes in io.f90 system.mod const.mod]
   mpifort -c io.f90

   [triggered by changes in parallel.f90]
   mpifort -c parallel.f90

   [triggered by changes in bands.f90 system.mod io.mod const.mod parallel.mod]
   mpifort -c bands.f90

   [triggered by changes in polmob.f90 io.mod system.mod bands.mod parallel.mod]
   mpifort -o polmob polmob.f90 io.f90 system.f90 bands.f90 parallel.f90

3: "no output"

4: [triggered by changes in bands.f90]
   mpifort -c bands.f90

5: [triggered by changes in bands.f90]
   mpifort -c bands.f90

На шаге 5 make должен сказать, что компилировать нечего. Но там написано, что в bands.f90 были изменения и поэтому его нужно перекомпилировать.

Вот мой Makefile:

polmob: polmob.f90 io.mod system.mod bands.mod parallel.mod
    @echo [triggered by changes in $?]
    mpifort -o polmob polmob.f90 io.f90 system.f90 bands.f90 parallel.f90

io.mod: io.f90 system.mod const.mod
    @echo [triggered by changes in $?]
    mpifort -c io.f90

system.mod: system.f90 const.mod
    @echo [triggered by changes in $?]
    mpifort -c system.f90

bands.mod: bands.f90 system.mod io.mod const.mod parallel.mod
    @echo [triggered by changes in $?]
    mpifort -c bands.f90

const.mod: const.f90
    @echo [triggered by changes in $?]
    mpifort -c const.f90

parallel.mod: parallel.f90
    @echo [triggered by changes in $?]
    mpifort -c parallel.f90

clean:
    find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +

Флаг $? говорит мне, что перекомпиляция запускается изменениями в конкретном файле .f90. Так что как-то относитесь к старым изменениям. Кто-нибудь знает, что может быть причиной такого поведения?


person CiPoint    schedule 13.05.2019    source источник
comment
Как я повторял много раз, *make * не является адекватным инструментом для современного Фортрана, и PITA поддерживает его в актуальном состоянии при разработке сложного кода. Есть так много современных альтернатив... cmake, scons, fobis, waf,... Я лично использую scons, и мне вообще не нужно заботиться о зависимостях и ненужной перекомпиляции.   -  person Vladimir F    schedule 13.05.2019


Ответы (2)


Кто-нибудь знает, что может быть причиной такого поведения?

Я нашел причину: если я заменю "mod" на "o" везде в моем Makefile, проблема исчезнет. Однако я не совсем понимаю, почему.

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

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

В противном случае ваш компилятор делает наверняка обещает, что если вы попросите его скомпилировать исходный файл в объектный файл, то в случае успеха он напишет новый объектный файл. Более того, хотя я не обязательно догадался бы, что то же самое будет неверно для файлов .mod, важно понимать, что последние обычно описывают интерфейсы к вашим модулям, а не их реализации< /strong>, поэтому ваше основное правило семантически неверно:

polmob: polmob.f90 io.mod system.mod bands.mod parallel.mod
    @echo [triggered by changes in $?]
    mpifort -o polmob polmob.f90 io.f90 system.f90 bands.f90 parallel.f90

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

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

SOURCES = polmob.f90 io.f90 system.f90 bands.f90 parallel.f90

polmob: $(SOURCES)
    @echo [triggered by changes in $?]
    mpifort -o $@ $(SOURCES)

clean:
    find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +

Там цель фактически создается из указанных предварительных условий, и рецепта сборки достаточно для создания цели из этих предварительных условий. Однако, если какой-либо из исходных кодов изменится, он перестроит их все (аналогично тому, что делает ваш исходный make-файл).

Альтернативой является создание отдельных объектов, а затем их связывание на отдельном шаге. Ваш исходный файл makefile, кажется, склоняется в этом направлении, но затем он отбрасывает его с помощью основного правила. Если вы хотите использовать такой подход, то он усложняется тем фактом, что

  • на самом деле это модуль interfaces, от которого зависят отдельные объекты (в дополнение к их собственным источникам), поэтому выражение предварительных условий в терминах других объектных файлов в этих случаях некорректно;
  • оба файла .o и .mod создаются одним и тем же процессом (т.е. он имеет несколько выходных данных);
  • и, по-видимому, этот процесс использует другую логику, чем make, чтобы определить, устарели ли файлы .mod.

Проблема с несколькими выходами, вероятно, самая сложная; вы можете найти обсуждение и альтернативные решения различной строгости в Automake документации (но не для Automake).

Вот способ, которым вы могли бы подойти к этому, вдохновленный документами Automake, но с учетом особенности, которую вы обнаружили в своей реализации Fortran:

# All the object files contributing to the program:
OBJECTS = bands.o const.o io.o parallel.o polmob.o system.o

# Link all the objects together to form the final program
# (.mod files are typically not needed for this step)
polmob: $(OBJECTS)
    mpifort -o $@ $(OBJECTS)

# Rules for building the objects

bands.o : bands.f90 const.mod io.mod parallel.mod system.mod
    mpifort -c bands.f90 -o $@

const.o : const.f90
    mpifort -c const.f90 -o $@

io.o : io.f90 const.mod system.mod
    mpifort -c io.f90 -o $@

parallel.o : parallel.f90
    mpifort -c parallel.f90 -o $@

polmob.o : polmob.f90 bands.mod const.mod io.mod parallel.mod system.mod
    mpifort -c polmob.f90 -o $@

system.o : system.f90 const.mod
    mpifort -c system.f90 -o $@

# Rules for ensuring that .mod files are (re)created when needed, and
# that their timestamps do not fall behind those of their corresponding
# sources

bands.mod : bands.o
    @if test -f $@; then touch $@; else \
      rm -f bands.o; \
      $(MAKE) bands.o; \
    fi

const.mod : const.o
    @if test -f $@; then touch $@; else \
      rm -f const.o; \
      $(MAKE) const.o; \
    fi

io.mod : io.o
    @if test -f $@; then touch $@; else \
      rm -f io.o; \
      $(MAKE) io.o; \
    fi

parallel.mod : parallel.o
    @if test -f $@; then touch $@; else \
      rm -f parallel.o; \
      $(MAKE) parallel.o; \
    fi

system.mod : system.o
    @if test -f $@; then touch $@; else \
      rm -f system.o; \
      $(MAKE) system.o; \
    fi

####

clean:
    find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +

Это показывает правильные зависимости:

  • Основная программа зависит (только) от всех объектов.
  • Каждый объект зависит от своего собственного источника и от файлов .mod для тех модулей, которые он использует.

Он также учитывает тот факт, что один и тот же процесс компиляции создает как объектный файл, так и (где это уместно) соответствующий файл .mod, и заботится об обновлении временных меток .mod там, где это необходимо для удовлетворения make. Иногда это может привести к пересборке файлов, когда в этом нет необходимости, потому что это сорвет любую попытку компилятора избежать обновления временных меток .mod файлов. Но это должно быть единовременно, а не при каждой последующей сборке.

person John Bollinger    schedule 13.05.2019

Я нашел причину: если я заменю "mod" на "o" везде в моем Makefile, проблема исчезнет. Однако я не совсем понимаю, почему.

person CiPoint    schedule 13.05.2019