CMake — Как проверить, является ли строка путем к файлу?

#cmake

Вопрос:

Я собираю все файлы лицензий для подмодулей git моего проекта, которые находятся в дочернем каталоге, в корневой каталог моего проекта. По моему опыту, файл лицензии обычно называется ЛИЦЕНЗИЕЙ или АВТОРСКИМ ПРАВОМ, но в конечном счете я просто добавлю список имен файлов для поиска.

Я столкнулся с проблемой с одной из библиотек, который имеет другое имя для файла лицензии, а именно копирование , а не лицензии (которые будут обрабатываться в конечном итоге со списком, которые я упомянул выше), но что более важно, что в нем есть каталог под названием RELICENSES, которая не имеет ничего общего с Лицензии файлы. Излишне говорить, что для этой зависимости моя функция завершается ошибкой и ошибкой, из-за которой она не может прочитать ее (отказано в разрешении) в виде текстового файла.

Вот моя функция CMake:

 # extract_deps_licenses
# For a given directory (root directory for all dependencies) the function will look
# into ONLY the first level of each subdirectory (meaning non-recursive behaviour) and
# look for a license text file. If it finds one, its contents will be read and copyied
# into the provided target license file thus generatng a single license file for all 
# dependencies
# Currently looks for a license file containing the word "LICENSE" (not case-sensitive!)
# Special cases such as a license file not being a text file (e.g. executable) will lead 
# to exiting with error
#
# @param root_dir Root directory for all dependencies
# @param dest_license Path to license file that will be populated
#
# TODO For the future a third argument of type list will be provided. The list will contain
# possible names  for license files incl. COPYING, COPYRIGHT etc.
function(extract_deps_licenses root_dir dest_license)
    message("Retrieving git submodule status for dependencies")
    execute_process(
        COMMAND ${GIT_EXECUTABLE} submodule status
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        RESULT_VARIABLE deps_git_exit_code
        OUTPUT_VARIABLE deps_git_status
    )

    file(APPEND
        "${dest_license}"
        "nGit information:n"
        "n${deps_git_status}n"
    )

    # Get all directories under root_dir without recursion (subdirectories will not be traversed)
    file(GLOB dep_dirs "${root_dir}/*" DIRECTORY)
    foreach(dep_dir ${dep_dirs})
        # Get all but the last directory
        string(REGEX MATCH ".*/" dep_parent_dirs ${dep_dir})
        # Get the last directory by excluding everything else
        # This leaves the dependency directory name, which will be used later
        string(REGEX REPLACE ${dep_parent_dirs} "" dep_name ${dep_dir})
        string(TOUPPER ${dep_name} dep_name)
        message("Found dependency ${dep_name} in "${dep_dir}"")

        # Get all items inside 
        file(GLOB paths "${dep_dir}/*")
        message("Looking for license file (non-recursive)")
        foreach(path_license ${paths})
            #get_filename_component(fname ${item} NAME_WE)
            string(TOUPPER "${path_license}" path_up)
            
            # Check if full path contains LICENSE
            # Will not work if LICENSE is a directory!
            string(REGEX MATCH "LICENSE" match ${path_up})

            # Exclude prefixes and suffixes for name of license file
            # directory that has nothing to do with license file)
            # Skip if path does not include LICENSE
            # FIXME Check if match is a file and not directory
            if((NOT match))# OR (NOT path_license FILE)) # <-------------------- CHECK IF A FILE HERE!!!
                continue()
            endif()

            set(license_path ${path_license})
            message("Found license "${license_path}"")

            # Read license text and append to full license text
            message("Copying license text to final license file at '${license_path}'")
            file(READ ${license_path} license_content)
            file(APPEND
                "${dest_license}"
                "n${dep_name}nn"
                "${license_content}"
                "n-----------------------------------------------------------------------------------------------------------n"
            )
        endforeach()
    endforeach()
 

Я знаю, что функция не идеальна, но я изучаю решение конкретной проблемы, упомянутой в вопросе — как проверить, является ли строка путем к файлу (не каталогу!). Если я смогу это сделать, я могу пропустить большую часть обработки, просто исключив запись из списка элементов, найденных в каталоге, если это не файл (я не буду обрабатывать случай, когда лицензия на файл странная, например, исполняемый файл).

Комментарии:

1. Но… почему бы просто не file(GLOB_RECURSE licenses */LICENSE */COPYING) сделать это ? how do I check if a string is a path to a file (not directory! су if (EXISTS string AND NOT IS_DIRECTORY string) ?

2. Смотрите комментарий к ответу от @peter_w

3. @KamilCuk Есть несколько причин, по которым я не использую глобализацию, как вы предлагаете в своем комментарии: имена файлов с учетом регистра, извлечение имени зависимости из каждого каталога, в котором найден файл лицензии, и размещение (вместе с текстом лицензии) в файле назначения и т. Д. Я все еще могу многое сделать из этого постглоббинга, но это более или менее то же самое.

Ответ №1:

Вы можете вызвать файл(ГЛОБУС) с дополнительным LIST_DIRECTORIES false параметром:

 file(GLOB paths LIST_DIRECTORIES false "${dep_dir}/*")
 

Или используйте if(IS_DIRECTORY):

 if((NOT match) OR IS_DIRECTORY "${path_license}")
  continue()
endif()
 

Комментарии:

1. Ах, LIST_DIRECTORIES false это было действительно здорово! Если я не могу использовать регулярное выражение в качестве входных данных для глобирования, каким-то образом делать то, что вы делаете, все равно заставляет меня много обрабатывать. Проверка IS_DIRECTORY (как предложил @KamilCuk) — это то, что я ищу. 🙂

2. Странно, когда я использую LIST_DIRECTORIES false , я получаю нулевые элементы paths . Я использую CMake 3.21, но установил для проекта минимальную версию 3.12.

3.@rbaleksandar Извините, но я не могу воспроизвести это. Использование CMake 3.21.2 и требования к минимальной версии 3.12 LIST_DIRECTORIES false работает, как и ожидалось. Единственное, о чем я могу подумать, что вам нужно быть осторожным, это то, что вам нужно поставить LIST_DIRECTORIES false перед глобальными утверждениями, в противном LIST_DIRECTORIES случае и false они также интерпретируются как глобальные утверждения.

4. Я сделал то, что вы предложили, и заказ, по — видимому, очень важен.