Ninja: Add option for parallel install
Adds the global property ``INSTALL_PARALLEL`` to enable a parallel install target for Ninja. Fixes: #25459
This commit is contained in:
parent
daeb8fffa2
commit
0e5250e63c
@ -24,6 +24,13 @@ Builtin Targets
|
|||||||
The ``CMAKE_STRIP`` variable will contain the platform's ``strip`` utility, which
|
The ``CMAKE_STRIP`` variable will contain the platform's ``strip`` utility, which
|
||||||
removes symbols information from generated binaries.
|
removes symbols information from generated binaries.
|
||||||
|
|
||||||
|
``install/parallel``
|
||||||
|
|
||||||
|
.. versionadded:: 3.30
|
||||||
|
|
||||||
|
Created only if the :prop_gbl:`INSTALL_PARALLEL` global property is ``ON``.
|
||||||
|
Runs the install step for each subdirectory independently and in parallel.
|
||||||
|
|
||||||
For each subdirectory ``sub/dir`` of the project, additional targets
|
For each subdirectory ``sub/dir`` of the project, additional targets
|
||||||
are generated:
|
are generated:
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ Properties of Global Scope
|
|||||||
/prop_gbl/GENERATOR_IS_MULTI_CONFIG
|
/prop_gbl/GENERATOR_IS_MULTI_CONFIG
|
||||||
/prop_gbl/GLOBAL_DEPENDS_DEBUG_MODE
|
/prop_gbl/GLOBAL_DEPENDS_DEBUG_MODE
|
||||||
/prop_gbl/GLOBAL_DEPENDS_NO_CYCLES
|
/prop_gbl/GLOBAL_DEPENDS_NO_CYCLES
|
||||||
|
/prop_gbl/INSTALL_PARALLEL
|
||||||
/prop_gbl/IN_TRY_COMPILE
|
/prop_gbl/IN_TRY_COMPILE
|
||||||
/prop_gbl/JOB_POOLS
|
/prop_gbl/JOB_POOLS
|
||||||
/prop_gbl/PACKAGES_FOUND
|
/prop_gbl/PACKAGES_FOUND
|
||||||
|
23
Help/prop_gbl/INSTALL_PARALLEL.rst
Normal file
23
Help/prop_gbl/INSTALL_PARALLEL.rst
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
INSTALL_PARALLEL
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. versionadded:: 3.30
|
||||||
|
|
||||||
|
Enables parallel installation option for the Ninja generator.
|
||||||
|
|
||||||
|
When this property is ``ON``, ``install/local`` targets have the
|
||||||
|
console pool disabled, allowing them to run concurrently.
|
||||||
|
|
||||||
|
This property also provides the target ``install/parallel``, which has an
|
||||||
|
explicit dependency on the ``install/local`` target for each subdirectory,
|
||||||
|
recursing down the project.
|
||||||
|
|
||||||
|
Setting this property has no affect on the behavior of ``cmake --install``.
|
||||||
|
The install must be invoked by building the ``install/parallel`` target
|
||||||
|
directly.
|
||||||
|
|
||||||
|
Calls to :command:`install(CODE)` or :command:`install(SCRIPT)` might depend
|
||||||
|
on actions performed by an earlier :command:`install` command in a different
|
||||||
|
directory such as files installed or variable settings. If the project has
|
||||||
|
such order-dependent installation logic, parallel installation should be
|
||||||
|
not be enabled, in order to prevent possible race conditions.
|
@ -3048,7 +3048,9 @@ void cmGlobalGenerator::AddGlobalTarget_Install(
|
|||||||
if (const char* install_local = this->GetInstallLocalTargetName()) {
|
if (const char* install_local = this->GetInstallLocalTargetName()) {
|
||||||
gti.Name = install_local;
|
gti.Name = install_local;
|
||||||
gti.Message = "Installing only the local directory...";
|
gti.Message = "Installing only the local directory...";
|
||||||
gti.UsesTerminal = true;
|
gti.UsesTerminal =
|
||||||
|
!this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool(
|
||||||
|
"INSTALL_PARALLEL");
|
||||||
gti.CommandLines.clear();
|
gti.CommandLines.clear();
|
||||||
|
|
||||||
cmCustomCommandLine localCmdLine = singleLine;
|
cmCustomCommandLine localCmdLine = singleLine;
|
||||||
|
@ -1835,6 +1835,21 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
|
|||||||
if (!this->DefaultFileConfig.empty()) {
|
if (!this->DefaultFileConfig.empty()) {
|
||||||
this->WriteTargetDefault(*this->GetDefaultFileStream());
|
this->WriteTargetDefault(*this->GetDefaultFileStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->InstallTargetEnabled &&
|
||||||
|
this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool(
|
||||||
|
"INSTALL_PARALLEL") &&
|
||||||
|
!this->Makefiles[0]->IsOn("CMAKE_SKIP_INSTALL_RULES")) {
|
||||||
|
cmNinjaBuild build("phony");
|
||||||
|
build.Comment = "Install every subdirectory in parallel";
|
||||||
|
build.Outputs.emplace_back(this->GetInstallParallelTargetName());
|
||||||
|
for (auto const& mf : this->Makefiles) {
|
||||||
|
build.ExplicitDeps.emplace_back(
|
||||||
|
this->ConvertToNinjaPath(cmStrCat(mf->GetCurrentBinaryDirectory(), "/",
|
||||||
|
this->GetInstallLocalTargetName())));
|
||||||
|
}
|
||||||
|
WriteBuild(os, build);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmGlobalNinjaGenerator::WriteTargetDefault(std::ostream& os)
|
void cmGlobalNinjaGenerator::WriteTargetDefault(std::ostream& os)
|
||||||
|
@ -218,6 +218,10 @@ public:
|
|||||||
{
|
{
|
||||||
return "install/strip";
|
return "install/strip";
|
||||||
}
|
}
|
||||||
|
const char* GetInstallParallelTargetName() const
|
||||||
|
{
|
||||||
|
return "install/parallel";
|
||||||
|
}
|
||||||
const char* GetTestTargetName() const override { return "test"; }
|
const char* GetTestTargetName() const override { return "test"; }
|
||||||
const char* GetPackageTargetName() const override { return "package"; }
|
const char* GetPackageTargetName() const override { return "package"; }
|
||||||
const char* GetPackageSourceTargetName() const override
|
const char* GetPackageSourceTargetName() const override
|
||||||
|
@ -723,10 +723,12 @@ void cmLocalGenerator::GenerateInstallRules()
|
|||||||
" set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
|
" set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
|
||||||
"endif()\n"
|
"endif()\n"
|
||||||
"\n"
|
"\n"
|
||||||
"string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n"
|
"if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n"
|
||||||
|
" string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n"
|
||||||
" \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n"
|
" \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n"
|
||||||
"file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n"
|
" file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n"
|
||||||
" \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n";
|
" \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n"
|
||||||
|
"endif()\n";
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,6 +250,7 @@ if(CMAKE_GENERATOR MATCHES "Ninja")
|
|||||||
add_RunCMake_test(NinjaPrivateDeps
|
add_RunCMake_test(NinjaPrivateDeps
|
||||||
-DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION}
|
-DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION}
|
||||||
-DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig})
|
-DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig})
|
||||||
|
add_RunCMake_test(InstallParallel)
|
||||||
endif()
|
endif()
|
||||||
add_RunCMake_test(CTest)
|
add_RunCMake_test(CTest)
|
||||||
|
|
||||||
|
3
Tests/RunCMake/InstallParallel/CMakeLists.txt
Normal file
3
Tests/RunCMake/InstallParallel/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.29)
|
||||||
|
project(${RunCMake_TEST} NONE)
|
||||||
|
include(${RunCMake_TEST}.cmake)
|
17
Tests/RunCMake/InstallParallel/RunCMakeTest.cmake
Normal file
17
Tests/RunCMake/InstallParallel/RunCMakeTest.cmake
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
include(RunCMake)
|
||||||
|
|
||||||
|
function(install_test test parallel install_target check_script)
|
||||||
|
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-install)
|
||||||
|
set(RunCMake_TEST_OPTIONS -DINSTALL_PARALLEL=${parallel})
|
||||||
|
if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
|
||||||
|
endif()
|
||||||
|
run_cmake(install)
|
||||||
|
set(RunCMake_TEST_NO_CLEAN 1)
|
||||||
|
run_cmake_command(${test}-install ${CMAKE_COMMAND} --build . --config Debug -t ${install_target})
|
||||||
|
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_SOURCE_DIR})
|
||||||
|
run_cmake_command(verify-parallel ${CMAKE_COMMAND} -P ${check_script} ${RunCMake_TEST_BINARY_DIR}/.ninja_log)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
install_test(parallel 1 install/parallel check-parallel.cmake)
|
||||||
|
install_test(no-parallel 0 install check-single.cmake)
|
15
Tests/RunCMake/InstallParallel/check-parallel.cmake
Normal file
15
Tests/RunCMake/InstallParallel/check-parallel.cmake
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
include(read-ninja-install.cmake)
|
||||||
|
|
||||||
|
foreach(line ${lines})
|
||||||
|
string(REPLACE "\t" ";" line ${line})
|
||||||
|
list(GET line 0 start)
|
||||||
|
list(GET line 1 end)
|
||||||
|
list(APPEND start_times ${start})
|
||||||
|
list(APPEND end_times ${end})
|
||||||
|
endforeach()
|
||||||
|
list(GET start_times 1 start_2)
|
||||||
|
list(GET end_times 0 end_1)
|
||||||
|
|
||||||
|
if (NOT start_2 LESS end_1)
|
||||||
|
message(FATAL_ERROR "Install is not parallel")
|
||||||
|
endif()
|
5
Tests/RunCMake/InstallParallel/check-single.cmake
Normal file
5
Tests/RunCMake/InstallParallel/check-single.cmake
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include(read-ninja-install.cmake)
|
||||||
|
list(LENGTH lines len)
|
||||||
|
if (NOT ${len} STREQUAL "1")
|
||||||
|
message(FATAL_ERROR "Expected single installation call")
|
||||||
|
endif()
|
6
Tests/RunCMake/InstallParallel/install.cmake
Normal file
6
Tests/RunCMake/InstallParallel/install.cmake
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
|
||||||
|
if (INSTALL_PARALLEL)
|
||||||
|
set_property(GLOBAL PROPERTY INSTALL_PARALLEL ON)
|
||||||
|
endif()
|
||||||
|
add_subdirectory(subdir-1)
|
||||||
|
add_subdirectory(subdir-2)
|
@ -0,0 +1,5 @@
|
|||||||
|
installing:.*
|
||||||
|
installing:.*
|
||||||
|
installing:.*
|
||||||
|
installing:.*
|
||||||
|
installing:.*
|
15
Tests/RunCMake/InstallParallel/parallel-install-stdout.txt
Normal file
15
Tests/RunCMake/InstallParallel/parallel-install-stdout.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
\[1\/5\] Installing only the local directory...
|
||||||
|
\-\- Install configuration: \"Debug\"
|
||||||
|
installing:.*
|
||||||
|
\[2\/5\] Installing only the local directory...
|
||||||
|
\-\- Install configuration: \"Debug\"
|
||||||
|
installing:.*
|
||||||
|
\[3\/5\] Installing only the local directory...
|
||||||
|
\-\- Install configuration: \"Debug\"
|
||||||
|
installing:.*
|
||||||
|
\[4\/5\] Installing only the local directory...
|
||||||
|
\-\- Install configuration: \"Debug\"
|
||||||
|
installing:.*
|
||||||
|
\[5\/5\] Installing only the local directory...
|
||||||
|
\-\- Install configuration: \"Debug\"
|
||||||
|
installing:.*
|
4
Tests/RunCMake/InstallParallel/read-ninja-install.cmake
Normal file
4
Tests/RunCMake/InstallParallel/read-ninja-install.cmake
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
set(ninja_log ${CMAKE_ARGV3})
|
||||||
|
file(STRINGS ${ninja_log} lines)
|
||||||
|
list(POP_FRONT lines)
|
||||||
|
list(FILTER lines INCLUDE REGEX ".*install.*util")
|
3
Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt
Normal file
3
Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
|
||||||
|
add_subdirectory(subdir-3)
|
||||||
|
add_subdirectory(subdir-4)
|
@ -0,0 +1 @@
|
|||||||
|
install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
|
@ -0,0 +1 @@
|
|||||||
|
install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
|
1
Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt
Normal file
1
Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
|
Loading…
Reference in New Issue
Block a user