summaryrefslogtreecommitdiffstats
path: root/cmake/QtInitProject.cmake
blob: a42f59f5c8a300632f3ca528b2641746b8be68ee (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
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)

if(NOT PROJECT_DIR)
    set(PROJECT_DIR "${CMAKE_SOURCE_DIR}")
endif()

get_filename_component(project_name "${PROJECT_DIR}" NAME)

get_filename_component(project_abs_dir "${PROJECT_DIR}" ABSOLUTE)
if(NOT IS_DIRECTORY "${project_abs_dir}")
    message(FATAL_ERROR "Unable to scan ${project_abs_dir}. The directory doesn't exist.")
endif()

set(known_extensions "")
set(types "")

# The function allows extending the capabilities of this script and establishes simple relation
# chains between the file types.
# Option Arguments:
#     EXPERIMENTAL
#         Marks that the support of the following files is experimental and the required Qt modules
#         are in Technical preview state.
#     DEPRECATED
#         Marks that the support of the following files will be discontinued soon and the required
#         Qt modules are deprecated.
# One-value Arguments:
#     TEMPLATE
#         The CMake code template. Use the '@files@' string for the files substitution.
# Multi-value Arguments:
#     EXTENSIONS
#         List of the file extensions treated as this source 'type'.
#     MODULES
#         List of Qt modules required for these file types.
#     DEPENDS
#         The prerequisite source 'type' needed by this source 'type'
macro(handle_type type)
    cmake_parse_arguments(arg
        "EXPERIMENTAL;DEPRECATED"
        "TEMPLATE"
        "EXTENSIONS;MODULES;DEPENDS"
        ${ARGN}
    )

    if(NOT arg_EXTENSIONS)
        message(FATAL_ERROR "Unexpected call handle_type of with no EXTENSIONS specified."
            " This is the Qt issue, please report a bug at https://bugreports.qt.io.")
    endif()
    set(unique_extensions_subset "${known_extensions}")
    list(REMOVE_ITEM unique_extensions_subset ${arg_EXTENSIONS})
    if(NOT "${known_extensions}" STREQUAL "${unique_extensions_subset}")
        message(FATAL_ERROR "${type} contains duplicated extensions, this is not supported."
            " This is the Qt issue, please report a bug at https://bugreports.qt.io.")
    endif()
    set(${type}_file_extensions "${arg_EXTENSIONS}")

    if(NOT arg_TEMPLATE)
        message(FATAL_ERROR "Unexpected call handle_type of with no TEMPLATE specified."
            " This is the Qt issue, please report a bug at https://bugreports.qt.io.")
    endif()
    set(${type}_template "${arg_TEMPLATE}")

    if(arg_MODULES)
        set(${type}_required_modules "${arg_MODULES}")
    endif()

    list(APPEND types ${type})

    if(arg_EXPERIMENTAL)
        set(${type}_is_experimental TRUE)
    else()
        set(${type}_is_experimental FALSE)
    endif()

    if(arg_DEPRECATED)
        set(${type}_is_deprecated TRUE)
    else()
        set(${type}_is_deprecated FALSE)
    endif()

    if(arg_DEPENDS)
        set(${type}_dependencies ${arg_DEPENDS})
    endif()
endmacro()

handle_type(cpp EXTENSIONS .c .cc .cpp .cxx .h .hh .hxx .hpp MODULES Core TEMPLATE
"\n\nqt_add_executable(${project_name}
    @files@
)"
)

handle_type(qml EXTENSIONS .qml .js .mjs MODULES Gui Qml Quick TEMPLATE
"\n\nqt_add_qml_module(${project_name}
    URI ${project_name}
    OUTPUT_DIRECTORY qml
    VERSION 1.0
    RESOURCE_PREFIX /qt/qml
    QML_FILES
        @files@
)"
)

handle_type(ui EXTENSIONS .ui MODULES Gui Widgets DEPENDS cpp TEMPLATE
"\n\ntarget_sources(${project_name}
    PRIVATE
        @files@
)"
)

handle_type(qrc EXTENSIONS .qrc DEPENDS cpp TEMPLATE
"\n\nqt_add_resources(${project_name}_resources @files@)
target_sources(${project_name}
    PRIVATE
        \\\${${project_name}_resources}
)"
)

handle_type(protobuf EXPERIMENTAL EXTENSIONS .proto MODULES Protobuf Grpc TEMPLATE
"\n\nqt_add_protobuf(${project_name}
    GENERATE_PACKAGE_SUBFOLDERS
    PROTO_FILES
        @files@
)"
)

set(extra_packages "")
file(GLOB_RECURSE files RELATIVE "${project_abs_dir}" "${project_abs_dir}/*")
foreach(f IN LISTS files)
    get_filename_component(file_extension "${f}" LAST_EXT)
    string(TOLOWER "${file_extension}" file_extension)

    foreach(type IN LISTS types)
        if(file_extension IN_LIST ${type}_file_extensions)
            list(APPEND ${type}_sources "${f}")
            list(APPEND packages ${${type}_required_modules})
            if(${type}_is_experimental)
                message("We found files with the following extensions in your directory:"
                    " ${${type}_file_extensions}\n"
                    "Note that the modules ${${type}_required_modules} are"
                    " in the technical preview state.")
            endif()
            if(${type}_is_deprecated)
                message("We found files with the following extensions in your directory:"
                    " ${${type}_file_extensions}\n"
                    "Note that the modules ${${type}_required_modules} are deprecated.")
            endif()
            break()
        endif()
    endforeach()
endforeach()

if(packages)
    list(REMOVE_DUPLICATES packages)
    list(JOIN packages " " packages_string)
    list(JOIN packages "\n        Qt::" deps_string)
    set(deps_string "Qt::${deps_string}")
endif()

set(content
"cmake_minimum_required(VERSION 3.16)
project(${project_name} LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS ${packages_string})
qt_standard_project_setup()"
)

set(has_useful_sources FALSE)
foreach(type IN LISTS types)
    if(${type}_sources)
        set(skip FALSE)
        foreach(dep IN LISTS ${type}_dependencies)
            if(NOT ${dep}_sources)
                set(skip TRUE)
                message("Sources of type ${${type}_file_extensions} cannot live in the project"
                " without ${${dep}_file_extensions} files. Skipping.")
                break()
            endif()
        endforeach()
        if(skip)
            continue()
        endif()

        set(has_useful_sources TRUE)
        string(REGEX MATCH "( +)@files@" unused "${${type}_template}")
        list(JOIN ${type}_sources "\n${CMAKE_MATCH_1}" ${type}_sources)
        string(REPLACE "@files@" "${${type}_sources}" ${type}_content
            "${${type}_template}")
        string(APPEND content "${${type}_content}")
    endif()
endforeach()

string(APPEND content "\n\ntarget_link_libraries(${project_name}
    PRIVATE
        ${deps_string}
)\n"
)

if(EXISTS "${project_abs_dir}/CMakeLists.txt")
    message(FATAL_ERROR "Project is already initialized in current directory."
        " Please remove CMakeLists.txt if you want to regenerate the project.")
endif()

if(NOT has_useful_sources)
    message(FATAL_ERROR "Could not find any files to generate the project.")
endif()
file(WRITE "${project_abs_dir}/CMakeLists.txt" "${content}")

message("The project file is successfully generated. To build the project run:"
    "\nmkdir build"
    "\ncd build"
    "\nqt-cmake ${project_abs_dir}"
    "\ncmake --build ${project_abs_dir}"
)