mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-20 04:24:36 +08:00
VS: Generate a custom command only in the least dependent target
If a custom command is assigned to multiple targets, generate the build rule only in the least-dependent `.vcxproj` file. Otherwise MSBuild will run the command on the first build of a dependent target even if its dependencies already brought the command up to date (in order to populates its build log). Generate targets in least-to-most-dependent order, and assign a custom command to the least dependent target. Added cmLocalVisualStudio10Generator::GenerateTargetsDepthFirst to call cmVisualStudio10TargetGenerator::Generate in least-dependent order. Moved SourcesVisited from cmVisualStudio10TargetGenerator to cmLocalVisualStudio10Generator to avoid attaching a custom command to multiple targets among the local generator. Fixes: #16767
This commit is contained in:
@@ -62,22 +62,48 @@ cmLocalVisualStudio10Generator::~cmLocalVisualStudio10Generator()
|
||||
{
|
||||
}
|
||||
|
||||
void cmLocalVisualStudio10Generator::Generate()
|
||||
void cmLocalVisualStudio10Generator::GenerateTargetsDepthFirst(
|
||||
cmGeneratorTarget* target, std::vector<cmGeneratorTarget*>& remaining)
|
||||
{
|
||||
const std::vector<cmGeneratorTarget*>& tgts = this->GetGeneratorTargets();
|
||||
for (cmGeneratorTarget* l : tgts) {
|
||||
if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
|
||||
continue;
|
||||
if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
|
||||
return;
|
||||
}
|
||||
// Find this target in the list of remaining targets.
|
||||
auto it = std::find(remaining.begin(), remaining.end(), target);
|
||||
if (it == remaining.end()) {
|
||||
// This target was already handled.
|
||||
return;
|
||||
}
|
||||
// Remove this target from the list of remaining targets because
|
||||
// we are handling it now.
|
||||
*it = nullptr;
|
||||
auto& deps = this->GlobalGenerator->GetTargetDirectDepends(target);
|
||||
for (auto& d : deps) {
|
||||
// FIXME: Revise CreateSingleVCProj so we do not have to drop `const` here.
|
||||
auto dependee = const_cast<cmGeneratorTarget*>(&*d);
|
||||
GenerateTargetsDepthFirst(dependee, remaining);
|
||||
// Take the union of visited source files of custom commands
|
||||
auto visited = GetSourcesVisited(dependee);
|
||||
GetSourcesVisited(target).insert(visited.begin(), visited.end());
|
||||
}
|
||||
if (static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator)
|
||||
->TargetIsFortranOnly(l)) {
|
||||
this->CreateSingleVCProj(l->GetName(), l);
|
||||
->TargetIsFortranOnly(target)) {
|
||||
this->CreateSingleVCProj(target->GetName(), target);
|
||||
} else {
|
||||
cmVisualStudio10TargetGenerator tg(
|
||||
l, static_cast<cmGlobalVisualStudio10Generator*>(
|
||||
target, static_cast<cmGlobalVisualStudio10Generator*>(
|
||||
this->GetGlobalGenerator()));
|
||||
tg.Generate();
|
||||
}
|
||||
}
|
||||
|
||||
void cmLocalVisualStudio10Generator::Generate()
|
||||
{
|
||||
std::vector<cmGeneratorTarget*> remaining = this->GetGeneratorTargets();
|
||||
for (auto& t : remaining) {
|
||||
if (t) {
|
||||
GenerateTargetsDepthFirst(t, remaining);
|
||||
}
|
||||
}
|
||||
this->WriteStampFiles();
|
||||
}
|
||||
|
@@ -33,10 +33,19 @@ public:
|
||||
void ReadAndStoreExternalGUID(const std::string& name,
|
||||
const char* path) override;
|
||||
|
||||
std::set<cmSourceFile const*>& GetSourcesVisited(cmGeneratorTarget* target)
|
||||
{
|
||||
return SourcesVisited[target];
|
||||
};
|
||||
|
||||
protected:
|
||||
const char* ReportErrorLabel() const override;
|
||||
bool CustomCommandUseLocal() const override { return true; }
|
||||
|
||||
private:
|
||||
void GenerateTargetsDepthFirst(cmGeneratorTarget* target,
|
||||
std::vector<cmGeneratorTarget*>& remaining);
|
||||
|
||||
std::map<cmGeneratorTarget*, std::set<cmSourceFile const*>> SourcesVisited;
|
||||
};
|
||||
#endif
|
||||
|
@@ -1178,7 +1178,6 @@ void cmVisualStudio10TargetGenerator::WriteNsightTegraConfigurationValues(
|
||||
|
||||
void cmVisualStudio10TargetGenerator::WriteCustomCommands()
|
||||
{
|
||||
this->SourcesVisited.clear();
|
||||
this->CSharpCustomCommandNames.clear();
|
||||
std::vector<cmSourceFile const*> customCommands;
|
||||
this->GeneratorTarget->GetCustomCommands(customCommands, "");
|
||||
@@ -1199,7 +1198,9 @@ void cmVisualStudio10TargetGenerator::WriteCustomCommands()
|
||||
void cmVisualStudio10TargetGenerator::WriteCustomCommand(
|
||||
cmSourceFile const* sf)
|
||||
{
|
||||
if (this->SourcesVisited.insert(sf).second) {
|
||||
if (this->LocalGenerator->GetSourcesVisited(this->GeneratorTarget)
|
||||
.insert(sf)
|
||||
.second) {
|
||||
if (std::vector<cmSourceFile*> const* depends =
|
||||
this->GeneratorTarget->GetSourceDepends(sf)) {
|
||||
for (cmSourceFile const* di : *depends) {
|
||||
|
@@ -209,7 +209,6 @@ private:
|
||||
cmGlobalVisualStudio10Generator* const GlobalGenerator;
|
||||
cmGeneratedFileStream* BuildFileStream;
|
||||
cmLocalVisualStudio10Generator* const LocalGenerator;
|
||||
std::set<cmSourceFile const*> SourcesVisited;
|
||||
std::set<std::string> CSharpCustomCommandNames;
|
||||
bool IsMissingFiles;
|
||||
std::vector<std::string> AddedFiles;
|
||||
|
@@ -0,0 +1,13 @@
|
||||
enable_language(CXX)
|
||||
|
||||
add_custom_command(OUTPUT generated.cpp
|
||||
MAIN_DEPENDENCY a.c
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/generate-once.cmake ${CMAKE_CURRENT_BINARY_DIR}/generated.cpp
|
||||
VERBATIM)
|
||||
|
||||
add_executable(exe1 ${CMAKE_CURRENT_BINARY_DIR}/generated.cpp)
|
||||
add_executable(exe2 ${CMAKE_CURRENT_BINARY_DIR}/generated.cpp)
|
||||
add_executable(exe3 ${CMAKE_CURRENT_BINARY_DIR}/generated.cpp)
|
||||
|
||||
add_dependencies(exe1 exe2)
|
||||
add_dependencies(exe3 exe1)
|
@@ -14,3 +14,10 @@ run_cmake(TargetNotInDir)
|
||||
if(${RunCMake_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])")
|
||||
run_cmake(RemoveEmptyCommands)
|
||||
endif()
|
||||
|
||||
run_cmake(AssigningMultipleTargets)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssigningMultipleTargets-build)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
run_cmake_command(AssigningMultipleTargets-build ${CMAKE_COMMAND} --build .)
|
||||
unset(RunCMake_TEST_BINARY_DIR)
|
||||
unset(RunCMake_TEST_NO_CLEAN)
|
||||
|
3
Tests/RunCMake/add_custom_command/a.c
Normal file
3
Tests/RunCMake/add_custom_command/a.c
Normal file
@@ -0,0 +1,3 @@
|
||||
void a()
|
||||
{
|
||||
}
|
8
Tests/RunCMake/add_custom_command/generate-once.cmake
Normal file
8
Tests/RunCMake/add_custom_command/generate-once.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
if (${CMAKE_ARGC} LESS 4)
|
||||
message(FATAL_ERROR "Too few arguments")
|
||||
endif()
|
||||
set(output "${CMAKE_ARGV3}")
|
||||
if(EXISTS ${output})
|
||||
message(FATAL_ERROR "${output} already exists")
|
||||
endif()
|
||||
file(WRITE ${output} "int main() { return 0; }\n")
|
Reference in New Issue
Block a user