Merge topic 'makefiles-custom_command-depfile'

3eacf857e3 Tests: Add case for DEPFILE in add_custom_command
cfd8a5ac1f Makefiles: Add support of DEPFILE for add_custom_command
a526f71266 cmGccDepfileReader: Add new function ensuring paths are valid

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !5617
This commit is contained in:
Marc Chevrier
2020-12-26 10:55:46 +00:00
committed by Kitware Robot
29 changed files with 476 additions and 95 deletions

View File

@@ -201,6 +201,10 @@ The options are:
Note that the ``IMPLICIT_DEPENDS`` option is currently supported
only for Makefile generators and will be ignored by other generators.
.. note::
This option cannot be specified at the same time as ``DEPFILE`` option.
``JOB_POOL``
.. versionadded:: 3.15
@@ -263,15 +267,26 @@ The options are:
``DEPFILE``
.. versionadded:: 3.7
Specify a ``.d`` depfile for the :generator:`Ninja` generator.
Specify a ``.d`` depfile for the :generator:`Ninja` generator and
:ref:`Makefile Generators`.
A ``.d`` file holds dependencies usually emitted by the custom
command itself.
Using ``DEPFILE`` with other generators than Ninja is an error.
Using ``DEPFILE`` with other generators than :generator:`Ninja` or
:ref:`Makefile Generators` is an error.
.. versionadded:: 3.20
Added the support of :ref:`Makefile Generators`.
If the ``DEPFILE`` argument is relative, it should be relative to
:variable:`CMAKE_CURRENT_BINARY_DIR`, and any relative paths inside the
``DEPFILE`` should also be relative to :variable:`CMAKE_CURRENT_BINARY_DIR`
(see policy :policy:`CMP0116`.)
(see policy :policy:`CMP0116`. This policy is always ``NEW`` for
:ref:`Makefile Generators`).
.. note::
For :ref:`Makefile Generators`, this option cannot be specified at the
same time as ``IMPLICIT_DEPENDS`` option.
Examples: Generating Files
^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -0,0 +1,5 @@
makefile-depfile
----------------
* The :command:`add_custom_command` command gained ``DEPFILE`` support on
:ref:`Makefile Generators`.

View File

