Компиляция программного проекта на Fortran

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

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

Напомним, что файлы в фортрановском проекте зависят друг от друга через модули. Если в одном файле есть module A, а в другом — use A, то первый файл должен быть скомпилирован раньше. При этом подобная информация нигде не прописывается и генерируется на лету. Интеграция компилятора Intel Fortran с Visual Studio в большинстве случаев правильно определяет последовательность компиляции, однако и она может ошибиться, что уж говорить о специальных утилитах, нацеленных на создание make-файлов.

Ниже приводится метод определения зависимостей в файлах проекта и описание процесса автоматической сборки проекта без использования специальных программ.

Поставим задачу в таком виде: автоматически собрать проект, имея на входе только исходные файлы проекта, проект vfproj и в общем случае файл sln.

Вероятно, некоторые существующие методы построения exe-файла требуют вызова специальной промежуточной утилиты, генерирующей по файлу проекта (vfproj) зависимости в виде make-файла.
Поиски такой утилиты к успеху не привели. Да и бывает, что после успешной компиляции и компоновки в студии возможна повторная компиляция некоторых файлов из-за неверно определённых зависимостей, неверной трактовки условной компиляции (!DEC$) и т.п. Таким образом, внешние утилиты не могут считаться надёжным способом определения порядка компиляции.

Задача делится на две:
  1. файл проекта (который хранится в формате XML) переписать в виде списка файлов с параметрами компиляции.
  2. скомпилировать в нужном порядке.

Первая задача решается несложно. Создаётся соответствие атрибутов в формате XML параметрам компилятора. Пример: вместо WarnUnusedVariables="true" подставляем /warn:unused. К слову, такой подход настраивает на критическую оценку настроек уже имеющегося проекта, можно выкинуть лишние настройки, понять, поменять назначение уже имеющихся.

Для решения второй задачи был избран следующий подход: ловить зависимости на основе поведения самого компилятора. предположим, что программа правильно написана с точки зрения компилятора:
каждый программист регулярно проводит компиляцию всего проекта для проверки реализованной функциональности. Тогда компиляция файла может завершиться либо успешно, либо в случае отсутствия модуля первой ошибкой будет Module not found. Запоминаем имя отсутствующего модуля и переходим к следующему файлу в списке.

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

Далее, когда компилятор ошибок не выдаёт, мы смотрим, какие модули создались. Если находим, какой-то из предыдущих файлов не был скомпилирован из-за отсутствия такого модуля,
можем его скомпилировать повторно.

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

Отметим, что здесь речь не идёт об ускорении сборки проекта и make-файлы в обычном понимании этого слова не создаются.

Особенности реализации

Возможно использование языка VBScript, поскольку под Windows никакой компилятор для него устанавливать не требуется. Автор использовал именно VBScript во многом по этой причине:
настроить компиляцию можно на любом компьютере с ОС Windows, которых в офисе абсолютное большинство. В VBScript есть встроенное чтение и поддержка файлов XML, что актуально для проектов, хранимых в таком формате. Переписывать скрипт для ОС Linux до сих пор не требовалось.

Особое внимание в VBScript требуется уделить перехвату буфера вывода, с которым, как известно, есть определённые проблемы. Для контроля и перехвата стандартного и ошибочного вывода требуется использовать вызов WSHShell.Exec. Для тех же целей требуется установить лимит на время выполнения одной компиляции. Связанная проблема — если программа сложная, компилятор может зависнуть, если скомпилировать произвольный файл из проекта, — просто из-за несовершенства компилятора. Первый запуск, таким образом, может оказаться довольно долгим.

Ещё некоторый недостаток — если VBScript работает не в режиме консольного окна,
то будут постоянно выскакивать чёрные консольные окна для каждого компилируемого файла.
А в режиме консольного окна всё время открыто одно окно скрипта. Поскольку весь процесс запускается ночью, либо на сервере сборки-тестирования, такое поведение приемлемо.

К слову

Скрипт можно использовать ещё для одной цели. Дело в том, что за день код может быть испорчен, так что компиляция не пройдёт и тесты запущены не будут, что будет очень печально, если тесты отрабатывают за время порядка нескольких часов. Так что даже в случае очень простой для исправления ошибки проверка и запуск днём приведёт к потере времени.

