A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
How-to-compile-project / docks / Makefiles.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cannot retrieve contributors at this time
327 lines (256 sloc) 13.1 KB
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents Copy raw contents
Copy raw contents
Для кого и для чего
Для всех. Для автоматизации сборки вашего проекта. Сборка — это когда из файликов вашего проекта собирается исполняемый файл.
Это макро процессор, который преобразовывает вашу программу для дальнейшего компилирования. На данной стадии происходит работа с препроцессорными директивами. Например, препроцессор добавляет заголовочные файлы в код (#include) , убирает комментирования, заменяет макросы (#define) их значениями, выбирает нужные куски кода в соответствии с условиями #if, #ifdef и #ifndef .
Этапы компиляции на Си: предобработка, трансляция, компоновка
На этом этапе не происходит компиляция как такова. Происходит вставка все заголовочных файлов, дефайнов и уже полученный файл потом компилируется. Используем флаг -E
gcc -E main.c -o 1.txt
В файле 1.txt будет настоящий код вашей программки, который будет компилироваться.
На данном шаге компилятор выполняет свою главную задачу — компилирует, то есть преобразует полученный на прошлом шаге код без директив в ассемблерный код. Это промежуточный шаг между высокоуровневым языком и машинным (бинарным) кодом.
gcc -S main.c -o main.s
Так как x86 процессоры исполняют команды на бинарном коде, необходимо перевести ассемблерный код в машинный с помощью ассемблера.
Ассемблер преобразовывает ассемблерный код в машинный код, сохраняя его в объектном файле (файле с расширением .о)
as main.s -o main.o
Компоновщик (линкер) связывает все объектные файлы и статические библиотеки в единый исполняемый файл, который мы и сможем запустить в дальнейшем.
gcc main.o -o bin
bin — исполняемый файл
Объединение этапов Когда мы пишем
gcc main.c -o bin
И получаем из файлика с исходным кодом исполняемый файл. Но мы же не пишем руками все эти флаги, верно? Потому что, если в явном виде не указан один из флагов выше, то выполняются все четыере этапа сборки в исполняемый файлы.
Если у нас все в одном файле, то проблем нет. А если в проекте несколько файлов? Руками каждый раз компилировать и линковать файлы каждый достаточно грустно. Давайте автоматизирум?
В качествет тестового примера предлагаю скачать данные из репозитория: https://github.com/Klavishnik/How_to_fuzz_lab
Там уже есть Makefile. Мы напишем свой. В данном проекте есть три файлика
project │ ├── lib │ └──string.c │ └──string.h │── main.c
Нам нужно отдельно скомпилировать файлы нашей папки lib и main.c
C++ с нуля | 08 | Компиляция и линковка
Выполним следующие команды (будем использовать компилятор clang. Он круче)
clang -c main.c -o main.o clang -c lib/string.c -o string.o clang main.o string.o -o bin -lreadline
Флагом -c мы объединяем два этапа — компиляция и ассемблирования. Из файла с исходным кодом получаем объектный файлик. Флагом -o в янвном виде указываем название файла, который у нас получится в результате выполнения команды.
На этапе линковки мы также подключаем динамические (shared) библиотеки. Так для этой задачи мы заиствовали фнукцию из билиотки readline. Данная библиотека есть в системе. Чтобы использовать функцию, мы должны прилинковать данную библиотеку флагом -lreadline
Для автоматизации сборки есть ряд программ, но все они инкапсулируют работу программы make . Данная утилитка принимает на вход Makefile (именно с таким названием).
Создаем данный флайик и пишем туда следующее:
all: clang -c main.c -o main.o clang -c lib/string.c -o string.o clang main.o string.o -o bin -lreadline
Теперь, если находясь внутри папки мы запустим make make То просиходит следующее:
make начинает искать внутри директории Makefile и если находит, выполянет стадию all .
Все, простейший makefile написан. Вы автоматизировали сборку своего проекта.
Что же такое all ? Make работает стадиями, которые принимает в качестве аргумента. Если аргумента нет, то make выполняет стадию all . Т.е. в Makefile должна быть стадия all. Как описать стадию?
Добавим к файлику выше еще одну стадию. Пишем название, например clean Далее, нажимет Enter и tab (табаемся). Все что внутри стадии должно быть с отступом. Пишем:
clean: rm -rf *.o rm bin
make clean
То внутри папки будут удалены все файлики с расширением .o, а также исполняемый файлик bin.
Т.е. make в качестве аргумента примет название той стадии, которую нужно выполнить. А как выполнить несколько стадий?
Изменим Makefile вот так:
all: build clean build: clang -c main.c -o main.o clang -c lib/string.c -o string.o clang main.o string.o -o bin -lreadline clean: rm -rf *.o rm bin
Старую стадию all мы заменили на стадию build . В стадии all появился вызов стадий build и clean Теперь, вызвав просто make мы выполним обе стадии, т.е. скопмилируем проект и удалим все новые файлы.
Напоминаю, чтобы вызвать конкретную стадию, пишем:
make build make clean
Так мы по очереди вызвали обе стадии.
all: build clean build: clang -c main.c -o main.o clang -c lib/string.c -o string.o clang main.o string.o -o bin -lreadline debug: clang -c main.c -o main.o -g clang -c lib/string.c -o string.o -g clang main.o string.o -o bin -lreadline clean: rm -rf *.o rm bin
Тут мы добавили стадию debug — в ней добавлены отладочные стадии при компиляции. Это пригодится, если придется отлаживать программу с помощью gdb .
Makefile который не Makefile
А если у нас Makefile имеет другое название? Например file_make? Используй флаг -f
make -f file_make make debug -f file_make
Слышали про переменные? Раскрою секрет — в make они тоже есть.
CC — так мы назвали переменную. Вообще это стандартное название ( CC — compiler C) для переменной, которая хранит компилятор для Си.
CC=clang all: build clean build: $(CC) -c main.c -o main.o $(CC) -c lib/string.c -o string.o $(CC) main.o string.o -o bin -lreadline debug: $(CC) -c main.c -o main.o -g $(CC) -c lib/string.c -o string.o -g $(CC) main.o string.o -o bin -lreadline clean: rm -rf *.o rm bin
Тут мы заменили clang на переменную CC. Чтобы make подставил значение переменной, при её вызове нужно использовать значок доллара $.
Некоторые флаги, например флаг -g или 02 — это флаги компилятора. Их нужно ставить на этапе компиляции .
Для флагов компилятора есть стандартная переменная CFLAGS.
Сделаем ткаой код:
CC=clang CFLAGS=-g all: build clean build: $(CC) -c main.c -o main.o $(CC) -c lib/string.c -o string.o $(CC) main.o string.o -o bin -lreadline debug: $(CC) -c main.c -o main.o $(CFLAGS) $(CC) -c lib/string.c -o string.o $(CFLAGS) $(CC) main.o string.o -o bin -lreadline clean: rm -rf *.o rm bin
Переменная LD и LDFLAGS
LD — это линковщик. В нашей ситуации компилятор и линковщик — одинаковы, поэтому данной переменной переопределять его не будем. А вот LDFLAGS — это флаги линковки. Это -lm и -lreadline
CC=clang CFLAGS=-g LDFLAGS=-lreadline all: build clean build: $(CC) -c main.c -o main.o $(CC) -c lib/string.c -o string.o $(CC) main.o string.o -o bin -$(LDFLAGS) debug: $(CC) -c main.c -o main.o $(CFLAGS) $(CC) -c lib/string.c -o string.o $(CFLAGS) $(CC) main.o string.o -o bin -lreadline clean: rm -rf *.o rm bin
Переменная на то и переменная, что её можно определить любую. Например, я хочу добавить санитайзеры к нашему проектуи под это дело сделаю свою переменную ASAN
CC=clang CFLAGS=-g LDFLAGS=-lreadline ASAN=-fsanitize=address all: build clean build: $(CC) -c main.c -o main.o $(CC) -c lib/string.c -o string.o $(CC) main.o string.o -o bin -$(LDFLAGS) debug: $(CC) -c main.c -o main.o $(CFLAGS) $(ASAN) $(CC) -c lib/string.c -o string.o $(CFLAGS) $(ASAN) $(CC) main.o string.o -o bin -lreadline $(ASAN) clean: rm -rf *.o rm bin
Переопредление переменных из системы
Удалим из Makefile’a переменную СС
CC=clang
и выполним make .
Ошибок не будет — код соберется. Почему? Потому что СС это стандартная переменная и она определена в системе. На сервере это сс (а это clang).
Переменные можно переоределить внутри системы
Допустим, хочу не clang , а gcc в качестве компилятора
Пишу в консоли (не в Makefile)
export CC=gcc
Данной коммандой мы переоделели переменную оболочки. Выполним make И видим, что сборка пошла с gcc.
Чтобы убрать переменную в оружении
unset CC
Источник: github.com
Понятие компиляции в информатике
Компиляция в информатике — это формирование программы, которое включает транслирование всех программных модулей.
Введение
Под компиляцией понимаются процессы, которые облегчают диалог специалиста по написанию программ и компьютера. Формируя на этапе завершения свою программу, каждый программист вынужден использовать программу компиляции. В техническом описании эта программа занимает очень скромное место и определяется как утилита, которая осуществляет компиляцию.
Компиляцией является операция преобразования программного приложения, которое выполнено на известном людям языке (определяется как язык высокого уровня), в команды языка низкого уровня, которые понимает компьютер. В итоге получается программа, приближённая к машинным кодам. Программа имеет вид объектного модуля или абсолютного кода. В отдельных случаях эта программа может походить на команды ассемблера. То есть, компиляцией является преобразование входных данных (исходного кода), описывающих некий алгоритм, выполненный на проблемном языке, в выбранный набор команд объектного кода (машинный язык).
Сдай на права пока
учишься в ВУЗе
Вся теория в удобном приложении. Выбери инструктора и начни заниматься!
Если сформулировать более коротко, то процесс компиляции — это трансляция программы из проблемно-ориентированного языка в машинно-ориентированный. Это простая и прозрачная формулировка, но на самом деле процесс компиляции представляется очень многоплановым.
Компиляция в информатике
Есть различные виды компиляции:
- Компиляция пакетного типа. Она преобразует сразу несколько исходных модулей в едином комплекте.
- Компиляция построчного типа. Аналог интерпретации, то есть пошаговой независимой компиляции всех следующих операторов.
- Условная компиляция, при которой преобразование исходного текста зависит от заданных исходных условий с помощью директив компилятора. То есть путём изменения величины некоторых констант, возможно отключать или наоборот включать преобразование участков исходной программы.
Чтобы программистам было удобно решать разнообразные задачи, можно выбрать наиболее подходящий для данного случая компилятор. Если же выполнить подробную классификацию компиляторов, то возможно определить некоторое количество аналогичных утилит:
«Понятие компиляции в информатике»
Готовые курсовые работы и рефераты
Решение учебных вопросов в 2 клика
Помощь в написании учебной работы
- Векторный компилятор. Выполняет перевод входных данных в машинный код, при этом выполняя подстройку под векторные процессоры.
- Гибкий компилятор. Базируется на модульном принципе и управляется при помощи таблиц. Его программа выполнена на языке высокого уровня и имеется возможность реализовать его посредством компилятора компиляторов.
- Инкрементальный компилятор. Выполняет вторичное преобразование компонентов исходной программы и приложений к ней, не делая перекомпиляцию всей программы.
- Компилятор пошаговый или выполняющий интерпретацию. Применяет методику поочерёдного проведения самостоятельной компиляции любого выбранного процесса исходного модуля.
- Компилятор компиляторов. Программа трансляции, воспринимающая формализованное представление языка программирования. У него имеется возможность самому сформировать необходимый компилятор для выбранного программного языка.
- Компилятор отладки. Способен выявить и устранить различные типы синтаксических ошибок.
- Резидентный компилятор. Имеет своё фиксированное расположение в оперативной памяти и может повторно применяться обширным диапазоном задач.
- Компилятор самокомпилируемый. Он формируется на том же языке, что и транслируемая программа. Это универсальный компилятор, в котором заложены семантика и синтаксис исходного языка. В его состав входят ядро, семантический и синтаксический загрузчики.
Наиболее часто встречаются задачи компиляции ядра для операционной системы Linux. Эта процедура способна разрешить обширный круг задач, которые связаны с привязкой аппаратного обеспечения и адаптации оптимальной версии операционной системы. При компиляции Java возможно применение компиляторов, которые работают под самыми разными платформами. Это даёт возможность перекомпиляции исходных кодов под нужды различных операционных систем разных брендов.
Структурное построение компилятора
В процессе выполнения компиляции, реализуются следующие этапы:
- Анализ лексики. Выполняется преобразование символики исходной программы в набор лексем (абстрактных единиц).
- Анализ синтаксиса (грамматики). Выполняется преобразование набора лексем в дерево разбора.
- Анализ семантики. Выполняется обработка дерева разбора для определения его смысловых значений (семантики). Это может быть процесс привязки идентификаторов к их видам, определение совместимости т так далее. В итоге формируется промежуточное представление (код), и возможно формирование дополненного дерева разбора или даже нового дерева. Возможно так же формирование абстрактного набора команд, удобного для последующего применения.
- Повышение оптимальности. Удаляются лишние образования и формируется упрощённый код без смысловых потерь. Процесс оптимизации возможно осуществлять на различных этапах. К примеру, можно оптимизировать промежуточный или окончательный машинный код.
- Процесс генерации кода. Выполняется трансформация промежуточных представлений в коды конечного языка.
Замечание 1
Для разных типов компиляторов возможно подразделение или совмещение данных этапов в различных вариациях.
Трансляция и компоновка
Следует отметить один важный исторический момент. На ранних стадиях своего развития, компилятор имел возможность выполнять не только трансляцию, но также и компоновку программы. Он состоял из двух составляющих, собственно транслятора и компоновщика.
Это можно объяснить тем обстоятельством, что отделение компиляции от компоновки, как отдельного этапа, произошло гораздо позднее разработки компиляции. Но и сегодня во многих известных компиляторах есть физическое объединение с компоновщиками. По этой причине, терминология «компилятор» иногда заменяется словом «транслятор», которое является фактически синонимом.
Источник: spravochnick.ru
Компиляция и компоновка программ в С/C++
Когда программисты разговаривают о программировании, они часто говорят: «программа откомпилировалась без ошибок», или, когда говорят программисту: «скомпилируй программу, посмотрим на результат работы». Такие разговорчики позднее могут стать источником путаницы для начинающих программистов. Компиляция и создание исполняемого файла — не синонимы!
Создание исполняемых файлов — это многоступенчатый процесс, основные составляющие которого: компиляция и компоновка. На самом деле, даже если программа «откомпилировалась без ошибок», она может не работать из-за возможной ошибки во время стадии компоновки. Весь процесс трансляции файлов исходного кода в исполняемый файл лучше было бы называть построением проекта.
Компиляция!
Компиляция относится к обработке файлов исходного кода (.c, .cc, или .cpp) и создании объектных файлов проекта. На этом этапе не создается исполняемый файл. Вместо этого компилятор просто транслирует высокоуровневый код в машинный язык.
Например, если вы создали (но не скомпоновали) три отдельных файла, у вас будет три объектных файла, созданные в качестве выходных данных на этапе компиляции. Расширение таких файлов будет зависеть от вашего компилятора, например *.obj или *.o. Каждый из этих файлов содержит машинные инструкции, которые эквивалентны исходному коду. Но вы не можете запустить эти файлы!
Вы должны превратить их в исполняемые файлы операционной системы, только после этого их можно использовать. Вот тут за дело берётся компоновщик.
Компоновка!
Вы спросите, почему этапы компиляции и компоновки разделены. Во-первых, таким образом легче реализовать процесс построения программ. Компилятор делает свое дело, а компоновщик делает свое дело — посредством разделения функций, сложность программы
снижается. Другим (более очевидным) преимуществом является то, что это позволяет создавать большие программы без необходимости повторения шага компиляции каждый раз, когда некоторые файлы будут изменены. Вместо этого, используется так называемая «условная компиляция». То есть объекты составляются только для тех исходных файлов, которые были изменены, для остальных, объектные файлы не пересоздаются. Тот факт, что каждый файл компилируется отдельно от информации, содержащейся в других файлах,
существует благодаря разделению процесса построения проекта на этапы компиляции и компоновки.
Интегрированная среда разработки (IDE) эти два этапа берёт на себя и вам не стоит беспокоиться о том, какие из файлов были изменены. IDE сама решает,когда создавать объекты файлов, а когда нет.
Зная разницу между фазами компиляции и компоновки вам будет намного проще находить ошибки в своих проектах. Компилятор отлавливает, как правило, синтаксические ошибки — отсутствие точки с запятой или скобок. Если вы получаете сообщение об ошибке, множественного определения функции или переменной, знайте, вам об этом сообщает компоновщик. Эта ошибка может означать только одно, что в нескольких файлах проекта определены одна и та же функция или переменная.
Источник: cppstudio.com