1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-06-20 11:47:23 +08:00
CMake/Source/cmXCodeScheme.cxx
Jake Turner e09a3eddb6 Xcode: Support "GPU Frame Capture" scheme property
Added XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE variable which
sets the scheme property value for "GPU Frame Capture" in the Options section by setting the Xcode project variable "enableGPUFrameCaptureMode".

Example values are "Metal" (1) and "Disabled" (3).

XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE is initialized by the property CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE.

Implements: #22700
2021-10-19 15:24:10 +01:00

468 lines
15 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmXCodeScheme.h"
#include <iomanip>
#include <iostream>
#include <sstream>
#include <utility>
#include <cmext/algorithm>
#include "cmsys/String.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmXMLSafe.h"
cmXCodeScheme::cmXCodeScheme(cmLocalGenerator* lg, cmXCodeObject* xcObj,
TestObjects tests,
const std::vector<std::string>& configList,
unsigned int xcVersion)
: LocalGenerator(lg)
, Target(xcObj)
, Tests(std::move(tests))
, TargetName(xcObj->GetTarget()->GetName())
, ConfigList(configList)
, XcodeVersion(xcVersion)
{
}
void cmXCodeScheme::WriteXCodeSharedScheme(const std::string& xcProjDir,
const std::string& container)
{
// Create shared scheme sub-directory tree
//
std::string xcodeSchemeDir = cmStrCat(xcProjDir, "/xcshareddata/xcschemes");
cmSystemTools::MakeDirectory(xcodeSchemeDir);
std::string xcodeSchemeFile =
cmStrCat(xcodeSchemeDir, '/', this->TargetName, ".xcscheme");
cmGeneratedFileStream fout(xcodeSchemeFile);
fout.SetCopyIfDifferent(true);
if (!fout) {
return;
}
WriteXCodeXCScheme(fout, container);
}
void cmXCodeScheme::WriteXCodeXCScheme(std::ostream& fout,
const std::string& container)
{
cmXMLWriter xout(fout);
xout.SetIndentationElement(std::string(3, ' '));
xout.StartDocument();
xout.StartElement("Scheme");
xout.BreakAttributes();
xout.Attribute("LastUpgradeVersion", WriteVersionString());
xout.Attribute("version", "1.3");
WriteBuildAction(xout, container);
WriteTestAction(xout, FindConfiguration("Debug"), container);
WriteLaunchAction(xout, FindConfiguration("Debug"), container);
WriteProfileAction(xout, FindConfiguration("Release"));
WriteAnalyzeAction(xout, FindConfiguration("Debug"));
WriteArchiveAction(xout, FindConfiguration("Release"));
xout.EndElement();
}
void cmXCodeScheme::WriteBuildAction(cmXMLWriter& xout,
const std::string& container)
{
xout.StartElement("BuildAction");
xout.BreakAttributes();
xout.Attribute("parallelizeBuildables", "YES");
xout.Attribute("buildImplicitDependencies", "YES");
xout.StartElement("BuildActionEntries");
xout.StartElement("BuildActionEntry");
xout.BreakAttributes();
xout.Attribute("buildForTesting", "YES");
xout.Attribute("buildForRunning", "YES");
xout.Attribute("buildForProfiling", "YES");
xout.Attribute("buildForArchiving", "YES");
xout.Attribute("buildForAnalyzing", "YES");
WriteBuildableReference(xout, this->Target, container);
xout.EndElement(); // BuildActionEntry
xout.EndElement(); // BuildActionEntries
xout.EndElement(); // BuildAction
}
void cmXCodeScheme::WriteTestAction(cmXMLWriter& xout,
const std::string& configuration,
const std::string& container)
{
xout.StartElement("TestAction");
xout.BreakAttributes();
xout.Attribute("buildConfiguration", configuration);
xout.Attribute("selectedDebuggerIdentifier",
"Xcode.DebuggerFoundation.Debugger.LLDB");
xout.Attribute("selectedLauncherIdentifier",
"Xcode.DebuggerFoundation.Launcher.LLDB");
xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
xout.StartElement("Testables");
for (auto test : this->Tests) {
xout.StartElement("TestableReference");
xout.BreakAttributes();
xout.Attribute("skipped", "NO");
WriteBuildableReference(xout, test, container);
xout.EndElement(); // TestableReference
}
xout.EndElement();
if (IsTestable()) {
xout.StartElement("MacroExpansion");
WriteBuildableReference(xout, this->Target, container);
xout.EndElement(); // MacroExpansion
}
xout.StartElement("AdditionalOptions");
xout.EndElement();
xout.EndElement(); // TestAction
}
void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
const std::string& configuration,
const std::string& container)
{
xout.StartElement("LaunchAction");
xout.BreakAttributes();
xout.Attribute("buildConfiguration", configuration);
xout.Attribute("selectedDebuggerIdentifier",
"Xcode.DebuggerFoundation.Debugger.LLDB");
xout.Attribute("selectedLauncherIdentifier",
"Xcode.DebuggerFoundation.Launcher.LLDB");
xout.Attribute("launchStyle", "0");
WriteCustomWorkingDirectory(xout, configuration);
xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
"XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
true);
xout.Attribute("debugServiceExtension", "internal");
xout.Attribute("allowLocationSimulation", "YES");
if (cmValue gpuFrameCaptureMode = this->Target->GetTarget()->GetProperty(
"XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE")) {
std::string value = *gpuFrameCaptureMode;
if (cmsysString_strcasecmp(value.c_str(), "Metal") == 0) {
value = "1";
} else if (cmsysString_strcasecmp(value.c_str(), "Disabled") == 0) {
value = "3";
}
xout.Attribute("enableGPUFrameCaptureMode", value);
}
// Diagnostics tab begin
bool useAddressSanitizer = WriteLaunchActionAttribute(
xout, "enableAddressSanitizer",
"XCODE_SCHEME_ADDRESS_SANITIZER"); // not allowed with
// enableThreadSanitizer=YES
WriteLaunchActionAttribute(
xout, "enableASanStackUseAfterReturn",
"XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
bool useThreadSanitizer = false;
if (!useAddressSanitizer) {
useThreadSanitizer = WriteLaunchActionAttribute(
xout, "enableThreadSanitizer",
"XCODE_SCHEME_THREAD_SANITIZER"); // not allowed with
// enableAddressSanitizer=YES
}
WriteLaunchActionAttribute(xout, "stopOnEveryThreadSanitizerIssue",
"XCODE_SCHEME_THREAD_SANITIZER_STOP");
WriteLaunchActionAttribute(xout, "enableUBSanitizer",
"XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
WriteLaunchActionAttribute(
xout, "stopOnEveryUBSanitizerIssue",
"XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
WriteLaunchActionAttribute(
xout, "disableMainThreadChecker",
"XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); // negative enabled!
WriteLaunchActionAttribute(xout, "stopOnEveryMainThreadCheckerIssue",
"XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
if (this->Target->GetTarget()->GetPropertyAsBool(
"XCODE_SCHEME_DEBUG_AS_ROOT")) {
xout.Attribute("debugAsWhichUser", "root");
}
// Diagnostics tab end
if (IsExecutable(this->Target)) {
xout.StartElement("BuildableProductRunnable");
xout.BreakAttributes();
xout.Attribute("runnableDebuggingMode", "0");
} else {
xout.StartElement("MacroExpansion");
}
WriteBuildableReference(xout, this->Target, container);
xout.EndElement(); // MacroExpansion
// Info tab begin
if (cmValue exe =
this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
xout.StartElement("PathRunnable");
xout.BreakAttributes();
xout.Attribute("runnableDebuggingMode", "0");
xout.Attribute("FilePath", *exe);
xout.EndElement(); // PathRunnable
}
// Info tab end
// Arguments tab begin
if (cmValue argList =
this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
std::vector<std::string> arguments = cmExpandedList(*argList);
if (!arguments.empty()) {
xout.StartElement("CommandLineArguments");
for (auto const& argument : arguments) {
xout.StartElement("CommandLineArgument");
xout.BreakAttributes();
xout.Attribute("argument", argument);
xout.Attribute("isEnabled", "YES");
xout.EndElement(); // CommandLineArgument
}
xout.EndElement(); // CommandLineArguments
}
}
if (cmValue envList =
this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
std::vector<std::string> envs = cmExpandedList(*envList);
if (!envs.empty()) {
xout.StartElement("EnvironmentVariables");
for (auto env : envs) {
xout.StartElement("EnvironmentVariable");
xout.BreakAttributes();
std::string envValue;
const auto p = env.find_first_of('=');
if (p != std::string::npos) {
envValue = env.substr(p + 1);
env.resize(p);
}
xout.Attribute("key", env);
xout.Attribute("value", envValue);
xout.Attribute("isEnabled", "YES");
xout.EndElement(); // EnvironmentVariable
}
xout.EndElement(); // EnvironmentVariables
}
}
// Arguments tab end
xout.StartElement("AdditionalOptions");
if (!useThreadSanitizer) {
WriteLaunchActionAdditionalOption(xout, "MallocScribble", "",
"XCODE_SCHEME_MALLOC_SCRIBBLE");
}
if (!useThreadSanitizer && !useAddressSanitizer) {
WriteLaunchActionAdditionalOption(xout, "MallocGuardEdges", "",
"XCODE_SCHEME_MALLOC_GUARD_EDGES");
}
if (!useThreadSanitizer && !useAddressSanitizer) {
WriteLaunchActionAdditionalOption(xout, "DYLD_INSERT_LIBRARIES",
"/usr/lib/libgmalloc.dylib",
"XCODE_SCHEME_GUARD_MALLOC");
}
WriteLaunchActionAdditionalOption(xout, "NSZombieEnabled", "YES",
"XCODE_SCHEME_ZOMBIE_OBJECTS");
if (!useThreadSanitizer && !useAddressSanitizer) {
WriteLaunchActionAdditionalOption(xout, "MallocStackLogging", "",
"XCODE_SCHEME_MALLOC_STACK");
}
WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_APIS", "",
"XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_LIBRARIES", "",
"XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
xout.EndElement();
xout.EndElement(); // LaunchAction
}
bool cmXCodeScheme::WriteLaunchActionAttribute(cmXMLWriter& xout,
const std::string& attrName,
const std::string& varName)
{
if (Target->GetTarget()->GetPropertyAsBool(varName)) {
xout.Attribute(attrName.c_str(), "YES");
return true;
}
return false;
}
bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
bool defaultValue)
{
cmValue property = Target->GetTarget()->GetProperty(varName);
bool isOn = (!property && defaultValue) || cmIsOn(property);
if (isOn) {
xout.Attribute(attrName.c_str(), "YES");
} else {
xout.Attribute(attrName.c_str(), "NO");
}
return isOn;
}
bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
cmXMLWriter& xout, const std::string& key, const std::string& value,
const std::string& varName)
{
if (Target->GetTarget()->GetPropertyAsBool(varName)) {
xout.StartElement("AdditionalOption");
xout.BreakAttributes();
xout.Attribute("key", key);
xout.Attribute("value", value);
xout.Attribute("isEnabled", "YES");
xout.EndElement(); // AdditionalOption
return true;
}
return false;
}
void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
const std::string& configuration)
{
xout.StartElement("ProfileAction");
xout.BreakAttributes();
xout.Attribute("buildConfiguration", configuration);
xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
xout.Attribute("savedToolIdentifier", "");
WriteCustomWorkingDirectory(xout, configuration);
WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
"XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
true);
xout.EndElement();
}
void cmXCodeScheme::WriteAnalyzeAction(cmXMLWriter& xout,
const std::string& configuration)
{
xout.StartElement("AnalyzeAction");
xout.BreakAttributes();
xout.Attribute("buildConfiguration", configuration);
xout.EndElement();
}
void cmXCodeScheme::WriteArchiveAction(cmXMLWriter& xout,
const std::string& configuration)
{
xout.StartElement("ArchiveAction");
xout.BreakAttributes();
xout.Attribute("buildConfiguration", configuration);
xout.Attribute("revealArchiveInOrganizer", "YES");
xout.EndElement();
}
void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
const cmXCodeObject* xcObj,
const std::string& container)
{
xout.StartElement("BuildableReference");
xout.BreakAttributes();
xout.Attribute("BuildableIdentifier", "primary");
xout.Attribute("BlueprintIdentifier", xcObj->GetId());
std::string const noConfig; // FIXME: What config to use here?
xout.Attribute("BuildableName", xcObj->GetTarget()->GetFullName(noConfig));
xout.Attribute("BlueprintName", xcObj->GetTarget()->GetName());
xout.Attribute("ReferencedContainer", "container:" + container);
xout.EndElement();
}
void cmXCodeScheme::WriteCustomWorkingDirectory(
cmXMLWriter& xout, const std::string& configuration)
{
std::string const& propertyValue =
this->Target->GetTarget()->GetSafeProperty(
"XCODE_SCHEME_WORKING_DIRECTORY");
if (propertyValue.empty()) {
xout.Attribute("useCustomWorkingDirectory", "NO");
} else {
xout.Attribute("useCustomWorkingDirectory", "YES");
auto customWorkingDirectory = cmGeneratorExpression::Evaluate(
propertyValue, this->LocalGenerator, configuration);
xout.Attribute("customWorkingDirectory", customWorkingDirectory);
}
}
std::string cmXCodeScheme::WriteVersionString()
{
std::ostringstream v;
v << std::setfill('0') << std::setw(4) << this->XcodeVersion * 10;
return v.str();
}
std::string cmXCodeScheme::FindConfiguration(const std::string& name)
{
// Try to find the desired configuration by name,
// and if it's not found return first from the list
//
if (!cm::contains(this->ConfigList, name) && !this->ConfigList.empty()) {
return this->ConfigList[0];
}
return name;
}
bool cmXCodeScheme::IsTestable() const
{
return !this->Tests.empty() || IsExecutable(this->Target);
}
bool cmXCodeScheme::IsExecutable(const cmXCodeObject* target)
{
cmGeneratorTarget* gt = target->GetTarget();
if (!gt) {
cmSystemTools::Error("Error no target on xobject\n");
return false;
}
return gt->GetType() == cmStateEnums::EXECUTABLE;
}