Текст книги "Основы программирования в Linux"
Автор книги: Нейл Мэтью
Соавторы: Ричард Стоунс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 33 (всего у книги 67 страниц)
sprintf(qs, "SELECT DISTINCT artist.id, cd.id FROM artist, cd WHERE artist.id = cd.artist_id and (artist.name LIKE '%%%s%%' OR cd.title LIKE '%%%s%%' OR cd.catalogue LIKE '%%%s%%')", ss, ss, ss);
Сейчас можно выполнить запрос:
res = mysql_query(&my_connection, qs);
if (res) {
fprintf(stderr, «SELECT error: %sn», mysql_error(&my_connection));
} else {
res_ptr = mysql_store_result(&my_connection);
if (res_ptr) {
num_rows = mysql_num_rows(res_ptr);
if (num_rows > 0) {
while ((mysqlrow = mysql_fetch_row(res_ptr)) && i < MAX_CD_RESULT) {
sscanf(mysqlrow[1], «%d», &dest->cd_id[i]);
i++;
}
}
mysql_free_result(res_ptr);
}
}
return num_rows;
} /* find_cds */
Последнее, но не по значимости, – ваша реализация способа удаления компакт-дисков. В соответствии с политикой скрытого управления элементами таблицы исполнителей вы будете удалять исполнителя заданного компакт-диска, если нет других дисков с той же самой строкой исполнителя. Удивительно, но в языке SQL нет средств описания удаления из нескольких таблиц, поэтому вы должны удалять данные из каждой таблицы по очереди:
int delete_cd(int cd_id) {
int res;
char qs[250];
int artist_id, num_rows;
MYSQL_RES *res_ptr;
MYSQL_ROW mysqlrow;
if (!dbconnected) return 0;
artist_id = -1;
sprintf(qs, "SELECT artist_id FROM cd WHERE artist_id =
(SELECT artist_id FROM cd WHERE id = '%d')", cd_id);
res = mysql_query(&my_connection, qs);
if (res) {
fprintf(stderr, «SELECT error: %sn», mysql_error(&my_connection));
} else {
res_ptr = mysql_store_result(&my_connection);
if (res_ptr) {
num_rows = mysql_num_rows(res_ptr);
if (num_rows == 1) {
/* Исполнитель не упоминается в других CD */
mysqlrow = mysql_fetch_row(res_ptr);
sscanf(mysqlrow[0], «%d», &artist_id);
}
mysql_free_result(res_ptr);
}
}
sprintf(qs, «DELETE FROM track WHERE cd_id = '%d'», cd_id);
res = mysql_query(&my_connection, qs);
if (res) {
fprintf(stderr, «Delete error (track) %d: %sn»,
mysql_errno(&my_connection), mysql_error(&my_connection));
return 0;
}
sprintf(qs, «DELETE FROM cd WHERE id = '%d'», cd_id);
res = mysql_query(&my_connection, qs);
if (res) {
fprintf(stderr, «Delete error (cd) %d: %sn»,
mysql_errno(&my_connection), mysql_error(&my_connection));
return 0;
}
if (artist_id != -1) {
/* Теперь элемент artist не связан ни с одним CD, удалите его */
sprintf(qs, «DELETE FROM artist WHERE id = '%d'», artist_id);
res = mysqlquery(&my_connection, qs);
if (res) {
fprintf(stderr, «Delete error (artist) %d: %sn»,
mysql_errno(&my_connection), mysql_error(&my_connection));
}
}
return 1;
} /* delete_cd */
На этом программный код завершается.
Для законченности и облегчения жизни добавьте файл Makefile. Возможно, вам придется откорректировать в нем путь к файлам include
, зависящий от установки СУРБД MySQL в вашей системе.
all: арр
арр: app_mysql.с app_test.с app_mysql.h
gcc -о app -I/usr/include/mysql appmysql.с app_test.с -lmysqlclient -L/usr/lib/mysql
В последующих главах вы увидите применение этого интерфейса с реальным интерфейсом GUI. Сейчас, если вы хотите увидеть изменения в базе данных по мере выполнения программы, мы предлагаем в одном окне выполнить программу по шагам с помощью отладчика gdb, а в другом следить за изменениями в базе данных. Если вы будете использовать MySQL Query Browser, не забудьте о необходимости обновлять отображаемые данные для отслеживания текущих изменений.
Резюме
В этой главе мы кратко рассмотрели СУРБД MySQL. Более опытные пользователи обнаружат, что многие сложные средства не обсуждались в данной главе, например, ограничения внешнего ключа и триггеры.
Вы получили основные сведения об установке MySQL и узнали об основах администрирования баз данных MySQL с помощью утилит-клиентов. Мы рассмотрели API языка С, который наряду с другими языками программирования может применяться с СУРБД MySQL. Вы также познакомились с некоторыми операторами языка SQL в действии.
Мы надеемся, что эта глава вдохновит вас на использование баз данных на основе SQL для хранения ваших данных и заставит искать дополнительную информацию об этих мощных средствах управления базами данных.
И как напоминание, основной информационный ресурс MySQL – исходная страница MySQL на сайте www.mysql.com.
Глава 9
Средства разработки
В этой главе рассматриваются средства разработки программ, применяемые в ОС Linux, некоторые из них доступны и в ОС UNIX. В дополнение к обязательным составляющим, таким как компиляторы и отладчики, Linux предлагает ряд средств, каждое из которых предназначено для одного вида работы и позволяет разработчику комбинировать эти средства новыми оригинальными способами. Такой подход – часть идеологии UNIX, которую унаследовала ОС Linux. В данной главе рассматривается несколько наиболее важных средств разработки и показан ряд примеров их использования для решения проблем. К этим средствам относятся следующие:
□ команда make
и make-файлы;
□ управление исходным программным кодом с помощью RCS и CVS;
□ написание интерактивного руководства;
□ распространение программного обеспечения с помощью patch
и tar
;
□ среды разработки.
Проблемы применения многочисленных исходных файлов
При написании маленьких программ многие программисты после корректировки просто перестраивают свое приложение, компилируя заново все файлы. Но в случае больших программ такой простой подход порождает проблемы. Время цикла «редактирование – компиляция – тестирование» быстро растет. Даже самому терпеливому программисту хотелось бы избежать перекомпиляции всех файлов при изменении лишь одного из них.
Гораздо более серьезная проблема может возникнуть при создании многочисленных заголовочных файлов и включении их в разные исходные файлы. Предположим, что у вас есть заголовочные файлы a.h, b.h и c.h и исходные файлы на языке С main.c, 2.с и 3.c (мы надеемся, что в реальных проектах вы выберете более удачные имена, чем приведенные здесь). Вы можете столкнуться со следующей ситуацией.
/* main.c */
#include «a.h»
...
/* 2.с */
#include «a.h»
#include «b.h»
...
/* 3.c */
#include «b.h»
#include «c.h»
...
Если программист изменяет файл c.h, файлы main.c и 2.с не нужно перекомпилировать, поскольку они не зависят от этого заголовочного файла. Файл 3.с зависит от c.h и, следовательно, должен быть откомпилирован заново, если изменился c.h. Но если был изменен файл b.h, и программист забыл откомпилировать заново файл 2.с, результирующая программа может перестать работать корректно.
Утилита make
может решить обе эти проблемы, обеспечивая в случае необходимости перекомпиляцию всех файлов, затронутых изменениями.
Примечание
Команда
make
применяется не только для компиляции программ. Ее можно использовать, когда формируются выходные файлы из нескольких входных файлов. Ещё одно ее применение включает обработку документов (такую же, как с помощью программ troff или ТеХ).
Команда make и make-файлы
Несмотря на то, что у команды make
много внутренних сведений и знаний, она не может самостоятельно решить, как скомпоновать ваше приложение. Вы должны предоставить файл, который сообщит ей, как устроено приложение. Этот файл называется make-файлом или файлом сборки.
Make-файл чаще всего расположен в том же каталоге, что и другие исходные файлы проекта. В любой момент времени на вашем компьютере может быть множество make-файлов. Действительно, если у вас большой проект, вы можете управлять им, используя отдельные make-файлы для разных частей проекта.
Сочетание команды make
и make-файла – мощное средство управления проектами. Оно часто применяется не только для управления компиляцией исходного программного кода, но и для подготовки интерактивного справочного руководства или установки приложения в нужный каталог.
Make-файл состоит из набора зависимостей и правил. У зависимости есть цель или задание (выходной файл, который нужно создать) и набор исходных файлов, от которых она зависит. Правила или инструкции описывают, как создать выходной файл из зависимых файлов. Обычно цель – это единый исполняемый файл.
Make-файл читается командой make
, определяющей выходной файл или файлы, которые должны быть сформированы, и затем сравнивающей даты и время исходных файлов, чтобы решить, какие инструкции нужно вызвать для формирования выходного файла. Часто следует выполнить другие промежуточные задания, прежде чем может быть сформирована заключительная цель. Команда make
использует make-файл для определения порядка, в котором должны выполняться задания, и корректной последовательности запуска правил.
У программы make
есть несколько опций. Наиболее часто применяются следующие:
□ -k
, сообщающая make
о необходимости продолжать выполнение, если обнаружена ошибка, а не останавливаться при появлении первой проблемы. Эту опцию можно использовать, например, для выявления за один проход всех исходных файлов, которые не могут быть откомпилированы;
□ -n
, сообщающая make
о необходимости вывода перечня требуемых действий без реального их выполнения;
□ -f <файл>
, позволяющая сообщить make
о том, какой файл применять как make-файл. Если вы не используете эту опцию, стандартная версия программы make
ищет в текущем каталоге первый файл, названный makefile. Если его нет, программа ищет файл, названный Makefile. Но если вы применяете GNU Make, что вероятно в ОС Linux, эта версия программы make сначала ищет файл GNUmakefile до поиска файла makefile и последующего поиска Makefile. В соответствии с соглашением программисты Linux применяют имя Makefile, которое позволяет поместить файл сборки первым в списке файлов каталога, заполненного именами файлов, состоящими из строчных букв. Мы полагаем, что вы не будете использовать имя GNUmakefile, поскольку оно специфично для. реализации GNU программы make
.
Для того чтобы заставить команду make
выполнить конкретное задание, как правило, собрать исполняемый файл, вы можете передать make
имя задания или цели как параметр. Если этого не сделать, программа make
попытается выполнить первое задание, указанное в make-файле. Многие программисты указывают в своих make-файлах в качестве первой цели или задания all
и затем перечисляют остальные задания, как зависимости для all
. Это соглашение делает понятным выбор задания по умолчанию, если не задана конкретная цель. Мы полагаем, что вы будете придерживаться этого соглашения.
Зависимости
Зависимости определяют, как каждый файл в окончательном приложении связан исходными файлами. В программном примере, приведенном ранее в этой главе, вы могли бы установить зависимости, говорящие о том, что вашему окончательному приложению требуются (оно зависит от) main.о, 2.о и 3.o; и также для main.о (main.c и a.h); 2.o (2.с, a.h и b.h) и 3.o (3.c, b.h и c.h). Таким образом, на файл main.о влияют изменения файлов main.c и a.h, и он нуждается в пересоздании с помощью повторной компиляции файла main.c, если был изменен любой из двух указанных файлов.
В make-файл вы записываете эти правила, указывая имя задания, двоеточие, пробелы или табуляции и затем разделенный пробелами или табуляциями перечень файлов, применяемых для создания выходного файла задания. Далее приведен список зависимостей для ранее приведенного примера:
myapp: main:о 2.о 3.o
main.о: main.c a.h
2.о: 2.с a.h b.h
3.o: 3.c b.h c.h
Список свидетельствует о том, что myapp зависит от main.о, 2.o и 3.o, a main.o зависит от main.c и a.h и т. д.
Данный набор зависимостей формирует иерархию, показывающую, как исходные файлы связаны друг с другом. Вы легко можете увидеть, что если изменяется b.h, то придется пересмотреть 2.o и 3.o, а поскольку 2.o и 3.o будут изменены, вам придется перестроить и myapp.
Если вы хотите собрать несколько файлов, можно использовать фиктивную цель или задание all
. Предположим, что ваше приложение состоит из двоичного файла myapp и интерактивного руководства myapp.1. Описать их можно следующей строкой:
all: myapp myapp.1
И еще раз, если вы не включите задание all
, программа make
просто создаст выходной файл, первым найденный в make-файле.
Правила
Второй, компонент make-файла – правила или инструкции, описывающие способ создания выходного файла задания. В примере из предыдущего раздела какую команду следует применить после того, как команда make
определила, что файл 2.o нуждается в перестройке? Возможно, достаточно простого применения команды gcc -с 2.с
(и как вы увидите в дальнейшем, make
на самом деле знает много стандартных правил), но что если вы хотите указать каталог include или задать опцию вывода символьной информации для последующей отладки? Сделать это можно, явно определив правила в make-файле.
Примечание
В данный момент мы должны информировать вас об очень странной и неудачной синтаксической записи, применяемой в make-файлах: разнице между пробелом и табуляцией. Все правила должны представлять собой строки, начинающиеся со знака табуляции; пробел не годится. Так как несколько пробелов и табуляция выглядят почти одинаково и поскольку почти во всех других случаях, касающихся программирования в системе Linux, нет большой разницы между пробелами и табуляциями, это может вызвать проблемы. Кроме того, пробел в конце строки в make-файле может вызвать сбой при выполнении команды
make
. Тем не менее, это исторический факт и в наше время слишком много make-файлов находится в обращении, чтобы можно было рассчитывать на изменение положения вещей, поэтому будьте внимательны! К счастью, если командаmake
не работает из-за пропущенной табуляции, это обычно довольно понятно.
А теперь выполните упражнение 9.1.
Упражнение 9.1. Простой make-файл
Большинство правил или инструкций состоит из простой команды, которая могла бы быть набрана в командной строке. Для примера создайте свой первый make-файл Makefile1:
myapp: main.о 2.o 3.o
gcc -о myapp main.о 2.o 3.o
main.о: main.c a.h
gcc -с main.c
2.о: 2.с a.h b.h
gcc -с 2.с
3.o: 3.c b.h c.h
gcc -с 3.c
Запустите команду make
с опцией -f
, потому что ваш make-файл не назван одним из стандартных имен makefile или Makefile. Если запустить приведенный код в каталоге, не содержащем исходных файлов, будет получено следующее сообщение:
$ make -f Makefile1
make: *** No rule to make target 'main.c', needed by 'main.o'. Stop.
$
Команда make
предположила, что первое задание в make-файле, myapp
, – это файл, который вы хотите создать. Затем она просмотрела остальные зависимости и прежде всего определила, что нужен файл, названный main.c. Поскольку вы все еще не создали этот файл и в make-файле не сказано, как он может быть создан, команда make
вывела сообщение об ошибке. Итак, создайте исходные файлы и попробуйте снова. Поскольку результат нас не интересует, эти файлы могут быть очень простыми. Заголовочные файлы на самом деле пустые, поэтому вы можете создать их командой touch
:
$ touch a.h
$ touch b.h
$ touch c.h
Файл main.c содержит функцию main
, вызывающую функции function_two
и function_three
. В других двух файлах определены функции function_two
и function_three
. В исходных файлах есть строки #include
для соответствующих заголовочных файлов, поэтому они оказываются зависимыми от содержимого включенных файлов заголовков. Это приложение не назовешь выдающимся, но, тем не менее, далее приведены листинги программ:
/* main.c */
#include
#include «a.h»
extern void function_two();
extern void function_three();
int main() {
function_two();
function_three();
exit(EXIT_SUCCESS);
}
/* 2.c */
#include «a.h»
#include «b.h»
void function_two() { }
/* 3.с */
#include «b.h»
#include «c.h»
void function_three() { }
Теперь попробуйте выполнить команду make еще раз:
$ make -f Makefile1
gcc -с main.с gcc -с 2.с
gcc -с 3.с
gcc -о myapp main.о 2.о 3.о
$
На этот раз сборка прошла успешно.
Как это работает
Команда make
обработала секцию зависимостей make-файла и определила файлы, которые нужно создать, и порядок их создания. Хотя вы сначала описали, как создать файл myapp, команда make
определила правильный порядок создания файлов. Затем она запустила соответствующие команды для создания этих файлов, приведенные вами в секции правил. Команда make
выводит на экран выполняемые ею команды. Теперь вы можете протестировать ваш make-файл, чтобы увидеть, корректно ли он обрабатывает изменения в файле b.h:
$ touch b.h
$ make -f Makefile1
gcc -c 2.с gcc -с 3.c
gcc -o myapp main.о 2.о 3.o
$
Команда make
прочла ваш make-файл, определивший минимальное количество команд, требуемых для повторного построения myapp, и выполнила их в правильной последовательности. Теперь посмотрите, что произойдет, если вы удалите объектный файл:
$ rm 2.o
$ make -f Makefile1
gcc -с 2.c
gcc -о myapp main.о 2.о 3.о
$
И снова команда make
правильно определяет нужные действия.
Комментарий в make-файле начинается со знака #
и продолжается до конца строки. Как и в исходных файлах на языке С, комментарии в make-файлах могут помочь как автору, так и другим пользователям понять, что имелось в виду во время написания данного файла.Sta
Даже если бы функциональные возможности команды make
и make-файлов ограничивались тем, что уже описано, они были бы мощным средством управления проектами с множеством исходных файлов. Но эти средства становятся громоздкими и неповоротливыми в проектах, содержащих большое количество файлов. Поэтому make-файлы предоставляют возможность использовать макросы, позволяющие писать эти файлы в более обобщенном виде.
Макросы в make-файле записываются в виде конструкции MAСRONAME=значение
, затем ссылаться на значение можно, указав $(MACRONAME)
или ${MACRONAME}
. Некоторые версии make
могут также принимать $MACRONAME
. Вы можете задать пустое значение макроса, оставив пустой часть строки после знака =
.
Макросы часто используют в make-файлах для задания опций компилятора. Обычно во время разработки приложение компилируется без оптимизации и с включенной отладочной информацией. Для окончательной версии приложения, как правило, нужны другие режимы: маленький двоичный файл без какой-либо отладочной информации, работающий как можно быстрее.
Еще одна проблема в файле Makefile1 – жестко заданное имя компилятора gcc
. В других UNIX-системах вы, возможно, будете использовать cc
или c89
. Если когда-нибудь вы захотите перенести ваш make-файл в другую версию UNIX или получите другой компилятор для имеющейся у вас системы, придется изменить несколько строк в вашем make-файле, чтобы заставить его работать. Макросы – хороший способ собрать все эти системнозависимые части и легко изменить их.
Обычно макросы определяются в самом make-файле, но их можно задать и при вызове команды make
, если добавить определение макроса, например, make CC=c89
. Определения, подобные данному, приведенные в командной строке, переопределяют заданные в make-файле определения. Заданные вне make-файла определения макросов должны передаваться как один аргумент, поэтому исключите пробелы или применяйте кавычки следующим образом: «CC = с89»
.
Выполните упражнение 9.2.
Упражнение 9.2. Make-файл с макросом
Далее приведена переработанная версия make-файла с именем Makefile2, в которой применяются макросы:
all: myapp
# Какой компилятор
СС = gcc
# Где хранятся файлы include
INCLUDE = .
# Опции для процесса разработки
СFLAGS = -g -Wall -ansi
# Опции для окончательной версии
# СFLAGS = -О -Wall -ansi
myapp: main.о 2.o 3.o
$(CC) -о myapp main.о 2.o 3.o
main.о: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -с main.c
2.о: 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o: 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
Если удалить прошлую версию приложения и создать новую с помощью только что приведенного нового make-файла, вы получите следующее:
$ rm *.о myapp
$ make -f Makefile2
gcc -I. -g -Wall -ansi -c main.c
gcc -I. -g -Wall -ansi -c 2.c
gcc -I. -g -Wall -ansi -c 3.c
gcc -o myapp main.о 2.o 3.o
$
Как это работает
Программа make заменяет ссылки на макросы $(CC)
, $(CFLAGS)
и $(INCLUDE)
соответствующими определениями так же, как компилятор С поступает с директивами #define
. Теперь, если вы захотите изменить команду компиляции, вам придется изменить только одну строку make-файла.
У команды make
есть несколько специальных внутренних макросов, которые можно применять для того, чтобы еще более сократить make-файлы. В табл. 9.1 мы перечислили наиболее часто используемые из них; в последующих примерах вы увидите их в действии. Подстановка каждого из этих макросов выполняется только перед его использованием, поэтому значение макроса может меняться по мере обработки make-файла. На самом деле в этих макросах было бы очень мало пользы, если бы они действовали иначе.
Таблица 9.1
$? | Список необходимых условий (файлов, от которых зависит выходной файл), измененных позже, чем текущий выходной файл |
$@ | Имя текущего задания |
$< | Имя текущего файла, от которого зависит выходной |
$* | Имя без суффикса текущего файла, от которого зависит выходной |
Есть еще два полезных специальных символа, которые можно увидеть перед командами в make-файле:
□ символ -
заставляет команду make
игнорировать любые ошибки. Например, если вы хотели бы создать каталог и при этом игнорировать любые ошибки, скажем, потому что такой каталог уже существует, вы просто ставите знак «минус» перед командой mkdir
. Чуть позже в этой главе вы увидите применение символа -
;
□ символ @
запрещает команде make
выводить команду в стандартный файл вывода перед ее выполнением. Этот символ очень удобен, если вы хотите использовать команду echo
для вывода некоторых инструкций.