@@ -296,6 +296,12 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
status.SetError("given APPEND option with no OUTPUT.");
return false;
}
if (!implicit_depends.empty() && !depfile.empty() &&
mf.GetGlobalGenerator()->GetName() != "Ninja") {
// Makefiles generators does not support both at the same time
status.SetError("IMPLICIT_DEPENDS and DEPFILE can not both be specified.");
return false;
}
// Check for an append request.
if (append) {

View File

@@ -19,6 +19,7 @@
#include "cmFileTime.h"
#include "cmGccDepfileReader.h"
#include "cmGccDepfileReaderTypes.h"
#include "cmGlobalUnixMakefileGenerator3.h"
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmStringAlgorithms.h"
@@ -86,7 +87,7 @@ bool cmDependsCompiler::CheckDependencies(
if (!forceReadDeps) {
depFileTime.Load(depFile);
}
if (forceReadDeps || depFileTime.Newer(internalDepFileTime)) {
if (forceReadDeps || depFileTime.Compare(internalDepFileTime) >= 0) {
status = false;
if (this->Verbose) {
cmSystemTools::Stdout(cmStrCat("Dependencies file \"", depFile,
@@ -95,59 +96,92 @@ bool cmDependsCompiler::CheckDependencies(
}
std::vector<std::string> depends;
if (format == "msvc"_s) {
cmsys::ifstream fin(depFile.c_str());
if (!fin) {
continue;
if (format == "custom"_s) {
std::string prefix;
if (this->LocalGenerator->GetCurrentBinaryDirectory() !=
this->LocalGenerator->GetBinaryDirectory()) {
prefix =
cmStrCat(this->LocalGenerator->MaybeConvertToRelativePath(
this->LocalGenerator->GetBinaryDirectory(),
this->LocalGenerator->GetCurrentBinaryDirectory()),
'/');
}
std::string line;
if (!isValidPath) {
// insert source as first dependency
depends.push_back(source);
}
while (cmSystemTools::GetLineFromStream(fin, line)) {
depends.emplace_back(std::move(line));
}
} else {
auto deps = cmReadGccDepfile(depFile.c_str());
auto deps = cmReadGccDepfile(depFile.c_str(), prefix);
if (!deps) {
continue;
}
// dependencies generated by the compiler contains only one target
depends = std::move(deps->front().paths);
if (depends.empty()) {
// unexpectedly empty, ignore it and continue
for (auto& entry : *deps) {
depends = std::move(entry.paths);
if (isValidPath) {
cm::erase_if(depends, isValidPath);
}
// copy depends for each target, except first one, which can be
// moved
for (auto index = entry.rules.size() - 1; index > 0; --index) {
dependencies[entry.rules[index]] = depends;
}
dependencies[entry.rules.front()] = std::move(depends);
}
} else {
if (format == "msvc"_s) {
cmsys::ifstream fin(depFile.c_str());
if (!fin) {
continue;
}
std::string line;
if (!isValidPath) {
// insert source as first dependency
depends.push_back(source);
}
while (cmSystemTools::GetLineFromStream(fin, line)) {
depends.emplace_back(std::move(line));
}
} else if (format == "gcc"_s) {
auto deps = cmReadGccDepfile(depFile.c_str());
if (!deps) {
continue;
}
// dependencies generated by the compiler contains only one target
depends = std::move(deps->front().paths);
if (depends.empty()) {
// unexpectedly empty, ignore it and continue
continue;
}
// depending of the effective format of the dependencies file
// generated by the compiler, the target can be wrongly identified
// as a dependency so remove it from the list
if (depends.front() == target) {
depends.erase(depends.begin());
}
// ensure source file is the first dependency
if (depends.front() != source) {
cm::erase(depends, source);
if (!isValidPath) {
depends.insert(depends.begin(), source);
}
} else if (isValidPath) {
// remove first dependency because it must not be filtered out
depends.erase(depends.begin());
}
} else {
// unknown format, ignore it
continue;
}
// depending of the effective format of the dependencies file generated
// by the compiler, the target can be wrongly identified as a
// dependency so remove it from the list
if (depends.front() == target) {
depends.erase(depends.begin());
if (isValidPath) {
cm::erase_if(depends, isValidPath);
// insert source as first dependency
depends.insert(depends.begin(), source);
}
// ensure source file is the first dependency
if (depends.front() != source) {
cm::erase(depends, source);
if (!isValidPath) {
depends.insert(depends.begin(), source);
}
} else if (isValidPath) {
// remove first dependency because it must not be filtered out
depends.erase(depends.begin());
}
dependencies[target] = std::move(depends);
}
if (isValidPath) {
cm::erase_if(depends, isValidPath);
// insert source as first dependency
depends.insert(depends.begin(), source);
}
dependencies[target] = std::move(depends);
}
}
@@ -168,6 +202,8 @@ void cmDependsCompiler::WriteDependencies(
// external dependencies file
for (auto& node : makeDependencies) {
auto target = LocalGenerator->ConvertToMakefilePath(
this->LocalGenerator->MaybeConvertToRelativePath(binDir, node.first));
auto& deps = node.second;
std::transform(
deps.cbegin(), deps.cend(), deps.begin(),
@@ -176,13 +212,16 @@ void cmDependsCompiler::WriteDependencies(
this->LocalGenerator->MaybeConvertToRelativePath(binDir, dep));
});
makeDepends << this->LocalGenerator->ConvertToMakefilePath(node.first)
<< ": " << deps.front();
// first dependency is the source, remove it because should not be declared
// as phony target
deps.erase(deps.begin());
bool first_dep = true;
makeDepends << target << ": ";
for (const auto& dep : deps) {
makeDepends << ' ' << lineContinue << " " << dep;
if (first_dep) {
first_dep = false;
makeDepends << dep;
} else {
makeDepends << ' ' << lineContinue << " " << dep;
}
phonyTargets.emplace(dep.data(), dep.length());
}
makeDepends << std::endl << std::endl;

View File

@@ -4,10 +4,13 @@
#include <type_traits>
#include <utility>
#include <vector>
#include <cm/optional>
#include "cmGccDepfileLexerHelper.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath)
{
@@ -17,3 +20,34 @@ cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath)
}
return cm::nullopt;
}
cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath,
const std::string& prefix)
{
auto deps = cmReadGccDepfile(filePath);
if (prefix.empty() || !deps) {
return deps;
}
for (auto& dep : *deps) {
for (auto& rule : dep.rules) {
if (!cmSystemTools::FileIsFullPath(rule)) {
rule = cmStrCat(prefix, rule);
}
if (cmSystemTools::FileIsFullPath(rule)) {
rule = cmSystemTools::CollapseFullPath(rule);
}
}
for (auto& path : dep.paths) {
if (!cmSystemTools::FileIsFullPath(path)) {
path = cmStrCat(prefix, path);
}
if (cmSystemTools::FileIsFullPath(path)) {
path = cmSystemTools::CollapseFullPath(path);
}
}
}
return deps;
}

View File

@@ -2,8 +2,16 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include <string>
#include <cm/optional>
#include "cmGccDepfileReaderTypes.h"
cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath);
/*
* Read dependencies file and append prefix to all relative paths
*/
cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath,
const std::string& prefix);

View File

@@ -93,6 +93,12 @@ public:
*/
static bool SupportsPlatform() { return false; }
/**
* Utilized to determine if this generator
* supports DEPFILE option.
*/
bool SupportsCustomCommandDepfile() const override { return true; }
/** Get the documentation entry for this generator. */
static void GetDocumentation(cmDocumentationEntry& entry);

View File

@@ -13,6 +13,7 @@
#include <cm/string_view>
#include <cm/vector>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
#include "cmsys/Terminal.h"
@@ -1435,7 +1436,7 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_DEPENDENCY_FILES");
if (!depends.empty()) {
// dependencies are managed by compiler
auto depFiles = cmExpandedList(depends);
auto depFiles = cmExpandedList(depends, true);
std::string const internalDepFile =
targetDir + "/compiler_depend.internal";
std::string const depFile = targetDir + "/compiler_depend.make";
@@ -1998,18 +1999,32 @@ void cmLocalUnixMakefileGenerator3::WriteDependLanguageInfo(
cmakefileStream << "\n# The set of dependency files which are needed:\n";
cmakefileStream << "set(CMAKE_DEPENDS_DEPENDENCY_FILES\n";
for (auto const& compilerLang : compilerLangs) {
auto depFormat = this->Makefile->GetSafeDefinition(
cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT"));
auto const& compilerPairs = compilerLang.second;
for (auto const& compilerPair : compilerPairs) {
for (auto const& src : compilerPair.second) {
cmakefileStream << " \"" << src << "\" \""
<< this->MaybeConvertToRelativePath(
this->GetBinaryDirectory(), compilerPair.first)
<< "\" \"" << depFormat << "\" \""
<< this->MaybeConvertToRelativePath(
this->GetBinaryDirectory(), compilerPair.first)
<< ".d\"\n";
if (compilerLang.first == "CUSTOM"_s) {
for (auto const& compilerPair : compilerPairs) {
for (auto const& src : compilerPair.second) {
cmakefileStream << R"( "" ")"
<< this->MaybeConvertToRelativePath(
this->GetBinaryDirectory(), compilerPair.first)
<< R"(" "custom" ")"
<< this->MaybeConvertToRelativePath(
this->GetBinaryDirectory(), src)
<< "\"\n";
}
}
} else {
auto depFormat = this->Makefile->GetSafeDefinition(
cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT"));
for (auto const& compilerPair : compilerPairs) {
for (auto const& src : compilerPair.second) {
cmakefileStream << " \"" << src << "\" \""
<< this->MaybeConvertToRelativePath(
this->GetBinaryDirectory(), compilerPair.first)
<< "\" \"" << depFormat << "\" \""
<< this->MaybeConvertToRelativePath(
this->GetBinaryDirectory(), compilerPair.first)
<< ".d\"\n";
}
}
}
}

View File

@@ -1617,6 +1617,16 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
std::vector<std::string> depends;
this->LocalGenerator->AppendCustomDepend(depends, ccg);
if (!ccg.GetCC().GetDepfile().empty()) {
// Add dependency over timestamp file for dependencies management
auto dependTimestamp = cmSystemTools::ConvertToOutputPath(
this->LocalGenerator->MaybeConvertToRelativePath(
this->LocalGenerator->GetBinaryDirectory(),
cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")));
depends.push_back(dependTimestamp);
}
// Write the rule.
const std::vector<std::string>& outputs = ccg.GetOutputs();
bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs,
@@ -1653,6 +1663,15 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
objFullPath, srcFullPath);
}
// Setup implicit depend for depfile if any
if (!ccg.GetCC().GetDepfile().empty()) {
std::string objFullPath = cmSystemTools::CollapseFullPath(
outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory());
this->LocalGenerator->AddImplicitDepends(
this->GeneratorTarget, "CUSTOM", objFullPath, ccg.GetFullDepfile(),
cmDependencyScannerKind::Compiler);
}
this->CustomCommandOutputs.insert(outputs.begin(), outputs.end());
}

View File

@@ -15,6 +15,7 @@
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmMakefile.h"
#include "cmOSXBundleGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator(
@@ -36,10 +37,42 @@ void cmMakefileUtilityTargetGenerator::WriteRuleFiles()
*this->BuildFileStream << "# Utility rule file for "
<< this->GeneratorTarget->GetName() << ".\n\n";
const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
? "$(CMAKE_BINARY_DIR)/"
: "");
// Include the dependencies for the target.
std::string dependFile =
cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make");
*this->BuildFileStream
<< "# Include any custom commands dependencies for this target.\n"
<< this->GlobalGenerator->IncludeDirective << " " << root
<< cmSystemTools::ConvertToOutputPath(
this->LocalGenerator->MaybeConvertToRelativePath(
this->LocalGenerator->GetBinaryDirectory(), dependFile))
<< "\n\n";
if (!cmSystemTools::FileExists(dependFile)) {
// Write an empty dependency file.
cmGeneratedFileStream depFileStream(
dependFile, false, this->GlobalGenerator->GetMakefileEncoding());
depFileStream << "# Empty custom commands generated dependencies file for "
<< this->GeneratorTarget->GetName() << ".\n"
<< "# This may be replaced when dependencies are built.\n";
}
std::string dependTimestamp =
cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts");
if (!cmSystemTools::FileExists(dependTimestamp)) {
// Write a dependency timestamp file.
cmGeneratedFileStream depFileStream(
dependTimestamp, false, this->GlobalGenerator->GetMakefileEncoding());
depFileStream << "# CMAKE generated file: DO NOT EDIT!\n"
<< "# Timestamp file for custom commands dependencies "
"management for "
<< this->GeneratorTarget->GetName() << ".\n";
}
if (!this->NoRuleMessages) {
const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
? "$(CMAKE_BINARY_DIR)/"
: "");
// Include the progress variables for the target.
*this->BuildFileStream
<< "# Include the progress variables for this target.\n"

View File

@@ -13,7 +13,6 @@
#include "cmGccDepfileReader.h"
#include "cmGccDepfileReaderTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
namespace {
@@ -79,26 +78,13 @@ bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix,
{
cmGccDepfileContent content;
if (cmSystemTools::FileExists(infile)) {
auto result = cmReadGccDepfile(infile.c_str());
auto result = cmReadGccDepfile(infile.c_str(), prefix);
if (!result) {
return false;
}
content = *std::move(result);
}
for (auto& dep : content) {
for (auto& rule : dep.rules) {
if (!cmSystemTools::FileIsFullPath(rule)) {
rule = cmStrCat(prefix, rule);
}
}
for (auto& path : dep.paths) {
if (!cmSystemTools::FileIsFullPath(path)) {
path = cmStrCat(prefix, path);
}
}
}
cmsys::ofstream fout(outfile.c_str());
if (!fout) {
return false;

View File

@@ -1272,6 +1272,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
snapshot.GetDirectory().SetCurrentBinary(startOutDir);
snapshot.GetDirectory().SetCurrentSource(startDir);
snapshot.GetDirectory().SetRelativePathTopSource(homeDir.c_str());
snapshot.GetDirectory().SetRelativePathTopBinary(homeOutDir.c_str());
cmMakefile mf(cm.GetGlobalGenerator(), snapshot);
auto lgd = cm.GetGlobalGenerator()->CreateLocalGenerator(&mf);

View File

@@ -0,0 +1,4 @@
CMake Error at CustomCommandDependencies-BadArgs.cmake:[0-9]+ \(add_custom_command\):
add_custom_command IMPLICIT_DEPENDS and DEPFILE can not both be specified.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,10 @@
enable_language(C)
add_custom_command(OUTPUT main.c
DEPFILE main.c.d
IMPLICIT_DEPENDS C main.c.in
COMMAND "${CMAKE_COMMAND}" -DINFILE=main.c.in -DOUTFILE=main.c -DDEPFILE=main.c.d
-P "${CMAKE_CURRENT_SOURCE_DIR}/GenerateDepFile.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_target(mainc ALL DEPENDS main.c)

View File

@@ -0,0 +1,73 @@
enable_language(C)
add_custom_command(OUTPUT main.c
DEPFILE main.c.d
COMMAND "${CMAKE_COMMAND}" -DINFILE=main.c.in -DOUTFILE=main.c -DDEPFILE=main.c.d
-P "${CMAKE_CURRENT_SOURCE_DIR}/GenerateDepFile.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_target(mainc ALL DEPENDS main.c)
add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
cmake_minimum_required(VERSION 3.19)
set(check_pairs
\"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c.in\"
\"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c\"
)
set(check_exes
\"$<TARGET_FILE:main>\"
)
if (check_step EQUAL 2)
include(\"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Makefile.cmake\")
if (NOT CMAKE_DEPEND_INFO_FILES)
set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\")
else()
foreach(DEPEND_INFO_FILE IN LISTS CMAKE_DEPEND_INFO_FILES)
include(\"${CMAKE_CURRENT_BINARY_DIR}/\${DEPEND_INFO_FILE}\")
if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES)
set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPENDS_DEPENDENCY_FILES not found.\")
else()
list(LENGTH CMAKE_DEPENDS_DEPENDENCY_FILES DEPENDENCY_FILES_SIZE)
math(EXPR STOP_INDEX \"\${DEPENDENCY_FILES_SIZE} - 1\")
foreach(INDEX RANGE 0 \${STOP_INDEX} 4)
math(EXPR OBJECT_INDEX \"\${INDEX} + 1\")
math(EXPR FORMAT_INDEX \"\${INDEX} + 2\")
math(EXPR DEP_INDEX \"\${INDEX} + 3\")
list(GET CMAKE_DEPENDS_DEPENDENCY_FILES \${OBJECT_INDEX} OBJECT_FILE)
list(GET CMAKE_DEPENDS_DEPENDENCY_FILES \${FORMAT_INDEX} DEP_FORMAT)
list(GET CMAKE_DEPENDS_DEPENDENCY_FILES \${DEP_INDEX} DEP_FILE)
if (NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\")
set(RunCMake_TEST_FAILED \"File \${DEP_FILE} not found.\")
else()
cmake_path(APPEND TARGET_DEP_FILE \"${CMAKE_CURRENT_BINARY_DIR}\" \"\${DEPEND_INFO_FILE}\")
cmake_path(REPLACE_FILENAME TARGET_DEP_FILE \"compiler_depend.make\")
file(READ \"\${TARGET_DEP_FILE}\" DEPENDS_CONTENT)
if (WIN32)
string (REPLACE \"\\\\\" \"/\" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\")
string (TOLOWER \"\${DEPENDS_CONTENT}\" DEPENDS_CONTENT)
string (TOLOWER \"\${OBJECT_FILE}\" OBJECT_FILE)
else()
string(REPLACE \"\\\\ \" \" \" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\")
endif()
if(DEPEND_INFO_FILE MATCHES \"main\\\\.dir\")
if (DEP_FORMAT STREQUAL \"gcc\" AND NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c\")
set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated:\\n\${DEPENDS_CONTENT}\")
endif()
if (DEP_FORMAT STREQUAL \"custom\" AND NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c.in\")
set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated:\\n\${DEPENDS_CONTENT}\")
endif()
else()
if (NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c.in\")
set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated:\\n\${DEPENDS_CONTENT}\")
endif()
endif()
endif()
endforeach()
endif()
endforeach()
endif()
endif()
")

View File

@@ -0,0 +1,3 @@
file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.c.in" [[
int main(void) { return 1; }
]])

View File

@@ -0,0 +1,3 @@
file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.c.in" [[
int main(void) { return 2; }
]])

View File

@@ -0,0 +1,61 @@
cmake_policy(SET CMP0116 NEW)
enable_language(C)
add_custom_command(
OUTPUT topcc.c
DEPFILE topcc.c.d
COMMAND ${CMAKE_COMMAND} -DOUTFILE=topcc.c -DINFILE=topccdep.txt -DDEPFILE=topcc.c.d -P "${CMAKE_CURRENT_LIST_DIR}/WriteDepfile.cmake"
)
add_custom_target(topcc ALL DEPENDS topcc.c)
add_custom_command(
OUTPUT topexe.c
DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/topexe.c.d
COMMAND ${CMAKE_COMMAND} -DOUTFILE=topexe.c "-DINFILE=${CMAKE_CURRENT_BINARY_DIR}/topexedep.txt" -DDEPFILE=topexe.c.d -P "${CMAKE_CURRENT_LIST_DIR}/WriteDepfile.cmake"
)
add_executable(topexe "${CMAKE_CURRENT_BINARY_DIR}/topexe.c")
add_custom_command(
OUTPUT toplib.c
DEPFILE toplib.c.d
COMMAND ${CMAKE_COMMAND} -DOUTFILE=toplib.c -DINFILE=toplibdep.txt -DDEPFILE=toplib.c.d -P "${CMAKE_CURRENT_LIST_DIR}/WriteDepfile.cmake"
)
add_library(toplib STATIC toplib.c)
add_subdirectory(DepfileSubdir)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
function(check_exists file)
if(NOT EXISTS \"\${file}\")
string(APPEND RunCMake_TEST_FAILED \"\${file} does not exist\\n\")
endif()
set(RunCMake_TEST_FAILED \"\${RunCMake_TEST_FAILED}\" PARENT_SCOPE)
endfunction()
function(check_not_exists file)
if(EXISTS \"\${file}\")
string(APPEND RunCMake_TEST_FAILED \"\${file} exists\\n\")
endif()
set(RunCMake_TEST_FAILED \"\${RunCMake_TEST_FAILED}\" PARENT_SCOPE)
endfunction()
set(check_pairs
\"${CMAKE_BINARY_DIR}/topcc.c|${CMAKE_BINARY_DIR}/topccdep.txt\"
\"$<TARGET_FILE:topexe>|${CMAKE_BINARY_DIR}/topexedep.txt\"
\"$<TARGET_FILE:toplib>|${CMAKE_BINARY_DIR}/toplibdep.txt\"
\"${CMAKE_BINARY_DIR}/DepfileSubdir/subcc.c|${CMAKE_BINARY_DIR}/DepfileSubdir/subccdep.txt\"
\"$<TARGET_FILE:subexe>|${CMAKE_BINARY_DIR}/DepfileSubdir/subexedep.txt\"
\"$<TARGET_FILE:sublib>|${CMAKE_BINARY_DIR}/DepfileSubdir/sublibdep.txt\"
)
if(check_step EQUAL 3)
list(APPEND check_pairs
\"${CMAKE_BINARY_DIR}/step3.timestamp|${CMAKE_BINARY_DIR}/topcc.c\"
\"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:topexe>\"
\"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:toplib>\"
\"${CMAKE_BINARY_DIR}/step3.timestamp|${CMAKE_BINARY_DIR}/DepfileSubdir/subcc.c\"
\"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:subexe>\"
\"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:sublib>\"
)
endif()
")

View File

@@ -0,0 +1,10 @@
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir")
file(REMOVE "${RunCMake_TEST_BINARY_DIR}/../sublib.c")
file(REMOVE "${RunCMake_TEST_BINARY_DIR}/step3.timestamp")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topccdep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topexedep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/toplibdep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subccdep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subexedep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/sublibdep.txt")

View File

@@ -0,0 +1,6 @@
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topccdep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topexedep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/toplibdep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subccdep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subexedep.txt")
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/sublibdep.txt")

View File

@@ -0,0 +1 @@
file(TOUCH "${RunCMake_TEST_BINARY_DIR}/step3.timestamp")

View File

@@ -0,0 +1,22 @@
cmake_policy(SET CMP0116 NEW)
add_custom_command(
OUTPUT subcc.c
DEPFILE subcc.c.d
COMMAND ${CMAKE_COMMAND} -DOUTFILE=subcc.c -DINFILE=subccdep.txt -DDEPFILE=subcc.c.d -P "${CMAKE_CURRENT_LIST_DIR}/../WriteDepfile.cmake"
)
add_custom_target(subcc ALL DEPENDS subcc.c)
add_custom_command(
OUTPUT subexe.c
DEPFILE subexe.c.d
COMMAND ${CMAKE_COMMAND} -DOUTFILE=subexe.c -DINFILE=subexedep.txt -DDEPFILE=subexe.c.d -P "${CMAKE_CURRENT_LIST_DIR}/../WriteDepfile.cmake"
)
add_executable(subexe subexe.c)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/../sublib.c
DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/sublib.c.d
COMMAND ${CMAKE_COMMAND} -DOUTFILE=${CMAKE_BINARY_DIR}/../sublib.c "-DINFILE=${CMAKE_CURRENT_BINARY_DIR}/sublibdep.txt" -DDEPFILE=sublib.c.d -P "${CMAKE_CURRENT_LIST_DIR}/../WriteDepfile.cmake"
)
add_library(sublib STATIC "${CMAKE_BINARY_DIR}/../sublib.c")

View File

@@ -0,0 +1,6 @@
file(READ "${INFILE}" INCONTENT)
file(WRITE "${OUTFILE}" "${INCONTENT}")
string(REPLACE [[ ]] [[\ ]] OUTFILE "${OUTFILE}")
string(REPLACE [[ ]] [[\ ]] INFILE "${INFILE}")
file(WRITE "${DEPFILE}" "${OUTFILE}: ${INFILE}\n")

View File

@@ -30,8 +30,17 @@ function(run_BuildDepends CASE)
include(${RunCMake_SOURCE_DIR}/${CASE}.step2.cmake OPTIONAL)
set(check_step 2)
run_cmake_command(${CASE}-build2 ${CMAKE_COMMAND} --build . --config Debug)
if(run_BuildDepends_skip_step_3)
return()
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${fs_delay}) # handle 1s resolution
include(${RunCMake_SOURCE_DIR}/${CASE}.step3.cmake OPTIONAL)
set(check_step 3)
run_cmake_command(${CASE}-build3 ${CMAKE_COMMAND} --build . --config Debug)
endfunction()
set(run_BuildDepends_skip_step_3 1)
run_BuildDepends(C-Exe)
if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
if(RunCMake_GENERATOR MATCHES "Visual Studio 10" OR
@@ -122,4 +131,15 @@ if ((RunCMake_GENERATOR STREQUAL "Unix Makefiles"
AND MSVC_VERSION GREATER 1300
AND CMAKE_C_COMPILER_ID STREQUAL "MSVC"))
run_BuildDepends(CompilerDependencies)
run_BuildDepends(CustomCommandDependencies)
endif()
if (RunCMake_GENERATOR MATCHES "Makefiles")
run_cmake(CustomCommandDependencies-BadArgs)
endif()
if(RunCMake_GENERATOR MATCHES "Make|Ninja")
unset(run_BuildDepends_skip_step_3)
run_BuildDepends(CustomCommandDepfile)
set(run_BuildDepends_skip_step_3 1)
endif()

View File

@@ -0,0 +1,8 @@
file(WRITE "${OUTFILE}" [[int main(void)
{
return 0;
}
]])
string(REPLACE [[ ]] [[\ ]] OUTFILE "${OUTFILE}")
string(REPLACE [[ ]] [[\ ]] INFILE "${INFILE}")
file(WRITE "${DEPFILE}" "${OUTFILE}: ${INFILE}\n")

View File

@@ -1,5 +0,0 @@
^CMake Error at CustomCommandDepfile-ERROR.cmake:1 \(add_custom_command\):
add_custom_command Option DEPFILE not supported by [^
]+
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -1,8 +0,0 @@
add_custom_command(
OUTPUT hello.copy.c
COMMAND "${CMAKE_COMMAND}" -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
hello.copy.c
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
DEPFILE "test.d"
)

View File

@@ -39,7 +39,6 @@ function(run_VerboseBuild)
endfunction()
run_VerboseBuild()
run_cmake(CustomCommandDepfile-ERROR)
run_cmake(IncludeRegexSubdir)
function(run_MakefileConflict)