Какие есть проблемы у систем сборки языка C++?
Проблема только одна – отсутствие каких бы то ни было стандартных систем сборки. Но нас спасут нестандартные! В данном случае великий и ужасный CMake, в котором есть две переменные с не совсем очевидными отличиями.
– CMAKE_SOURCE_DIR – путь к директории исходников (директория, в которой расположен корневой файл CMakeLists.txt, с которого началась обработка);
– CMAKE_CURRENT_SOURCE_DIR – путь к текущей директории исходников (директория, в которой расположен CMakeLists.txt, который обрабатывается в данный момент).
Какую переменную лучше использовать для относительных путей в иерархии проекта?
Предположим, мы написали ещё один велосипед (но на этот раз превзошли всех предшественников в «велосипедостроении») и реализовали свой модуль суммирования переменных типа int. Структура проекта примерно такая:
| CMakeLists.txt | +---SuperProject | CMakeLists.txt | Summator.cpp | Summator.h | \---Tests CMakeLists.txt Summator_test.cpp
Если вы, как и подобает нормальному человеку, не сразу воспринимаете вывод команды tree. Поясню: в корне проекта находится файл CMakeLists.txt и две директории – SuperProject (реализация нашего велосипеда) и Tests (так сказать, юнит-тесты). Думаю, теперь стало понятно, что где лежит.
С корневым CMakeLists.txt всё очевидно: в нём только добавляем две поддиректории (если уж совсем занудствовать, то было бы неплохо добавить минимальную требуемую версию CMake в начало файла, но опустим это во имя простоты восприятия):
add_subdirectory(SuperProject) add_subdirectory(Tests)
С файлом CMakeLists.txt из директории SuperProject тоже всё просто:
project(SuperProject) add_library(SuperProjectLib SHARED Summator.cpp)
И, наконец, самый смак: директория Tests и её файл CMakeLists.txt:
set(SOURCES ${CMAKE_SOURCE_DIR}/SuperProject/Summator.cpp Summator_test.cpp) include_directories(${CMAKE_SOURCE_DIR}) add_executable(Summator_test ${SOURCES} ${HEADERS})
Заметили?
Мы используем CMAKE_SOURCE_DIR, чтобы собирать исходники проекта вместе с тестами. Обычно так делается для тестирования внутренних модулей продукта (иначе можно было бы просто прилинковать уже собранную библиотеку SuperProject.so(dll)).
Вроде, всё работает, проект собирается, тесты собираются (проходят или нет в данном случае неважно), можно на этом и остановиться. Но лучше подумать её разок. А в идеале попробовать использовать наш проект так, как могли бы его использовать другие члены сообщества.
Один из возможных вариантов использования – добавить наш репозиторий в свой в качестве git submodule и где-то в своём корневом CMakeLists.txt добавить команду:
add_subdirectory(SuperProjectPackage)
Иерархия проекта нашего благодарного пользователя будет выглядеть примерно так:
\---OtherProject | CMakeLists.txt | +---sources \---SuperProjectPackage | CMakeLists.txt | +---SuperProject | CMakeLists.txt | Summator.cpp | Summator.h | \---Tests CMakeLists.txt Summator_test.cpp
Когда дело дойдёт до сборки проекта SuperProjectPackage, а если конкретнее, то до сборки тестов в директории Tests, куда будет указывать переменная CMAKE_SOURCE_DIR ?
Правильно, указывать она будет на директорию OtherProject, потому что именно она теперь и является хранилищем корневого файла CMakeLists.txt. А наш файл CMakeLists.txt корневым быть перестал.
В итоге на этапе CMake возникнет ошибка при выполнении вот этой строчки:
${CMAKE_SOURCE_DIR}/SuperProject/Summator.cpp
Что вполне законно, ибо корневая директория проекта теперь не содержит директорию SuperProject.
Что же делать?
Если вы читали эту заметку с начала, вероятно, вы можете заподозрить, что помочь нам призвана переменная CMAKE_CURRENT_SOURCE_DIR. И данный вывод совершенно верен!
Переменная CMAKE_CURRENT_SOURCE_DIR всегда указывает на директорию с текущим файлом CMakeFiles.txt, который в настоящий момент подвергается обработке. Использование этой переменной вместо CMAKE_SOURCE_DIR убережёт пользователей ваших супер-модулей от нервных расстройств, а вам даст дополнительный повод для гордости.
Ведь что может сравниться с ощущением того, что результаты твоей работы не пылятся на жёстком диске, а приносят пользу человечеству?
Было бы неплохо привести в конце статьи правильное решение. Можно было бы, конечно, оставить его в качестве домашнего задания. Но жестокость я оставлю для учебных занятий, а правильное решение выглядит следующим образом.
Правильное решение
В файле Tests/CMakeLists.txt следовало написать:
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})
Есть вопрос? Напишите в комментариях!