1cmake_minimum_required(VERSION 3.15) 2 3include(CheckSymbolExists) 4include(CheckIPOSupported) 5 6option(NINJA_BUILD_BINARY "Build ninja binary" ON) 7option(NINJA_FORCE_PSELECT "Use pselect() even on platforms that provide ppoll()" OFF) 8 9project(ninja CXX) 10 11# --- optional link-time optimization 12check_ipo_supported(RESULT lto_supported OUTPUT error) 13 14if(lto_supported) 15 message(STATUS "IPO / LTO enabled") 16 set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) 17else() 18 message(STATUS "IPO / LTO not supported: <${error}>") 19endif() 20 21# --- compiler flags 22if(MSVC) 23 set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") 24 string(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) 25 # Note that these settings are separately specified in configure.py, and 26 # these lists should be kept in sync. 27 add_compile_options(/W4 /wd4100 /wd4267 /wd4706 /wd4702 /wd4244 /GR- /Zc:__cplusplus) 28 add_compile_definitions(_CRT_SECURE_NO_WARNINGS) 29else() 30 include(CheckCXXCompilerFlag) 31 check_cxx_compiler_flag(-Wno-deprecated flag_no_deprecated) 32 if(flag_no_deprecated) 33 add_compile_options(-Wno-deprecated) 34 endif() 35 check_cxx_compiler_flag(-fdiagnostics-color flag_color_diag) 36 if(flag_color_diag) 37 add_compile_options(-fdiagnostics-color) 38 endif() 39 40 if(NOT NINJA_FORCE_PSELECT) 41 # Check whether ppoll() is usable on the target platform. 42 # Set -DUSE_PPOLL=1 if this is the case. 43 # 44 # NOTE: Use check_cxx_symbol_exists() instead of check_symbol_exists() 45 # because on Linux, <poll.h> only exposes the symbol when _GNU_SOURCE 46 # is defined. 47 # 48 # Both g++ and clang++ define the symbol by default, because the C++ 49 # standard library headers require it, but *not* gcc and clang, which 50 # are used by check_symbol_exists(). 51 include(CheckSymbolExists) 52 check_symbol_exists(ppoll poll.h HAVE_PPOLL) 53 if(HAVE_PPOLL) 54 add_compile_definitions(USE_PPOLL=1) 55 endif() 56 endif() 57endif() 58 59# --- optional re2c 60set(RE2C_MAJOR_VERSION 0) 61find_program(RE2C re2c) 62if(RE2C) 63 execute_process(COMMAND "${RE2C}" --vernum OUTPUT_VARIABLE RE2C_RAW_VERSION) 64 math(EXPR RE2C_MAJOR_VERSION "${RE2C_RAW_VERSION} / 10000") 65endif() 66if(${RE2C_MAJOR_VERSION} GREATER 1) 67 # the depfile parser and ninja lexers are generated using re2c. 68 function(re2c IN OUT) 69 add_custom_command(DEPENDS ${IN} OUTPUT ${OUT} 70 COMMAND ${RE2C} -b -i --no-generation-date --no-version -o ${OUT} ${IN} 71 ) 72 endfunction() 73 re2c(${PROJECT_SOURCE_DIR}/src/depfile_parser.in.cc ${PROJECT_BINARY_DIR}/depfile_parser.cc) 74 re2c(${PROJECT_SOURCE_DIR}/src/lexer.in.cc ${PROJECT_BINARY_DIR}/lexer.cc) 75 add_library(libninja-re2c OBJECT ${PROJECT_BINARY_DIR}/depfile_parser.cc ${PROJECT_BINARY_DIR}/lexer.cc) 76else() 77 message(WARNING "re2c 2 or later was not found; changes to src/*.in.cc will not affect your build.") 78 add_library(libninja-re2c OBJECT src/depfile_parser.cc src/lexer.cc) 79endif() 80target_include_directories(libninja-re2c PRIVATE src) 81 82# --- Check for 'browse' mode support 83function(check_platform_supports_browse_mode RESULT) 84 # Make sure the inline.sh script works on this platform. 85 # It uses the shell commands such as 'od', which may not be available. 86 87 execute_process( 88 COMMAND sh -c "echo 'TEST' | src/inline.sh var" 89 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 90 RESULT_VARIABLE inline_result 91 OUTPUT_QUIET 92 ERROR_QUIET 93 ) 94 if(NOT inline_result EQUAL "0") 95 # The inline script failed, so browse mode is not supported. 96 set(${RESULT} "0" PARENT_SCOPE) 97 if(NOT WIN32) 98 message(WARNING "browse feature omitted due to inline script failure") 99 endif() 100 return() 101 endif() 102 103 # Now check availability of the unistd header 104 check_symbol_exists(fork "unistd.h" HAVE_FORK) 105 check_symbol_exists(pipe "unistd.h" HAVE_PIPE) 106 set(browse_supported 0) 107 if (HAVE_FORK AND HAVE_PIPE) 108 set(browse_supported 1) 109 endif () 110 set(${RESULT} "${browse_supported}" PARENT_SCOPE) 111 if(NOT browse_supported) 112 message(WARNING "browse feature omitted due to missing `fork` and `pipe` functions") 113 endif() 114 115endfunction() 116 117set(NINJA_PYTHON "python" CACHE STRING "Python interpreter to use for the browse tool") 118 119check_platform_supports_browse_mode(platform_supports_ninja_browse) 120 121# Core source files all build into ninja library. 122add_library(libninja OBJECT 123 src/build_log.cc 124 src/build.cc 125 src/clean.cc 126 src/clparser.cc 127 src/dyndep.cc 128 src/dyndep_parser.cc 129 src/debug_flags.cc 130 src/deps_log.cc 131 src/disk_interface.cc 132 src/edit_distance.cc 133 src/eval_env.cc 134 src/graph.cc 135 src/graphviz.cc 136 src/json.cc 137 src/line_printer.cc 138 src/manifest_parser.cc 139 src/metrics.cc 140 src/missing_deps.cc 141 src/parser.cc 142 src/state.cc 143 src/status.cc 144 src/string_piece_util.cc 145 src/util.cc 146 src/version.cc 147) 148if(WIN32) 149 target_sources(libninja PRIVATE 150 src/subprocess-win32.cc 151 src/includes_normalize-win32.cc 152 src/msvc_helper-win32.cc 153 src/msvc_helper_main-win32.cc 154 src/getopt.c 155 src/minidump-win32.cc 156 ) 157 # Build getopt.c, which can be compiled as either C or C++, as C++ 158 # so that build environments which lack a C compiler, but have a C++ 159 # compiler may build ninja. 160 set_source_files_properties(src/getopt.c PROPERTIES LANGUAGE CXX) 161else() 162 target_sources(libninja PRIVATE src/subprocess-posix.cc) 163 if(CMAKE_SYSTEM_NAME STREQUAL "OS400" OR CMAKE_SYSTEM_NAME STREQUAL "AIX") 164 target_sources(libninja PRIVATE src/getopt.c) 165 # Build getopt.c, which can be compiled as either C or C++, as C++ 166 # so that build environments which lack a C compiler, but have a C++ 167 # compiler may build ninja. 168 set_source_files_properties(src/getopt.c PROPERTIES LANGUAGE CXX) 169 endif() 170 171 # Needed for perfstat_cpu_total 172 if(CMAKE_SYSTEM_NAME STREQUAL "AIX") 173 target_link_libraries(libninja PUBLIC "-lperfstat") 174 endif() 175endif() 176 177target_compile_features(libninja PUBLIC cxx_std_11) 178 179#Fixes GetActiveProcessorCount on MinGW 180if(MINGW) 181target_compile_definitions(libninja PRIVATE _WIN32_WINNT=0x0601 __USE_MINGW_ANSI_STDIO=1) 182endif() 183 184# On IBM i (identified as "OS400" for compatibility reasons) and AIX, this fixes missing 185# PRId64 (and others) at compile time in C++ sources 186if(CMAKE_SYSTEM_NAME STREQUAL "OS400" OR CMAKE_SYSTEM_NAME STREQUAL "AIX") 187 add_compile_definitions(__STDC_FORMAT_MACROS) 188endif() 189 190# Main executable is library plus main() function. 191if(NINJA_BUILD_BINARY) 192 add_executable(ninja src/ninja.cc) 193 target_link_libraries(ninja PRIVATE libninja libninja-re2c) 194 195 if(WIN32) 196 target_sources(ninja PRIVATE windows/ninja.manifest) 197 endif() 198endif() 199 200# Adds browse mode into the ninja binary if it's supported by the host platform. 201if(platform_supports_ninja_browse) 202 # Inlines src/browse.py into the browse_py.h header, so that it can be included 203 # by src/browse.cc 204 add_custom_command( 205 OUTPUT build/browse_py.h 206 MAIN_DEPENDENCY src/browse.py 207 DEPENDS src/inline.sh 208 COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/build 209 COMMAND src/inline.sh kBrowsePy 210 < src/browse.py 211 > ${PROJECT_BINARY_DIR}/build/browse_py.h 212 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 213 VERBATIM 214 ) 215 216 if(NINJA_BUILD_BINARY) 217 target_compile_definitions(ninja PRIVATE NINJA_HAVE_BROWSE) 218 target_sources(ninja PRIVATE src/browse.cc) 219 endif() 220 set_source_files_properties(src/browse.cc 221 PROPERTIES 222 OBJECT_DEPENDS "${PROJECT_BINARY_DIR}/build/browse_py.h" 223 INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}" 224 COMPILE_DEFINITIONS NINJA_PYTHON="${NINJA_PYTHON}" 225 ) 226endif() 227 228include(CTest) 229if(BUILD_TESTING) 230 find_package(GTest) 231 if(NOT GTest_FOUND) 232 include(FetchContent) 233 FetchContent_Declare( 234 googletest 235 URL https://github.com/google/googletest/archive/release-1.10.0.tar.gz 236 URL_HASH SHA1=9c89be7df9c5e8cb0bc20b3c4b39bf7e82686770 237 ) 238 FetchContent_MakeAvailable(googletest) 239 240 # Before googletest-1.11.0, the CMake files provided by the source archive 241 # did not define the GTest::gtest target, only the gtest one, so define 242 # an alias when needed to ensure the rest of this file works with all 243 # GoogleTest releases. 244 # 245 # Note that surprisingly, this is not needed when using GTEST_ROOT to 246 # point to a local installation, because this one contains CMake-generated 247 # files that contain the right target definition, and which will be 248 # picked up by the find_package(GTest) file above. 249 # 250 # This comment and the four lines below can be removed once Ninja only 251 # depends on release-1.11.0 or above. 252 if (NOT TARGET GTest::gtest) 253 message(STATUS "Defining GTest::gtest alias to work-around bug in older release.") 254 add_library(GTest::gtest ALIAS gtest) 255 256 # NOTE: gtest uninit some variables, gcc >= 1.11.3 may cause error on compile. 257 # Remove this comment and six lines below, once ninja deps gtest-1.11.0 or above. 258 if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "1.11.3") 259 check_cxx_compiler_flag(-Wmaybe-uninitialized flag_maybe_uninit) 260 if (flag_maybe_uninit) 261 target_compile_options(gtest PRIVATE -Wno-maybe-uninitialized) 262 endif() 263 endif() 264 265 endif() 266 endif() 267 268 # Tests all build into ninja_test executable. 269 add_executable(ninja_test 270 src/build_log_test.cc 271 src/build_test.cc 272 src/clean_test.cc 273 src/clparser_test.cc 274 src/depfile_parser_test.cc 275 src/deps_log_test.cc 276 src/disk_interface_test.cc 277 src/dyndep_parser_test.cc 278 src/edit_distance_test.cc 279 src/graph_test.cc 280 src/json_test.cc 281 src/lexer_test.cc 282 src/manifest_parser_test.cc 283 src/missing_deps_test.cc 284 src/ninja_test.cc 285 src/state_test.cc 286 src/string_piece_util_test.cc 287 src/subprocess_test.cc 288 src/test.cc 289 src/util_test.cc 290 ) 291 if(WIN32) 292 target_sources(ninja_test PRIVATE src/includes_normalize_test.cc src/msvc_helper_test.cc 293 windows/ninja.manifest) 294 endif() 295 find_package(Threads REQUIRED) 296 target_link_libraries(ninja_test PRIVATE libninja libninja-re2c GTest::gtest Threads::Threads) 297 298 foreach(perftest 299 build_log_perftest 300 canon_perftest 301 clparser_perftest 302 depfile_parser_perftest 303 hash_collision_bench 304 manifest_parser_perftest 305 ) 306 add_executable(${perftest} src/${perftest}.cc) 307 target_link_libraries(${perftest} PRIVATE libninja libninja-re2c) 308 endforeach() 309 310 if(CMAKE_SYSTEM_NAME STREQUAL "AIX" AND CMAKE_SIZEOF_VOID_P EQUAL 4) 311 # These tests require more memory than will fit in the standard AIX shared stack/heap (256M) 312 target_link_options(hash_collision_bench PRIVATE "-Wl,-bmaxdata:0x80000000") 313 target_link_options(manifest_parser_perftest PRIVATE "-Wl,-bmaxdata:0x80000000") 314 endif() 315 316 add_test(NAME NinjaTest COMMAND ninja_test) 317endif() 318 319if(NINJA_BUILD_BINARY) 320 install(TARGETS ninja) 321endif() 322