summaryrefslogtreecommitdiffstats
path: root/cmake/FetchedSharedLibrary.cmake
blob: fa0bd9f5e45621ce8d55359eb57048f3c4f6db3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# Helper function to download and extract an archive
function(download_and_extract archive_name url)
    get_filename_component(extension ${url} EXT)
    set(archive_path "${CMAKE_CURRENT_BINARY_DIR}/${archive_name}${extension}")
    set(extract_dir "${CMAKE_CURRENT_BINARY_DIR}/${archive_name}")

    if(EXISTS ${url})
        message(STATUS "Using local file for ${archive_name}: ${url}")
        set(archive_path ${url})
    else()
        message(STATUS "Fetching ${archive_name} from ${url}")
        file(
            DOWNLOAD ${url} ${archive_path}
            # SHOW_PROGRESS
            STATUS status
        )

        list(GET status 0 status_code)
        list(GET status 1 status_string)
        if(NOT status_code EQUAL 0)
            message(
                WARNING
                "Failed to download ${archive_name} from ${url}: ${status_string}"
            )
            return()
        endif()
    endif()

    file(ARCHIVE_EXTRACT INPUT ${archive_path} DESTINATION ${extract_dir})

    set(${archive_name}_SOURCE_DIR ${extract_dir} PARENT_SCOPE)
    message(STATUS "${archive_name} downloaded and extracted to ${extract_dir}")
endfunction()

# Add rules to copy & install shared library of name 'library_name' in the 'module_subdir' directory.
# If 'url' is a directory, the shared library (with platform-specific shared library prefixes and suffixes) will be
# taken from the directory, and whatever is found there will be used to produce the install rule.
# If the 'url' is a path to a file with the platform-specific shared library prefix and suffix, then that file
# will be used to produce the install rule.
# Otherwise, the 'url' is interpreted as an URL, and the content of the URL will be fetched, extracted and searched
# for the shared library to produce the install rule.
function(copy_fetched_shared_library library_name url)
    cmake_parse_arguments(ARG "IGNORE_FAILURE" "" "" ${ARGN})
    if(ARG_IGNORE_FAILURE)
        set(error_type STATUS)
    else()
        set(error_type SEND_ERROR)
    endif()

    set(shared_library_filename
        "${CMAKE_SHARED_LIBRARY_PREFIX}${library_name}${CMAKE_SHARED_LIBRARY_SUFFIX}"
    )
    macro(from_glob dir)
        # A little helper function
        file(GLOB_RECURSE source_object "${dir}/${shared_library_filename}")
        list(LENGTH source_object nmatches)
        if(nmatches EQUAL 0)
            message(
                ${error_type}
                "Unable to find ${shared_library_filename} in ${url}"
            )
        elseif(nmatches GREATER 1)
            message(
                ${error_type}
                "Found multiple files named ${shared_library_filename} in ${url}"
            )
        endif()
    endmacro()

    if(IS_DIRECTORY "${url}")
        # Just glob directly from a local directory
        from_glob("${url}")
    elseif(
        url
            MATCHES
            "${CMAKE_SHARED_LIBRARY_PREFIX}.+${CMAKE_SHARED_LIBRARY_SUFFIX}$"
        AND EXISTS "${url}"
    )
        # Otherwise, if it's a direct path to a shared object, use that
        set(source_object "${url}")
    else()
        # Otherwise, download and extract from whatever URL we have
        download_and_extract("${library_name}" "${url}")
        if(DEFINED ${library_name}_SOURCE_DIR)
            from_glob(${${library_name}_SOURCE_DIR})
        elseif(ARG_IGNORE_FAILURE)
            return()
        else()
            message(
                SEND_ERROR
                "Unable to download and extract ${library_name} from ${url}"
            )
            return()
        endif()
    endif()

    # We didn't find it, just return and don't create a target and operation
    # which will fail
    if(NOT EXISTS ${source_object} AND ARG_IGNORE_FAILURE)
        return()
    endif()

    set(dest_object
        ${CMAKE_BINARY_DIR}/$<CONFIG>/${module_subdir}/${shared_library_filename}
    )
    add_custom_command(
        OUTPUT ${dest_object}
        COMMAND
            ${CMAKE_COMMAND} -E copy_if_different ${source_object}
            ${dest_object}
        DEPENDS ${source_object}
        VERBATIM
    )
    # Give this copying action a name
    add_custom_target(copy-${library_name} DEPENDS ${dest_object})
    set_target_properties(copy-${library_name} PROPERTIES FOLDER generated)

    # Put this into a library target
    add_library(${library_name} MODULE IMPORTED GLOBAL)
    add_dependencies(${library_name} copy-${library_name})
    set_property(
        TARGET ${library_name}
        PROPERTY IMPORTED_LOCATION ${dest_object}
    )
endfunction()

function(install_fetched_shared_library library_name url)
    copy_fetched_shared_library(${library_name} ${url} ${ARGN})
    set(shared_library_filename
        "${CMAKE_SHARED_LIBRARY_PREFIX}${library_name}${CMAKE_SHARED_LIBRARY_SUFFIX}"
    )
    set(dest_object
        ${CMAKE_BINARY_DIR}/$<CONFIG>/${module_subdir}/${shared_library_filename}
    )
    if(TARGET ${library_name})
        install(PROGRAMS ${dest_object} DESTINATION ${module_subdir})
    endif()
endfunction()