- •Оглавление
- •Об авторе
- •Благодарности
- •Предисловие
- •Глава 1. Держим оборону
- •На пути к хорошему коду
- •Готовьтесь к худшему
- •Что такое защитное программирование?
- •Этот страшный, ужасный мир
- •Технологии защитного программирования
- •Выберите хороший стиль кодирования и пользуйтесь крепкой архитектурой
- •Пишите код без спешки
- •Не верьте никому
- •Стремитесь к ясности, а не к краткости
- •Не позволяйте никому лезть туда, где ему нечего делать
- •Включайте вывод всех предупреждений при компиляции
- •Пользуйтесь средствами статического анализа
- •Применяйте безопасные структуры данных
- •Проверяйте все возвращаемые значения
- •Аккуратно обращайтесь с памятью (и другими ценными ресурсами)
- •Инициализируйте все переменные там, где вы их объявили
- •Объявляйте переменные как можно позже
- •Пользуйтесь стандартными средствами языка
- •Пользуйтесь хорошими средствами регистрации диагностических сообщений
- •Выполняйте приведение типов с осторожностью
- •Подробности
- •Ограничения
- •Какие ограничения налагать
- •Снятие ограничений
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Да в чем проблема?
- •Знайте своих клиентов
- •Что такое хорошее представление?
- •Размещение скобок
- •Скобки в стиле K&R
- •Расширенный стиль скобок
- •Стиль Уайтсмита (с отступами)
- •Другие стили скобок
- •Единственно верный стиль
- •Внутрифирменные стили (и когда их придерживаться)
- •Установка стандарта
- •Религиозные войны?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Зачем нужны хорошие имена?
- •Каким объектам мы даем имена?
- •Игра в названия
- •Описательность
- •Техническая корректность
- •Идиоматичность
- •Тактичность
- •Технические подробности
- •Имена переменных
- •Имена функций
- •Имена типов
- •Пространства имен
- •Имена макросов
- •Имена файлов
- •Роза пахнет розой
- •Соблюдайте единообразие
- •Связывайте имя с содержимым
- •Извлекайте выгоду из выбора имени
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Самодокументируемый код
- •Техника написания самодокументируемого кода
- •Пишите простой код с хорошим форматированием
- •Выбирайте осмысленные имена
- •Разбивайте код на самостоятельные функции
- •Выбирайте содержательные имена типов
- •Применяйте именованные константы
- •Выделяйте важные фрагменты кода
- •Объединяйте взаимосвязанные данные
- •Снабжайте файлы заголовками
- •Правильно обрабатывайте ошибки
- •Пишите осмысленные комментарии
- •Практические методологии самодокументирования
- •Грамотное программирование
- •Инструментарий документирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Что есть комментарий в коде?
- •Как выглядят комментарии?
- •Сколько комментариев требуется?
- •Что помещать в комментарии?
- •Не нужно описывать код
- •Не подменяйте код
- •Как сделать комментарии полезными
- •Не отвлекаться
- •На практике
- •Замечание об эстетичности
- •Единообразие
- •Четкие блочные комментарии
- •Отступы в комментариях
- •Комментарии в конце строки
- •Помощь в чтении кода
- •Стиль должен обеспечивать легкость сопровождения
- •Границы
- •Флажки
- •Комментарии в заголовке файла
- •Работа с комментариями
- •Помощь при написании программ
- •Заметки об исправлении ошибок
- •Устаревание комментариев
- •Сопровождение и бессодержательные комментарии
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Откуда что берется
- •Механизмы сообщения об ошибках
- •Без обработки ошибок
- •Возвращаемые значения
- •Переменные, содержащие состояние ошибки
- •Исключения
- •Сигналы
- •Обнаружение ошибок
- •Обработка ошибок
- •Когда обрабатывать ошибки
- •Варианты реагирования
- •Последствия для кода
- •Подымаем скандал
- •Управление ошибками
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Что такое инструмент программирования?
- •А зачем они нужны – инструменты?
- •Электроинструменты
- •Выясните, каковы его возможности
- •Научитесь им управлять
- •Выясните, для каких задач он пригоден
- •Убедитесь, что он работает
- •Имейте четкие данные о том, как получить дополнительные сведения
- •Узнайте, как получить новые версии
- •Какой инструмент необходим?
- •Средства редактирования исходного кода
- •Средства построения кода
- •Инструменты для отладки и тестирования
- •Средства поддержки языка
- •Инструменты различного назначения
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Проверка на подлинность
- •Кто, что, когда, зачем?
- •Зачем тестировать
- •Кому тестировать
- •В чем состоит тестирование
- •Когда тестировать
- •Типы тестирования
- •Выбор контрольных примеров для блочного тестирования
- •Архитектура и тестирование
- •Руками не трогать!
- •Анатомия провала
- •Справлюсь ли я сам?
- •Система контроля ошибок
- •Обсуждение ошибок
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Реальные факты
- •Природа этого зверя
- •Взгляд с высоты птичьего полета
- •Взгляд с поверхности земли
- •Взгляд из глубины
- •Борьба с вредителями
- •Обходная дорога
- •Правильный путь
- •Охота за ошибками
- •Ошибки этапа компиляции
- •Ошибки этапа исполнения
- •Как исправлять ошибки
- •Профилактика
- •Отладчик
- •Средство проверки доступа к памяти
- •Трассировщик системных вызовов
- •Дамп памяти
- •Журналирование
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Языковые барьеры
- •Интерпретируемые языки
- •Компилируемые языки
- •Делаем слона из мухи
- •Выполнение сборки
- •Что должна уметь хорошая система сборки?
- •Простота
- •Единообразие
- •Повторяемость и надежность
- •Атомарность
- •Борьба с ошибками
- •Механика сборки
- •Выбор целей
- •Уборка
- •Зависимости
- •Автоматическая сборка
- •Конфигурация сборки
- •Рекурсивное применение make
- •Мастер на все руки
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Что такое оптимизация?
- •От чего страдает оптимальность кода?
- •Доводы против оптимизации
- •Альтернативы
- •Нужна ли оптимизация
- •Технические подробности
- •Убедитесь, что нужна оптимизация
- •Определите самую медленную часть кода
- •Тестирование кода
- •Оптимизация кода
- •После оптимизации
- •Методы оптимизации
- •Конструктивные изменения
- •Модификация кода
- •Как писать эффективный код
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Риски
- •Наши оппоненты
- •Оправдания, оправдания
- •Ощущение незащищенности
- •Опасный проект и архитектура
- •Переполнение буфера
- •Встроенные строки запросов
- •Условия гонки
- •Целочисленное переполнение
- •Дела защитные
- •Технология установки системы
- •Технология конструирования программного обеспечения
- •Технологии реализации кода
- •Технологии процедуры
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Программирование как конструкторская работа
- •Что нужно проектировать?
- •Хороший проект программного продукта
- •Простота
- •Элегантность
- •Модульность
- •Хорошие интерфейсы
- •Расширяемость
- •Избегайте дублирования
- •Переносимость
- •Идиоматичность
- •Документированность
- •Как проектировать код
- •Методы и процедуры проектирования
- •Инструменты проектирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Что такое программная архитектура?
- •План программы
- •Точки зрения
- •Где и когда этим заниматься?
- •Для чего она применяется?
- •Компоненты и соединения
- •Какими качествами должна обладать архитектура?
- •Архитектурные стили
- •Без архитектуры
- •Многоуровневая архитектура
- •Архитектура с каналами и фильтрами
- •Архитектура клиент/сервер
- •Компонентная архитектура
- •Каркасы
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Гниение программного обеспечения
- •Тревожные симптомы
- •Как развивается код?
- •Вера в невозможное
- •Как с этим бороться?
- •Как писать новый код
- •Сопровождение существующего кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Мартышкин труд
- •Нетерпеливый
- •Кодер (Code Monkey)
- •Гуру
- •Псевдогуру
- •Высокомерный гений
- •Ковбой
- •Плановик
- •Ветеран
- •Фанатик
- •Монокультурный программист
- •Лодырь
- •Руководитель поневоле
- •Идеальный программист
- •И что из этого следует?
- •Для глупцов
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 17. Вместе мы – сила
- •Команды – общий взгляд
- •Организация команды
- •Методы управления
- •Разделение ответственности
- •Организация и структура кода
- •Инструменты для групповой работы
- •Болезни, которым подвержены команды
- •Вавилонская башня
- •Диктатура
- •Демократия
- •Большой Каньон
- •Зыбучие пески
- •Лемминги
- •Личное мастерство и качества, необходимые для работы в команде
- •Общение
- •Скромность
- •Разрешение конфликтов
- •Обучение и приспособляемость
- •Знание пределов своих возможностей
- •Принципы групповой работы
- •Коллективное владение кодом
- •Нормы кодирования
- •Определите, что считать успехом
- •Установите ответственность
- •Избегайте истощения
- •Жизненный цикл команды
- •Создание команды
- •Рост команды
- •Групповая работа
- •Роспуск команды
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Управление версиями исходного кода
- •Контроль версий
- •Контроль доступа
- •Работа с хранилищем
- •Пусть растут деревья
- •Краткая история систем контроля за исходным кодом
- •Управление конфигурацией
- •Резервное копирование
- •Выпуск исходного кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Что же это такое, конкретно?
- •Типы спецификаций
- •Спецификация требований
- •Функциональная спецификация
- •Спецификация системной архитектуры
- •Спецификация интерфейса пользователя
- •Проектная спецификация
- •Спецификация тестирования
- •Что должны содержать спецификации?
- •Процесс составления спецификаций
- •Почему мы не пишем спецификации?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Когда проводить рецензирование?
- •Нужно ли рецензировать
- •Какой код рецензировать
- •Проведение рецензирования кода
- •Рецензирование на собраниях
- •Интеграционное рецензирование
- •Пересмотрите свое отношение
- •Позиция автора
- •Позиция рецензента
- •Идеальный код
- •За пределами рецензирования кода
- •Резюме
- •Контрольный список
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Выстрел в темноте
- •Почему трудно делать оценки?
- •Под давлением
- •Практические способы оценки
- •Игры с планами
- •Не отставай!
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Стили программирования
- •Структурное программирование
- •Функциональное программирование
- •Логическое программирование
- •Рецепты: как и что
- •Процессы разработки
- •Каскадная модель
- •SSADM и PRINCE
- •Создание прототипов
- •Итеративная и инкрементная разработка
- •Спиральная модель
- •Другие процессы разработки
- •Спасибо, хватит!
- •Выбор процесса
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 23. За гранью возможного
- •Программирование приложений
- •Коробочные продукты
- •Заказные приложения
- •Программирование игр
- •Системное программирование
- •Встроенное программное обеспечение
- •Программирование масштаба предприятия
- •Численное программирование
- •И что дальше?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 24. Что дальше?
- •Но что же дальше?
- •Ответы и обсуждение
- •Глава 1. Держим оборону
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Вопросы для размышления
- •Глава 17. Вместе мы – сила
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 20. Рецензия на отстрел
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Вопросы для размышления
- •Глава 23. За гранью возможного
- •Вопросы для размышления
- •Вопросы личного характера
- •Библиография
- •Алфавитный указатель
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
624m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
Вопросы личного характера
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Ответы и обсуждениеClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
1.Каким количеством отладочных технологий/инструментов вы обычно пользуетесь?
Ответ очевиден: никаким. Вы всегда с первого же раза пишете идеаль% ный код!
2.Какие стандартные проблемы и ловушки существуют в языках, кото рыми вы пользуетесь? Какие меры вы предпринимаете против появ ления соответствующих ошибок в вашем коде?
О таких проблемах необходимо знать. Этим эксперты и отличаются от рядовых программистов. Если ты не знаешь, где живут драконы, то не сможешь уберечься от них.
3.Какие ошибки чаще встречаются в вашем коде – сделанные по невни мательности или связанные с более тонкими проблемами?
Если вы постоянно натыкаетесь на одни и те же мелкие промахи с язы% ком, значит, нужно писать код более внимательно. Не пишите код в спешке. Проверьте написанное и еще раз прочтите – в конечном сче% те вы сбережете время. Классическая ошибка – исправить ошибку, не протестировав исправление, а потом столкнуться с нежелательными побочными эффектами своего «исправления».
Нет ничего позорного в том, что в коде встретились ошибки. С кем не бывает. Нужно только следить за тем, чтобы не допускать глупых ошибок, которые можно предотвратить.
4.Умеете ли вы пользоваться отладчиком на своей рабочей платформе? Служит ли он вашим повседневным инструментом? Опишите свои дей ствия в случае:
a.Обратной трассировки
b.Изучения значений переменных
c.Изучения значений полей в структуре
d.Выполнения произвольной функции
e.Переключения контекста потока
Постоянно пользоваться отладчиком – излишество. Никогда не при% менять его – тоже неправильно. Не бойтесь воспользоваться отладчи% ком, но и не опирайтесь на него постоянно. Разумно пользуясь отлад% чиком, вы очень быстро отыщете местонахождение ошибки.
Глава 10. Код, который построил Джек
Вопросы для размышления
1.Зачем при наличии удобной интегрированной среды обращаться к ути лите командной строки make, если среда позволяет собрать проект, на
жав одну кнопку?
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Главаm |
10. Код, который построил Джек |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
625Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Помимо понимания того, что в действительности происходит при на% жатии кнопки сборки, умение пользоваться make открывает дорогу к более мощному и гибкому построению программ. Графические ин% струменты сборки редко приближаются по своим возможностям и гиб% кости к make%файлам. Упрощение часто оказывается полезным, и ин% струменты с GUI помогают разработчикам быстро создавать програм% мы, но за все приходится платить.
Графические инструменты сборки плохо масштабируются и малопри% годны для достаточно крупных проектов. Синтаксис make не прост, но эта утилита предоставляет значительно более широкие возможности. Например, make%файлы допускают вложенные каталоги и иерархиче% скую сборку. Примитивные инструменты GUI предоставляют только один уровень глубины – вложенность проектов внутри рабочего про% странства.
Можно слышать жалобы на сложность make и трудности ее примене% ния. Это действительно так, но то же справедливо в отношении любых мощных инструментов – можно получить увечья при неправильном их применении.
Это не значит, что вы должны выкинуть все графические инструменты сборки и начать писать кучу make%файлов, которые их заменят. Со% всем наоборот: для каждой работы хорош свой инструмент. Сравните простоту и интеграцию с мощью и расширяемостью, и в каждом слу% чае выберите подходящий инструмент.
2.Почему необходимо рассматривать извлечение исходного кода как отдельный этап перед сборкой?
Это логически разные этапы. В правильно устроенной системе сборки у вас должна быть возможность получить любую версию программно% го продукта вне зависимости от ее давности, а затем выполнить ту ко% манду, которая ее соберет. После этого вы должны иметь возможность очистить дерево и собрать его заново с помощью той же команды, ни% чего не записывая в систему.
Вы ничего не теряете, разбив это на два этапа. Легко написать скрипт, который объединит оба этапа в одну процедуру извлечения/сборки – он будет полезен для ночной сборки. В этих скриптах ночной сборки важно каждый раз начинать со свежего исходного дерева (чтобы избе% жать проблем, имевшихся в предыдущем дереве). Это хорошая про% верка вашего исходного дерева: удалив его и выполнив полную сборку заново, вы удостоверитесь в наличии всех файлов и их свежести (мож% но забыть что%то сохранить в системе).
При включении в этап сборки извлечения исходного кода могут также возникнуть следующие проблемы:
•Вы не хотите, чтобы система сборки автоматически получала фай% лы из хранилища при выполнении сборки. Обычно не требуется,
чтобы при каждой новой сборке все исходные данные менялись.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
626m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Ответы и обсуждениеClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Важно управлять кодом, с которым вы работаете, а не подчиняться прихотям системы сборки.
•Существует проблема начальной загрузки: если извлечение кода входит в процедуру сборки, то откуда взять исходное дерево, чтобы начать сборку? Придется получать его вручную! Либо потребуются дополнительные заклинания, чтобы частично загрузить части дере% ва, управляющие сборкой, а потом уже выполнить настоящую за% грузку и сборку. Этим путем идти не нужно.
3.Куда следует записывать промежуточные (например, объектные) фай лы, возникающие при сборке?
В некоторых системах сборки объектные файлы размещаются рядом с исходными, которые их породили. Более развитые системы могут создавать параллельное дерево каталогов и собирать объекты в нем, оставляя исходные каталоги в неприкосновенности. Когда исходные и сгенерированные файлы разделены, наблюдается бо]льший порядок. Есть в этом и отрицательная сторона: сложнее осуществлять поиск в иерархии. Повторную компиляцию можно инициировать, удалив объектный файл .o, но при раздельных деревьях для этого придется дальше удалиться от источника.
Другой хороший подход – размещение промежуточных файлов внут% ри исходного дерева, но в собственном подкаталоге; они не будут пу% таться с исходными файлами, но все же будут под рукой. В итоге полу% чится иерархия каталогов, примерно как на рис. 1.
Это удобный способ сборки нескольких целей из одного исходного де% рева – у каждой цели оказывается собственный подкаталог сборки.
Рис. 1. Помещение генерируемых объектных файлов в подкаталог
Рис. 2. Еще лучше: помещение генерируемых объектных файлов в именованный подкаталог
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Главаm |
10. Код, который построил Джек |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
627Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
В ином случае сборка могла бы начаться как отладочная, а завершить% ся как окончательная, и этап сборки стал бы катастрофой. Такой под% ход приводит к дереву, показанному на рис. 2.
4.Следует ли при добавлении в систему сборки автоматизированного на бора тестов запускать его автоматически после сборки или лучше вво дить для запуска тестов отдельную команду?
Ввести отдельную команду нетрудно (например, сделать в make%файле цель tests и после make all ввести make tests). Однако есть шанс, что этот дополнительный шаг не будет выполнен – он не обязателен. Таким об% разом, тестирование будет пропущено. С учетом человеческих особенно% стей это вполне вероятно. Непротестированный код может вызвать са% мые разнообразные проблемы, и весь труд по написанию тестов окажет% ся напрасным. Лучше включить тесты в основную процедуру сборки.
Автоматически запускать тестирование под нагрузкой на этом этапе не стоит. Оно может отнять слишком много времени, и потому его лучше оставить для ночной сборки. Постройте структуру для автоматическо% го выполнения этих тестов, но в обычной сборке не запускайте ее.
5. Какую сборку выполнять ночью – отладочную или финальную?
Обе. Очень важно как можно раньше проверить конфигурацию окон% чательной сборки. Отладочные сборки не должны попадать в отдел QA и тем более выходить за пределы компании.
Важно проверять работоспособность процедур окончательной и отла% дочной сборок не только при организации системы сборки, но на по% стоянной основе. Крайне легко сорвать ту или другую процедуру в ре% зультате малозначительной модификации. Если откладывать провер% ку сборки до того момента, когда наступит окончательный срок, и ока% жется, что она не работает, это будет крайне неприятным сюрпризом.
Выполняемые модули, генерируемые при отладочной и окончатель% ной сборках, могут сильно различаться. Поведение некоторых компи% ляторов в отладочном и окончательном режимах разительно расходит% ся. Существует один популярный компилятор, который в отладочных сборках расширяет буферы данных, так что нехватка памяти не вызы% вает проблем и проходит незамеченной – едва ли это можно считать хорошей помощью в отладке. Если вы проверяли только отладочную сборку, то при переходе в окончательный режим перед самой постав% кой продукта неизбежно столкнетесь с проблемами.
6.Напишите для make правило, которое с помощью компиляторабудет автоматически генерировать информацию о зависимостях. Покажите, как пользоваться этой информацией в make файле.
Это можно сделать несколькими способами, что частично зависит от то% го, как вы получаете данные о зависимостях от своего компилятора. До% пустим, что некий гипотетический компилятор принимает параметрdep, выражающий просьбу создать наряду с объектным файлом файл
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
628m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Ответы и обсуждениеClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
зависимостей. Допустим, что формат этого файла уже соответствует формату зависимостей make.1 GNU Make позволяет задать правило ком% пиляции, побочным эффектом которого будет генерация зависимостей:
%.o: %.c
compiler object %.o !dep %.d %.c
Вы можете включить все сгенерированные файлы зависимостей прямо в make%файл с помощью следующей строки в его конце:
include *.d
Вот и все! Разумеется, это лишь простейший способ. Есть много путей развить его дальше. Например:
•Отправить файлы зависимостей в отдельный каталог. Тогда они не будут загромождать рабочий каталог и скрывать важные файлы.
•Написать правило include так, чтобы оно включало только нужные файлы .d. Кругом могут находиться другие файлы .d, которые не должны включаться, и применение маски * может нарушить рабо% ту make. Такая проблема может легко возникнуть: если вы уберете из make%файла какой%то исходный файл и не почистите перед тем дерево, то старые файлы .o и .d будут болтаться в рабочих катало% гах, пока вы не удалите их вручную.
•Если позволит компилятор, можно написать отдельное правило для создания .d%файлов, сделав их равноправными участниками сборки. Недостаток этого в замедлении процесса сборки: для каж% дого исходного файла компилятор будет вызываться дважды.
7.Рекурсивная сборка – популярный метод создания модульной системы сборки, охватывающей несколько каталогов. Однако ей свойственен ряд недостатков. Опишите их и предложите альтернативные методы.
Здравый смысл подсказывает, что все большие проекты, собираемые с помощью make%файлов, должны пользоваться техникой рекурсии. Однако, несмотря на мощь рекурсивной make, ей присущи фундамен% тальные недостатки, которые не следует игнорировать. Необходимо понимать, как работает (или не работает) рекурсивная make, потому что она очень часто встречается; необходимо знать связанные с ней проблемы, чтобы выбрать лучшее решение.
Почему рекурсивная make может стать обузой? Она таит ряд ловушек:
Скорость
Ну, о#о#очень медленно работает. Если вы запустите повторную сборку дерева, которое уже готово, рекурсивная сборка все равно честно пройдет по всем каталогам. Для проекта хорошего размера
1Это вполне оправданное допущение: есть много систем, действующих та%
ким образом.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Главаm |
10. Код, который построил Джек |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
629Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
это займет массу времени, что неоправданно, когда никаких дейст% вий не требуется.
Каждый каталог собирается посредством отдельного вызова.1 Это исключает ряд возможностей оптимизации: общие включаемые файлы будут обрабатываться раз за разом снова. Хотя файловые системы умеют кэшировать данные, все равно здесь возникают лишние потери. В разумной системе сборки каждый файл нужно прочесть только один раз.
Зависимости
Рекурсивная make не может корректно отслеживать зависимости; make%файлы в подкаталогах не имеют возможности получить все сведения о зависимостях. make%файл вашего модуля может обнару% жить, что его локальный исходный файл func1.c зависит от заголо% вочного файла shared.h в другом каталоге. Он благополучно станет компилировать func1.c после каждой модификации shared.h. Но shared.h может автоматически генерироваться другим модулем по некоторому шаблону, скажем shared.tmpl. Ваш модуль не будет знать об этой дополнительной зависимости. Даже если бы он знал о ней, он не смог бы собрать shared.h – это просто не его работа. Та% ким образом, после модификации shared.tmpl исходный файл func1.c не будет собран заново, как следовало бы.
Эту прореху можно залатать, только если собирать shared.h раньше, чем модуль func1.c. Чтобы обеспечить правильность сборки, про% граммисту необходимо тщательно выбирать порядок рекурсии.2 Чем более опосредованными оказываются зависимости, тем больше беспорядка возникает в итоге.
Сталкиваясь с такими проблемами, программисты придумывают всякие некрасивые способы их обойти, например выполнение не% скольких проходов сборки дерева или ручное удаление некоторых файлов, которое инициирует повторную сборку. Такие приемы приводят к лишнему удлинению сборки и неоправданному услож% нению процедуры.
Перенесение вины на разработчика
Make была придумана, чтобы справиться со сложностью повторной сборки кода. Рекурсивная make ставит все с ног на голову и снова заставляет вас заниматься процедурой сборки. Как мы видели, про% граммисту приходится управлять порядком рекурсии и заставлять каждый make%файл обходить ограничения.
1Представьте себе только накладные расходы, связанные с запуском всех этих дочерних процессов!
2Это очко в пользу графических средств – в них нет рекурсии, и обычно они
правильно справляются с зависимостями.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
630m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Ответы и обсуждениеClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Скрытость
Проблемы рекурсивной make не бросаются в глаза. По этой причи% не многие благосклонно относятся к рекурсии. Когда же что%то пе% рестает ладиться, это проявляется странным образом. Причину проблемы редко удается установить, и ее списывают на влияние «нечистой силы».
Тем самым создаются дополнительные условия для появления опасно неустойчивых систем.
Эти проблемы несправедливо приписывают самой make, утверждая, что в ней есть дефекты. Но в данном случае make оказывается лишь не% винным свидетелем. Порок не в make, а в том, как мы ею пользуемся. Все проблемы создаются рекурсией, которая мешает make правильно делать свое дело.
Как же справиться с этими неприятностями? Очевидно, не хотелось бы отказываться от вложенности в деревьях исходного кода. Нужен такой процесс сборки, который поддерживает вложенность, но не ве% дет сборку рекурсивно. Это не слишком сложно; такой прием называ% ется вложенной make. Он просто предполагает помещение всех дан% ных о сборке в один главный make%файл. При этом нет необходимости в отдельных make%файлах для каждого подкаталога. Главный make% файл управляет всеми подкаталогами исходных файлов внутри себя.
Вопреки распространенному мнению, рекурсивное применение make – плохой прием. Вместо него следует пользоваться более надежной процедурой вло& женной make.
Может показаться, что такой подход сложнее и менее гибок. Как упра% виться с большим деревом сборки с помощью лишь одного make%файла?
Эта задача облегчается с помощью ряда практических методов реали% зации:
•Воспользуйтесь механизмом make для включения файлов. Помести% те в каждом каталоге список содержащихся в нем исходных файлов – так будет гораздо проще и понятнее. Сохраните список в файле, дав ему имя типа files.mk, и включайте этот файл из главного Makefile.
•Можно сохранить модульность рекурсивной make – вход в любой каталог и выполнение там команды make – путем определения про% межуточных целей. Эти цели образуют специфические части про% екта. Организация модульной сборки таким способом может ока% заться более осмысленной, чем подход рекурсивной make на основе случайного обхода каталогов, и гарантирует правильную сборку каждой промежуточной цели.
Вложенная make нисколько не сложнее рекурсивной; на практике она даже может быть проще. С ее помощью сборка становится надежнее, точнее и быстрее.