forked from musescore/MuseScore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.cmake
executable file
·305 lines (267 loc) · 11.7 KB
/
build.cmake
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
#!/usr/bin/env -S cmake -P
# This cross-platform script automates the process of creating a build directory and
# compiling MuseScore. Despite being written in the CMake language, you should think
# of this file as like a shell script or batch file rather than as part of the CMake
# project tree. As always, the project tree starts in the top-level CMakeLists.txt.
string(TIMESTAMP SCRIPT_START_TIMESTAMP "%s" UTC)
if(NOT DEFINED CMAKE_SCRIPT_MODE_FILE)
file(RELATIVE_PATH SCRIPT_PATH "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_LIST_FILE}")
message(FATAL_ERROR
"This file is a script. You should run it with:\n"
" \$ cmake -P ${SCRIPT_PATH} [args...]\n"
"Don't try to use it in other CMake files with include().\n"
"See https://cmake.org/cmake/help/latest/manual/cmake.1.html#run-a-script"
)
endif()
cmake_minimum_required(VERSION 3.16) # should match version in CMakeLists.txt
# CMake arguments up to '-P' (ignore these)
set(i "1")
while(i LESS "${CMAKE_ARGC}")
if("${CMAKE_ARGV${i}}" STREQUAL "-P")
math(EXPR i "${i} + 1") # ignore '-P' option
break() # done with CMake arguments
endif()
math(EXPR i "${i} + 1") # next argument
endwhile()
# Script arguments (store these for later processing)
# By convention, SCRIPT_ARG[0] is the name of the script
set(SCRIPT_ARGS "") # empty argument list
while(i LESS "${CMAKE_ARGC}")
list(APPEND SCRIPT_ARGS "${CMAKE_ARGV${i}}")
math(EXPR i "${i} + 1") # next argument
endwhile()
# load custom CMake functions and macros
include("${CMAKE_CURRENT_LIST_DIR}/build/cmake/GetUtilsFunctions.cmake") # "fn__" namespace
# Set the name of the build folder (just the folder name, not the full path)
function(build_folder
VAR_NAME # Name of variable to store build folder name
)
# Windows has a 260 character limit on file paths, so the build folder
# name must be abbreviated to prevent build files exceeding this limit.
# The limit applies to the *entire* path, not just individual components.
# Abbreviation is not essential on other platforms but is still welcome.
if(WIN32)
set(PLATFORM "Win")
elseif(APPLE)
set(PLATFORM "Mac")
else()
set(PLATFORM "${CMAKE_HOST_SYSTEM_NAME}")
endif()
set(GEN_SHORT "${GENERATOR}")
string(REGEX REPLACE ".*Visual Studio ([0-9]+).*" "VS\\1" GEN_SHORT "${GEN_SHORT}")
string(REGEX REPLACE ".*MinGW.*" "MinGW" GEN_SHORT "${GEN_SHORT}")
string(REGEX REPLACE ".*Makefile.*" "Make" GEN_SHORT "${GEN_SHORT}")
set(BUILD_FOLDER "${PLATFORM}-Qt${QT_VERSION}-${QT_COMPILER}-${GEN_SHORT}-${BUILD_TYPE}")
string(REGEX REPLACE "[ /\\]" "" BUILD_FOLDER "${BUILD_FOLDER}") # remove spaces and slashes
set("${VAR_NAME}" "${BUILD_FOLDER}" PARENT_SCOPE)
endfunction()
# Default values for independent build variables
set(SOURCE_PATH "${CMAKE_CURRENT_LIST_DIR}") # MuseScore repo (directory of this script)
set(ALL_BUILDS_PATH "${SOURCE_PATH}/builds") # Where all build folders will be created
# CPUS
cmake_host_system_information(RESULT CPUS QUERY NUMBER_OF_LOGICAL_CORES)
if(NOT "${CPUS}" GREATER "0")
include(ProcessorCount)
ProcessorCount(CPUS)
endif()
# Overrides
# Use this file to replace build variable defaults with your own values.
# E.g. set your own GENERATOR, BUILD_TYPE, BUILD_FOLDER, or CPUS.
if(EXISTS "${SOURCE_PATH}/build_overrides.cmake")
message(STATUS "Including personal build overrides")
include("${SOURCE_PATH}/build_overrides.cmake")
endif()
# QTDIR environment variable is set automatically by Qt Creator. It can also
# be set manually in build_overrides.cmake for use with other IDEs.
# Typical value for ENV{QTDIR}: /path/to/Qt/<version>/<compiler>
if(DEFINED ENV{QTDIR})
# Add selected build kit to PATH.
if(WIN32)
set(ENV{PATH} "$ENV{QTDIR}/bin;$ENV{PATH}") # semicolon
else()
set(ENV{PATH} "$ENV{QTDIR}/bin:$ENV{PATH}") # colon
endif()
endif()
fn__require_program(QMAKE Qt --version "https://musescore.org/en/handbook/developers-handbook/compilation" qmake)
fn__set_qt_variables("${QMAKE}")
message(STATUS "QT_LOCATION: ${QT_LOCATION}")
message(STATUS "QT_VERSION: ${QT_VERSION}")
message(STATUS "QT_COMPILER: ${QT_COMPILER}")
# Process script arguments
set(i "1")
list(LENGTH SCRIPT_ARGS nargs)
while(i LESS "${nargs}")
list(GET SCRIPT_ARGS "${i}" ARG)
if("${ARG}" STREQUAL "clean")
set(ARG_CLEAN "TRUE")
elseif("${ARG}" STREQUAL "configure")
set(ARG_CONFIGURE "TRUE")
elseif("${ARG}" STREQUAL "build")
set(ARG_CONFIGURE "TRUE")
set(ARG_BUILD "TRUE")
elseif("${ARG}" STREQUAL "install")
set(ARG_CONFIGURE "TRUE")
set(ARG_BUILD "TRUE")
set(ARG_INSTALL "TRUE")
elseif("${ARG}" STREQUAL "run")
set(ARG_RUN "TRUE")
else()
# Other arguments are used by certain subprocesses in build steps
if(ARG_RUN)
list(APPEND RUN_ARGS "${ARG}") # args after "run" belong to Run step
else()
list(APPEND CONFIGURE_ARGS "${ARG}") # all other args belong to Configure step
endif()
endif()
math(EXPR i "${i} + 1") # next argument
endwhile()
# Default if no build steps given as arguments
if(NOT (ARG_CLEAN OR ARG_CONFIGURE OR ARG_BUILD OR ARG_INSTALL OR ARG_RUN))
set(ARG_CONFIGURE "TRUE")
set(ARG_BUILD "TRUE")
set(ARG_INSTALL "TRUE")
endif()
# Default values for build variables that depend on other build variables.
# These will only be set here if they were not already defined elsewhere,
# e.g. by script arguments or in build_overrides.cmake.
fn__get_option(GENERATOR -G ${CONFIGURE_ARGS})
if(WIN32)
fn__set_default(GENERATOR "Visual Studio 16 2019")
else()
fn__set_default(GENERATOR "Unix Makefiles")
endif()
fn__get_option(BUILD_TYPE -DCMAKE_BUILD_TYPE ${CONFIGURE_ARGS})
fn__set_default(BUILD_TYPE "RelWithDebInfo")
if(NOT DEFINED BUILD_FOLDER)
build_folder(BUILD_FOLDER)
endif()
set(BUILD_PATH "${ALL_BUILDS_PATH}/${BUILD_FOLDER}")
fn__get_option(INSTALL_PATH -DCMAKE_INSTALL_PREFIX ${CONFIGURE_ARGS})
fn__set_default(INSTALL_PATH "install") # relative to BUILD_PATH
# MSCORE_EXECUTABLE (path relative to INSTALL_PATH)
if(WIN32)
fn__set_default(MSCORE_EXECUTABLE "bin/MuseScore4.exe")
elseif(APPLE)
fn__set_default(MSCORE_EXECUTABLE "mscore.app/Contents/MacOS/mscore")
else()
fn__set_default(MSCORE_EXECUTABLE "bin/mscore")
endif()
# make paths absolute if they are not already
get_filename_component(BUILD_PATH "${BUILD_PATH}" ABSOLUTE BASE_DIR "${SOURCE_PATH}")
get_filename_component(INSTALL_PATH "${INSTALL_PATH}" ABSOLUTE BASE_DIR "${BUILD_PATH}")
get_filename_component(MSCORE_EXECUTABLE "${MSCORE_EXECUTABLE}" ABSOLUTE BASE_DIR "${INSTALL_PATH}")
message(STATUS "SOURCE_PATH: ${SOURCE_PATH}")
message(STATUS "BUILD_PATH: ${BUILD_PATH}")
message(STATUS "INSTALL_PATH: ${INSTALL_PATH}")
message(STATUS "MSCORE_EXECUTABLE: ${MSCORE_EXECUTABLE}")
message(STATUS "CPUS: ${CPUS}")
message(STATUS "GENERATOR: ${GENERATOR}")
message(STATUS "BUILD_TYPE: ${BUILD_TYPE}")
list(APPEND CONFIGURE_ARGS "-G" "${GENERATOR}")
list(APPEND CONFIGURE_ARGS "-DCMAKE_INSTALL_PREFIX=${INSTALL_PATH}")
list(APPEND CONFIGURE_ARGS "-DCMAKE_BUILD_TYPE=${BUILD_TYPE}")
if(QT_COMPILER MATCHES "_64$" OR QT_COMPILER MATCHES "mingw64")
list(APPEND CONFIGURE_ARGS "-DBUILD_64=ON")
else()
list(APPEND CONFIGURE_ARGS "-DBUILD_64=OFF")
endif()
#### ACTUAL BUILD STEPS START HERE ####
# Clean - delete an existing build directory.
#
# We usually avoid this because performing a clean build takes much longer
# than an incremental build, but it is occassionally necessary. If you
# encounter errors during a build then you should try doing a clean build.
if(ARG_CLEAN)
message("\n~~~~ Actualizing Clean step ~~~~\n")
message("Deleting ${BUILD_PATH}")
file(REMOVE_RECURSE "${BUILD_PATH}")
endif()
# Configure - generate build system for the native build tool.
#
# We only do this explicitly on the very first build. On subsequent builds
# the native build tool will redo the configuration if any CMake files were
# edited. You can force this to happen by deleting CMakeCache.txt.
if(ARG_CONFIGURE AND NOT EXISTS "${BUILD_PATH}/CMakeCache.txt")
message("\n~~~~ Actualizing Configure step ~~~~\n")
file(MAKE_DIRECTORY "${BUILD_PATH}")
if(QT_COMPILER MATCHES "msvc")
set(CMAKE_WRAPPER "${SOURCE_PATH}/build/cmake_wrapper.bat")
else()
set(CMAKE_WRAPPER "cmake")
endif()
fn__command_string(ARGS_STR ${CONFIGURE_ARGS})
message("CONFIGURE_ARGS: ${ARGS_STR}")
execute_process(
# List of CMake arguments in CONFIGURE_ARGS variable. It must be unquoted here.
COMMAND "${CMAKE_WRAPPER}" -S "${SOURCE_PATH}" -B . ${CONFIGURE_ARGS}
WORKING_DIRECTORY "${BUILD_PATH}"
RESULT_VARIABLE EXIT_STATUS
)
if(NOT "${EXIT_STATUS}" EQUAL "0")
file(REMOVE "${BUILD_PATH}/CMakeCache.txt") # need to configure again next time
message(FATAL_ERROR "Configure step failed with status ${EXIT_STATUS}. See output above for details.")
endif()
endif()
# Build - compile code with the native build tool.
#
# We always do this. We can rely on the native build tool to be efficient and
# only (re)compile source files that have been edited since the last build.
if(ARG_BUILD)
message("\n~~~~ Actualizing Build step ~~~~\n")
execute_process(
COMMAND cmake --build . --config "${BUILD_TYPE}" --parallel "${CPUS}"
WORKING_DIRECTORY "${BUILD_PATH}"
RESULT_VARIABLE EXIT_STATUS
)
if(NOT "${EXIT_STATUS}" EQUAL "0")
message(FATAL_ERROR "Build step failed with status ${EXIT_STATUS}. See output above for details.")
endif()
endif()
# Install - move compiled files to destination folders.
#
# Again, we always do this and rely on the tool itself to be efficient and
# only install files that have changed since last time.
if(ARG_INSTALL)
message("\n~~~~ Actualizing Install step ~~~~\n")
execute_process(
COMMAND cmake --install . --config "${BUILD_TYPE}"
WORKING_DIRECTORY "${BUILD_PATH}"
RESULT_VARIABLE EXIT_STATUS
)
if(NOT "${EXIT_STATUS}" EQUAL "0")
message(FATAL_ERROR "Install step failed with status ${EXIT_STATUS}. See output above for details.")
endif()
endif()
# Run - attempt to run the compiled program within the installation folder.
#
# The working directory is unchanged. Use build_override.cmake to set the
# CMake variable RUN_ARGS to contain a list of arguments to pass to MuseScore
# on the command line. In addition, script arguments after "run" will be
# appened to this list, but note that certain arguments cannot be passed this
# way (e.g. --help, --version) because they cancel CMake script processing.
if(ARG_RUN)
message("\n~~~~ Actualizing Run step ~~~~\n")
if(WIN32)
set(CMD "cmd.exe" "/c") # allow CMake to launch a GUI application
else()
set(CMD "") # not an issue on other platforms
endif()
fn__command_string(ARGS_STR ${RUN_ARGS})
message("RUN_ARGS: ${ARGS_STR}")
execute_process(
# List of MuseScore arguments in RUN_ARGS variable. It must be unquoted here.
COMMAND ${CMD} "${MSCORE_EXECUTABLE}" ${RUN_ARGS}
RESULT_VARIABLE EXIT_STATUS
)
if(NOT "${EXIT_STATUS}" EQUAL "0")
message(FATAL_ERROR "Run step failed with status ${EXIT_STATUS}. See output above for details.")
endif()
endif()
# Package - create installer archive for distribution to end users.
# TODO
string(TIMESTAMP SCRIPT_END_TIMESTAMP "%s" UTC)
math(EXPR SCRIPT_ELAPSED_TIME "${SCRIPT_END_TIMESTAMP} - ${SCRIPT_START_TIMESTAMP}")
math(EXPR SCRIPT_ELAPSED_MINS "${SCRIPT_ELAPSED_TIME} / 60")
math(EXPR SCRIPT_ELAPSED_SECS "${SCRIPT_ELAPSED_TIME} % 60")
list(GET SCRIPT_ARGS "0" SCRIPT_NAME)
message("\n${SCRIPT_NAME}: Complete after ${SCRIPT_ELAPSED_MINS} minutes and ${SCRIPT_ELAPSED_SECS} seconds")