Improve incremental compile speed on windows (#12881)

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
This commit is contained in:
yw4z
2026-06-08 17:30:10 +03:00
committed by GitHub
parent 9024e5ba2d
commit 93efd6497e
3 changed files with 108 additions and 50 deletions

View File

@@ -220,7 +220,9 @@ if (MSVC)
# /bigobj (Increase Number of Sections in .Obj file)
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
# Generate symbols at every build target, even for the release.
add_compile_options(-bigobj -Zm520 /Zi)
# -Zm520 fixes error C3859 but forces the compiler to pre-allocate that memory for every translation unit regardless
# combining /Zi with /FS frees up a significant amount of memory pressure across all parallel compile jobs and makes /MP faster overall.
add_compile_options(-bigobj /Zi /FS)
# Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17.
#FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules.
add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
@@ -231,6 +233,11 @@ if (MSVC)
# Disable warnings on comparison of unsigned and signed
# C4018: signed/unsigned mismatch
add_compile_options(/wd4018)
# Prevent linker restart when TBB (or other deps) built with /GL are linked
# Make your full (clean) build slower because it enables link-time code generation across all translation units.
# helps incremental builds by preventing the double-link restart from TBB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /LTCG")
endif ()
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang" AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15)

View File

@@ -28,17 +28,34 @@ function(encoding_check TARGET)
# Obtain target source files
get_target_property(T_SOURCES ${TARGET} SOURCES)
# Define top-level encoding check target for this ${TARGET}
add_custom_target(encoding-check-${TARGET}
DEPENDS encoding-check ${T_SOURCES}
COMMAND $<TARGET_FILE:encoding-check> ${TARGET} ${T_SOURCES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
set(STAMP_FILE "${CMAKE_CURRENT_BINARY_DIR}/encoding-check-${TARGET}.stamp")
set(RSP_FILE "${CMAKE_CURRENT_BINARY_DIR}/encoding-check-${TARGET}.rsp")
# Resolve absolute paths
set(ABS_SOURCES "")
foreach(file ${T_SOURCES})
if(IS_ABSOLUTE "${file}")
list(APPEND ABS_SOURCES "${file}")
else()
list(APPEND ABS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
endif()
endforeach()
# Write response file at configure time
string(REPLACE ";" "\n" RSP_CONTENT "${ABS_SOURCES}")
file(WRITE "${RSP_FILE}" "${RSP_CONTENT}")
# Single process call via response file — no command line length limit
add_custom_command(
OUTPUT "${STAMP_FILE}"
DEPENDS encoding-check ${ABS_SOURCES}
COMMENT "Checking source files encodings for target ${TARGET}"
COMMAND $<TARGET_FILE:encoding-check> ${TARGET} "@${RSP_FILE}"
COMMAND ${CMAKE_COMMAND} -E touch "${STAMP_FILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# This adds dependency on encoding-check-${TARGET} to ${TARET}
# via the global-encoding-check
add_custom_target(encoding-check-${TARGET} DEPENDS "${STAMP_FILE}")
add_dependencies(global-encoding-check encoding-check-${TARGET})
add_dependencies(${TARGET} global-encoding-check)
endif()

View File

@@ -2,7 +2,7 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
/*
* The utf8_check() function scans the '\0'-terminated string starting
@@ -68,63 +68,97 @@ unsigned char *utf8_check(unsigned char *s)
return NULL;
}
static const char* target;
void error_exit(const char* error, const char* filename)
{
std::cerr << "\n\tError: " << error << ": " << filename << "\n"
<< "\tTarget: " << target << "\n"
<< std::endl;
std::exit(-2);
}
void utf8_check_file(const char* filename)
// ORCA
bool check_file(const char* target, const char* filename)
{
std::ifstream file(filename, std::ios::binary | std::ios::ate);
const auto size = file.tellg();
if (size == 0) {
return;
if (!file.is_open()) {
std::cerr << "\n\tError: Could not open file: " << filename
<< "\n\tTarget: " << target << "\n" << std::endl;
return false;
}
const std::streampos pos = file.tellg();
// Fix: tellg() returns -1 (cast to streampos) on failure.
// Also guard against implausibly large files before casting to size_t.
if (pos < 0) {
std::cerr << "\n\tError: Could not determine file size: " << filename
<< "\n\tTarget: " << target << "\n" << std::endl;
return false;
}
const auto size = static_cast<std::size_t>(pos);
if (size == 0)
return true;
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (file.read(buffer.data(), size)) {
buffer.push_back('\0');
// Check UTF-8 validity
if (utf8_check(reinterpret_cast<unsigned char*>(buffer.data())) != nullptr) {
error_exit("Source file does not contain (valid) UTF-8", filename);
}
// Check against a BOM mark
if (buffer.size() >= 3
&& buffer[0] == '\xef'
&& buffer[1] == '\xbb'
&& buffer[2] == '\xbf') {
error_exit("Source file is valid UTF-8 but contains a BOM mark", filename);
}
} else {
error_exit("Could not read source file", filename);
// Fix: cast size to streamsize explicitly — streampos and streamsize are
// distinct types and passing streampos where streamsize is expected is
// implementation-defined behaviour on some platforms.
if (!file.read(buffer.data(), static_cast<std::streamsize>(size))) {
std::cerr << "\n\tError: Could not read source file: " << filename
<< "\n\tTarget: " << target << "\n" << std::endl;
return false;
}
}
buffer.push_back('\0');
if (utf8_check(reinterpret_cast<unsigned char*>(buffer.data())) != nullptr) {
std::cerr << "\n\tError: Source file does not contain (valid) UTF-8: " << filename
<< "\n\tTarget: " << target << "\n" << std::endl;
return false;
}
if (buffer.size() >= 3
&& buffer[0] == '\xef'
&& buffer[1] == '\xbb'
&& buffer[2] == '\xbf') {
std::cerr << "\n\tError: Source file is valid UTF-8 but contains a BOM mark: " << filename
<< "\n\tTarget: " << target << "\n" << std::endl;
return false;
}
return true;
}
int main(int argc, char const *argv[])
{
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " <program/library> <file[s]>" << std::endl;
std::cerr << "Usage: " << argv[0] << " <target> <file|@responsefile> [...]" << std::endl;
return -1;
}
target = argv[1];
const char* target = argv[1];
// Collect all files — support @responsefile syntax
std::vector<std::string> files;
for (int i = 2; i < argc; i++) {
utf8_check_file(argv[i]);
if (argv[i][0] == '@') {
// Response file — read paths from it, one per line
std::ifstream rsp(argv[i] + 1);
if (!rsp.is_open()) {
std::cerr << "Could not open response file: " << (argv[i]+1) << std::endl;
return -1;
}
std::string line;
while (std::getline(rsp, line)) {
if (!line.empty())
files.push_back(line);
}
} else {
files.push_back(argv[i]);
}
}
return 0;
}
bool all_ok = true;
for (const auto& f : files) {
if (!check_file(target, f.c_str()))
all_ok = false;
}
return all_ok ? 0 : -2;
}