Легко и просто настроить проверку компилируемости скажем в 19—20—21 час вечера. Допустим, в команде есть человек, умеющий исправить случайно испорченный код. Тогда если проект не собрался, можно настроить отправку смс на телефон об этой проблеме, так что как минимум утром будет существовать собранный экзешник, с результатами тестирования.

Отправить смс на конкретный номер телефона с согласия абонента можно без привлечения специальных сервисов, бесплатно (см. посты 76867 и 81630).
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 2

    +5
    Не уверен что много народу на хабре использую fortran.
    К слову а где вообще сам скрипт или вы решили рассказать о нем но не показывать?
      0
      Часть существующих проектов написаны на фортране, такие проекты нужно поддерживать.
      Сам скрипт довольно большой. Искал куски, которые можно было бы вставить целиком, но не нашёл. Поэтому решил привести самую содержательную часть скрипта.

      moretimetothisfile = false
      nmodules=0
      do 'главный цикл
      
      if not moretimetothisfile then
      	curfile = 0
      else
      			Reason="Time limit temporarily disabled"
      end if
      if curfile = 0 then
      for i=1 to nmodules
      	for j=1 to nfiles
      		if needmodule(j)=modules(i) then
      			'must be compilationstatus(j)=CMPL_WAIT
      			curfile=j
      			Reason="Required module found - "&modules(i)
      			exit for
      		end if
      	next
      	if curfile > 0 then exit for
      next
      end if
      if curfile = 0 then
      	for j=1 to nfiles
      		if compilationstatus(j)=CMPL_FUTURE then
      			curfile=j
      			Reason="Next file in list"
      			exit for
      		end if
      	next
      end if
      if curfile = 0 then
      	for j=1 to nfiles
      		if compilationstatus(j)=CMPL_WAIT then
      			call MsgLog("err","Error!!! all files are in dependence")
      			Wscript.Quit 1
      		end if
      	next
      			call MsgLog("info","Bingo!!! all files are compiled")
      			exit do
      end if
      call MsgLog("info","File to compile: "&files(curfile)&" Reason: "&Reason)
      'запускаем компилятор
      Set oExec = WSHShell.Exec(files(curfile))
      'здесь ждём.
      'если слишком долго компилируется (больше ста секунд), выставляем флаг terminated
      'весь вывод сохраняется в allInput
      '
      if allInput = "" then
      	allInput="(empty)"
      end if
      allInputsplit=split(allInput,chr(10))
      	'analyzing dependence
      	modulerequired=false
      	moretimetothisfile = false
      	for j=0 to ubound(allInputsplit)
      		call MsgLog("info",allInputsplit(j))
      	next
      for j=0 to ubound(allInputsplit)
      
      	if terminated and instr(allInputsplit(j),"ifort: error #10273:") then
      		'terminated wrongly
      		moretimetothisfile = true
      		call MsgLog("info","Premature termination detected")
      	else
      'дальнейший анализ вывода.
      'определили, что нужен модуль modulename
      				needmodule(curfile)=modulename
      				compilationstatus(curfile)=CMPL_WAIT
      				call MsgLog("info","Dependence found: "&needmodule(curfile))
      				modulerequired=true
      				exit for
      	end if
      next 'j
      if not modulerequired and not terminated then
      	needmodule(curfile)=""
      	compilationstatus(curfile)=CMPL_DONE
      	call MsgLog("info","Successfully compiled")
      	forderout.writeline mid(files(curfile),instrrev(files(curfile)," ")+1) 'assuming no quotes
      end if
      'здесь определяем созданные модули на основе созданных файлов (массив modules)
      'newfound будет true, если создались новые файлы
      '
      if compilationstatus(curfile) = CMPL_DONE and not newfound then
      	call MsgLog("info","Nothing generated after successful compilation")
      	for j=0 to ubound(allInputsplit)
      		call MsgLog("info",allInputsplit(j))
      	next
      		Wscript.Quit 1
      end if
      loop 'конец главного цикла
      

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое