aboutsummaryrefslogtreecommitdiffstats
path: root/cmake/QtIRCommandLineHelpers.cmake
blob: 465a994be800de447cdf0e3a2b582c3d4a982ec9 (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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause

# This file contains a modified subset of the qtbase/QtProcessConfigureArgs.cmake commands
# with renamed functions, because we need similar logic for init-repository, but
# we can't access qtbase before we clone it.

# Call a function with the given arguments.
function(qt_ir_call_function func)
    set(call_code "${func}(")
    math(EXPR n "${ARGC} - 1")
    foreach(i RANGE 1 ${n})
        string(APPEND call_code "\"${ARGV${i}}\" ")
    endforeach()
    string(APPEND call_code ")")
    string(REPLACE "\\" "\\\\" call_code "${call_code}")
    if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
        set(incfile qt_tmp_func_call.cmake)
        file(WRITE "${incfile}" "${call_code}")
        include(${incfile})
        file(REMOVE "${incfile}")
    else()
        cmake_language(EVAL CODE "${call_code}")
    endif()
endfunction()

# Show an error.
function(qt_ir_add_error)
    message(FATAL_ERROR ${ARGV})
endfunction()

# Check if there are still unhandled command line arguments.
function(qt_ir_args_has_next_command_line_arg out_var)
    qt_ir_get_unhandled_args(args)

    list(LENGTH args n)
    if(n GREATER 0)
        set(result TRUE)
    else()
        set(result FALSE)
    endif()
    set(${out_var} ${result} PARENT_SCOPE)
endfunction()

# Get the next unhandled command line argument without popping it.
function(qt_ir_args_peek_next_command_line_arg out_var)
    qt_ir_get_unhandled_args(args)
    list(GET args 0 result)
    set(${out_var} ${result} PARENT_SCOPE)
endfunction()

# Get the next unhandled command line argument.
function(qt_ir_args_get_next_command_line_arg out_var)
    qt_ir_get_unhandled_args(args)
    list(POP_FRONT args result)
    qt_ir_set_unhandled_args("${args}")
    set(${out_var} ${result} PARENT_SCOPE)
endfunction()

# Helper macro to parse the arguments for the command line options.
macro(qt_ir_commandline_option_parse_arguments)
    set(options UNSUPPORTED)
    set(oneValueArgs TYPE NAME SHORT_NAME ALIAS VALUE DEFAULT_VALUE)
    set(multiValueArgs VALUES MAPPING)
    cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
endmacro()

# We use this to define the command line options that init-repository accepts.
# Arguments
#  name - name of the long form option
#    e.g. 'module-subset' will parse '--module-subset'
#  UNSUPPORTED - mark the option as unsupported in the cmake port of init-repository,
#    which means we will fall back to calling the perl script instead
#  TYPE - the type of the option, currently we support boolean, string and void
#  VALUE - the value to be set for a 'void' type option
#  VALUES - the valid values for an option
#  MAPPING - currently unused
#  SHORT_NAME - an alternative short name flag,
#    e.g. 'f' will parse -f for --force
#  ALIAS - mark the option as an alias of another option, both will have the
#    same value when retrieved.
#  DEFAULT_VALUE - the default value to be set for the option when it's not specified
#    on the command line
#
# NOTE: Make sure to update the SHORT_NAME code path when adding new options.
function(qt_ir_commandline_option_helper name)
    qt_ir_commandline_option_parse_arguments(${ARGN})

    set(unsupported_options "${commandline_known_unsupported_options}")
    if(arg_UNSUPPORTED)
        set(commandline_option_${name}_unsupported
            "${arg_UNSUPPORTED}" PARENT_SCOPE)
        list(APPEND unsupported_options "${name}")
    endif()
    set(commandline_known_unsupported_options "${unsupported_options}" PARENT_SCOPE)

    set(commandline_known_options
        "${commandline_known_options};${name}" PARENT_SCOPE)

    set(commandline_option_${name}_type "${arg_TYPE}" PARENT_SCOPE)

    if(NOT "${arg_VALUE}" STREQUAL "")
        set(commandline_option_${name}_value "${arg_VALUE}" PARENT_SCOPE)
    endif()

    if(arg_VALUES)
        set(commandline_option_${name}_values ${arg_VALUES} PARENT_SCOPE)
    elseif(arg_MAPPING)
        set(commandline_option_${name}_mapping ${arg_MAPPING} PARENT_SCOPE)
    endif()

    if(NOT "${arg_SHORT_NAME}" STREQUAL "")
        set(commandline_option_${name}_short_name "${arg_SHORT_NAME}" PARENT_SCOPE)
    endif()

    if(NOT "${arg_ALIAS}" STREQUAL "")
        set(commandline_option_${name}_alias "${arg_ALIAS}" PARENT_SCOPE)
    endif()

    # Should be last, in case alias was specified
    if(NOT "${arg_DEFAULT_VALUE}" STREQUAL "")
        set(commandline_option_${name}_default_value "${arg_DEFAULT_VALUE}" PARENT_SCOPE)
        qt_ir_command_line_set_input("${name}" "${arg_DEFAULT_VALUE}")
    endif()
endfunction()

# Defines an option that init-repository understands.
# Uses qt_ir_commandline_option_helper to define both long and short option names.
macro(qt_ir_commandline_option name)
    # Define the main option
    qt_ir_commandline_option_helper("${name}" ${ARGN})

    qt_ir_commandline_option_parse_arguments(${ARGN})

    # Define the short name option if it's requested
    if(NOT "${arg_SHORT_NAME}" STREQUAL ""
        AND "${commandline_option_${arg_SHORT_NAME}_type}" STREQUAL "")
        set(unsupported "")
        if(arg_UNSUPPORTED)
            set(unsupported "${arg_UNSUPPORTED}")
        endif()

        qt_ir_commandline_option_helper("${arg_SHORT_NAME}"
            TYPE "${arg_TYPE}"
            ALIAS "${name}"
            VALUE "${arg_VALUE}"
            VALUES ${arg_VALUES}
            MAPPING ${arg_MAPPING}
            DEFAULT_VALUE ${arg_DEFAULT_VALUE}
            ${unsupported}
        )
    endif()
endmacro()

# Saves the value of a command line option into a global property.
function(qt_ir_command_line_set_input name val)
    if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
        set(name "${commandline_option_${name}_alias}")
    endif()

    set_property(GLOBAL PROPERTY _qt_ir_input_${name} "${val}")
    set_property(GLOBAL APPEND PROPERTY _qt_ir_inputs ${name})
endfunction()

# Appends a value of a command line option into a global property.
# Currently unused
function(qt_ir_command_line_append_input name val)
    if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
        set(name "${commandline_option_${name}_alias}")
    endif()

    get_property(oldval GLOBAL PROPERTY _qt_ir_input_${name})
    if(NOT "${oldval}" STREQUAL "")
        string(PREPEND val "${oldval};")
    endif()
    qt_ir_command_line_set_input(${name} "${val}" )
endfunction()

# Checks if the value of a command line option is valid.
function(qt_ir_validate_value opt val out_var)
    set(${out_var} TRUE PARENT_SCOPE)

    set(valid_values ${commandline_option_${arg}_values})
    list(LENGTH valid_values n)
    if(n EQUAL 0)
        return()
    endif()

    foreach(v ${valid_values})
        if(val STREQUAL v)
            return()
        endif()
    endforeach()

    set(${out_var} FALSE PARENT_SCOPE)
    list(JOIN valid_values " " valid_values_str)
    qt_ir_add_error("Invalid value '${val}' supplied to command line option '${opt}'."
        "\nAllowed values: ${valid_values_str}\n")
endfunction()

# Sets / handles the value of a command line boolean option.
function(qt_ir_commandline_boolean arg val nextok)
    if("${val}" STREQUAL "")
        set(val "yes")
    endif()
    if(NOT val STREQUAL "yes" AND NOT val STREQUAL "no")
        message(FATAL_ERROR
            "Invalid value '${val}' given for boolean command line option '${arg}'.")
    endif()
    qt_ir_command_line_set_input("${arg}" "${val}")
endfunction()

# Sets / handles the value of a command line string option.
function(qt_ir_commandline_string arg val nextok)
    if(nextok)
        qt_ir_args_get_next_command_line_arg(val)

        if("${val}" MATCHES "^-")
            qt_ir_add_error("No value supplied to command line options '${arg}'.")
        endif()
    endif()
    qt_ir_validate_value("${arg}" "${val}" success)
    if(success)
        qt_ir_command_line_set_input("${arg}" "${val}")
    endif()
endfunction()

# Sets / handles the value of a command line void option.
# This is an option like --force, which doesn't take any arguments.
# Currently unused
function(qt_ir_commandline_void arg val nextok)
    if(NOT "${val}" STREQUAL "")
        qt_i_add_error("Command line option '${arg}' expects no argument ('${val}' given).")
    endif()
    if(DEFINED commandline_option_${arg}_value)
        set(val ${commandline_option_${arg}_value})
    endif()
    if("${val}" STREQUAL "")
        set(val yes)
    endif()
    qt_ir_command_line_set_input("${arg}" "${val}")
endfunction()

# Reads the command line arguments from the optfile_path.
function(qt_ir_get_raw_args_from_optfile optfile_path out_var)
    file(STRINGS "${optfile_path}" args)
    set(${out_var} "${args}" PARENT_SCOPE)
endfunction()

# Reads the optfile_path, iterates over the given command line arguments,
# sets the input for recongized options.
#
# Handles the following styles of CLI arguments:
#  --no-foo / --disable-foo
#  -no-foo / -disable-foo
#  --foo=<values>
#  --foo <values>
#  -foo <values>
#  --foo
#  -foo
#  --f
#  -f
#
# Currently handles the following types of CLI arguments:
#  string
#  boolean
#  void
#
# IGNORE_UNKNOWN_ARGS tells the function not to fail if it encounters an unknown
# option, and instead append it to a global list of unknown options.
# It is needed when the script is called from the configure script with
# configure-only-known options.
function(qt_ir_process_args_from_optfile optfile_path)
    set(options IGNORE_UNKNOWN_ARGS)
    set(oneValueArgs "")
    set(multiValueArgs "")
    cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    qt_ir_get_raw_args_from_optfile("${optfile_path}" configure_args)
    qt_ir_set_unhandled_args("${configure_args}")

    while(1)
        qt_ir_args_has_next_command_line_arg(has_next)
        if(NOT has_next)
            break()
        endif()
        qt_ir_args_get_next_command_line_arg(arg)

        # parse out opt and val
        set(nextok FALSE)
        if(arg MATCHES "^--?(disable|no)-(.*)")
            set(opt "${CMAKE_MATCH_2}")
            set(val "no")
        elseif(arg MATCHES "^--([^=]+)=(.*)")
            set(opt "${CMAKE_MATCH_1}")
            set(val "${CMAKE_MATCH_2}")
        elseif(arg MATCHES "^--(.*)")
            set(nextok TRUE)
            set(opt "${CMAKE_MATCH_1}")
            unset(val)
        elseif(arg MATCHES "^-(.*)")
            set(nextok TRUE)
            set(opt "${CMAKE_MATCH_1}")
            unset(val)
        else()
            if(NOT arg_IGNORE_UNKNOWN_ARGS)
                qt_ir_add_error("Invalid command line parameter '${arg}'.")
            else()
                message(DEBUG "Unknown command line parameter '${arg}'. Collecting.")
                qt_ir_append_unknown_args("${arg}")
                continue()
            endif()
        endif()

        set(type "${commandline_option_${opt}_type}")

        if("${type}" STREQUAL "")
            if(NOT arg_IGNORE_UNKNOWN_ARGS)
                qt_ir_add_error("Unknown command line option '${arg}'.")
            else()
                message(DEBUG "Unknown command line option '${arg}'. Collecting.")
                qt_ir_append_unknown_args("${arg}")
                continue()
            endif()
        endif()

        if(NOT COMMAND "qt_ir_commandline_${type}")
            qt_ir_add_error("Unknown type '${type}' for command line option '${opt}'.")
        endif()
        qt_ir_call_function("qt_ir_commandline_${type}" "${opt}" "${val}" "${nextok}")
    endwhile()
endfunction()

# Shows help for the command line options.
function(qt_ir_show_help)
    set(help_file "${CMAKE_CURRENT_LIST_DIR}/QtIRHelp.txt")
    if(EXISTS "${help_file}")
        file(READ "${help_file}" content)
        message("${content}")
    endif()

    message([[
General Options:
-help, -h ............ Display this help screen
]])
endfunction()

# Gets the unhandled command line args.
function(qt_ir_get_unhandled_args out_var)
    get_property(args GLOBAL PROPERTY _qt_ir_unhandled_args)
    set(${out_var} "${args}" PARENT_SCOPE)
endfunction()

# Sets the unhandled command line args.
function(qt_ir_set_unhandled_args args)
    set_property(GLOBAL PROPERTY _qt_ir_unhandled_args "${args}")
endfunction()

# Adds to the unknown command line args.
function(qt_ir_append_unknown_args args)
    set_property(GLOBAL APPEND PROPERTY _qt_ir_unknown_args ${args})
endfunction()

# Gets the unhandled command line args.
function(qt_ir_get_unknown_args out_var)
    get_property(args GLOBAL PROPERTY _qt_ir_unknown_args)
    set(${out_var} "${args}" PARENT_SCOPE)
endfunction()

# Gets the unsupported options that init-repository.pl supports, but the cmake port does
# not support.
function(qt_ir_get_unsupported_options out_var)
    set(${out_var} "${commandline_known_unsupported_options}" PARENT_SCOPE)
endfunction()

# Get the value of a command line option.
function(qt_ir_get_option_value name out_var)
    if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
        set(name "${commandline_option_${name}_alias}")
    endif()

    get_property(value GLOBAL PROPERTY _qt_ir_input_${name})
    set(${out_var} "${value}" PARENT_SCOPE)
endfunction()

# Set the value of a command line option manually.
function(qt_ir_set_option_value name value)
    if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
        set(name "${commandline_option_${name}_alias}")
    endif()

    qt_ir_command_line_set_input("${name}" "${value}")
endfunction()

# Get the value of a command line option as a cmakke flag option, to be passed
# to functions that use cmake_parse_arguments.
function(qt_ir_get_option_as_cmake_flag_option cli_name cmake_option_name out_var)
    qt_ir_get_option_value("${cli_name}" bool_value)
    set(cmake_option "")
    if(bool_value)
        set(cmake_option "${cmake_option_name}")
    endif()
    set(${out_var} "${cmake_option}" PARENT_SCOPE)
endfunction()