make перекомпилирует неизмененные файлы - PullRequest
1 голос
/ 13 мая 2019

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

  1. make clean, чтобы остались только * .90 файлы
  2. make
  3. сохранить одну из *.Файл f90, например, "touch bands.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.Так что как-то сделать относится к старым изменениям.Кто-нибудь знает, что причина такого поведения может быть?

Ответы [ 2 ]

1 голос
/ 13 мая 2019

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

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

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

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

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

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-файл в следующий момент:

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-файл).

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

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

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

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

# 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 файлов. Но это должно быть на разовой основе, а не на каждой последующей сборке.

0 голосов
/ 13 мая 2019

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

...