|
|
|
/* 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 "cmGeneratedFileStream.h"
|
|
|
|
#include "cmGeneratorTarget.h"
|
|
|
|
#include "cmXMLSafe.h"
|
|
|
|
|
|
|
|
cmXCodeScheme::cmXCodeScheme(cmXCodeObject* xcObj, const TestObjects& tests,
|
|
|
|
const std::vector<std::string>& configList,
|
|
|
|
unsigned int xcVersion)
|
|
|
|
: Target(xcObj)
|
|
|
|
, Tests(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 = xcProjDir;
|
|
|
|
xcodeSchemeDir += "/xcshareddata/xcschemes";
|
|
|
|
cmSystemTools::MakeDirectory(xcodeSchemeDir.c_str());
|
|
|
|
|
|
|
|
std::string xcodeSchemeFile = xcodeSchemeDir;
|
|
|
|
xcodeSchemeFile += "/";
|
|
|
|
xcodeSchemeFile += this->TargetName;
|
|
|
|
xcodeSchemeFile += ".xcscheme";
|
|
|
|
|
|
|
|
cmGeneratedFileStream fout(xcodeSchemeFile.c_str());
|
|
|
|
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");
|
|
|
|
xout.Attribute("useCustomWorkingDirectory", "NO");
|
|
|
|
xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
|
|
|
|
xout.Attribute("debugDocumentVersioning", "YES");
|
|
|
|
xout.Attribute("debugServiceExtension", "internal");
|
|
|
|
xout.Attribute("allowLocationSimulation", "YES");
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
|
|
|
|
// 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 (const char* 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 (const char* argList =
|
|
|
|
this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
|
|
|
|
std::vector<std::string> arguments;
|
|
|
|
cmSystemTools::ExpandListArgument(argList, arguments);
|
|
|
|
if (!arguments.empty()) {
|
|
|
|
xout.StartElement("CommandLineArguments");
|
|
|
|
|
|
|
|
for (auto argument : arguments) {
|
|
|
|
xout.StartElement("CommandLineArgument");
|
|
|
|
xout.BreakAttributes();
|
|
|
|
|
|
|
|
xout.Attribute("argument", argument);
|
|
|
|
xout.Attribute("isEnabled", "YES");
|
|
|
|
|
|
|
|
xout.EndElement(); // CommandLineArgument
|
|
|
|
}
|
|
|
|
|
|
|
|
xout.EndElement(); // CommandLineArguments
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const char* envList =
|
|
|
|
this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
|
|
|
|
std::vector<std::string> envs;
|
|
|
|
cmSystemTools::ExpandListArgument(envList, envs);
|
|
|
|
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::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", "");
|
|
|
|
xout.Attribute("useCustomWorkingDirectory", "NO");
|
|
|
|
xout.Attribute("debugDocumentVersioning", "YES");
|
|
|
|
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());
|
|
|
|
xout.Attribute("BuildableName", xcObj->GetTarget()->GetFullName());
|
|
|
|
xout.Attribute("BlueprintName", xcObj->GetTarget()->GetName());
|
|
|
|
xout.Attribute("ReferencedContainer", "container:" + container);
|
|
|
|
xout.EndElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (std::find(this->ConfigList.begin(), this->ConfigList.end(), name) ==
|
|
|
|
this->ConfigList.end() &&
|
|
|
|
!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;
|
|
|
|
}
|