Студенческая лаборатория МФТИ-Intel Модульное программирование. Сборка проектов из нескольких файлов. Утилита make. Максим Кузнецов maks.kuznetsov@gmail.com 30.11.2012 http://ilab.mipt.ru Мотивация Зачем разбивать программный проект на несколько файлов? (чем плохо, когда весь код написан в одном файле?) http://ilab.mipt.ru 2 Мотивация Зачем разбивать программный проект на несколько файлов? (чем плохо, когда весь код написан в одном файле?) Для программы, по сложности сопоставимой с «Hello World» или домашним заданием по информатике на 1-м курсе — незачем. Рассмотрим случай более сложных программ. http://ilab.mipt.ru 3 Мотивация • Сложно удержать в голове тысячи строк кода, расположенных в одном месте; • Сложность совместной разработки проекта; • Желание использовать уже написанный код в других проектах; • Длительное время компиляции проекта http://ilab.mipt.ru 4 Мотивация • Сложно удержать в голове тысячи строк кода, расположенных в одном месте; • Сложность совместной разработки проекта; • Желание использовать некоторый код в других проектах; • Длительное время компиляции проекта Выход: разделение проекта на функциональные части (модули) http://ilab.mipt.ru 5 Модуль • Функционально законченный фрагмент программы, оформленный в виде отдельного файла с исходным кодом, предназначенный для использования в других программах. • Позволяют разбивать сложные задачи на более мелкие. • Предоставляет программистам удобную для многократного использования функциональность (интерфейс) в виде набора функций, классов, констант. http://ilab.mipt.ru 6 Модульный подход Схема заимствована отсюда: http://mmcs.sfedu.ru/~dubrov/files/sl_oop_02_module.pdf http://ilab.mipt.ru 7 Пример модульной структуры (код – в комментарии к слайду и во вложении к лекции) main.exe calc.o calc.c http://ilab.mipt.ru hello.o hello.c calc.h 8 hello.h Сборка проекта Самый простой путь: перечислить компилятору все исходные файлы: gcc ./calc.c ./hello.c ./main.c -o main.exe http://ilab.mipt.ru 9 Сборка проекта Самый простой путь: перечислить компилятору все исходные файлы: gcc ./calc.c ./hello.c ./main.c -o main.exe Работает, но не самое лучшее решение. Почему? http://ilab.mipt.ru 10 Сборка проекта Самый простой путь: перечислить компилятору все исходные файлы: gcc ./calc.c ./hello.c ./main.c -o main.exe Работает, но не самое лучшее решение. Почему? Если в проекте тысячи строк кода и много исходных файлов, то каждая перекомпиляция будет длиться очень долго. http://ilab.mipt.ru 11 Сборка проекта Сборка объектов по отдельности: gcc gcc gcc gcc ./calc.c –c ./calc.o ./hello.c –c ./hello.o ./main.c –c ./main.o ./calc.o ./hello.o ./main.o –o main.exe http://ilab.mipt.ru 12 Сборка проекта Сборка объектов по отдельности: gcc gcc gcc gcc ./calc.c –c ./calc.o ./hello.c –c ./hello.o ./main.c –c ./main.o ./calc.o ./hello.o ./main.o –o main.exe Если теперь мы изменим, например, calc.c, то достаточно будет пересобрать соответствующий объектный файл и объединить все объектные файлы в исполняемый. http://ilab.mipt.ru 13 Сборка проекта Сборка объектов по отдельности: gcc gcc gcc gcc ./calc.c –c ./calc.o ./hello.c –c ./hello.o ./main.c –c ./main.o ./calc.o ./hello.o ./main.o –o main.exe Если теперь мы изменим, например, calc.c, то достаточно будет пересобрать соответствующий объектный файл и объединить все объектные файлы в исполняемый. http://ilab.mipt.ru Всего два запуска компилятора (вместо, скажем, тысячи ) 14 Сборка проекта Сборка объектов по отдельности: gcc gcc gcc gcc ./calc.c –c ./calc.o ./hello.c –c ./hello.o ./main.c –c ./main.o ./calc.o ./hello.o ./main.o –o main.exe Если теперь мы изменим, например, calc.c, то достаточно будет пересобрать соответствующий объектный файл и объединить все объектные файлы в исполняемый. http://ilab.mipt.ru Всего два запуска компилятора (вместо, скажем, тысячи ) Было бы хорошо, если бы это делалось автоматически… 15 Концепция автоматической сборки Вспомним дерево сборки для нашего примера: main.exe calc.o calc.c http://ilab.mipt.ru hello.o hello.c calc.h 16 hello.h Концепция автоматической сборки • Каждый узел дерева можно рассматривать как некоторую цель, для выполнения которой требуются зависимости, представленные дочерними узлами и листьями (исходные и заголовочные файлы), а также выполнение некоторых действий (сборка); • Начальная вершина – наша главная цель (сборка исполняемого файла) http://ilab.mipt.ru 17 Концепция автоматической сборки main.exe calc.o calc.c hello.o hello.c calc.h hello.h Сборка проекта ≡ обход дерева сборки http://ilab.mipt.ru 18 Концепция автоматической сборки main.exe calc.o calc.c hello.o hello.c calc.h Изменим hello.с http://ilab.mipt.ru 19 hello.h Концепция автоматической сборки main.exe calc.o calc.c http://ilab.mipt.ru hello.o hello.c calc.h 20 hello.h Концепция автоматической сборки main.exe calc.o calc.c http://ilab.mipt.ru hello.o hello.c calc.h 21 hello.h Концепция автоматической сборки main.exe calc.o calc.c http://ilab.mipt.ru hello.o hello.c calc.h 22 hello.h Система сборки make • GNU make — утилита, автоматизирующая процесс преобразования файлов из одной формы в другую. • В нашем случае это компиляция исходного кода в объектные файлы и последующая компоновка в исполняемые файлы. • Производит эффективный обход дерева сборки, обновляя только ту часть, которая зависит от изменённых файлов. http://ilab.mipt.ru 23 Система сборки make • Утилита make выполняет команды согласно правилам, указанным в файле Makefile в текущем каталоге, в котором должна быть структура дерева сборки в формате: цель1 цель2...: реквизит1 реквизит2 ... <tab> команда1 команда2 ... http://ilab.mipt.ru 24 Система сборки make • Утилита make выполняет команды согласно правилам, указанным в файле Makefile в текущем каталоге, в котором должна быть структура дерева сборки в формате: цель1 цель2...: реквизит1 реквизит2 ... <tab> команда1 команда2 ... Обычные консольные команды Символ табуляции обязателен! http://ilab.mipt.ru 25 Пример Makefile main.exe: main.o calc.o hello.o gcc ./main.o ./calc.o ./hello.o -o ./main.exe calc.o: calc.c calc.h gcc -Wall ./calc.c -c -o ./calc.o hello.o: hello.c hello.h gcc -Wall ./hello.c -c -o ./hello.o main.o: main.c calc.h hello.h gcc -Wall ./main.c -c -o ./main.o http://ilab.mipt.ru 26 Пример Makefile main.exe: main.o calc.o hello.o gcc ./main.o ./calc.o ./hello.o -o ./main.exe calc.o: calc.c calc.h gcc -Wall ./calc.c -c -o ./calc.o hello.o: hello.c hello.h gcc -Wall ./hello.c -c -o ./hello.o main.o: main.c calc.h hello.h gcc -Wall ./main.c -c -o ./main.o При запуске make без параметров начнётся сборка первой цели в файле, т.е. main.exe По умолчанию используется имя файла Makefile. С помощью опции -f можно явно указать другой файл: make -f Makefile_simple http://ilab.mipt.ru 27 Пример Makefile ~/share/ilab/lect> make gcc -Wall ./main.c -c -o ./main.o gcc -Wall ./calc.c -c -o ./calc.o gcc -Wall ./hello.c -c -o ./hello.o gcc ./main.o ./calc.o ./hello.o -o ./main.exe /* поменяли код в файле calc.c */ ~/share/ilab/lect> make gcc -Wall ./calc.c -c -o ./calc.o gcc ./main.o ./calc.o ./hello.o -o ./main.exe http://ilab.mipt.ru 28 Улучшения Makefile CFLAGS=-Wall -MD CC=gcc SOURCES=main.c calc.c hello.c TARGET=main.exe $(TARGET): $(SOURCES:.c=.o) $(CC) $^ -o $@ include $(wildcard *.d) clean: rm -f *.o *.d *.exe $(TARGET) http://ilab.mipt.ru 29 Улучшения Makefile CFLAGS=-Wall -MD CC=gcc Переменные SOURCES=main.c calc.c hello.c TARGET=main.exe $(TARGET): $(SOURCES:.c=.o) $(CC) $^ -o $@ include $(wildcard *.d) clean: Сокращения: Список зависимостей Название цели Отсутствие целей для объектных файлов (Makefile сделает их сам) Вспомогательные цели rm -f *.o *.d *.exe $(TARGET) http://ilab.mipt.ru 30 Дополнительные материалы • GNU Make Manual http://www.gnu.org/software/make/manual/ • Небольшая статья “Create a makefile” http://www.fortystones.com/create-makefile/ • Шпаргалки по Makefile – http://www.schacherer.de/frank/technology/tools/make.html – http://www.cs.duke.edu/~ola/courses/programming/Makefiles/node1.html http://ilab.mipt.ru 31 Студенческая лаборатория МФТИ-Intel Спасибо за внимание! Материалы лекций можно найти: • • • http://ilab.mipt.ru http://wiki.ilab.mipt.ru http://groups.google.com/group/ilab_edu http://ilab.mipt.ru