aboutsummaryrefslogtreecommitdiff
path: root/scripts/cmake/vcpkg_check_features.cmake
blob: 7679b0d11aa5474825ecebef8f26e3efceaca96e (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#[===[.md:
# vcpkg_check_features
Check if one or more features are a part of a package installation.

```cmake
vcpkg_check_features(
    OUT_FEATURE_OPTIONS <out-var>
    [PREFIX <prefix>]
    [FEATURES
        [<feature-name> <feature-var>]...
        ]
    [INVERTED_FEATURES
        [<feature-name> <feature-var>]...
        ]
)
```

The `<out-var>` should be set to `FEATURE_OPTIONS` by convention.

`vcpkg_check_features()` will:

- for each `<feature-name>` passed in `FEATURES`:
    - if the feature is set, add `-D<feature-var>=ON` to `<out-var>`,
      and set `<prefix>_<feature-var>` to ON.
    - if the feature is not set, add `-D<feature-var>=OFF` to `<out-var>`,
      and set `<prefix>_<feature-var>` to OFF.
- for each `<feature-name>` passed in `INVERTED_FEATURES`:
    - if the feature is set, add `-D<feature-var>=OFF` to `<out-var>`,
      and set `<prefix>_<feature-var>` to OFF.
    - if the feature is not set, add `-D<feature-var>=ON` to `<out-var>`,
      and set `<prefix>_<feature-var>` to ON.

If `<prefix>` is not passed, then the feature vars set are simply `<feature-var>`,
not `_<feature-var>`.

If `INVERTED_FEATURES` is not passed, then the `FEATURES` keyword is optional.
This behavior is deprecated.

If the same `<feature-var>` is passed multiple times,
then `vcpkg_check_features` will cause a fatal error,
since that is a bug.

## Examples

### Example 1: Regular features

```cmake
$ ./vcpkg install mimalloc[asm,secure]

# ports/mimalloc/portfile.cmake
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
    FEATURES
        asm       MI_SEE_ASM
        override  MI_OVERRIDE
        secure    MI_SECURE
)

vcpkg_configure_cmake(
    SOURCE_PATH ${SOURCE_PATH}
    PREFER_NINJA
    OPTIONS
        # Expands to "-DMI_SEE_ASM=ON;-DMI_OVERRIDE=OFF;-DMI_SECURE=ON"
        ${FEATURE_OPTIONS}
)
```

### Example 2: Inverted features

```cmake
$ ./vcpkg install cpprestsdk[websockets]

# ports/cpprestsdk/portfile.cmake
vcpkg_check_features(
    INVERTED_FEATURES
        brotli      CPPREST_EXCLUDE_BROTLI
        websockets  CPPREST_EXCLUDE_WEBSOCKETS
)

vcpkg_configure_cmake(
    SOURCE_PATH ${SOURCE_PATH}
    PREFER_NINJA
    OPTIONS
        # Expands to "-DCPPREST_EXCLUDE_BROTLI=ON;-DCPPREST_EXCLUDE_WEBSOCKETS=OFF"
        ${FEATURE_OPTIONS}
)
```

### Example 3: Set multiple options for same feature

```cmake
$ ./vcpkg install pcl[cuda]

# ports/pcl/portfile.cmake
vcpkg_check_features(
    FEATURES
        cuda  WITH_CUDA
        cuda  BUILD_CUDA
        cuda  BUILD_GPU
)

vcpkg_configure_cmake(
    SOURCE_PATH ${SOURCE_PATH}
    PREFER_NINJA
    OPTIONS
        # Expands to "-DWITH_CUDA=ON;-DBUILD_CUDA=ON;-DBUILD_GPU=ON"
        ${FEATURE_OPTIONS}
)
```

### Example 4: Use regular and inverted features

```cmake
$ ./vcpkg install rocksdb[tbb]

# ports/rocksdb/portfile.cmake
vcpkg_check_features(
    FEATURES
        tbb   WITH_TBB
    INVERTED_FEATURES
        tbb   ROCKSDB_IGNORE_PACKAGE_TBB
)

vcpkg_configure_cmake(
    SOURCE_PATH ${SOURCE_PATH}
    PREFER_NINJA
    OPTIONS
        # Expands to "-DWITH_TBB=ON;-DROCKSDB_IGNORE_PACKAGE_TBB=OFF"
        ${FEATURE_OPTIONS}
)
```

## Examples in portfiles

* [cpprestsdk](https://github.com/microsoft/vcpkg/blob/master/ports/cpprestsdk/portfile.cmake)
* [pcl](https://github.com/microsoft/vcpkg/blob/master/ports/pcl/portfile.cmake)
* [rocksdb](https://github.com/microsoft/vcpkg/blob/master/ports/rocksdb/portfile.cmake)
#]===]

function(z_vcpkg_check_features_last_feature out_var features_name features_list)
    list(LENGTH features_list features_length)
    math(EXPR features_length_mod_2 "${features_length} % 2")
    if(NOT features_length_mod_2 EQUAL 0)
        message(FATAL_ERROR "vcpkg_check_features has an incorrect number of arguments to ${features_name}")
    endif()

    math(EXPR last_feature "${features_length} / 2 - 1")
    set("${out_var}" "${last_feature}" PARENT_SCOPE)
endfunction()

function(z_vcpkg_check_features_get_feature idx features_list out_feature_name out_feature_var)
    math(EXPR feature_name_idx "${idx} * 2")
    math(EXPR feature_var_idx "${feature_name_idx} + 1")

    list(GET features_list "${feature_name_idx}" feature_name)
    list(GET features_list "${feature_var_idx}" feature_var)

    set("${out_feature_name}" "${feature_name}" PARENT_SCOPE)
    set("${out_feature_var}" "${feature_var}" PARENT_SCOPE)
endfunction()

function(vcpkg_check_features)
    cmake_parse_arguments(
        PARSE_ARGV 0 "arg"
        ""
        "OUT_FEATURE_OPTIONS;PREFIX"
        "FEATURES;INVERTED_FEATURES"
    )

    if(NOT DEFINED arg_OUT_FEATURE_OPTIONS)
        message(FATAL_ERROR "OUT_FEATURE_OPTIONS must be defined.")
    endif()
    if(NOT DEFINED arg_PREFIX)
        set(prefix "")
    else()
        set(prefix "${arg_PREFIX}_")
    endif()

    set(feature_options)
    set(feature_variables)

    if(NOT DEFINED arg_FEATURES AND NOT DEFINED arg_INVERTED_FEATURES)
        message(DEPRECATION
"calling `vcpkg_check_features` without the `FEATURES` keyword has been deprecated.
    Please add the `FEATURES` keyword to the call.")
        set(arg_FEATURES "${arg_UNPARSED_ARGUMENTS}")
    elseif(DEFINED arg_UNPARSED_ARGUMENTS)
        message(FATAL_ERROR "vcpkg_check_features called with unknown arguments: ${arg_UNPARSED_ARGUMENTS}")
    endif()



    z_vcpkg_check_features_last_feature(last_feature "FEATURES" "${arg_FEATURES}")
    if(last_feature GREATER_EQUAL 0)
        foreach(feature_pair_idx RANGE "${last_feature}")
            z_vcpkg_check_features_get_feature("${feature_pair_idx}" "${arg_FEATURES}" feature_name feature_var)

            list(APPEND feature_variables "${feature_var}")
            if(feature_name IN_LIST FEATURES)
                list(APPEND feature_options "-D${feature_var}=ON")
                set("${prefix}${feature_var}" ON PARENT_SCOPE)
            else()
                list(APPEND feature_options "-D${feature_var}=OFF")
                set("${prefix}${feature_var}" OFF PARENT_SCOPE)
            endif()
        endforeach()
    endif()

    z_vcpkg_check_features_last_feature(last_inverted_feature "INVERTED_FEATURES" "${arg_INVERTED_FEATURES}")
    if(last_inverted_feature GREATER_EQUAL 0)
        foreach(feature_pair_idx RANGE "${last_inverted_feature}")
            z_vcpkg_check_features_get_feature("${feature_pair_idx}" "${arg_INVERTED_FEATURES}" feature_name feature_var)

            list(APPEND feature_variables "${feature_var}")
            if(feature_name IN_LIST FEATURES)
                list(APPEND feature_options "-D${feature_var}=OFF")
                set("${prefix}${feature_var}" OFF PARENT_SCOPE)
            else()
                list(APPEND feature_options "-D${feature_var}=ON")
                set("${prefix}${feature_var}" ON PARENT_SCOPE)
            endif()
        endforeach()
    endif()

    list(SORT feature_variables)
    set(last_variable)
    foreach(variable IN LISTS feature_variables)
        if(variable STREQUAL last_variable)
            message(FATAL_ERROR "vcpkg_check_features passed the same feature variable multiple times: '${variable}'")
        endif()
    endforeach()

    set("${arg_OUT_FEATURE_OPTIONS}" "${feature_options}" PARENT_SCOPE)
endfunction()