Возможно, CMake — это стандартный инструмент для структурирования и сборки проектов на C++. Инструмент очень прост и интуитивно понятен в использовании. Но иногда это может быть сложно, и могут возникнуть неожиданные проблемы. И может потребоваться значительное количество времени, чтобы понять, что происходит. В этом примере я покажу одну из таких проблем, с которыми столкнулся недавно. Я попытался воспроизвести проблему с очень упрощенным проектом (который можно найти здесь).

.
├── CMakeLists.txt
├── README.md
├── main.cpp
└── models
└── Limits.h

У проекта есть одна основная цель, которая включает каталог models. Код очень прост и просто возвращает максимальное значение целого числа, которое представлено стандартным определением `INT_MAX`. Для этого необходимо #include заголовочный файл climits или limits.h.

Следующая команда успешно создает и генерирует исполняемый файл на моем ПК с Linux:

cmake -B построить -S . && cmake — сборка сборка — параллельная

Но когда я попытался собрать этот проект на MacOS, то получил следующую ошибку:

main.cpp:5:19: ошибка: использование необъявленного идентификатора 'INT_MAX'
static_assert(INT_MAX == std::numeric_limits‹int›::max(), «Что происходит?!»);< br /> ^
Произошла 1 ошибка.

Что здесь происходит?

На самом деле, я столкнулся с этой проблемой, когда пытался собрать один из своих проектов внутри виртуальной машины Ubuntu. Проект представляет собой проект C++ на основе CMake и упаковывается компанией conan. Я смонтировал каталог проекта внутри работающего экземпляра виртуальной машины Ubuntu (хост — MacOS) и создал проект с помощью команды conan create. Все было отлично. Но когда я попытался использовать локальный рабочий процесс разработки, я получил следующую ошибку:

main.cpp: 5:19: ошибка: «INT_MAX» не был объявлен в этой области
5 | static_assert(INT_MAX == std::numeric_limits‹int›::max(), «Что происходит?!»);
| ^~~~~~~
/home/ubuntu/fs/main.cpp:3:1: примечание: ‘INT_MAX’ определен в заголовке ‘‹climits›’; вы забыли включить ‘#include ‹climits›’?
2 | #include ‹limits›
+++ |+#include ‹climits›
3 |

Почему? Я добавил climits всего парой строк выше. И это работало с conan create, и команды, которые я выполнял вручную, просто копировали команду create. Почти. Я проверил кэш сборки и увидел, что папка с успешной сборкой указана в списке /usr/include/c++/v1/limits.h (внутри CMakeFiles/main.dir/main.cpp.o.d ). И неудачная сборка в списке models/limits.h. Таким образом, он использовал файл из проекта, но регистр имени файла был проигнорирован.

Очевидно, существовала разница между conan create и выполнением команд вручную. А именно, conan source. Внутренне первое, что выполняется с помощью conan create, — это копирование исходников в ~/.conan/data/…. Таким образом, файловые системы должны быть разными для этих двух мест. Действительно, я использовал мультипроход для создания виртуальных машин Ubuntu, и он внутри использует SSHFS для монтирования локальной файловой системы внутри удаленной машины. Но локальная файловая система (в моем случае APFS) нечувствительна к регистру. А поскольку каталог models был включен целью, файл Limits.h был найден и использован вместо limits.h. Для решения проблемы есть несколько вариантов:

1. изменить файловую систему, чтобы она учитывала регистр.
2. изменить, какие каталоги включаются в цели CMake.
3. Будьте осторожны с именами.
4. и т. д.

Это был еще один важный урок, показывающий, что мы должны быть осведомлены о возможностях системы, над которой мы работаем (какую файловую систему мы используем) и инструментах, которые мы используем (как структурировать проект? каталог? какое имя лучше всего подходит для файла? и т. д.).