Как установить время выполнения PATH для пользовательской команды CMake в Windows

Я пытаюсь перенести проект * nix, CMake на Windows. Один файл заголовка, необходимый основной библиотеке, генерируется специальной программой, поэтому файл CMakeLists.txt содержит примерно следующее:

add_executable(TableGenerator "TableGenerator.cpp")
target_link_libraries(TableGenerator ${LibFoo_LIBRARY})

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   COMMAND TableGenerator "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   DEPENDS TableGenerator)

Важная деталь: TableGenerator использует внешнюю общую библиотеку LibFoo. Например, в Linux все работает отлично, потому что libfoo.so установлен в одной из каталогов системных библиотек, таких как /usr/local/lib, или CMake даже устанавливает атрибут rpath в исполняемый файл, говоря, где именно найти библиотеку.

Однако в Windows эти библиотеки обычно не устанавливаются в систему, а скорее просто извлекаются или компилируются в какой-либо произвольный каталог в дереве сборки или рядом с ним. Чтобы запустить TableGenerator, foo.dll должен быть доступен или скопирован в один из Порядок поиска библиотеки Dynamic-Link (скажем %WINDIR%\System32 или каталог вывода сборки для TableGenerator), что нежелательно.

Как я могу установить переменную среды PATH для пользовательской команды, т.е. использоваться не во время запуска CMake, но во время выполнения пользовательской сборки?

Ответ 1

Пока я занимаюсь своими исследованиями, чтобы правильно задать вопрос, я нашел три решения. Учитывая, как трудно было найти эту информацию, я решил оставить вопрос и ответить здесь в любом случае.


1. Использование глобальной переменной CMAKE_MSVCIDE_RUN_PATH

Существует специальная переменная, предназначенная для решения этой точной проблемы - CMAKE_MSVCIDE_RUN_PATH. Если установлено, это приводит к тому, что строка добавляется к шагу пользовательской сборки script:

set PATH=<CMAKE_MSVCIDE_RUN_PATH>;%PATH%

Итак, все, что вам нужно, это что-то вроде этого в хорошем месте:

set(CMAKE_MSVCIDE_RUN_PATH ${LibFoo_RUNTIME_LIBRARY_DIRS})

Я первоначально заметил эту переменную только в источниках CMake, потому что раньше она была недокументирована до CMake 3.10. Таким образом, вы не сможете найти его в документации для старых версий CMake, но не волнуйтесь, это было поддерживаемое с 2006 года.

Преимущества:
▪ Может быть включен в одном центральном месте
▪ Никаких изменений вообще в каких-либо командах add_custom_command() в другом месте не требуется

▪ Установлен только сам путь, никакие командные команды не должны быть явно записаны
▪ Очевидный выбор с четким названием и намерением

Недостатки:
▪ Глобальный для всего проекта CMake и всех пользовательских команд
▪ Работает только с "Visual Studio 9 2008" и выше генераторами


2. Установка PATH явно с использованием двух параметров COMMAND

Созданный script для этапа пользовательской сборки в Visual Studio содержит некоторый пролог, а затем сами команды, а затем некоторый эпилог. Не удалось бы просто добавить set PATH=... перед реальной командой через другой параметр COMMAND?

Документация для add_custom_command() гласит:

COMMAND
Укажите командные строки для выполнения во время сборки. Если указано более одного COMMAND, они будут исполняться по порядку, но не обязательно сгруппированы в оболочку с выражением состояния или пакет script.

Так что нет, это не гарантировано. Но генератор проекта Visual Studio на самом деле делает это как это, т.е. отдельные команды просто добавляются один за другим, поэтому следующее выполняет задание:

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   COMMAND set "PATH=${LibFoo_RUNTIME_LIBRARY_DIRS};%PATH%"
                   COMMAND TableGenerator "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   DEPENDS TableGenerator)

Преимущества:
▪ PATH может быть изменен для каждой пользовательской команды явно

Недостатки:
▪ Опирается на недокументированное поведение генератора
▪ Необходимо переписать всю команду для Windows и сохранить обе версии в синхронизации. ▪ Каждая пользовательская команда должна быть явно изменена


3. Используя file(GENERATE ...) для создания пользовательского script

Продолжается документация для add_custom_command(), приведенная выше:

Чтобы запустить полный script, используйте команду configure_file() или команду file(GENERATE) для ее создания, а затем укажите COMMAND, чтобы запустить его.

Это немного беспорядочно из-за дополнительных временных файлов и команд:

file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/RunTableGenerator.cmd"
              CONTENT "set PATH=${LibFoo_RUNTIME_LIBRARY_DIRS};%PATH%
                       %1 ${CMAKE_CURRENT_BINARY_DIR}/Table.h")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   COMMAND "${CMAKE_CURRENT_BINARY_DIR}/RunTableGenerator.cmd" "$<TARGET_FILE:TableGenerator>"
                   DEPENDS TableGenerator)

Обратите внимание на неудобный способ отправки пути к исполняемому файлу в качестве аргумента. Это необходимо, потому что script записывается один раз, но TableGenerator может быть в разных местах для разных конфигураций (отладка и выпуск). Если выражение генератора использовалось непосредственно в контенте, была бы напечатана ошибка CMake, и проект не будет правильно построен для всех, кроме одной конфигурации.

Преимущества:
▪ PATH может быть изменен для каждой пользовательской команды явно | ▪ Полностью документированное и рекомендуемое решение

Недостатки:
▪ Очень шумно в CMakefiles
▪ Необходимо переписать всю команду для Windows и сохранить обе версии в синхронизации. ▪ Каждая пользовательская команда должна быть явно изменена


4. Запустите пользовательскую команду через оболочку CMake

См. другой ответ, представленный Дивиром Ицхаки.


Я лично остановился на решении №1, потому что он был чистым и простым, даже до того, как он получил надлежащую документацию и поддерживался CMake в версии 3.10. Это должен быть лучший путь для вас, если вам не нужно делать что-то еще более особенное.

Ответ 2

Существует еще один способ, кроме того, что написал Yirkha, и запускать исполняемый файл через cmake и использовать параметр cmake -E для установки среды.

Итак, в вашем случае это будет:

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
               COMMAND ${CMAKE_COMMAND} -E env "PATH=${LibFoo_RUNTIME_LIBRARY_DIRS}" $<TARGET_FILE:TableGenerator> "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
               DEPENDS TableGenerator)

Подробнее см. http://www.cmake.org/pipermail/cmake/2006-March/008522.html.