0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Теги: c++, cpp, cmake, cmake_source_dir, cmake_current_source_dir, int, tree, superproject, tests, superproject.so(dll), git submodule, otherproject, cplus</p>
1
<p>Теги: c++, cpp, cmake, cmake_source_dir, cmake_current_source_dir, int, tree, superproject, tests, superproject.so(dll), git submodule, otherproject, cplus</p>
2
<p>Проблема только одна - отсутствие каких бы то ни было стандартных систем сборки. Но нас спасут нестандартные! В данном случае великий и ужасный<strong>CMake</strong>, в котором есть две переменные с не совсем очевидными отличиями.</p>
2
<p>Проблема только одна - отсутствие каких бы то ни было стандартных систем сборки. Но нас спасут нестандартные! В данном случае великий и ужасный<strong>CMake</strong>, в котором есть две переменные с не совсем очевидными отличиями.</p>
3
<p>- <strong>CMAKE_SOURCE_DIR</strong>- путь к директории исходников (директория, в которой расположен корневой файл<strong>CMakeLists.txt</strong>, с которого началась обработка);</p>
3
<p>- <strong>CMAKE_SOURCE_DIR</strong>- путь к директории исходников (директория, в которой расположен корневой файл<strong>CMakeLists.txt</strong>, с которого началась обработка);</p>
4
<p>- <strong>CMAKE_CURRENT_SOURCE_DIR</strong>- путь к текущей директории исходников (директория, в которой расположен<strong>CMakeLists.txt</strong>, который обрабатывается в данный момент).</p>
4
<p>- <strong>CMAKE_CURRENT_SOURCE_DIR</strong>- путь к текущей директории исходников (директория, в которой расположен<strong>CMakeLists.txt</strong>, который обрабатывается в данный момент).</p>
5
<h3>Какую переменную лучше использовать для относительных путей в иерархии проекта?</h3>
5
<h3>Какую переменную лучше использовать для относительных путей в иерархии проекта?</h3>
6
<p>Предположим, мы написали ещё один велосипед (но на этот раз превзошли всех предшественников в "велосипедостроении") и реализовали свой модуль суммирования переменных типа<strong>int</strong>. Структура проекта примерно такая:</p>
6
<p>Предположим, мы написали ещё один велосипед (но на этот раз превзошли всех предшественников в "велосипедостроении") и реализовали свой модуль суммирования переменных типа<strong>int</strong>. Структура проекта примерно такая:</p>
7
| CMakeLists.txt | +---SuperProject | CMakeLists.txt | Summator.cpp | Summator.h | \---Tests CMakeLists.txt Summator_test.cpp<p>Если вы, как и подобает нормальному человеку, не сразу воспринимаете вывод команды<strong>tree</strong>. Поясню: в корне проекта находится файл<strong>CMakeLists.txt</strong>и две директории -<strong>SuperProject</strong>(реализация нашего велосипеда) и<strong>Tests</strong>(так сказать, юнит-тесты). Думаю, теперь стало понятно, что где лежит.</p>
7
| CMakeLists.txt | +---SuperProject | CMakeLists.txt | Summator.cpp | Summator.h | \---Tests CMakeLists.txt Summator_test.cpp<p>Если вы, как и подобает нормальному человеку, не сразу воспринимаете вывод команды<strong>tree</strong>. Поясню: в корне проекта находится файл<strong>CMakeLists.txt</strong>и две директории -<strong>SuperProject</strong>(реализация нашего велосипеда) и<strong>Tests</strong>(так сказать, юнит-тесты). Думаю, теперь стало понятно, что где лежит.</p>
8
<p>С корневым<strong>CMakeLists.txt</strong>всё очевидно: в нём только добавляем две поддиректории (если уж совсем занудствовать, то было бы неплохо добавить минимальную требуемую версию<strong>CMake</strong>в начало файла, но опустим это во имя простоты восприятия):</p>
8
<p>С корневым<strong>CMakeLists.txt</strong>всё очевидно: в нём только добавляем две поддиректории (если уж совсем занудствовать, то было бы неплохо добавить минимальную требуемую версию<strong>CMake</strong>в начало файла, но опустим это во имя простоты восприятия):</p>
9
add_subdirectory(SuperProject) add_subdirectory(Tests)<p>С файлом<strong>CMakeLists.txt</strong>из директории<strong>SuperProject</strong>тоже всё просто:</p>
9
add_subdirectory(SuperProject) add_subdirectory(Tests)<p>С файлом<strong>CMakeLists.txt</strong>из директории<strong>SuperProject</strong>тоже всё просто:</p>
10
project(SuperProject) add_library(SuperProjectLib SHARED Summator.cpp)<p>И, наконец, самый смак: директория<strong>Tests</strong>и её файл<strong>CMakeLists.txt</strong>:</p>
10
project(SuperProject) add_library(SuperProjectLib SHARED Summator.cpp)<p>И, наконец, самый смак: директория<strong>Tests</strong>и её файл<strong>CMakeLists.txt</strong>:</p>
11
set(SOURCES ${CMAKE_SOURCE_DIR}/SuperProject/Summator.cpp Summator_test.cpp) include_directories(${CMAKE_SOURCE_DIR}) add_executable(Summator_test ${SOURCES} ${HEADERS})<h2>Заметили?</h2>
11
set(SOURCES ${CMAKE_SOURCE_DIR}/SuperProject/Summator.cpp Summator_test.cpp) include_directories(${CMAKE_SOURCE_DIR}) add_executable(Summator_test ${SOURCES} ${HEADERS})<h2>Заметили?</h2>
12
<p>Мы используем<strong>CMAKE_SOURCE_DIR</strong>, чтобы собирать исходники проекта вместе с тестами. Обычно так делается для тестирования внутренних модулей продукта (иначе можно было бы просто прилинковать уже собранную библиотеку<strong>SuperProject.so(dll)</strong>).</p>
12
<p>Мы используем<strong>CMAKE_SOURCE_DIR</strong>, чтобы собирать исходники проекта вместе с тестами. Обычно так делается для тестирования внутренних модулей продукта (иначе можно было бы просто прилинковать уже собранную библиотеку<strong>SuperProject.so(dll)</strong>).</p>
13
<p>Вроде, всё работает, проект собирается, тесты собираются (проходят или нет в данном случае неважно), можно на этом и остановиться. Но лучше подумать её разок. А в идеале попробовать использовать наш проект так, как могли бы его использовать другие члены сообщества.</p>
13
<p>Вроде, всё работает, проект собирается, тесты собираются (проходят или нет в данном случае неважно), можно на этом и остановиться. Но лучше подумать её разок. А в идеале попробовать использовать наш проект так, как могли бы его использовать другие члены сообщества.</p>
14
<p>Один из возможных вариантов использования - добавить наш репозиторий в свой в качестве<strong>git submodule</strong>и где-то в своём корневом<strong>CMakeLists.txt</strong>добавить команду:</p>
14
<p>Один из возможных вариантов использования - добавить наш репозиторий в свой в качестве<strong>git submodule</strong>и где-то в своём корневом<strong>CMakeLists.txt</strong>добавить команду:</p>
15
add_subdirectory(SuperProjectPackage)<p>Иерархия проекта нашего благодарного пользователя будет выглядеть примерно так:</p>
15
add_subdirectory(SuperProjectPackage)<p>Иерархия проекта нашего благодарного пользователя будет выглядеть примерно так:</p>
16
\---OtherProject | CMakeLists.txt | +---sources \---SuperProjectPackage | CMakeLists.txt | +---SuperProject | CMakeLists.txt | Summator.cpp | Summator.h | \---Tests CMakeLists.txt Summator_test.cpp<p>Когда дело дойдёт до сборки проекта<strong>SuperProjectPackage</strong>, а если конкретнее, то до сборки тестов в директории<strong>Tests</strong>, куда будет указывать переменная<strong>CMAKE_SOURCE_DIR</strong>?</p>
16
\---OtherProject | CMakeLists.txt | +---sources \---SuperProjectPackage | CMakeLists.txt | +---SuperProject | CMakeLists.txt | Summator.cpp | Summator.h | \---Tests CMakeLists.txt Summator_test.cpp<p>Когда дело дойдёт до сборки проекта<strong>SuperProjectPackage</strong>, а если конкретнее, то до сборки тестов в директории<strong>Tests</strong>, куда будет указывать переменная<strong>CMAKE_SOURCE_DIR</strong>?</p>
17
<p>Правильно, указывать она будет на директорию<strong>OtherProject</strong>, потому что именно она теперь и является хранилищем корневого файла<strong>CMakeLists.txt</strong>. А наш файл<strong>CMakeLists.txt</strong>корневым быть перестал.</p>
17
<p>Правильно, указывать она будет на директорию<strong>OtherProject</strong>, потому что именно она теперь и является хранилищем корневого файла<strong>CMakeLists.txt</strong>. А наш файл<strong>CMakeLists.txt</strong>корневым быть перестал.</p>
18
<p>В итоге на этапе<strong>CMake</strong>возникнет ошибка при выполнении вот этой строчки:</p>
18
<p>В итоге на этапе<strong>CMake</strong>возникнет ошибка при выполнении вот этой строчки:</p>
19
${CMAKE_SOURCE_DIR}/SuperProject/Summator.cpp<p>Что вполне законно, ибо корневая директория проекта теперь не содержит директорию<strong>SuperProject</strong>.</p>
19
${CMAKE_SOURCE_DIR}/SuperProject/Summator.cpp<p>Что вполне законно, ибо корневая директория проекта теперь не содержит директорию<strong>SuperProject</strong>.</p>
20
<h2>Что же делать?</h2>
20
<h2>Что же делать?</h2>
21
<p>Если вы читали эту заметку с начала, вероятно, вы можете заподозрить, что помочь нам призвана переменная<strong>CMAKE_CURRENT_SOURCE_DIR</strong>. И данный вывод совершенно верен!</p>
21
<p>Если вы читали эту заметку с начала, вероятно, вы можете заподозрить, что помочь нам призвана переменная<strong>CMAKE_CURRENT_SOURCE_DIR</strong>. И данный вывод совершенно верен!</p>
22
<p>Переменная<strong>CMAKE_CURRENT_SOURCE_DIR</strong>всегда указывает на директорию с текущим файлом<strong>CMakeFiles.txt</strong>, который в настоящий момент подвергается обработке. Использование этой переменной вместо<strong>CMAKE_SOURCE_DIR</strong>убережёт пользователей ваших супер-модулей от нервных расстройств, а вам даст дополнительный повод для гордости.</p>
22
<p>Переменная<strong>CMAKE_CURRENT_SOURCE_DIR</strong>всегда указывает на директорию с текущим файлом<strong>CMakeFiles.txt</strong>, который в настоящий момент подвергается обработке. Использование этой переменной вместо<strong>CMAKE_SOURCE_DIR</strong>убережёт пользователей ваших супер-модулей от нервных расстройств, а вам даст дополнительный повод для гордости.</p>
23
<p><em>Ведь что может сравниться с ощущением того, что результаты твоей работы не пылятся на жёстком диске, а приносят пользу человечеству?</em></p>
23
<p><em>Ведь что может сравниться с ощущением того, что результаты твоей работы не пылятся на жёстком диске, а приносят пользу человечеству?</em></p>
24
<p>Было бы неплохо привести в конце статьи правильное решение. Можно было бы, конечно, оставить его в качестве домашнего задания. Но жестокость я оставлю для учебных занятий, а правильное решение выглядит следующим образом.</p>
24
<p>Было бы неплохо привести в конце статьи правильное решение. Можно было бы, конечно, оставить его в качестве домашнего задания. Но жестокость я оставлю для учебных занятий, а правильное решение выглядит следующим образом.</p>
25
<h2>Правильное решение</h2>
25
<h2>Правильное решение</h2>
26
<p>В файле<strong>Tests/CMakeLists.txt</strong>следовало написать:</p>
26
<p>В файле<strong>Tests/CMakeLists.txt</strong>следовало написать:</p>
27
project(Tests) set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../SuperProject/Summator.cpp Summator_test.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../.) add_executable(Summator_test ${SOURCES} ${HEADERS})<p><em>Есть вопрос? Напишите в комментариях!</em></p>
27
project(Tests) set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../SuperProject/Summator.cpp Summator_test.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../.) add_executable(Summator_test ${SOURCES} ${HEADERS})<p><em>Есть вопрос? Напишите в комментариях!</em></p>
28
28