Подключишь к проекту библиотеку GLFW, которая откроет окно, и проверишь, что код GLFW собирается вместе с app.
Откроем наш проект в Visual Studio. Сейчас программа app только печатает строку в консоль. Движку нужно окно, в котором видеокарта будет рисовать картинку.
Окно в Windows создают через WinAPI
Набор функций, которые Windows даёт программам: создать окно, прочитать нажатую клавишу, открыть файл. Функции объявлены в заголовке windows.h.
, и это несколько сотен строк кода: зарегистрировать в Windows вид окна, создать окно, разбирать события, которые Windows присылает окну (нажатия клавиш, движение мыши, щелчок по крестику), и отдельно подготовить окно к рисованию через OpenGL. Такой код работает только в Windows. Поэтому окно откроет библиотекаGLFW: она делает ту же работу несколькими вызовами, и те же вызовы работают на Linux и macOS.
FetchContent
GLFW — чужой код, и этот код нужно как-то получить. Можно скачать готовую библиотеку с сайта GLFW и прописать в проекте пути к файлам, но тогда каждый, кто скачает твой проект, должен повторить скачивание и настройку путей руками и угадать ту же версию
Номер вида 3.5.1, под которым автор выпускает очередное состояние библиотеки. От версии к версии в библиотеке что-то добавляют или меняют, поэтому код, написанный под одну версию, на другой может не собраться или работать иначе.
библиотеки. Мы попросим CMake скачать GLFW самому. Это умеет модуль
Файл с готовыми командами CMake, который приходит вместе с самим CMake. Строка include подключает модуль к проекту, и после неё команды из модуля можно писать в CMakeLists.txt.
CMake FetchContent («получить содержимое»). Адрес и версия GLFW будут записаны в CMakeLists.txt, поэтому любой, кто откроет проект, получит ту же GLFW 3.5.1.
Если не получилось1
Failed to clone repository: 'https://github.com/glfw/glfw.git' Строка стоит в окне OutputВывод среди строк CMake Error, и строки CMake generation finished. нет.
Почему
Скачивает GLFW программа Git, которую запустил CMake. Git не смог получить GLFW с GitHub: либо нет интернета, либо в адресе после GIT_REPOSITORY или в номере после GIT_TAG есть опечатка, и на GitHub нет такого репозитория или тега.
Что сделать
Проверь, что интернет работает, например открой в браузере https://github.com/glfw/glfw. Сравни строки GIT_REPOSITORY и GIT_TAG в твоём CMakeLists.txt с карточкой выше: совпадать должна каждая буква. Потом сохрани CMakeLists.txt (Ctrl+S), и Visual Studio запустит настройку заново.
Вот что делал CMake, пока шла настройка. Реплики идут по строкам раздела «Dependencies», который ты дописал в CMakeLists.txt.
Как CMake FetchContent скачивает и подключает библиотеку
CMakeLists.txtстроки 11–25коммит a24c0c1
include(FetchContent)# --- GLFW -------------------------------------------------------------------set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)FetchContent_Declare( glfwGIT_REPOSITORY https://github.com/glfw/glfw.gitGIT_TAG 3.5.1GIT_SHALLOW TRUE)FetchContent_MakeAvailable(glfw)
На схеме четыре полки. Сверху — что CMake запомнил при настройке, ниже — GitHub, сайт, где лежит GLFW, затем папка _deps внутри папки сборки out\build\vs2026, а в самом низу — цели проекта. До этой правки в проекте была одна цель — app. Первая строка блока, include(FetchContent), подключает модуль FetchContent
Модуль CMake, который умеет скачивать чужой код во время настройки проекта. Команды модуля называются по одной схеме. Возьмём команду FetchContent_Declare: сначала имя модуля, затем подчёркивание, затем действие, которое команда выполняет. Так по названию видно, из какого модуля команда.
: в модуле записаны команды FetchContent_Declare и FetchContent_MakeAvailable, без этой строки CMake таких команд не знает.
Четыре строки set выключают части GLFW, которые нам не нужны: GLFW_BUILD_EXAMPLES — примеры программ, GLFW_BUILD_TESTS — тесты, GLFW_BUILD_DOCS — документацию, GLFW_INSTALL — правила установки
Команды, которые копируют готовую библиотеку и заголовки в общую папку на компьютере, например в C:\Program Files, чтобы библиотеку могли брать другие проекты. Нам это не нужно: GLFW живёт внутри папки сборки нашего проекта.
. Каждая строка set берёт одну настройку GLFW и записывает для неё значение OFF («выключено») в файл CMakeCache.txt. Это общий файл настроек
Файл CMakeCache.txt в папке out\build\vs2026. CMake записывает туда, что выяснил при настройке: где компилятор, какие значения у настроек. При следующей настройке CMake сначала читает этот файл.
в папке сборки. Слово CACHE значит «записать в этот файл», BOOL — тип настройки: переключатель с двумя значениями, ON («включено») и OFF («выключено»), поэтому у нас стоит OFF, пустые кавычки — описание настройки, которое здесь не нужно. FORCE («принудительно») нужен, чтобы OFF записалось, даже если в файле уже лежит другое значение
Файл CMakeCache.txt не стирается при новой настройке проекта: CMake читает его в начале и дописывает новое. Допустим, настройка уже проходила, и в файле записано GLFW_BUILD_EXAMPLES = ON. Если теперь добавить строку set со словом CACHE, но без FORCE, CMake увидит, что такая настройка в файле уже есть, и оставит ON: примеры снова собрались бы. С FORCE команда set заменяет значение на OFF при каждой настройке, что бы там ни лежало.
.
FetchContent_Declare ничего не скачивает, а только записывает, то есть объявляет (по-английски declare), что такое glfw и где GLFW взять. Слово glfw здесь — имя, под которым FetchContent запомнит эту запись. GIT_REPOSITORY — адрес репозитория
Папка проекта вместе с историей коммитов в Git. Репозиторий GLFW лежит на GitHub, и его может скачать любой.
GLFW на GitHub. GIT_TAG — какую версию взять: 3.5.1 — тег
Тег не двигается: 3.5.1 всегда указывает на один и тот же коммит, что бы авторы ни дописывали в репозиторий потом.
, то есть имя, которым авторы GLFW пометили в истории один коммит: тот, на котором работа над версией 3.5.1 закончена. Поэтому CMake получит ровно эту версию, а не то, что авторы дописали после неё. GIT_SHALLOW TRUE — скачать только этот коммит, без истории
Git хранит не только последнее состояние проекта, но и каждое прошлое изменение: у GLFW это тысячи коммитов за много лет. Эта цепочка прошлых коммитов и есть история. Обычно Git скачивает её целиком, а GIT_SHALLOW TRUE («неглубокая копия») оставляет один коммит, нужный для сборки.
.
FetchContent_MakeAvailable скачивает GLFW. Для этого CMake запускает программу Git — ту же, что ты установил отдельно, когда ставил Visual Studio и CMake, — и Git копирует файлы GLFW с GitHub в папку glfw-src внутри _deps, которую можно найти на диске
Если проект лежит в D:\dev\MyEngine, то папка с исходниками GLFW — D:\dev\MyEngine\out\build\vs2026\_deps\glfw-src. Открой эту папку в Проводнике, и там будут файлы GLFW.
. Без GIT_SHALLOW TRUE Git скачал бы всю историю GLFW, тысячи коммитов за много лет: это дольше и занимает больше места на диске, а нужен нам только один коммит.
Затем та же команда FetchContent_MakeAvailable велит CMake прочитать файл CMakeLists.txt
У GLFW есть собственный CMakeLists.txt, такой же по назначению, как твой: авторы GLFW написали в нём, какие файлы собирать и какие цели создавать. Файл лежит в корне скачанной папки, например в D:\dev\MyEngine\out\build\vs2026\_deps\glfw-src\CMakeLists.txt. Файл GLFW не заменяет твой CMakeLists.txt и ничего в нём не меняет: CMake читает оба файла по очереди, и в проекте появляются цели из каждого.
из папки glfw-src, как будто этот файл — часть твоего проекта. CMake выполняет команды, которые авторы GLFW записали в этом файле. Каждая из четырёх настроек, которые ты выключил строками set, проверяется в главном файле GLFW, glfw-src\CMakeLists.txt, который лежит прямо в папке glfw-src, условием вида if (GLFW_BUILD_EXAMPLES)
У каждой из четырёх настроек в файле CMakeLists.txt для GLFW есть своё условие if. Команды внутри выполняются, только если настройка включена (ON). У нас OFF, поэтому CMake условия пропускает. Для GLFW_INSTALL показано начало: внутри ещё несколько строк install.glfw-src\CMakeLists.txtстроки 95–105
if (GLFW_BUILD_EXAMPLES) add_subdirectory(examples)endif()if (GLFW_BUILD_TESTS) add_subdirectory(tests)endif()if (GLFW_BUILD_DOCS) add_subdirectory(docs)endif()
glfw-src\CMakeLists.txtстроки 111–112
if (GLFW_INSTALL) install(…)
. CMake берёт значение настройки из CMakeCache.txt, видит там OFF и пропускает примеры, тесты, документацию и правила установки. Затем CMake записывает в папку glfw-buildсписок дел
Файлы проекта Visual Studio: GLFW.slnx и src\glfw.vcxproj. В этих файлах CMake перечисляет, какие файлы GLFW скомпилировать, то есть перевести код на C в машинные команды, и с какими флагами.
для сборщика
Программа MSBuild, которую Visual Studio запускает, когда ты собираешь проект. MSBuild читает файлы проекта и по очереди вызывает компилятор и линкер.
.
В файле GLFW, который CMake прочитал, стоит строка add_subdirectory(src). Эта строка велит CMake прочитать ещё один файл — CMakeLists.txt из подпапки src внутри glfw-src. В файле glfw-src\src\CMakeLists.txt есть команда add_library
Команда add_library(glfw …) стоит в начале файла glfw-src\src\CMakeLists.txt. Команда add_library создаёт цель с именем glfw: задание «собрать из файлов GLFW на языке C библиотеку». ${GLFW_LIBRARY_TYPE} — какую библиотеку собрать, статическую или динамическую. Мы эту настройку не задавали, и GLFW по умолчанию собирается статической.glfw-src\src\CMakeLists.txtстроки 2–3
add_library(glfw ${GLFW_LIBRARY_TYPE} …)
, и эта команда создаёт новую цель. До этого в проекте была одна цель, программа app. Теперь рядом с ней вторая цель — glfw. Это не программа, а библиотека: код GLFW, который при сборке превратится в один файл. Когда ты соберёшь проект, этот файл, готовая библиотека
Файл glfw3.lib в папке out\build\vs2026\_deps\glfw-build\src\Debug: код GLFW, уже переведённый в машинные команды и собранный в один файл. Линкер добавит этот файл к app.exe, и функции GLFW, например glfwInit, начнут работать в твоей программе.
, появится в папке glfw-build, и цель app сможет его использовать.
Теперь проверь в Visual Studio, что цель glfw появилась. Окно Solution ExplorerОбозреватель решений сейчас показывает папку проекта, а цели, о которых знает CMake, видны в другом его представлении. Переключи окно на CMake Targets View
Сначала нажми кнопку Switch Views над деревом файлов (1), затем дважды щёлкни строку CMake Targets View (2):
. В дереве раскрой строку Engine Project: там будет строка app (executable), то есть твоя программа, и ниже папка GLFW3 со строкой glfw (static library). Это и есть новая цель: «static library» значит статическая библиотека
Библиотека, из которой линкер при сборке берёт нужный программе код и вставляет внутрь app.exe. Отдельный файл библиотеки рядом с программой при запуске не нужен.
. Рядом в папке GLFW3 стоит ещё служебная цель update_mappings
Эта цель обновляет встроенный в GLFW список геймпадов, скачивая свежий список из интернета. При сборке проекта цель update_mappings не запускается.
. Чтобы вернуться к папке на диске, снова нажми Switch Views и дважды щёлкни строку Folder View.
Строки про компилятор C и слово Failed в Output
Когда CMake читает файл CMakeLists.txt из папки GLFW, в окне OutputВывод появляются строки, которых не было в проекте без GLFW.
Строка The C compiler identification is MSVC значит, что CMake нашёл компилятор языка C. В твоём CMakeLists.txt записан только язык C++ (LANGUAGES CXX), но GLFW написана на C, и в файле CMakeLists.txt из папки GLFW стоит строка project(GLFW VERSION 3.5.1 LANGUAGES C …). Компилятор C CMake нашёл в той же Visual Studio: компилятор MSVC собирает и C, и C++.
Строки со словами Failed и not found, например Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed и Looking for pthread_create in pthreads - not found, — не ошибка. CMake ищет для GLFW библиотеку pthread, которая есть в Linux и macOS. В Windows библиотеки pthread нет, и GLFW на Windows обходится без этой библиотеки, поэтому настройка идёт дальше. Настоящая ошибка выглядит иначе: строка начинается со слов CMake Error, и на этой строке настройка останавливается.
Подключить GLFW к app
Цель glfw есть в проекте, но app про неё ещё не знает. В CMakeLists.txt уже есть две команды, которые что-то сообщают цели app: target_compile_features
Строка в CMakeLists.txt, которая говорит, что программе app нужен C++20:
CMakeLists.txtстрока 32
target_compile_features(app PRIVATE cxx_std_20)
говорила, что нужно программе («C++20»), а target_compile_options
Строка внутри if(MSVC), которая передаёт компилятору флаг /W4:
передавала компилятору флаг /W4. Третья команда того же вида, target_link_libraries, подключает к цели библиотеку.
Ты допишешь в CMakeLists.txt команду target_link_libraries. Эта одна команда настраивает сразу два шага сборки, и каждому нужно своё. Компилятору, который читает main.cpp, нужно знать, где лежит заголовок GLFW\glfw3.h, который подключает строка #include. Линкеру, который собирает готовую программу, нужен файл glfw3.lib с готовым кодом GLFW.
Ни путь к заголовку, ни имя файла glfw3.lib в команде писать не надо, хватает имени glfw: цель glfw сама хранит, что нужно тем, кто её подключает. Это записали авторы GLFW в файле glfw-src\src\CMakeLists.txt
Команда target_include_directories записывает в цель glfw папку include из glfw-src: эту папку называет отмеченная строка. Слово PUBLIC значит, что папку получит и каждая цель, которая подключает glfw, то есть цель app.
glfw-src\src\CMakeLists.txtстроки 133–135
target_include_directories(glfw PUBLIC "$<BUILD_INTERFACE:${GLFW_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
. Когда app подключает glfw, CMake сам передаёт компилятору папку out\build\vs2026\_deps\glfw-src\include, где лежит заголовок, а линкеру — файл glfw3.lib. Этот файл появится при сборке в папке out\build\vs2026\_deps\glfw-build\src\Debug.
Если не получилось2
C1083 Cannot open include file: 'GLFW/glfw3.h': No such file or directory Ошибка на вкладке Error List, сборка остановилась.
Почему
Компилятор ищет заголовок GLFW\glfw3.h только в папках, которые CMake передал цели app. Папку glfw-src\include CMake передаёт цели app только после строки target_link_libraries(app PRIVATE glfw). Значит, строки нет в файле, файл не сохранён или настройка после сохранения ещё не закончилась.
Что сделать
Проверь, что в конце CMakeLists.txt стоит строка target_link_libraries(app PRIVATE glfw), как в карточке выше, и сохрани файл (Ctrl+S). Дождись в окне OutputВывод строки CMake generation finished. и снова запусти программу без отладки (Ctrl+F5).
Строка #include <GLFW/glfw3.h> в main.cpp подчёркнута красной волнистой линией.
Почему
Подчёркивание рисует не компилятор, а подсказчик кода в редакторе Visual Studio, IntelliSense. IntelliSense узнаёт папки с заголовками от CMake и обновляет их, только когда настройка проекта закончилась.
Что сделать
Дождись в окне OutputВывод строки CMake generation finished.: через несколько секунд линия пропадёт. Если линия осталась, проверь строку target_link_libraries, как в совете выше.
glfwGetVersionString() только возвращает строку с версией GLFW. Эту функцию можно вызвать сразу, без подготовки библиотеки к работе, поэтому glfwGetVersionString удобна для проверки. Если строка напечаталась, сработали оба шага: компилятор нашёл заголовок GLFW/glfw3.h в папке, которую CMake передал от цели glfw, а линкер нашёл код функции в glfw3.lib.
Строка в консоли рассказывает, как собрана GLFW.
Консоль app
GLFW 3.5.1Версия GLFW. Она взята из GIT_TAG в CMakeLists.txt: именно этот тег скачал FetchContent.Win32GLFW создаёт окна через WinAPI. Название осталось с тех времён, когда Windows была 32-битной, и не значит, что программа 32-битная.WGLЧасть Windows, через которую GLFW подготовит окно к рисованию через OpenGL.NullЗапасная платформа без окон. GLFW может работать, не создавая настоящих окон, например на сервере без монитора. Нам эта возможность не понадобится.EGL OSMesaЕщё два способа подготовить окно к рисованию. GLFW возьмёт любой из двух, только если программа сама попросит, а в Windows по умолчанию работает WGL.VisualCGLFW собрана компилятором из Visual Studio.
Библиотеку можно собрать двумя способами: статической, файлом .lib, код которого входит в программу, или динамической, файлом .dll, который лежит рядом с программой отдельно. Если бы GLFW была динамической, строка версии в консоли заканчивалась бы словом DLL
Так выглядела бы строка версии у динамической GLFW:
Консоль app
GLFW 3.5.1 Win32 WGL Null EGL OSMesa VisualC DLL
. Этого слова в строке нет, значит GLFW статическая: GLFW по умолчанию собирается статической, если в проекте не попросить другое, и код GLFW вошёл прямо в app.exe. Поэтому отдельный файл GLFW рядом с программой не нужен.
Итог
Проект скачивает GLFW 3.5.1 при настройке, собирает её как цель glfw и подключает к app, а программа печатает версию GLFW.
Твой ход · коммит
В окне Git ChangesИзменения Git дважды щёлкни каждый файл из списка ChangesИзменения и прочитай изменения. Если всё устраивает, закоммить и отправь на GitHub
Впиши в поле сообщения Add GLFW and print its version.
Нажми стрелку справа от кнопки Commit AllЗафиксировать все и выбери Commit All and PushЗафиксировать все и отправить: коммит сохранится и сразу уйдёт на GitHub.