1include(RunCMake) 2 3set(RunCMake_GENERATOR "Ninja") 4set(RunCMake_GENERATOR_IS_MULTI_CONFIG 0) 5 6# Detect ninja version so we know what tests can be supported. 7execute_process( 8 COMMAND "${RunCMake_MAKE_PROGRAM}" --version 9 OUTPUT_VARIABLE ninja_out 10 ERROR_VARIABLE ninja_out 11 RESULT_VARIABLE ninja_res 12 OUTPUT_STRIP_TRAILING_WHITESPACE 13 ) 14if(ninja_res EQUAL 0 AND "x${ninja_out}" MATCHES "^x[0-9]+\\.[0-9]+") 15 set(ninja_version "${ninja_out}") 16 message(STATUS "ninja version: ${ninja_version}") 17else() 18 message(FATAL_ERROR "'ninja --version' reported:\n${ninja_out}") 19endif() 20 21# Sanitize NINJA_STATUS since we expect default behavior. 22unset(ENV{NINJA_STATUS}) 23 24if(CMAKE_HOST_WIN32) 25 run_cmake(SelectCompilerWindows) 26else() 27 run_cmake(SelectCompilerUNIX) 28endif() 29 30function(run_NinjaToolMissing) 31 set(RunCMake_MAKE_PROGRAM ninja-tool-missing) 32 run_cmake(NinjaToolMissing) 33endfunction() 34run_NinjaToolMissing() 35 36function(run_NoWorkToDo) 37 run_cmake(NoWorkToDo) 38 set(RunCMake_TEST_NO_CLEAN 1) 39 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/NoWorkToDo-build) 40 set(RunCMake_TEST_OUTPUT_MERGE 1) 41 run_cmake_command(NoWorkToDo-build ${CMAKE_COMMAND} --build .) 42 run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain) 43endfunction() 44run_NoWorkToDo() 45 46function(run_VerboseBuild) 47 run_cmake(VerboseBuild) 48 set(RunCMake_TEST_NO_CLEAN 1) 49 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VerboseBuild-build) 50 set(RunCMake_TEST_OUTPUT_MERGE 1) 51 run_cmake_command(VerboseBuild-build ${CMAKE_COMMAND} --build . -v --clean-first) 52 run_cmake_command(VerboseBuild-nowork ${CMAKE_COMMAND} --build . --verbose) 53endfunction() 54run_VerboseBuild() 55 56function(run_CMP0058 case) 57 # Use a single build tree for a few tests without cleaning. 58 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0058-${case}-build) 59 set(RunCMake_TEST_NO_CLEAN 1) 60 file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") 61 file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") 62 run_cmake(CMP0058-${case}) 63 run_cmake_command(CMP0058-${case}-build ${CMAKE_COMMAND} --build .) 64endfunction() 65 66run_CMP0058(OLD-no) 67run_CMP0058(OLD-by) 68run_CMP0058(WARN-no) 69run_CMP0058(WARN-by) 70run_CMP0058(NEW-no) 71run_CMP0058(NEW-by) 72 73run_cmake_with_options(CustomCommandDepfile -DCMAKE_BUILD_TYPE=Debug) 74run_cmake(CustomCommandJobPool) 75run_cmake(JobPoolUsesTerminal) 76 77run_cmake(RspFileC) 78run_cmake(RspFileCXX) 79if(TEST_Fortran) 80 run_cmake(RspFileFortran) 81endif() 82 83function(run_CommandConcat) 84 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CommandConcat-build) 85 set(RunCMake_TEST_NO_CLEAN 1) 86 file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") 87 file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") 88 run_cmake(CommandConcat) 89 run_cmake_command(CommandConcat-build ${CMAKE_COMMAND} --build .) 90endfunction() 91run_CommandConcat() 92 93function(run_SubDir) 94 # Use a single build tree for a few tests without cleaning. 95 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SubDir-build) 96 set(RunCMake_TEST_NO_CLEAN 1) 97 file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") 98 file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") 99 run_cmake(SubDir) 100 if(WIN32) 101 set(SubDir_all [[SubDir\all]]) 102 set(SubDir_test [[SubDir\test]]) 103 set(SubDir_install [[SubDir\install]]) 104 set(SubDirBinary_test [[SubDirBinary\test]]) 105 set(SubDirBinary_all [[SubDirBinary\all]]) 106 set(SubDirBinary_install [[SubDirBinary\install]]) 107 else() 108 set(SubDir_all [[SubDir/all]]) 109 set(SubDir_test [[SubDir/test]]) 110 set(SubDir_install [[SubDir/install]]) 111 set(SubDirBinary_all [[SubDirBinary/all]]) 112 set(SubDirBinary_test [[SubDirBinary/test]]) 113 set(SubDirBinary_install [[SubDirBinary/install]]) 114 endif() 115 run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all}) 116 run_cmake_command(SubDir-test ${CMAKE_COMMAND} --build . --target ${SubDir_test}) 117 run_cmake_command(SubDir-install ${CMAKE_COMMAND} --build . --target ${SubDir_install}) 118 run_cmake_command(SubDirBinary-build ${CMAKE_COMMAND} --build . --target ${SubDirBinary_all}) 119 run_cmake_command(SubDirBinary-test ${CMAKE_COMMAND} --build . --target ${SubDirBinary_test}) 120 run_cmake_command(SubDirBinary-install ${CMAKE_COMMAND} --build . --target ${SubDirBinary_install}) 121endfunction() 122run_SubDir() 123 124function(run_ninja dir) 125 execute_process( 126 COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} 127 WORKING_DIRECTORY "${dir}" 128 OUTPUT_VARIABLE ninja_stdout 129 ERROR_VARIABLE ninja_stderr 130 RESULT_VARIABLE ninja_result 131 ) 132 if(NOT ninja_result EQUAL 0) 133 message(STATUS " 134============ beginning of ninja's stdout ============ 135${ninja_stdout} 136=============== end of ninja's stdout =============== 137") 138 message(STATUS " 139============ beginning of ninja's stderr ============ 140${ninja_stderr} 141=============== end of ninja's stderr =============== 142") 143 message(FATAL_ERROR 144 "top ninja build failed exited with status ${ninja_result}") 145 endif() 146 set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) 147endfunction(run_ninja) 148 149function (run_LooseObjectDepends) 150 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LooseObjectDepends-build) 151 run_cmake(LooseObjectDepends) 152 run_ninja("${RunCMake_TEST_BINARY_DIR}" "CMakeFiles/top.dir/top.c${CMAKE_C_OUTPUT_EXTENSION}") 153 if (EXISTS "${RunCMake_TEST_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}dep${CMAKE_SHARED_LIBRARY_SUFFIX}") 154 message(FATAL_ERROR 155 "The `dep` library was created when requesting an object file to be " 156 "built; this should no longer be necessary.") 157 endif () 158 if (EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/dep.dir/dep.c${CMAKE_C_OUTPUT_EXTENSION}") 159 message(FATAL_ERROR 160 "The `dep.c` object file was created when requesting an object file to " 161 "be built; this should no longer be necessary.") 162 endif () 163endfunction () 164run_LooseObjectDepends() 165 166function (run_AssumedSources) 167 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build) 168 run_cmake(AssumedSources) 169 run_ninja("${RunCMake_TEST_BINARY_DIR}" "${RunCMake_TEST_BINARY_DIR}/target.c") 170 if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/target.c") 171 message(FATAL_ERROR 172 "Dependencies for an assumed source did not hook up properly for 'target.c'.") 173 endif () 174 run_ninja("${RunCMake_TEST_BINARY_DIR}" "${RunCMake_TEST_BINARY_DIR}/target-no-depends.c") 175 if (EXISTS "${RunCMake_TEST_BINARY_DIR}/target-no-depends.c") 176 message(FATAL_ERROR 177 "Dependencies for an assumed source were magically hooked up for 'target-no-depends.c'.") 178 endif () 179endfunction () 180run_AssumedSources() 181 182function(sleep delay) 183 execute_process( 184 COMMAND ${CMAKE_COMMAND} -E sleep ${delay} 185 RESULT_VARIABLE result 186 ) 187 if(NOT result EQUAL 0) 188 message(FATAL_ERROR "failed to sleep for ${delay} second.") 189 endif() 190endfunction(sleep) 191 192macro(ninja_escape_path path out) 193 string(REPLACE "\$ " "\$\$" "${out}" "${path}") 194 string(REPLACE " " "\$ " "${out}" "${${out}}") 195 string(REPLACE ":" "\$:" "${out}" "${${out}}") 196endmacro(ninja_escape_path) 197 198macro(shell_escape string out) 199 string(REPLACE "\"" "\\\"" "${out}" "${string}") 200endmacro(shell_escape) 201 202function(run_sub_cmake test ninja_output_path_prefix) 203 set(top_build_dir "${RunCMake_BINARY_DIR}/${test}-build/") 204 file(REMOVE_RECURSE "${top_build_dir}") 205 file(MAKE_DIRECTORY "${top_build_dir}") 206 207 ninja_escape_path("${ninja_output_path_prefix}" 208 escaped_ninja_output_path_prefix) 209 210 # Generate top build ninja file. 211 set(top_build_ninja "${top_build_dir}/build.ninja") 212 shell_escape("${top_build_ninja}" escaped_top_build_ninja) 213 set(build_ninja_dep "${top_build_dir}/build_ninja_dep") 214 ninja_escape_path("${build_ninja_dep}" escaped_build_ninja_dep) 215 shell_escape("${CMAKE_COMMAND}" escaped_CMAKE_COMMAND) 216 file(WRITE "${build_ninja_dep}" "fake dependency of top build.ninja file\n") 217 if(WIN32) 218 set(cmd_prefix "cmd.exe /C \"") 219 set(cmd_suffix "\"") 220 else() 221 set(cmd_prefix "") 222 set(cmd_suffix "") 223 endif() 224 set(fs_delay 3) # We assume the system as 1 sec timestamp resolution. 225 file(WRITE "${top_build_ninja}" "\ 226subninja ${escaped_ninja_output_path_prefix}/build.ninja 227default ${escaped_ninja_output_path_prefix}/all 228 229# Sleep for long enough before regenerating to make sure the timestamp of 230# the top build.ninja will be strictly greater than the timestamp of the 231# sub/build.ninja file. 232rule RERUN 233 command = ${cmd_prefix}\"${escaped_CMAKE_COMMAND}\" -E sleep ${fs_delay} && \"${escaped_CMAKE_COMMAND}\" -E touch \"${escaped_top_build_ninja}\"${cmd_suffix} 234 description = Testing regeneration 235 generator = 1 236 237build build.ninja: RERUN ${escaped_build_ninja_dep} || ${escaped_ninja_output_path_prefix}/build.ninja 238 pool = console 239") 240 241 # Run sub cmake project. 242 set(RunCMake_TEST_OPTIONS "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=${ninja_output_path_prefix}") 243 set(RunCMake_TEST_BINARY_DIR "${top_build_dir}/${ninja_output_path_prefix}") 244 run_cmake(${test}) 245 246 # Check there is no 'default' statement in Ninja file generated by CMake. 247 set(sub_build_ninja "${RunCMake_TEST_BINARY_DIR}/build.ninja") 248 file(READ "${sub_build_ninja}" sub_build_ninja_file) 249 if(sub_build_ninja_file MATCHES "\ndefault [^\n][^\n]*all\n") 250 message(FATAL_ERROR 251 "unexpected 'default' statement found in '${sub_build_ninja}'") 252 endif() 253 254 # Run ninja from the top build directory. 255 run_ninja("${top_build_dir}") 256 257 # Test regeneration rules run in order. 258 set(main_cmakelists "${RunCMake_SOURCE_DIR}/CMakeLists.txt") 259 sleep(${fs_delay}) 260 file(TOUCH "${main_cmakelists}") 261 file(TOUCH "${build_ninja_dep}") 262 run_ninja("${top_build_dir}") 263 file(TIMESTAMP "${main_cmakelists}" mtime_main_cmakelists UTC) 264 file(TIMESTAMP "${sub_build_ninja}" mtime_sub_build_ninja UTC) 265 file(TIMESTAMP "${top_build_ninja}" mtime_top_build_ninja UTC) 266 267 # Check sub build.ninja is regenerated. 268 if(mtime_main_cmakelists STRGREATER mtime_sub_build_ninja) 269 message(FATAL_ERROR 270 "sub build.ninja not regenerated: 271 CMakeLists.txt = ${mtime_main_cmakelists} 272 sub/build.ninja = ${mtime_sub_build_ninja}") 273 endif() 274 275 # Check top build.ninja is regenerated after sub build.ninja. 276 if(NOT mtime_top_build_ninja STRGREATER mtime_sub_build_ninja) 277 message(FATAL_ERROR 278 "top build.ninja not regenerated strictly after sub build.ninja: 279 sub/build.ninja = ${mtime_sub_build_ninja} 280 build.ninja = ${mtime_top_build_ninja}") 281 endif() 282 283endfunction(run_sub_cmake) 284 285if("${ninja_version}" VERSION_LESS 1.6) 286 message(WARNING "Ninja is too old; skipping rest of test.") 287 return() 288endif() 289 290foreach(ninja_output_path_prefix "sub space" "sub") 291 run_sub_cmake(Executable "${ninja_output_path_prefix}") 292 run_sub_cmake(StaticLib "${ninja_output_path_prefix}") 293 run_sub_cmake(SharedLib "${ninja_output_path_prefix}") 294 run_sub_cmake(TwoLibs "${ninja_output_path_prefix}") 295 run_sub_cmake(SubDirPrefix "${ninja_output_path_prefix}") 296 run_sub_cmake(CustomCommandWorkingDirectory "${ninja_output_path_prefix}") 297endforeach(ninja_output_path_prefix) 298 299function (run_PreventTargetAliasesDupBuildRule) 300 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PreventTargetAliasesDupBuildRule-build) 301 run_cmake(PreventTargetAliasesDupBuildRule) 302 run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err) 303endfunction () 304run_PreventTargetAliasesDupBuildRule() 305 306function (run_PreventConfigureFileDupBuildRule) 307 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PreventConfigureFileDupBuildRule-build) 308 run_cmake(PreventConfigureFileDupBuildRule) 309 run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err) 310endfunction() 311run_PreventConfigureFileDupBuildRule() 312 313function (run_ChangeBuildType) 314 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ChangeBuildType-build) 315 set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug") 316 run_cmake(ChangeBuildType) 317 unset(RunCMake_TEST_OPTIONS) 318 run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err) 319endfunction() 320run_ChangeBuildType() 321 322function(run_QtAutoMocDeps) 323 set(QtX Qt${CMake_TEST_Qt_version}) 324 if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0) 325 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps-build) 326 run_cmake_with_options(QtAutoMocDeps 327 "-Dwith_qt_version=${CMake_TEST_Qt_version}" 328 "-D${QtX}_DIR=${${QtX}_DIR}" 329 "-D${QtX}Core_DIR=${${QtX}Core_DIR}" 330 "-D${QtX}Widgets_DIR=${${QtX}Widgets_DIR}" 331 "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" 332 ) 333 # Build the project. 334 run_ninja("${RunCMake_TEST_BINARY_DIR}") 335 # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC 336 # for app_with_qt target. 337 file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp") 338 # Build and assert that AUTOMOC was not run for app_with_qt. 339 run_ninja("${RunCMake_TEST_BINARY_DIR}") 340 if(ninja_stdout MATCHES "Automatic MOC for target app_with_qt") 341 message(FATAL_ERROR 342 "AUTOMOC should not have executed for 'app_with_qt' target:\nstdout:\n${ninja_stdout}") 343 endif() 344 # Assert that the subdir executables were not rebuilt. 345 if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_1") 346 message(FATAL_ERROR 347 "AUTOMOC should not have executed for 'sub_exe_1' target:\nstdout:\n${ninja_stdout}") 348 endif() 349 if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_2") 350 message(FATAL_ERROR 351 "AUTOMOC should not have executed for 'sub_exe_2' target:\nstdout:\n${ninja_stdout}") 352 endif() 353 # Touch a header file to make sure an automoc dependency cycle is not introduced. 354 file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h") 355 run_ninja("${RunCMake_TEST_BINARY_DIR}") 356 # Need to run a second time to hit the dependency cycle. 357 run_ninja("${RunCMake_TEST_BINARY_DIR}") 358 endif() 359endfunction() 360if(CMake_TEST_Qt_version) 361 run_QtAutoMocDeps() 362endif() 363