Файл: А. Б. Шнейвайс основы программирования.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.12.2023

Просмотров: 325

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

$ make RESULT
# то make не позволит зря make: ‘RESULT’ не требует обновления. # транжирить время.
7. Если же изменили, например, subfunt.f90 и, не пытаясь сначала до- стичь главную цель – получить исполнимый код, сразу потребовали провести расчёт:
$ touch subfunt.f90
# make получит новые версии
$ make result
# subfunt.o, исполнимого кода gfortran -c -Wall -O0 subfunt.f90
#
и результата gfortran funt.o probtask.o subfunt.o -o probtask
./probtask
Таким образом, последняя предложенная версия make-файла полно- стью решает возложенную на неё задачу:
1) не допускает необоснованной перекомпиляции исходных файлов, ко- торые уже отлажены, и при сборке исполнимого кода использует со- ответствующие им и хранимые в текущей директории объектники;
2) обеспечивает за счёт «комбинирования правил с одинаковой целью»
перекомпиляцию файла, содержащего include-вставку, если послед- няя была модифицирована;
3) позволяет практически неограниченно увеличивать количество ис- ходных файлов, если возникнет такая необходимость, не изменяя по существу дела размера make-файла, за счет применения шаб- лонов *.for, %.f90, %.o, встроенных make-функций wildcard и patsubst, а также шаблонного правила с зависимостью %.o : %.f90.
345

11.3
Make-файл для программы, использующей модуль
В современном ФОРТРАНе наряду с внешними процедурами (функци- ями и подпрограммами) используется ещё один вид программной еди- ницы, называемой модулем. Смысловая нагрузка последнего термина отлична от смысловой нагрузки термина модуль, который ранее исполь- зовался в словосочетаниях: исходный модуль, объектный модуль или за- грузочный модуль.
В новой трактовке термин модуль означает программную единицу,
обеспечивающую при её подключении к какой-нибудь процедуре доступ этой процедуры к описанным в модуле объектам (типам, переменным и процедурам). Модуль не вызывают (как, например, процедуру), но подключают, получая возможность пользоваться объектами, импорти- руемыми из module.
Программная единица типа модуль часто содержит, например, опи- сания процедур, нацеленных на решение определённого класса задач.
Так модуль численного интегрирования может содержать процедуры,
реализующие различные схемы расчёта интеграла (формулы трапеций,
прямоугольников, Симпсона, Гаусса и т.д.).

1   ...   15   16   17   18   19   20   21   22   23

Возникает вопрос: как следует отражать в make-файле правила, ка- сающиеся компиляции и подключения модуля?
В качестве примера используем соответствующую нашей задаче моди- фикацию ранее рассмотренной программы. Именно: поместим функцию funt и подпрограмму subfunt не в отдельных файлах, а в модуле, на- пример, с именем myunit module myunit; implicit none contains function funt(t) result(w)
real(8) w, t w=2d0*t end function funt subroutine subfunt(t,res)
real(8) t, res res=2d0*t end subroutine subfunt end module myunit
Обратите внимание, что нет нужды в каждой из процедур, помещаемой в модуль, отменять действие правила умолчания — достаточно это сделать один раз в разделе описаний самого модуля.
346

