diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index 4b165b1197..1e079abb17 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx @@ -219,6 +219,7 @@ void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index) emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); + // Build link dependencies before the current target. if (cmLinkImplementation const* impl = depender->GetLinkImplementation( it, cmGeneratorTarget::UseTo::Link)) { for (cmLinkItem const& lib : impl->Libraries) { @@ -236,6 +237,18 @@ void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index) } } + // Build compile dependencies before the current target. + if (cmLinkImplementation const* impl = depender->GetLinkImplementation( + it, cmGeneratorTarget::UseTo::Compile)) { + for (cmLinkItem const& lib : impl->Libraries) { + // Don't emit the same library twice for this target. + if (emitted.insert(lib).second) { + this->AddTargetDepend(depender_index, lib, true, false, emitted); + this->AddInterfaceDepends(depender_index, lib, it, emitted); + } + } + } + // Add dependencies on object libraries not otherwise handled above. std::vector objectFiles; depender->GetExternalObjects(objectFiles, it); diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json index 104f30e95a..4c502dafd2 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json @@ -96,6 +96,23 @@ }, "archive": null, "dependencies": [ + { + "id": "^usage_lib::@79b6367c9e05c77f6a80$", + "backtrace": [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 40, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, { "id": "^ZERO_CHECK::@79b6367c9e05c77f6a80$", "backtrace": null diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps-consumer.c b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps-consumer.c new file mode 100644 index 0000000000..2a8d167f78 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps-consumer.c @@ -0,0 +1,4 @@ +#include "generated.h" +void consume(void) +{ +} diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps.cmake b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps.cmake new file mode 100644 index 0000000000..3c31171d23 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps.cmake @@ -0,0 +1,21 @@ +enable_language(C) + +# A custom command produces a header that does not exist at configure time. +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated.h" + COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_CURRENT_BINARY_DIR}/generated.h" +) + +# An INTERFACE library generates the header. +add_library(header_provider INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/generated.h) +target_include_directories(header_provider INTERFACE "${CMAKE_CURRENT_BINARY_DIR}") +# Not in ALL so it is only built when something depends on it. +set_property(TARGET header_provider PROPERTY EXCLUDE_FROM_ALL 1) + +# A consumer uses $ so it gets the include +# path but does not link against header_provider. The build-order +# dependency on header_provider must be propagated through the +# COMPILE_ONLY expression so that generated.h exists when consumer.c +# is compiled. +add_library(consumer SHARED COMPILE_ONLY-custom-target-deps-consumer.c) +target_link_libraries(consumer PRIVATE "$") diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index e75b53b88b..c826e5da99 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -75,6 +75,22 @@ function(run_cmake_build test) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release) endfunction() +function(run_cmake_build_target test target) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Release) + else() + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + block() + unset(RunCMake-check-file) + run_cmake(${test}) + endblock() + set(RunCMake_TEST_NO_CLEAN TRUE) + run_cmake_command(${test}-build + ${CMAKE_COMMAND} --build . --target ${target} --config Release) +endfunction() + function(run_linker_genex test lang type) set(options_args CHECK_RESULT EXECUTE) cmake_parse_arguments(PARSE_ARGV 3 RLG "${options_args}" "" "") @@ -132,6 +148,8 @@ endif() run_cmake(CONFIG-empty-entries) unset(RunCMake_TEST_OPTIONS) +run_cmake_build_target(COMPILE_ONLY-custom-target-deps consumer) + run_cmake(CMP0199-WARN) run_cmake_build(CMP0199-OLD) run_cmake_build(CMP0199-NEW)