Xcode: Add support of DEPFILE for add_custom_command

Issue: #20286
This commit is contained in:
Marc Chevrier 2021-04-15 14:13:57 +02:00 committed by Brad King
parent 498b916cdd
commit d67cc4882d
9 changed files with 115 additions and 27 deletions

View File

@ -271,32 +271,42 @@ The options are:
``DEPFILE``
.. versionadded:: 3.7
Specify a ``.d`` depfile for the :generator:`Ninja` generator and
:ref:`Makefile Generators`. The depfile may use "generator expressions" with
the syntax ``$<...>``. See the :manual:`generator-expressions(7)
<cmake-generator-expressions(7)>` manual for available expressions.
A ``.d`` file holds dependencies usually emitted by the custom
command itself.
Using ``DEPFILE`` with other generators than :generator:`Ninja` or
:ref:`Makefile Generators` is an error.
Specify a ``.d`` depfile for the :generator:`Ninja`, :generator:`Xcode` and
:ref:`Makefile <Makefile Generators>` generators. The depfile may use
"generator expressions" with the syntax ``$<...>``. See the
:manual:`generator-expressions(7) <cmake-generator-expressions(7)>` manual
for available expressions. A ``.d`` file holds dependencies usually emitted
by the custom command itself.
Using ``DEPFILE`` with other generators than :generator:`Ninja`,
:generator:`Xcode` or :ref:`Makefile <Makefile Generators>` is an error.
.. versionadded:: 3.20
Added the support of :ref:`Makefile Generators`.
.. versionadded:: 3.21
Added the support of :manual:`generator expressions <cmake-generator-expressions(7)>`.
Added the support of :generator:`Xcode` generator and
:manual:`generator expressions <cmake-generator-expressions(7)>`.
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`. This policy is always ``NEW`` for
:ref:`Makefile Generators`).
:ref:`Makefile <Makefile Generators>` and :generator:`Xcode` generators).
.. note::
For :ref:`Makefile Generators`, this option cannot be specified at the
same time as ``IMPLICIT_DEPENDS`` option.
.. note::
For the :generator:`Xcode` generator, this option requires that the
:ref:`Xcode Build System Selection` uses the ``buildsystem=12`` variant
or higher. This is the default when using Xcode 12 or above.
The :variable:`CMAKE_XCODE_BUILD_SYSTEM` variable indicates which variant
of the Xcode build system is used.
Examples: Generating Files
^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,5 @@
Xcode-add_custom_command-DEPFILE
--------------------------------
* The :command:`add_custom_command` command gained ``DEPFILE`` support on
:generator:`Xcode` generator.

View File

@ -151,7 +151,9 @@ std::string EvaluateDepfile(std::string const& path,
cmCustomCommandGenerator::cmCustomCommandGenerator(
cmCustomCommand const& cc, std::string config, cmLocalGenerator* lg,
bool transformDepfile, cm::optional<std::string> crossConfig)
bool transformDepfile, cm::optional<std::string> crossConfig,
std::function<std::string(const std::string&, const std::string&)>
computeInternalDepfile)
: CC(&cc)
, OutputConfig(crossConfig ? *crossConfig : config)
, CommandConfig(std::move(config))
@ -159,7 +161,15 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(
, OldStyle(cc.GetEscapeOldStyle())
, MakeVars(cc.GetEscapeAllowMakeVars())
, EmulatorsWithArguments(cc.GetCommandLines().size())
, ComputeInternalDepfile(std::move(computeInternalDepfile))
{
if (!this->ComputeInternalDepfile) {
this->ComputeInternalDepfile =
[this](const std::string& cfg, const std::string& file) -> std::string {
return this->GetInternalDepfileName(cfg, file);
};
}
cmGeneratorExpression ge(cc.GetBacktrace());
const cmCustomCommandLines& cmdlines = this->CC->GetCommandLines();
@ -413,13 +423,9 @@ std::string cmCustomCommandGenerator::GetFullDepfile() const
return cmSystemTools::CollapseFullPath(depfile);
}
std::string cmCustomCommandGenerator::GetInternalDepfile() const
std::string cmCustomCommandGenerator::GetInternalDepfileName(
const std::string& /*config*/, const std::string& depfile)
{
std::string depfile = this->GetFullDepfile();
if (depfile.empty()) {
return "";
}
cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
std::string extension;
switch (*this->LG->GetGlobalGenerator()->DepfileFormat()) {
@ -434,6 +440,16 @@ std::string cmCustomCommandGenerator::GetInternalDepfile() const
hash.HashString(depfile), extension);
}
std::string cmCustomCommandGenerator::GetInternalDepfile() const
{
std::string depfile = this->GetFullDepfile();
if (depfile.empty()) {
return "";
}
return this->ComputeInternalDepfile(this->OutputConfig, depfile);
}
const char* cmCustomCommandGenerator::GetComment() const
{
return this->CC->GetComment();

View File

@ -4,6 +4,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <functional>
#include <set>
#include <string>
#include <utility>
@ -19,6 +20,8 @@ class cmLocalGenerator;
class cmCustomCommandGenerator
{
std::string GetInternalDepfileName(const std::string&, const std::string&);
cmCustomCommand const* CC;
std::string OutputConfig;
std::string CommandConfig;
@ -32,15 +35,19 @@ class cmCustomCommandGenerator
std::vector<std::string> Depends;
std::string WorkingDirectory;
std::set<BT<std::pair<std::string, bool>>> Utilities;
std::function<std::string(const std::string&, const std::string&)>
ComputeInternalDepfile;
void FillEmulatorsWithArguments();
std::vector<std::string> GetCrossCompilingEmulator(unsigned int c) const;
const char* GetArgv0Location(unsigned int c) const;
public:
cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config,
cmLocalGenerator* lg, bool transformDepfile = true,
cm::optional<std::string> crossConfig = {});
cmCustomCommandGenerator(
cmCustomCommand const& cc, std::string config, cmLocalGenerator* lg,
bool transformDepfile = true, cm::optional<std::string> crossConfig = {},
std::function<std::string(const std::string&, const std::string&)>
computeInternalDepfile = {});
cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete;
cmCustomCommandGenerator(cmCustomCommandGenerator&&) = default;
cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) =

View File

@ -17,6 +17,7 @@
#include "cmsys/RegularExpression.hxx"
#include "cmCMakePath.h"
#include "cmComputeLinkInformation.h"
#include "cmCryptoHash.h"
#include "cmCustomCommand.h"
@ -1864,9 +1865,20 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
std::set<std::string> allConfigInputs;
std::set<std::string> allConfigOutputs;
cmXCodeObject* buildPhase =
this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase,
cmStrCat(gt->GetName(), ':', sf->GetFullPath()));
auto depfilesDirectory = cmStrCat(
gt->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/CMakeFiles/d/");
auto depfilesPrefix = cmStrCat(depfilesDirectory, buildPhase->GetId(), ".");
std::string shellScript = "set -e\n";
for (std::string const& configName : this->CurrentConfigurationTypes) {
cmCustomCommandGenerator ccg(cc, configName, this->CurrentLocalGenerator);
cmCustomCommandGenerator ccg(
cc, configName, this->CurrentLocalGenerator, true, {},
[&depfilesPrefix](const std::string& config, const std::string&)
-> std::string { return cmStrCat(depfilesPrefix, config, ".d"); });
std::vector<std::string> realDepends;
realDepends.reserve(ccg.GetDepends().size());
for (auto const& d : ccg.GetDepends()) {
@ -1886,9 +1898,22 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
"\"; then :\n", this->ConstructScript(ccg), "fi\n");
}
cmXCodeObject* buildPhase =
this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase,
cmStrCat(gt->GetName(), ':', sf->GetFullPath()));
if (!cc.GetDepfile().empty()) {
buildPhase->AddAttribute(
"dependencyFile",
this->CreateString(cmStrCat(depfilesDirectory, buildPhase->GetId(),
".$(CONFIGURATION).d")));
// to avoid spurious errors during first build, create empty dependency
// files
cmSystemTools::MakeDirectory(depfilesDirectory);
for (std::string const& configName : this->CurrentConfigurationTypes) {
auto file = cmStrCat(depfilesPrefix, configName, ".d");
if (!cmSystemTools::FileExists(file)) {
cmSystemTools::Touch(file, true);
}
}
}
buildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);

View File

@ -14,6 +14,7 @@
#include <cm/string_view>
#include "cmGlobalGenerator.h"
#include "cmTransformDepfile.h"
#include "cmXCodeObject.h"
class cmCustomCommand;
@ -111,6 +112,18 @@ public:
bool ShouldStripResourcePath(cmMakefile*) const override;
/**
* Used to determine if this generator supports DEPFILE option.
*/
bool SupportsCustomCommandDepfile() const override
{
return this->XcodeBuildSystem >= BuildSystem::Twelve;
}
virtual cm::optional<cmDepfileFormat> DepfileFormat() const override
{
return cmDepfileFormat::GccDepfile;
}
bool SetSystemName(std::string const& s, cmMakefile* mf) override;
bool SetGeneratorToolset(std::string const& ts, bool build,
cmMakefile* mf) override;

View File

@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmTransformDepfile.h"
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
@ -13,6 +14,7 @@
#include "cmGccDepfileReader.h"
#include "cmGccDepfileReaderTypes.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmSystemTools.h"
@ -38,6 +40,14 @@ void WriteGccDepfile(cmsys::ofstream& fout, const cmLocalGenerator& lg,
const cmGccDepfileContent& content)
{
const auto& binDir = lg.GetBinaryDirectory();
std::function<std::string(const std::string&)> formatPath =
[&lg, &binDir](const std::string& path) -> std::string {
return lg.MaybeConvertToRelativePath(binDir, path);
};
if (lg.GetGlobalGenerator()->GetName() == "Xcode") {
// full paths must be preserved for Xcode compliance
formatPath = [](const std::string& path) -> std::string { return path; };
}
for (auto const& dep : content) {
bool first = true;
@ -46,12 +56,12 @@ void WriteGccDepfile(cmsys::ofstream& fout, const cmLocalGenerator& lg,
fout << " \\\n ";
}
first = false;
WriteFilenameGcc(fout, lg.MaybeConvertToRelativePath(binDir, rule));
WriteFilenameGcc(fout, formatPath(rule));
}
fout << ':';
for (auto const& path : dep.paths) {
fout << " \\\n ";
WriteFilenameGcc(fout, lg.MaybeConvertToRelativePath(binDir, path));
WriteFilenameGcc(fout, formatPath(path));
}
fout << '\n';
}

View File

@ -155,7 +155,8 @@ if (RunCMake_GENERATOR MATCHES "Makefiles")
run_cmake(CustomCommandDependencies-BadArgs)
endif()
if(RunCMake_GENERATOR MATCHES "Make|Ninja")
if(RunCMake_GENERATOR MATCHES "Make|Ninja" OR
(RunCMake_GENERATOR STREQUAL "Xcode" AND CMAKE_XCODE_BUILD_SYSTEM GREATER_EQUAL "12"))
unset(run_BuildDepends_skip_step_3)
run_BuildDepends(CustomCommandDepfile)
set(run_BuildDepends_skip_step_3 1)

View File

@ -222,6 +222,7 @@ endif()
add_RunCMake_test(BuildDepends
-DMSVC_VERSION=${MSVC_VERSION}
-DCMAKE_XCODE_BUILD_SYSTEM=${CMAKE_XCODE_BUILD_SYSTEM}
-DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
-DCMake_TEST_BuildDepends_GNU_AS=${CMake_TEST_BuildDepends_GNU_AS}
)