Модуль — программная единица, т.е. от компилятора можно требо- вать получить соответствующий модулю объектный файл. Например,
$ gfortran myunit.f90 -c
# откомпилировали модуль
$ ls myunit.*
# хотим посмотреть на имя результата myunit.f90
myunit.mod myunit.o
После компиляции главной программы или внешней процедуры в ка- честве результата получаем только соответствующий объектный файл.
После компиляции модуля myunit.f90 (помимо его объектного файла myunit.o) получается и второй файл с именем myunit.mod.
myunit.o потребуется утилите make при сборке исполнимого файла.
myunit.mod потребуется компилятору при компиляции тех программ- ных единиц, которые этот модуль используют.
В нашем случае модуль использует главная программа:
program probtask
! Модифицированная программа.
Файл probtask.f90
use myunit
! Оператор подключения модуля.
include ’probtask.hdr’
open(unit=ninp,file=’probtask.par’)
open(unit=nres,file=’result’,status=’replace’)
read(ninp,100) n,a,b
! Ввод числа узлов и границ отрезка write(nres,110) n, a, b
! Контрольная печать введенного.
h=(b-a)/(n-1)
! Расчет шага дробления [a,b].
write(nres,1000)
! Печать заголовка первой таблицы.
do i=1, n
! Для каждого из узлов:
t=a+(i-1)*h; x=1.5d0+t; y=funt(t)/2! расчёт текущего результата call subfunt(t,z)
write(nres,1001) i, t, x, y, z/4
! и его вывод.
enddo close(nres)
100 format(i10/d10.3/d10.3)
! Список форматов ввода-вывода:
110 format(1x,’#
Число узлов дискретизации (n)=’,i4/&
&
1x,’# Левая граница промежутка
(a)=’,d23.16/&
&
1x,’# Правая граница промежутка (b)=’,d23.16)
1000 format(//1x,’# N’,5x,’t’,12x,’x’,13x,’funt(t)/2’,14x,’ z/4 ’)
1001 format(1x,i3,d10.2,f14.10,1x,d21.14,1x,d21.14)
end program probtask
Если функции описаны в модуле (т.е. являются модульными, а не внеш- ними), то их интерфейс описывать через оператор interface не нужно,
поскольку действует модульный интерфейс. Поэтому при наличии опера- тора use myunit в главной программе явное описание интерфейса проце- дур, включённых в модуль, необходимо убрать из файла probtask.hdr:
347


!
Файл probtask.hdr implicit none
! Отмена правила умолчания ФОРТРАНа.
integer ninp / 5 /
! Номер файла с вводимыми параметрами задачи.
integer nres / 6 /
! --"--"--"-- c выводимымым результатом.
integer n
! Количество узлов дискретизации промежутка.
integer i
! Номер текущего узла.
real(8) a, b
! Левая и правая абсциссы его границ.
real(8) h
! Шаг его равномерной дискретизации.
real(8) t, x
! Текущие аргументы real(8) y
! Хранитель значения, получаемого функцией.
real(8) z
! -"--"--"--"--"--"--"--"--"- подпрограммой.
11.3.1
Кустарный «ручной» пропуск задачи с модулем.
$ gfortran myunit.f90 -c
# откомпилировали модуль
$ ls myunit.*
myunit.f90
myunit.mod myunit.o
$ gfortran probtask.f90 -c
# --"-- главную программу
$ gfortran probtask.o myunit.o -o probtask # получили исполнимый файл
$ ./probtask
# активировали его
Понятно, что перед компиляцией главной программы файл myunit.mod уже должен иметься в наличии и быть доступным — ведь именно из него компилируемая программа узнаёт имена, импортируемые ею из модуля.
Если, например, удалим из текущей директории файл myunit.mod и попытаемся получить исполнимый файл probtask, то получим:
rm myunit.mod gfortran probtask.f90 -o probtask use myunit
1
Фатальная ошибка:
Can’t open module file ’myunit.mod’
for reading at (1): Нет такого файла или каталога
11.3.2
Попытка использования make-файла из 11.2.2
При наличии в текущей директории файлов myunit.mod и myunit.o работоспособен make-файл из пункта 11.2.2. Например,
1.
$ gfortran -c myunit.f90
# откомпилировали файл с модулем
$ ls myunit.*
# убедились в появлении myunit.for myunit.mod myunit.o
#
myunit.mod и myunit.o
$ make
# убедились в работоспособности gfortran probtask.o myunit.o -o probtask # make-файла
348

2.
$ touch probtask.f90
# изменили время модификации главной
$ make
# программы и убеждаемся, что gfortran -c -Wall -O0 probtask.f90
#
make-файл из 11.2.2 работает!
gfortran probtask.o myunit.o -o probtask
3.
$ touch probtask.hdr
# изменили время модификации
$ make
# include-файла и убеждаемся, что gfortran -c -Wall -O0 probtask.f90
#
make-файл из 11.2.2 работает!
gfortran probtask.o myunit.o -o probtask
4.
$ touch myunit.f90
# изменили время модификации модуля
$ make
# myunit.f90 и снова убеждаемся, что gfortran -c -Wall -O0 myunit.f90
#
make-файл из 11.2.2 работает!
gfortran probtask.o myunit.o -o probtask
5. Если, в модуле “закомментировать”, например, описание функции funt, то получим:
$ make
# и опять работа make-файла не вызывает gfortran -c -Wall -O0 myunit.f90
#
нареканий.
Ситуация выявлена им gfortran probtask.o myunit.o -o probtask
#
адекватно.
probtask.o(.text+0x201): In function ‘MAIN__’:
probtask.for:13: undefined reference to ‘__myunit__funt’
collect2: ld returned 1 exit status make: *** [probtask] Ошибка 1 6. После раскомментирования в модуле описания функции funt и за- пуске make-файла получаем:
make gfortran -c -Wall -O0 myunit.f90
gfortran probtask.o myunit.o -o probtask
7. Создаётся впечатление, что make-файл универсален. Тем не менее,
можно привести примеры, когда при использовании единицы компи- ляции myunit тестируемый make-файл не обеспечит корректную сборку исполнимого файла. Например,
• Имена из make-переменной PATTERN, могут расположить- ся в неудобном порядке, так что сначала, начнёт компилиро- ваться, например, главная программа, использующая какой-то module, который ещё не откомпилирован (нет для него файла с расширением .mod).
• При модификации главной программы можно случайно стереть какой-то из файлов с расширением .mod и не заметить этого.
349


11.3.3 1-ая попытка коррекции make-файла
#
Работает Makefile:
COMP:=gfortran
OPT:=-c -Wall -O0
PATTERN:=*.f90
SOURCE:=$(wildcard $(PATTERN))
OBJ:=$(patsubst %.f90,%.o,$(SOURCE))
MAIN:=probtask
$(MAIN) : $(OBJ)
[
TAB
]
$(COMP) $^ -o $@
%.o : %.f90
[
TAB
]
$(COMP) $(OPT) $<
$(MAIN).o : $(MAIN).hdr
CLEAR :
[
TAB
]
rm -f *.o *.mod $(MAIN)
RESULT : $(MAIN) $(MAIN).par
[
TAB
]
./$<
Уничтожим в текущей директории файл myunit.mod и изменим про- граммную единицу probtask.f90, использующую myunit.mod модуль:
rm myunit.mod
# При отсутствии файла myunit.mod запуск touch probtask.f90
# make-файла после модификации главной
$ make
# программы заканчивается аварийно.
gfortran -c -Wall -O0 probtask.f90
In file probtask.f90:2
# Автоматической перекомпиляции
# модуля с восстановлением файла use myunit
# myunit.mod НЕ происходит, а хотелось бы.
1
Fatal Error: Can’t open module file ’myunit.mod’ for reading at (1):
No such file or directory make: *** [probtask.o] Ошибка 1
Замечаем, что утилита в первую очередь пытается откомпилировать главную программу, хотя (по идее) сначала нужно откомпилировать мо- дуль. Можно, конечно, изменить порядок имён исходников в переменной
SOURCE, указав, что первой нужна компиляция файла myunit.f90.
Однако, при этом программисту придёться брать на себя процесс сорти- ровки имён исходных файлов в переменной SOURCE, что очень неудоб- но (особенно, если проект включает большое число модулей). Тем не ме- нее, попробуем предложенный вариант.
Для лучшего уяснения ситуации включим в make-файл псевдоцель echo, которая при её вызове должна вывести содержимое переменных
PATTERN, SOURCE и OBJ.
350

COMP:=gfortran
OPT:=-c -Wall -O0
PATTERN:=*.f90
SOURCE:=$(wildcard $(PATTERN)))
OBJ:=$(patsubst %.f90,%.o, $(SOURCE))
MAIN:=probtask
$(MAIN)
: $(OBJ)
$(COMP) $^ -o $@
%.o : %.f90
$(COMP) $(OPT) $<
$(MAIN).o
:
$(MAIN).hdr
RESULT : $(MAIN) $(MAIN).par
./$^
cat RESULT
CLEAR :
rm -f *.o *.mod $(MAIN)
echo:
@echo $(PATTERN)
@echo $(SOURCE)
@echo $(OBJ)
$ make CLEAR
# Повторим пропуск предыдущего rm -f *.o *.mod probtask
# make-файла, выведя после make
# завершения его работы gfortran -c -Wall -O0 probtask.f90
# содержимое переменных probtask.f90:2.4:
# PATTERN, SOURCE и OBJ.
use myunit
# Ошибка, естественно осталась
1
# прежней.
Фатальная ошибка:
Can’t open module # Однако теперь после make echo file ’myunit.mod’ for reading at
# видим, что имена в PATTERN и
(1): Нет такого файла или каталога
# в SOURCE находятся в обратном make: *** [probtask.o] Ошибка 1
# порядке. Поэтому probtask.f90
make echo
# начинает компилироваться myunit.f90 probtask.f90
# раньше, что и приводит к probtask.f90 myunit.f90
# указанной ошибке.
probtask.o myunit.o
Поместим в переменную SOURCE список имён исходных файлов,
получаемый $(wildcard ...), но отсортированный в алфавитном порядке посредством команды
SOURCE:=$(sort $(wildcard $(PATTERN)))
т.е. теперь будет работать make-файл:
351


COMP:=gfortran
OPT:=-c -Wall -O0
PATTERN:=*.f90
SOURCE:=$(sort $(wildcard $(PATTERN)))
OBJ:=$(patsubst %.f90,%.o, $(SOURCE))
MAIN:=probtask
$(MAIN)
: $(OBJ)
$(COMP) $^ -o $@
%.o : %.f90
$(COMP) $(OPT) $<
$(MAIN).o
:
$(MAIN).hdr
RESULT : $(MAIN) $(MAIN).par
./$^
cat RESULT
CLEAR :
rm -f *.o *.mod $(MAIN)
echo:
@echo $(PATTERN)
@echo $(SOURCE)
@echo $(OBJ)
# Результаты его пропуска таковы:
make CLEAR
# Теперь первым rm -f *.o *.mod probtask
# компилируется make
# модуль, файл gfortran -c -Wall -O0 myunit.f90
# myunit.mod gfortran -c -Wall -O0 probtask.f90
# появляется первым.
gfortran myunit.o probtask.o -o probtask make make: ‘probtask’ не требует обновления.
make echo myunit.f90 probtask.f90
# PATTERN
myunit.f90 probtask.f90
# SOURCE
!!!
myunit.o probtask.o
# OBJ
rm *.mod
# Однако, как touch probtask.f90
# только myuit.mod make
# случайно уничтожен gfortran -c -Wall -O0 probtask.f90
# опять probtask.f90:2.4:
#
получаем:
use myunit
1
Фатальная ошибка:
Can’t open module file ’myunit.mod’
for reading at (1): Нет такого файла или каталога ...
Таким образом, подобная модификация make-файла не решит проблему в целом. Кроме того, очень неудобно вручную указывать граф зависимо- стей программных единиц от используемых модулей. Хотелось бы, чтобы make автоматически разобралась с порядком компилирования.
352

11.3.4 2-ая попытка коррекции make-файла
Откажемся от сортировки имён исходников (тем более, что распола- гать имена файлов в лексикографическом порядке вряд ли удобно). Но- вая коррекция заключается в указании зависимости цели probtask.o не только от probtask.f90 и от probtask.hdr, но и от myunit.mod:
COMP:=gfortran
OPT:=-c -Wall -O0
PATTERN:=*.f90
SOURCE:=$(wildcard $(PATTERN))
OBJ:=$(patsubst %.f90,%.o, $(SOURCE))
MAIN:=probtask
$(MAIN)
: $(OBJ)
$(COMP) $^ -o $@
%.o : %.f90
$(COMP) $(OPT) $<
$(MAIN).o
:
$(MAIN).hdr myunit.mod
RESULT : $(MAIN) $(MAIN).par
./$^
cat RESULT
CLEAR :
rm -f *.o *.mod $(MAIN)
Правда, в этом случае, если в текущей директории не будет файла с именем myunit.mod, то при запуске make-файла получим:
make CLEAR
rm -f *.o *.mod probtask make make: *** Нет правила для сборки цели ‘myunit.mod’,
требуемой для ‘probtask.o’.
Останов.
Это сообщение отлично от предыдущего. Предыдущее информировало о фатальной ошибке — об отсутствии файла myunit.mod. Сейчас его тоже нет. Но зато есть указание о том, что он должен быть (имя файла указано в зависимостях). Так что утилита make полагает, что програм- мист просто забыл указать правило, по которому этот файл должен быть получен (нет имени с расширением .mod в качестве имени настоящей це- ли), и напоминает об этом. Поэтому добавим в строку %.o : %.f90 слева от двоеточия шаблон %.mod. Тогда make-файл примет вид:
353