CMake/Source/cmGlobalXCodeGenerator.cxx

3625 lines
126 KiB
C++
Raw Normal View History

Simplify CMake per-source license notices Per-source copyright/license notice headers that spell out copyright holder names and years are hard to maintain and often out-of-date or plain wrong. Precise contributor information is already maintained automatically by the version control tool. Ultimately it is the receiver of a file who is responsible for determining its licensing status, and per-source notices are merely a convenience. Therefore it is simpler and more accurate for each source to have a generic notice of the license name and references to more detailed information on copyright holders and full license terms. Our `Copyright.txt` file now contains a list of Contributors whose names appeared source-level copyright notices. It also references version control history for more precise information. Therefore we no longer need to spell out the list of Contributors in each source file notice. Replace CMake per-source copyright/license notice headers with a short description of the license and links to `Copyright.txt` and online information available from "https://cmake.org/licensing". The online URL also handles cases of modules being copied out of our source into other projects, so we can drop our notices about replacing links with full license text. Run the `Utilities/Scripts/filter-notices.bash` script to perform the majority of the replacements mechanically. Manually fix up shebang lines and trailing newlines in a few files. Manually update the notices in a few files that the script does not handle.
2016-09-27 19:01:08 +00:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2005-01-24 22:35:54 +00:00
#include "cmGlobalXCodeGenerator.h"
#include "cmsys/RegularExpression.hxx"
2016-11-22 23:41:44 +00:00
#include <assert.h>
#include <iomanip>
#include <memory> // IWYU pragma: keep
2016-11-22 23:41:44 +00:00
#include <sstream>
#include <stdio.h>
#include <string.h>
#include "cmAlgorithms.h"
#include "cmComputeLinkInformation.h"
#include "cmCustomCommandGenerator.h"
2016-11-22 23:41:44 +00:00
#include "cmDocumentationEntry.h"
#include "cmGeneratedFileStream.h"
2016-11-22 23:41:44 +00:00
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGeneratorFactory.h"
2016-11-22 23:41:44 +00:00
#include "cmLocalGenerator.h"
#include "cmLocalXCodeGenerator.h"
#include "cmMakefile.h"
2016-11-22 23:41:44 +00:00
#include "cmOutputConverter.h"
#include "cmSourceFile.h"
2016-11-22 23:41:44 +00:00
#include "cmSourceGroup.h"
#include "cmState.h"
2016-11-22 23:41:44 +00:00
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmXCode21Object.h"
#include "cmXCodeObject.h"
#include "cmXCodeScheme.h"
#include "cmake.h"
2016-11-22 23:41:44 +00:00
struct cmLinkImplementation;
#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(__APPLE__)
#define HAVE_APPLICATION_SERVICES
#include <ApplicationServices/ApplicationServices.h>
#endif
#if defined(CMAKE_BUILD_WITH_CMAKE)
#include "cmXMLParser.h"
// parse the xml file storing the installed version of Xcode on
// the machine
class cmXcodeVersionParser : public cmXMLParser
{
public:
cmXcodeVersionParser()
: Version("1.5")
{
}
void StartElement(const std::string&, const char**) override
{
this->Data = "";
}
void EndElement(const std::string& name) override
{
if (name == "key") {
this->Key = this->Data;
} else if (name == "string") {
if (this->Key == "CFBundleShortVersionString") {
this->Version = this->Data;
}
}
}
void CharacterDataHandler(const char* data, int length) override
{
this->Data.append(data, length);
}
std::string Version;
2006-03-15 17:02:34 +00:00
std::string Key;
std::string Data;
};
#endif
// Builds either an object list or a space-separated string from the
// given inputs.
class cmGlobalXCodeGenerator::BuildObjectListOrString
{
cmGlobalXCodeGenerator* Generator;
cmXCodeObject* Group;
bool Empty;
std::string String;
public:
BuildObjectListOrString(cmGlobalXCodeGenerator* gen, bool buildObjectList)
: Generator(gen)
, Group(nullptr)
, Empty(true)
{
if (buildObjectList) {
this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST);
}
}
bool IsEmpty() const { return this->Empty; }
void Add(const std::string& newString)
{
this->Empty = false;
if (this->Group) {
this->Group->AddObject(this->Generator->CreateString(newString));
} else {
this->String += newString;
this->String += ' ';
}
}
const std::string& GetString() const { return this->String; }
cmXCodeObject* CreateList()
{
if (this->Group) {
return this->Group;
}
return this->Generator->CreateString(this->String);
}
};
class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory
{
public:
cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
cmake* cm) const override;
void GetDocumentation(cmDocumentationEntry& entry) const override
{
cmGlobalXCodeGenerator::GetDocumentation(entry);
}
void GetGenerators(std::vector<std::string>& names) const override
{
names.push_back(cmGlobalXCodeGenerator::GetActualName());
}
bool SupportsToolset() const override { return true; }
bool SupportsPlatform() const override { return false; }
};
2017-04-21 17:08:30 +00:00
cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(
cmake* cm, std::string const& version_string, unsigned int version_number)
: cmGlobalGenerator(cm)
2005-01-24 22:35:54 +00:00
{
2017-04-21 17:08:30 +00:00
this->VersionString = version_string;
this->XcodeVersion = version_number;
this->RootObject = nullptr;
this->MainGroupChildren = nullptr;
this->CurrentMakefile = nullptr;
this->CurrentLocalGenerator = nullptr;
this->XcodeBuildCommandInitialized = false;
this->ObjectDirArchDefault = "$(CURRENT_ARCH)";
this->ObjectDirArch = this->ObjectDirArchDefault;
cm->GetState()->SetIsGeneratorMultiConfig(true);
}
cmGlobalGeneratorFactory* cmGlobalXCodeGenerator::NewFactory()
{
return new Factory;
}
cmGlobalGenerator* cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator(
const std::string& name, cmake* cm) const
{
if (name != GetActualName()) {
return nullptr;
}
#if defined(CMAKE_BUILD_WITH_CMAKE)
cmXcodeVersionParser parser;
std::string versionFile;
{
std::string out;
std::string::size_type pos;
if (cmSystemTools::RunSingleCommand("xcode-select --print-path", &out,
nullptr, nullptr, nullptr,
cmSystemTools::OUTPUT_NONE) &&
2017-05-30 19:37:46 +00:00
(pos = out.find(".app/"), pos != std::string::npos)) {
versionFile = out.substr(0, pos + 5) + "Contents/version.plist";
}
}
if (!versionFile.empty() && cmSystemTools::FileExists(versionFile.c_str())) {
parser.ParseFile(versionFile.c_str());
} else if (cmSystemTools::FileExists(
"/Applications/Xcode.app/Contents/version.plist")) {
parser.ParseFile("/Applications/Xcode.app/Contents/version.plist");
} else {
parser.ParseFile(
"/Developer/Applications/Xcode.app/Contents/version.plist");
}
2017-04-21 17:08:30 +00:00
std::string const& version_string = parser.Version;
// Compute an integer form of the version number.
unsigned int v[2] = { 0, 0 };
sscanf(version_string.c_str(), "%u.%u", &v[0], &v[1]);
unsigned int version_number = 10 * v[0] + v[1];
if (version_number < 30) {
cm->IssueMessage(cmake::FATAL_ERROR,
"Xcode " + version_string + " not supported.");
2017-08-22 21:42:36 +00:00
return nullptr;
}
auto gg = cm::make_unique<cmGlobalXCodeGenerator>(cm, version_string,
version_number);
return gg.release();
#else
std::cerr << "CMake should be built with cmake to use Xcode, "
"default to Xcode 1.5\n";
return new cmGlobalXCodeGenerator(cm);
#endif
2005-01-24 22:35:54 +00:00
}
bool cmGlobalXCodeGenerator::FindMakeProgram(cmMakefile* mf)
{
// The Xcode generator knows how to lookup its build tool
// directly instead of needing a helper module to do it, so we
// do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
if (cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
mf->AddDefinition("CMAKE_MAKE_PROGRAM",
this->GetXcodeBuildCommand().c_str());
}
return true;
}
std::string const& cmGlobalXCodeGenerator::GetXcodeBuildCommand()
{
if (!this->XcodeBuildCommandInitialized) {
this->XcodeBuildCommandInitialized = true;
this->XcodeBuildCommand = this->FindXcodeBuildCommand();
}
return this->XcodeBuildCommand;
}
std::string cmGlobalXCodeGenerator::FindXcodeBuildCommand()
{
if (this->XcodeVersion >= 40) {
std::string makeProgram = cmSystemTools::FindProgram("xcodebuild");
if (makeProgram.empty()) {
makeProgram = "xcodebuild";
}
return makeProgram;
}
// Use cmakexbuild wrapper to suppress environment dump from output.
return cmSystemTools::GetCMakeCommand() + "xbuild";
}
bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts,
cmMakefile* mf)
{
2017-05-30 19:37:46 +00:00
if (ts.find_first_of(",=") != std::string::npos) {
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"does not recognize the toolset\n"
" " << ts << "\n"
"that was specified.";
/* clang-format on */
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
this->GeneratorToolset = ts;
if (!this->GeneratorToolset.empty()) {
mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET",
this->GeneratorToolset.c_str());
}
return true;
}
void cmGlobalXCodeGenerator::EnableLanguage(
std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
{
mf->AddDefinition("XCODE", "1");
mf->AddDefinition("XCODE_VERSION", this->VersionString.c_str());
if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
mf->AddCacheDefinition(
"CMAKE_CONFIGURATION_TYPES", "Debug;Release;MinSizeRel;RelWithDebInfo",
"Semicolon separated list of supported configuration types, "
"only supports Debug, Release, MinSizeRel, and RelWithDebInfo, "
"anything else will be ignored.",
cmStateEnums::STRING);
}
2005-02-01 20:48:33 +00:00
mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
this->ComputeArchitectures(mf);
2005-01-24 22:35:54 +00:00
}
bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
const std::string& projectName, bool dryRun)
{
bool ret = false;
#ifdef HAVE_APPLICATION_SERVICES
std::string url = bindir + "/" + projectName + ".xcodeproj";
if (dryRun) {
return cmSystemTools::FileExists(url, false);
}
CFStringRef cfStr = CFStringCreateWithCString(
kCFAllocatorDefault, url.c_str(), kCFStringEncodingUTF8);
if (cfStr) {
CFURLRef cfUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfStr,
kCFURLPOSIXPathStyle, true);
if (cfUrl) {
OSStatus err = LSOpenCFURLRef(cfUrl, nullptr);
ret = err == noErr;
CFRelease(cfUrl);
}
CFRelease(cfStr);
}
#endif
return ret;
}
void cmGlobalXCodeGenerator::GenerateBuildCommand(
std::vector<std::string>& makeCommand, const std::string& makeProgram,
const std::string& projectName, const std::string& /*projectDir*/,
const std::string& targetName, const std::string& config, bool /*fast*/,
bool /*verbose*/, std::vector<std::string> const& makeOptions)
2005-01-24 22:35:54 +00:00
{
2005-02-01 18:07:42 +00:00
// now build the test
makeCommand.push_back(
this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand()));
2005-02-01 18:07:42 +00:00
makeCommand.push_back("-project");
std::string projectArg = projectName;
projectArg += ".xcode";
projectArg += "proj";
makeCommand.push_back(projectArg);
2005-09-02 20:29:32 +00:00
bool clean = false;
2014-02-06 22:31:47 +00:00
std::string realTarget = targetName;
if (realTarget == "clean") {
clean = true;
2014-02-06 22:31:47 +00:00
realTarget = "ALL_BUILD";
}
if (clean) {
makeCommand.push_back("clean");
} else {
makeCommand.push_back("build");
}
makeCommand.push_back("-target");
if (!realTarget.empty()) {
2014-02-06 22:31:47 +00:00
makeCommand.push_back(realTarget);
} else {
makeCommand.push_back("ALL_BUILD");
}
makeCommand.push_back("-configuration");
makeCommand.push_back(!config.empty() ? config : "Debug");
makeCommand.insert(makeCommand.end(), makeOptions.begin(),
makeOptions.end());
2005-01-24 22:35:54 +00:00
}
///! Create a local generator appropriate to this Global Generator
cmLocalGenerator* cmGlobalXCodeGenerator::CreateLocalGenerator(cmMakefile* mf)
2005-01-24 22:35:54 +00:00
{
return new cmLocalXCodeGenerator(this, mf);
2005-01-24 22:35:54 +00:00
}
void cmGlobalXCodeGenerator::AddExtraIDETargets()
{
2007-05-10 14:05:36 +00:00
// make sure extra targets are added before calling
// the parent generate which will call trace depends
for (auto keyVal : this->ProjectMap) {
cmLocalGenerator* root = keyVal.second[0];
this->SetGenerationRoot(root);
2005-02-18 18:32:51 +00:00
// add ALL_BUILD, INSTALL, etc
this->AddExtraTargets(root, keyVal.second);
}
}
void cmGlobalXCodeGenerator::Generate()
{
2007-05-10 14:05:36 +00:00
this->cmGlobalGenerator::Generate();
if (cmSystemTools::GetErrorOccuredFlag()) {
return;
}
for (auto keyVal : this->ProjectMap) {
cmLocalGenerator* root = keyVal.second[0];
bool generateTopLevelProjectOnly =
root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");
if (generateTopLevelProjectOnly) {
cmStateSnapshot snp = root->GetStateSnapshot();
if (snp.GetBuildsystemDirectoryParent().IsValid()) {
continue;
}
}
this->SetGenerationRoot(root);
2005-02-18 18:32:51 +00:00
// now create the project
this->OutputXCodeProject(root, keyVal.second);
}
2005-02-18 18:32:51 +00:00
}
void cmGlobalXCodeGenerator::SetGenerationRoot(cmLocalGenerator* root)
{
this->CurrentProject = root->GetProjectName();
this->SetCurrentLocalGenerator(root);
cmSystemTools::SplitPath(
this->CurrentLocalGenerator->GetCurrentSourceDirectory(),
this->ProjectSourceDirectoryComponents);
cmSystemTools::SplitPath(
this->CurrentLocalGenerator->GetCurrentBinaryDirectory(),
this->ProjectOutputDirectoryComponents);
this->CurrentXCodeHackMakefile = root->GetCurrentBinaryDirectory();
this->CurrentXCodeHackMakefile += "/CMakeScripts";
cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile.c_str());
this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make";
}
std::string cmGlobalXCodeGenerator::PostBuildMakeTarget(
std::string const& tName, std::string const& configName)
{
std::string target = tName;
std::replace(target.begin(), target.end(), ' ', '_');
std::string out = "PostBuild." + target;
out += "." + configName;
return out;
}
#define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK"
void cmGlobalXCodeGenerator::AddExtraTargets(
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens)
2005-02-18 18:32:51 +00:00
{
cmMakefile* mf = root->GetMakefile();
2005-02-18 18:32:51 +00:00
// Add ALL_BUILD
const char* no_working_directory = nullptr;
std::vector<std::string> no_depends;
cmTarget* allbuild = mf->AddUtilityCommand(
"ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_depends,
no_working_directory, "echo", "Build all projects");
cmGeneratorTarget* allBuildGt = new cmGeneratorTarget(allbuild, root);
root->AddGeneratorTarget(allBuildGt);
// Add XCODE depend helper
std::string dir = root->GetCurrentBinaryDirectory();
cmCustomCommandLine makeHelper;
makeHelper.push_back("make");
makeHelper.push_back("-C");
makeHelper.push_back(dir);
makeHelper.push_back("-f");
makeHelper.push_back(this->CurrentXCodeHackMakefile);
makeHelper.push_back(""); // placeholder, see below
// Add ZERO_CHECK
bool regenerate = !mf->IsOn("CMAKE_SUPPRESS_REGENERATION");
if (regenerate) {
this->CreateReRunCMakeFile(root, gens);
std::string file =
this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile.c_str());
cmSystemTools::ReplaceString(file, "\\ ", " ");
cmTarget* check = mf->AddUtilityCommand(
CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator,
true, no_depends, no_working_directory, "make", "-f", file.c_str());
cmGeneratorTarget* checkGt = new cmGeneratorTarget(check, root);
root->AddGeneratorTarget(checkGt);
}
2005-02-28 20:07:13 +00:00
2005-02-18 18:32:51 +00:00
// now make the allbuild depend on all the non-utility targets
// in the project
for (auto& gen : gens) {
if (this->IsExcluded(root, gen)) {
2005-02-18 18:32:51 +00:00
continue;
}
for (auto target : gen->GetGeneratorTargets()) {
if (target->GetType() == cmStateEnums::GLOBAL_TARGET) {
continue;
}
std::string targetName = target->GetName();
2015-10-18 15:06:14 +00:00
if (regenerate && (targetName != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET);
}
// make all exe, shared libs and modules
// run the depend check makefile as a post build rule
// this will make sure that when the next target is built
// things are up-to-date
if (target->GetType() == cmStateEnums::OBJECT_LIBRARY ||
(this->XcodeVersion < 50 &&
(target->GetType() == cmStateEnums::EXECUTABLE ||
target->GetType() == cmStateEnums::STATIC_LIBRARY ||
target->GetType() == cmStateEnums::SHARED_LIBRARY ||
target->GetType() == cmStateEnums::MODULE_LIBRARY))) {
makeHelper[makeHelper.size() - 1] = // fill placeholder
this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)");
cmCustomCommandLines commandLines;
commandLines.push_back(makeHelper);
Add an option for explicit BYPRODUCTS of custom commands (#14963) A common idiom in CMake-based build systems is to have custom commands that generate files not listed explicitly as outputs so that these files do not have to be newer than the inputs. The file modification times of such "byproducts" are updated only when their content changes. Then other build rules can depend on the byproducts explicitly so that their dependents rebuild when the content of the original byproducts really does change. This "undeclared byproduct" approach is necessary for Makefile, VS, and Xcode build tools because if a byproduct were listed as an output of a rule then the rule would always rerun when the input is newer than the byproduct but the byproduct may never be updated. Ninja solves this problem by offering a 'restat' feature to check whether an output was really modified after running a rule and tracking the fact that it is up to date separately from its timestamp. However, Ninja also stats all dependencies up front and will only restat files that are listed as outputs of rules with the 'restat' option enabled. Therefore an undeclared byproduct that does not exist at the start of the build will be considered missing and the build will fail even if other dependencies would cause the byproduct to be available before its dependents build. CMake works around this limitation by adding 'phony' build rules for custom command dependencies in the build tree that do not have any explicit specification of what produces them. This is not optimal because it prevents Ninja from reporting an error when an input to a rule really is missing. A better approach is to allow projects to explicitly specify the byproducts of their custom commands so that no phony rules are needed for them. In order to work with the non-Ninja generators, the byproducts must be known separately from the outputs. Add a new "BYPRODUCTS" option to the add_custom_command and add_custom_target commands to specify byproducts explicitly. Teach the Ninja generator to specify byproducts as outputs of the custom commands. In the case of POST_BUILD, PRE_LINK, and PRE_BUILD events on targets that link, the byproducts must be specified as outputs of the link rule that runs the commands. Activate 'restat' for such rules so that Ninja knows it needs to check the byproducts, but not for link rules that have no byproducts.
2014-11-13 23:54:52 +00:00
std::vector<std::string> no_byproducts;
gen->GetMakefile()->AddCustomCommandToTarget(
target->GetName(), no_byproducts, no_depends, commandLines,
cmTarget::POST_BUILD, "Depend check for xcode", dir.c_str(), true,
false, "", false, cmMakefile::AcceptObjectLibraryCommands);
}
if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
!target->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
allbuild->AddUtility(target->GetName());
}
2005-01-24 22:35:54 +00:00
}
}
2005-01-24 22:35:54 +00:00
}
void cmGlobalXCodeGenerator::CreateReRunCMakeFile(
cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens)
2005-02-28 20:07:13 +00:00
{
std::vector<std::string> lfiles;
for (auto gen : gens) {
std::vector<std::string> const& lf = gen->GetMakefile()->GetListFiles();
lfiles.insert(lfiles.end(), lf.begin(), lf.end());
}
2005-02-28 20:07:13 +00:00
// sort the array
std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>());
std::vector<std::string>::iterator new_end =
2005-02-28 20:07:13 +00:00
std::unique(lfiles.begin(), lfiles.end());
lfiles.erase(new_end, lfiles.end());
this->CurrentReRunCMakeMakefile = root->GetCurrentBinaryDirectory();
2006-03-15 16:02:08 +00:00
this->CurrentReRunCMakeMakefile += "/CMakeScripts";
cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile.c_str());
this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
cmGeneratedFileStream makefileStream(
this->CurrentReRunCMakeMakefile.c_str());
makefileStream.SetCopyIfDifferent(true);
makefileStream << "# Generated by CMake, DO NOT EDIT\n\n";
makefileStream << "empty:= \n";
makefileStream << "space:= $(empty) $(empty)\n";
makefileStream << "spaceplus:= $(empty)\\ $(empty)\n\n";
for (const auto& lfile : lfiles) {
makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard "
<< this->ConvertToRelativeForMake(lfile.c_str()) << "))\n";
}
std::string checkCache = root->GetBinaryDirectory();
checkCache += "/";
checkCache += cmake::GetCMakeFilesDirectoryPostSlash();
checkCache += "cmake.check_cache";
makefileStream << "\n"
<< this->ConvertToRelativeForMake(checkCache.c_str())
<< ": $(TARGETS)\n";
makefileStream << "\t"
<< this->ConvertToRelativeForMake(
cmSystemTools::GetCMakeCommand().c_str())
<< " -H"
<< this->ConvertToRelativeForMake(root->GetSourceDirectory())
<< " -B"
<< this->ConvertToRelativeForMake(root->GetBinaryDirectory())
<< "\n";
2005-02-28 20:07:13 +00:00
}
static bool objectIdLessThan(cmXCodeObject* l, cmXCodeObject* r)
{
return l->GetId() < r->GetId();
}
void cmGlobalXCodeGenerator::SortXCodeObjects()
{
std::sort(this->XCodeObjects.begin(), this->XCodeObjects.end(),
objectIdLessThan);
}
2005-01-24 22:35:54 +00:00
void cmGlobalXCodeGenerator::ClearXCodeObjects()
{
2006-03-15 16:02:08 +00:00
this->TargetDoneSet.clear();
for (auto& obj : this->XCodeObjects) {
delete obj;
}
2006-03-15 16:02:08 +00:00
this->XCodeObjects.clear();
this->XCodeObjectIDs.clear();
this->XCodeObjectMap.clear();
2006-03-15 16:02:08 +00:00
this->GroupMap.clear();
this->GroupNameMap.clear();
this->TargetGroup.clear();
this->FileRefs.clear();
2005-01-24 22:35:54 +00:00
}
void cmGlobalXCodeGenerator::addObject(cmXCodeObject* obj)
{
if (obj->GetType() == cmXCodeObject::OBJECT) {
const std::string& id = obj->GetId();
// If this is a duplicate id, it's an error:
//
if (this->XCodeObjectIDs.count(id)) {
cmSystemTools::Error(
"Xcode generator: duplicate object ids not allowed");
}
this->XCodeObjectIDs.insert(id);
}
this->XCodeObjects.push_back(obj);
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(
cmXCodeObject::PBXType ptype)
2005-01-24 22:35:54 +00:00
{
cmXCodeObject* obj = new cmXCode21Object(ptype, cmXCodeObject::OBJECT);
this->addObject(obj);
2005-01-24 22:35:54 +00:00
return obj;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type)
{
2005-01-27 21:11:44 +00:00
cmXCodeObject* obj = new cmXCodeObject(cmXCodeObject::None, type);
this->addObject(obj);
2005-01-27 21:11:44 +00:00
return obj;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateString(const std::string& s)
2005-01-27 21:11:44 +00:00
{
cmXCodeObject* obj = this->CreateObject(cmXCodeObject::STRING);
obj->SetString(s);
return obj;
}
2005-02-18 18:32:51 +00:00
cmXCodeObject* cmGlobalXCodeGenerator::CreateObjectReference(
cmXCodeObject* ref)
2005-01-28 21:00:10 +00:00
{
cmXCodeObject* obj = this->CreateObject(cmXCodeObject::OBJECT_REF);
obj->SetObject(ref);
return obj;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateFlatClone(cmXCodeObject* orig)
{
cmXCodeObject* obj = this->CreateObject(orig->GetType());
obj->CopyAttributes(orig);
return obj;
}
std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target,
const std::string& fullpath)
{
2015-10-19 19:23:29 +00:00
std::string key(target->GetName());
key += "-";
key += fullpath;
return key;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath(
const std::string& fullpath, cmGeneratorTarget* target,
const std::string& lang, cmSourceFile* sf)
{
// Using a map and the full path guarantees that we will always get the same
// fileRef object for any given full path.
//
cmXCodeObject* fileRef =
2015-10-19 19:23:29 +00:00
this->CreateXCodeFileReferenceFromPath(fullpath, target, lang, sf);
cmXCodeObject* buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
buildFile->SetComment(fileRef->GetComment());
buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef));
return buildFile;
}
class XCodeGeneratorExpressionInterpreter
: public cmGeneratorExpressionInterpreter
{
CM_DISABLE_COPY(XCodeGeneratorExpressionInterpreter)
public:
XCodeGeneratorExpressionInterpreter(cmSourceFile* sourceFile,
cmLocalGenerator* localGenerator,
cmGeneratorTarget* generatorTarget)
: cmGeneratorExpressionInterpreter(localGenerator, generatorTarget,
"NO-PER-CONFIG-SUPPORT-IN-XCODE")
, SourceFile(sourceFile)
{
}
using cmGeneratorExpressionInterpreter::Evaluate;
const char* Evaluate(const char* expression, const char* property)
{
const char* processed = this->Evaluate(expression);
if (this->GetCompiledGeneratorExpression()
.GetHadContextSensitiveCondition()) {
std::ostringstream e;
/* clang-format off */
e <<
"Xcode does not support per-config per-source " << property << ":\n"
" " << expression << "\n"
"specified for source:\n"
" " << this->SourceFile->GetFullPath() << "\n";
/* clang-format on */
this->GetLocalGenerator()->IssueMessage(cmake::FATAL_ERROR, e.str());
}
return processed;
}
private:
cmSourceFile* SourceFile = nullptr;
};
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
cmLocalGenerator* lg, cmSourceFile* sf, cmGeneratorTarget* gtgt)
2005-01-27 21:11:44 +00:00
{
XCodeGeneratorExpressionInterpreter genexInterpreter(sf, lg, gtgt);
// Add flags from target and source file properties.
2005-02-11 19:25:05 +00:00
std::string flags;
const char* srcfmt = sf->GetProperty("Fortran_FORMAT");
switch (cmOutputConverter::GetFortranFormat(srcfmt)) {
case cmOutputConverter::FortranFormatFixed:
flags = "-fixed " + flags;
break;
case cmOutputConverter::FortranFormatFree:
flags = "-free " + flags;
break;
default:
break;
}
if (const char* cflags = sf->GetProperty("COMPILE_FLAGS")) {
lg->AppendFlags(flags, genexInterpreter.Evaluate(cflags, "COMPILE_FLAGS"));
}
// Add per-source definitions.
BuildObjectListOrString flagsBuild(this, false);
if (const char* compile_defs = sf->GetProperty("COMPILE_DEFINITIONS")) {
this->AppendDefines(
flagsBuild,
genexInterpreter.Evaluate(compile_defs, "COMPILE_DEFINITIONS"), true);
}
if (!flagsBuild.IsEmpty()) {
if (!flags.empty()) {
flags += ' ';
}
flags += flagsBuild.GetString();
}
std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
cmXCodeObject* buildFile =
2015-10-19 19:23:29 +00:00
this->CreateXCodeSourceFileFromPath(sf->GetFullPath(), gtgt, lang, sf);
cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
settings->AddAttributeIfNotEmpty("COMPILER_FLAGS",
this->CreateString(flags));
cmGeneratorTarget::SourceFileFlags tsFlags =
gtgt->GetTargetSourceFileFlags(sf);
cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
// Is this a "private" or "public" framework header file?
// Set the ATTRIBUTES attribute appropriately...
//
if (gtgt->IsFrameworkOnApple()) {
if (tsFlags.Type == cmGeneratorTarget::SourceFileTypePrivateHeader) {
attrs->AddObject(this->CreateString("Private"));
} else if (tsFlags.Type == cmGeneratorTarget::SourceFileTypePublicHeader) {
attrs->AddObject(this->CreateString("Public"));
}
}
// Add user-specified file attributes.
const char* extraFileAttributes = sf->GetProperty("XCODE_FILE_ATTRIBUTES");
if (extraFileAttributes) {
// Expand the list of attributes.
std::vector<std::string> attributes;
cmSystemTools::ExpandListArgument(extraFileAttributes, attributes);
// Store the attributes.
for (const auto& attribute : attributes) {
attrs->AddObject(this->CreateString(attribute));
}
}
settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs);
buildFile->AddAttributeIfNotEmpty("settings", settings);
return buildFile;
}
std::string GetSourcecodeValueFromFileExtension(const std::string& _ext,
const std::string& lang,
bool& keepLastKnownFileType)
{
std::string ext = cmSystemTools::LowerCase(_ext);
2005-02-01 20:48:33 +00:00
std::string sourcecode = "sourcecode";
if (ext == "o") {
2005-02-18 18:32:51 +00:00
sourcecode = "compiled.mach-o.objfile";
} else if (ext == "xctest") {
sourcecode = "wrapper.cfbundle";
} else if (ext == "xib") {
keepLastKnownFileType = true;
sourcecode = "file.xib";
} else if (ext == "storyboard") {
keepLastKnownFileType = true;
sourcecode = "file.storyboard";
} else if (ext == "mm") {
sourcecode += ".cpp.objcpp";
} else if (ext == "m") {
sourcecode += ".c.objc";
} else if (ext == "swift") {
sourcecode += ".swift";
} else if (ext == "plist") {
2006-01-05 14:13:06 +00:00
sourcecode += ".text.plist";
} else if (ext == "h") {
sourcecode += ".c.h";
} else if (ext == "hxx" || ext == "hpp" || ext == "txx" || ext == "pch" ||
ext == "hh") {
sourcecode += ".cpp.h";
} else if (ext == "png" || ext == "gif" || ext == "jpg") {
keepLastKnownFileType = true;
sourcecode = "image";
} else if (ext == "txt") {
sourcecode += ".text";
} else if (lang == "CXX") {
sourcecode += ".cpp.cpp";
} else if (lang == "C") {
sourcecode += ".c.c";
} else if (lang == "Fortran") {
sourcecode += ".fortran.f90";
} else if (lang == "ASM") {
sourcecode += ".asm";
} else if (ext == "metal") {
sourcecode += ".metal";
2016-06-04 01:27:39 +00:00
} else if (ext == "mig") {
sourcecode += ".mig";
}
// else
// {
// // Already specialized above or we leave sourcecode == "sourcecode"
// // which is probably the most correct choice. Extensionless headers,
// // for example... Or file types unknown to Xcode that do not map to a
// // valid explicitFileType value.
// }
return sourcecode;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
const std::string& fullpath, cmGeneratorTarget* target,
const std::string& lang, cmSourceFile* sf)
{
2015-10-19 19:23:29 +00:00
std::string key = GetGroupMapKeyFromPath(target, fullpath);
cmXCodeObject* fileRef = this->FileRefs[key];
if (!fileRef) {
fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
fileRef->SetComment(fullpath);
this->FileRefs[key] = fileRef;
}
cmXCodeObject* group = this->GroupMap[key];
cmXCodeObject* children = group->GetObject("children");
if (!children->HasObject(fileRef)) {
children->AddObject(fileRef);
}
fileRef->AddAttribute("fileEncoding", this->CreateString("4"));
bool useLastKnownFileType = false;
std::string fileType;
if (sf) {
if (const char* e = sf->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
fileType = e;
} else if (const char* l = sf->GetProperty("XCODE_LAST_KNOWN_FILE_TYPE")) {
useLastKnownFileType = true;
fileType = l;
}
}
if (fileType.empty()) {
// Compute the extension without leading '.'.
std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath);
if (!ext.empty()) {
ext = ext.substr(1);
}
// If fullpath references a directory, then we need to specify
// lastKnownFileType as folder in order for Xcode to be able to
// open the contents of the folder.
// (Xcode 4.6 does not like explicitFileType=folder).
if (cmSystemTools::FileIsDirectory(fullpath)) {
fileType = (ext == "xcassets" ? "folder.assetcatalog" : "folder");
useLastKnownFileType = true;
} else {
fileType =
GetSourcecodeValueFromFileExtension(ext, lang, useLastKnownFileType);
}
}
fileRef->AddAttribute(useLastKnownFileType ? "lastKnownFileType"
: "explicitFileType",
this->CreateString(fileType));
// Store the file path relative to the top of the source tree.
std::string path = this->RelativeToSource(fullpath.c_str());
std::string name = cmSystemTools::GetFilenameName(path);
const char* sourceTree =
(cmSystemTools::FileIsFullPath(path.c_str()) ? "<absolute>"
: "SOURCE_ROOT");
fileRef->AddAttribute("name", this->CreateString(name));
fileRef->AddAttribute("path", this->CreateString(path));
fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree));
return fileRef;
2005-01-27 21:11:44 +00:00
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReference(
cmSourceFile* sf, cmGeneratorTarget* target)
{
std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
return this->CreateXCodeFileReferenceFromPath(sf->GetFullPath(), target,
lang, sf);
}
2005-02-18 18:32:51 +00:00
bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname)
{
if (tname == "ALL_BUILD" || tname == "XCODE_DEPEND_HELPER" ||
tname == "install" || tname == "package" || tname == "RUN_TESTS" ||
tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
if (this->TargetDoneSet.find(tname) != this->TargetDoneSet.end()) {
2005-02-18 18:32:51 +00:00
return true;
}
2006-03-15 16:02:08 +00:00
this->TargetDoneSet.insert(tname);
2005-02-18 18:32:51 +00:00
return false;
}
2005-02-18 18:32:51 +00:00
return false;
}
2005-02-24 22:46:49 +00:00
void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen)
{
2006-03-15 16:02:08 +00:00
this->CurrentLocalGenerator = gen;
this->CurrentMakefile = gen->GetMakefile();
// Select the current set of configuration types.
2006-03-15 16:02:08 +00:00
this->CurrentConfigurationTypes.clear();
this->CurrentMakefile->GetConfigurations(this->CurrentConfigurationTypes);
if (this->CurrentConfigurationTypes.empty()) {
this->CurrentConfigurationTypes.push_back("");
}
2005-02-24 22:46:49 +00:00
}
struct cmSourceFilePathCompare
{
bool operator()(cmSourceFile* l, cmSourceFile* r)
{
return l->GetFullPath() < r->GetFullPath();
}
};
struct cmCompareTargets
{
bool operator()(std::string const& a, std::string const& b) const
{
if (a == "ALL_BUILD") {
return true;
}
if (b == "ALL_BUILD") {
return false;
}
return strcmp(a.c_str(), b.c_str()) < 0;
}
};
bool cmGlobalXCodeGenerator::CreateXCodeTargets(
cmLocalGenerator* gen, std::vector<cmXCodeObject*>& targets)
2005-01-27 21:11:44 +00:00
{
2005-02-24 22:46:49 +00:00
this->SetCurrentLocalGenerator(gen);
2015-10-18 15:06:14 +00:00
typedef std::map<std::string, cmGeneratorTarget*, cmCompareTargets>
cmSortedTargets;
cmSortedTargets sortedTargets;
for (auto tgt : this->CurrentLocalGenerator->GetGeneratorTargets()) {
sortedTargets[tgt->GetName()] = tgt;
}
for (auto& sortedTarget : sortedTargets) {
cmGeneratorTarget* gtgt = sortedTarget.second;
2015-10-18 15:06:14 +00:00
std::string targetName = gtgt->GetName();
2005-02-18 18:32:51 +00:00
// make sure ALL_BUILD, INSTALL, etc are only done once
if (this->SpecialTargetEmitted(targetName)) {
2005-02-18 18:32:51 +00:00
continue;
}
if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
continue;
}
if (gtgt->GetType() == cmStateEnums::UTILITY ||
gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) {
2015-10-19 19:23:29 +00:00
cmXCodeObject* t = this->CreateUtilityTarget(gtgt);
if (!t) {
return false;
}
targets.push_back(t);
2005-02-03 22:42:55 +00:00
continue;
}
2005-02-03 22:42:55 +00:00
// organize the sources
std::vector<cmSourceFile*> classes;
if (!gtgt->GetConfigCommonSourceFiles(classes)) {
return false;
}
// Add CMakeLists.txt file for user convenience.
std::string listfile =
gtgt->GetLocalGenerator()->GetCurrentSourceDirectory();
listfile += "/CMakeLists.txt";
classes.push_back(gtgt->Makefile->GetOrCreateSource(listfile));
std::sort(classes.begin(), classes.end(), cmSourceFilePathCompare());
gtgt->ComputeObjectMapping();
2005-02-18 18:32:51 +00:00
std::vector<cmXCodeObject*> externalObjFiles;
std::vector<cmXCodeObject*> headerFiles;
std::vector<cmXCodeObject*> resourceFiles;
std::vector<cmXCodeObject*> sourceFiles;
for (auto sourceFile : classes) {
cmXCodeObject* xsf = this->CreateXCodeSourceFile(
this->CurrentLocalGenerator, sourceFile, gtgt);
2005-02-18 18:32:51 +00:00
cmXCodeObject* fr = xsf->GetObject("fileRef");
cmXCodeObject* filetype = fr->GetObject()->GetObject("explicitFileType");
cmGeneratorTarget::SourceFileFlags tsFlags =
gtgt->GetTargetSourceFileFlags(sourceFile);
if (filetype && filetype->GetString() == "compiled.mach-o.objfile") {
if (sourceFile->GetObjectLibrary().empty()) {
externalObjFiles.push_back(xsf);
2005-02-18 18:32:51 +00:00
}
} else if (this->IsHeaderFile(sourceFile) ||
(tsFlags.Type ==
cmGeneratorTarget::SourceFileTypePrivateHeader) ||
(tsFlags.Type ==
cmGeneratorTarget::SourceFileTypePublicHeader)) {
headerFiles.push_back(xsf);
} else if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeResource) {
resourceFiles.push_back(xsf);
} else if (!sourceFile->GetPropertyAsBool("HEADER_FILE_ONLY")) {
// Include this file in the build if it has a known language
// and has not been listed as an ignored extension for this
// generator.
if (!this->CurrentLocalGenerator->GetSourceFileLanguage(*sourceFile)
.empty() &&
!this->IgnoreFile(sourceFile->GetExtension().c_str())) {
sourceFiles.push_back(xsf);
2005-02-18 18:32:51 +00:00
}
2005-01-27 21:11:44 +00:00
}
}
if (this->XcodeVersion < 50) {
// Add object library contents as external objects. (Equivalent to
// the externalObjFiles above, except each one is not a cmSourceFile
// within the target.)
std::vector<cmSourceFile const*> objs;
gtgt->GetExternalObjects(objs, "");
for (auto sourceFile : objs) {
if (sourceFile->GetObjectLibrary().empty()) {
continue;
}
std::string const& obj = sourceFile->GetFullPath();
cmXCodeObject* xsf =
this->CreateXCodeSourceFileFromPath(obj, gtgt, "", nullptr);
externalObjFiles.push_back(xsf);
}
}
// some build phases only apply to bundles and/or frameworks
bool isFrameworkTarget = gtgt->IsFrameworkOnApple();
bool isBundleTarget = gtgt->GetPropertyAsBool("MACOSX_BUNDLE");
bool isCFBundleTarget = gtgt->IsCFBundleOnApple();
cmXCodeObject* buildFiles = nullptr;
// create source build phase
cmXCodeObject* sourceBuildPhase = nullptr;
if (!sourceFiles.empty()) {
sourceBuildPhase =
this->CreateObject(cmXCodeObject::PBXSourcesBuildPhase);
sourceBuildPhase->SetComment("Sources");
sourceBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
for (auto& sourceFile : sourceFiles) {
buildFiles->AddObject(sourceFile);
}
sourceBuildPhase->AddAttribute("files", buildFiles);
sourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
}
// create header build phase - only for framework targets
cmXCodeObject* headerBuildPhase = nullptr;
if (!headerFiles.empty() && isFrameworkTarget) {
headerBuildPhase =
this->CreateObject(cmXCodeObject::PBXHeadersBuildPhase);
headerBuildPhase->SetComment("Headers");
headerBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
for (auto& headerFile : headerFiles) {
buildFiles->AddObject(headerFile);
}
headerBuildPhase->AddAttribute("files", buildFiles);
headerBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
}
// create resource build phase - only for framework or bundle targets
cmXCodeObject* resourceBuildPhase = nullptr;
if (!resourceFiles.empty() &&
(isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
resourceBuildPhase =
this->CreateObject(cmXCodeObject::PBXResourcesBuildPhase);
resourceBuildPhase->SetComment("Resources");
resourceBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
for (auto& resourceFile : resourceFiles) {
buildFiles->AddObject(resourceFile);
}
resourceBuildPhase->AddAttribute("files", buildFiles);
resourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
}
// create vector of "non-resource content file" build phases - only for
// framework or bundle targets
std::vector<cmXCodeObject*> contentBuildPhases;
if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
2017-08-25 21:25:09 +00:00
typedef std::map<std::string, std::vector<cmSourceFile*>>
mapOfVectorOfSourceFiles;
mapOfVectorOfSourceFiles bundleFiles;
for (auto sourceFile : classes) {
cmGeneratorTarget::SourceFileFlags tsFlags =
gtgt->GetTargetSourceFileFlags(sourceFile);
if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeMacContent) {
bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
}
}
for (auto keySources : bundleFiles) {
cmXCodeObject* copyFilesBuildPhase =
this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
copyFilesBuildPhase->SetComment("Copy files");
copyFilesBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
this->CreateString("6"));
std::ostringstream ostr;
if (gtgt->IsFrameworkOnApple()) {
// dstPath in frameworks is relative to Versions/<version>
ostr << keySources.first;
} else if (keySources.first != "MacOS") {
if (gtgt->Target->GetMakefile()->PlatformIsAppleIos()) {
ostr << keySources.first;
} else {
// dstPath in bundles is relative to Contents/MacOS
ostr << "../" << keySources.first;
}
}
copyFilesBuildPhase->AddAttribute("dstPath",
this->CreateString(ostr.str()));
copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
copyFilesBuildPhase->AddAttribute("files", buildFiles);
for (auto sourceFile : keySources.second) {
cmXCodeObject* xsf = this->CreateXCodeSourceFile(
this->CurrentLocalGenerator, sourceFile, gtgt);
buildFiles->AddObject(xsf);
}
contentBuildPhases.push_back(copyFilesBuildPhase);
}
}
// create vector of "resource content file" build phases - only for
// framework or bundle targets
if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
2017-08-25 21:25:09 +00:00
typedef std::map<std::string, std::vector<cmSourceFile*>>
mapOfVectorOfSourceFiles;
mapOfVectorOfSourceFiles bundleFiles;
for (auto sourceFile : classes) {
cmGeneratorTarget::SourceFileFlags tsFlags =
gtgt->GetTargetSourceFileFlags(sourceFile);
if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeDeepResource) {
bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
}
}
for (auto keySources : bundleFiles) {
cmXCodeObject* copyFilesBuildPhase =
this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
copyFilesBuildPhase->SetComment("Copy files");
copyFilesBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
this->CreateString("7"));
copyFilesBuildPhase->AddAttribute(
"dstPath", this->CreateString(keySources.first));
copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
copyFilesBuildPhase->AddAttribute("files", buildFiles);
for (auto sourceFile : keySources.second) {
cmXCodeObject* xsf = this->CreateXCodeSourceFile(
this->CurrentLocalGenerator, sourceFile, gtgt);
buildFiles->AddObject(xsf);
}
contentBuildPhases.push_back(copyFilesBuildPhase);
}
}
// create framework build phase
cmXCodeObject* frameworkBuildPhase = nullptr;
if (!externalObjFiles.empty()) {
frameworkBuildPhase =
this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase);
frameworkBuildPhase->SetComment("Frameworks");
frameworkBuildPhase->AddAttribute("buildActionMask",
this->CreateString("2147483647"));
2006-03-29 20:02:35 +00:00
buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
frameworkBuildPhase->AddAttribute("files", buildFiles);
for (auto& externalObjFile : externalObjFiles) {
buildFiles->AddObject(externalObjFile);
}
frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
this->CreateString("0"));
}
2007-05-24 21:06:32 +00:00
// create list of build phases and create the Xcode target
cmXCodeObject* buildPhases =
this->CreateObject(cmXCodeObject::OBJECT_LIST);
this->CreateCustomCommands(buildPhases, sourceBuildPhase, headerBuildPhase,
resourceBuildPhase, contentBuildPhases,
2015-10-19 19:23:29 +00:00
frameworkBuildPhase, gtgt);
2015-10-19 19:23:29 +00:00
targets.push_back(this->CreateXCodeTarget(gtgt, buildPhases));
}
return true;
}
void cmGlobalXCodeGenerator::ForceLinkerLanguages()
{
for (auto localGenerator : this->LocalGenerators) {
// All targets depend on the build-system check target.
for (auto tgt : localGenerator->GetGeneratorTargets()) {
// This makes sure all targets link using the proper language.
this->ForceLinkerLanguage(tgt);
}
}
}
void cmGlobalXCodeGenerator::ForceLinkerLanguage(cmGeneratorTarget* gtgt)
{
// This matters only for targets that link.
if (gtgt->GetType() != cmStateEnums::EXECUTABLE &&
gtgt->GetType() != cmStateEnums::SHARED_LIBRARY &&
gtgt->GetType() != cmStateEnums::MODULE_LIBRARY) {
return;
}
std::string llang = gtgt->GetLinkerLanguage("NOCONFIG");
if (llang.empty()) {
return;
}
// If the language is compiled as a source trust Xcode to link with it.
for (auto const& Language :
gtgt->GetLinkImplementation("NOCONFIG")->Languages) {
if (Language == llang) {
return;
}
}
// Add an empty source file to the target that compiles with the
// linker language. This should convince Xcode to choose the proper
// language.
cmMakefile* mf = gtgt->Target->GetMakefile();
std::string fname = gtgt->GetLocalGenerator()->GetCurrentBinaryDirectory();
fname += cmake::GetCMakeFilesDirectory();
fname += "/";
fname += gtgt->GetName();
fname += "-CMakeForceLinker";
fname += ".";
fname += cmSystemTools::LowerCase(llang);
{
cmGeneratedFileStream fout(fname.c_str());
fout << "\n";
}
if (cmSourceFile* sf = mf->GetOrCreateSource(fname)) {
sf->SetProperty("LANGUAGE", llang.c_str());
gtgt->AddSource(fname);
}
}
bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf)
{
const std::vector<std::string>& hdrExts =
this->CMakeInstance->GetHeaderExtensions();
return (std::find(hdrExts.begin(), hdrExts.end(), sf->GetExtension()) !=
hdrExts.end());
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateBuildPhase(
const char* name, const char* name2, cmGeneratorTarget* target,
const std::vector<cmCustomCommand>& commands)
2005-02-28 20:07:13 +00:00
{
if (commands.empty() && strcmp(name, "CMake ReRun") != 0) {
return nullptr;
}
cmXCodeObject* buildPhase =
2005-02-28 20:07:13 +00:00
this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
buildPhase->AddAttribute("buildActionMask",
2005-11-18 19:12:09 +00:00
this->CreateString("2147483647"));
2005-02-28 20:07:13 +00:00
cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
buildPhase->AddAttribute("files", buildFiles);
buildPhase->AddAttribute("name", this->CreateString(name));
buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
2005-02-28 20:07:13 +00:00
this->CreateString("0"));
buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
this->AddCommandsToBuildPhase(buildPhase, target, commands, name2);
2005-02-28 20:07:13 +00:00
return buildPhase;
}
void cmGlobalXCodeGenerator::CreateCustomCommands(
cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase,
cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
std::vector<cmXCodeObject*> contentBuildPhases,
cmXCodeObject* frameworkBuildPhase, cmGeneratorTarget* gtgt)
{
std::vector<cmCustomCommand> const& prebuild = gtgt->GetPreBuildCommands();
std::vector<cmCustomCommand> const& prelink = gtgt->GetPreLinkCommands();
std::vector<cmCustomCommand> postbuild = gtgt->GetPostBuildCommands();
if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
!gtgt->IsFrameworkOnApple()) {
cmCustomCommandLines cmd;
cmd.resize(1);
cmd[0].push_back(cmSystemTools::GetCMakeCommand());
cmd[0].push_back("-E");
cmd[0].push_back("cmake_symlink_library");
std::string str_file = "$<TARGET_FILE:";
str_file += gtgt->GetName();
str_file += ">";
std::string str_so_file = "$<TARGET_SONAME_FILE:";
str_so_file += gtgt->GetName();
str_so_file += ">";
std::string str_link_file = "$<TARGET_LINKER_FILE:";
str_link_file += gtgt->GetName();
str_link_file += ">";
cmd[0].push_back(str_file);
cmd[0].push_back(str_so_file);
cmd[0].push_back(str_link_file);
cmCustomCommand command(this->CurrentMakefile, std::vector<std::string>(),
std::vector<std::string>(),
std::vector<std::string>(), cmd,
"Creating symlinks", "");
postbuild.push_back(command);
}
std::vector<cmSourceFile*> classes;
if (!gtgt->GetConfigCommonSourceFiles(classes)) {
return;
}
// add all the sources
std::vector<cmCustomCommand> commands;
for (auto sourceFile : classes) {
if (sourceFile->GetCustomCommand()) {
commands.push_back(*sourceFile->GetCustomCommand());
}
}
// create prebuild phase
cmXCodeObject* cmakeRulesBuildPhase = this->CreateBuildPhase(
"CMake Rules", "cmakeRulesBuildPhase", gtgt, commands);
// create prebuild phase
cmXCodeObject* preBuildPhase = this->CreateBuildPhase(
"CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild);
// create prelink phase
cmXCodeObject* preLinkPhase = this->CreateBuildPhase(
"CMake PreLink Rules", "preLinkCommands", gtgt, prelink);
// create postbuild phase
cmXCodeObject* postBuildPhase = this->CreateBuildPhase(
"CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild);
// The order here is the order they will be built in.
// The order "headers, resources, sources" mimics a native project generated
// from an xcode template...
//
if (preBuildPhase) {
buildPhases->AddObject(preBuildPhase);
}
if (cmakeRulesBuildPhase) {
buildPhases->AddObject(cmakeRulesBuildPhase);
}
if (headerBuildPhase) {
2005-01-27 21:11:44 +00:00
buildPhases->AddObject(headerBuildPhase);
}
if (resourceBuildPhase) {
buildPhases->AddObject(resourceBuildPhase);
}
for (auto obj : contentBuildPhases) {
buildPhases->AddObject(obj);
}
if (sourceBuildPhase) {
buildPhases->AddObject(sourceBuildPhase);
}
if (preLinkPhase) {
buildPhases->AddObject(preLinkPhase);
}
if (frameworkBuildPhase) {
2005-01-27 21:11:44 +00:00
buildPhases->AddObject(frameworkBuildPhase);
}
if (postBuildPhase) {
buildPhases->AddObject(postBuildPhase);
}
}
// This function removes each occurrence of the flag and returns the last one
// (i.e., the dominant flag in GCC)
2005-09-02 20:29:32 +00:00
std::string cmGlobalXCodeGenerator::ExtractFlag(const char* flag,
std::string& flags)
{
std::string retFlag;
std::string::size_type lastOccurancePos = flags.rfind(flag);
bool saved = false;
2017-05-30 19:37:46 +00:00
while (lastOccurancePos != std::string::npos) {
// increment pos, we use lastOccurancePos to reduce search space on next
// inc
std::string::size_type pos = lastOccurancePos;
if (pos == 0 || flags[pos - 1] == ' ') {
while (pos < flags.size() && flags[pos] != ' ') {
if (!saved) {
retFlag += flags[pos];
}
flags[pos] = ' ';
pos++;
}
saved = true;
2005-09-02 20:29:32 +00:00
}
// decrement lastOccurancePos while making sure we don't loop around
// and become a very large positive number since size_type is unsigned
lastOccurancePos = lastOccurancePos == 0 ? 0 : lastOccurancePos - 1;
lastOccurancePos = flags.rfind(flag, lastOccurancePos);
}
2005-09-02 20:29:32 +00:00
return retFlag;
}
// This function removes each matching occurrence of the expression and
// returns the last one (i.e., the dominant flag in GCC)
std::string cmGlobalXCodeGenerator::ExtractFlagRegex(const char* exp,
int matchIndex,
std::string& flags)
{
std::string retFlag;
cmsys::RegularExpression regex(exp);
assert(regex.is_valid());
if (!regex.is_valid()) {
return retFlag;
}
std::string::size_type offset = 0;
while (regex.find(flags.c_str() + offset)) {
const std::string::size_type startPos = offset + regex.start(matchIndex);
const std::string::size_type endPos = offset + regex.end(matchIndex);
const std::string::size_type size = endPos - startPos;
offset = startPos + 1;
retFlag.assign(flags, startPos, size);
flags.replace(startPos, size, size, ' ');
}
return retFlag;
}
//----------------------------------------------------------------------------
// This function strips off Xcode attributes that do not target the current
// configuration
void cmGlobalXCodeGenerator::FilterConfigurationAttribute(
std::string const& configName, std::string& attribute)
{
// Handle [variant=<config>] condition explicitly here.
std::string::size_type beginVariant = attribute.find("[variant=");
if (beginVariant == std::string::npos) {
// There is no variant in this attribute.
return;
}
std::string::size_type endVariant = attribute.find(']', beginVariant + 9);
if (endVariant == std::string::npos) {
// There is no terminating bracket.
return;
}
// Compare the variant to the configuration.
std::string variant =
attribute.substr(beginVariant + 9, endVariant - beginVariant - 9);
if (variant == configName) {
// The variant matches the configuration so use this
// attribute but drop the [variant=<config>] condition.
attribute.erase(beginVariant, endVariant - beginVariant + 1);
} else {
// The variant does not match the configuration so
// do not use this attribute.
attribute.clear();
}
}
void cmGlobalXCodeGenerator::AddCommandsToBuildPhase(
cmXCodeObject* buildphase, cmGeneratorTarget* target,
std::vector<cmCustomCommand> const& commands, const char* name)
{
std::string dir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
2005-02-18 19:32:55 +00:00
dir += "/CMakeScripts";
cmSystemTools::MakeDirectory(dir.c_str());
std::string makefile = dir;
makefile += "/";
2015-10-19 19:23:29 +00:00
makefile += target->GetName();
makefile += "_";
makefile += name;
makefile += ".make";
for (const auto& currentConfig : this->CurrentConfigurationTypes) {
this->CreateCustomRulesMakefile(makefile.c_str(), target, commands,
currentConfig);
}
std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
cdir = this->ConvertToRelativeForMake(cdir.c_str());
std::string makecmd = "make -C ";
makecmd += cdir;
makecmd += " -f ";
makecmd +=
this->ConvertToRelativeForMake((makefile + "$CONFIGURATION").c_str());
makecmd += " all";
buildphase->AddAttribute("shellScript", this->CreateString(makecmd));
buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
}
void cmGlobalXCodeGenerator::CreateCustomRulesMakefile(
const char* makefileBasename, cmGeneratorTarget* target,
std::vector<cmCustomCommand> const& commands, const std::string& configName)
{
std::string makefileName = makefileBasename;
makefileName += configName;
cmGeneratedFileStream makefileStream(makefileName.c_str());
if (!makefileStream) {
return;
}
makefileStream.SetCopyIfDifferent(true);
makefileStream << "# Generated by CMake, DO NOT EDIT\n";
2015-10-19 19:23:29 +00:00
makefileStream << "# Custom rules for " << target->GetName() << "\n";
// disable the implicit rules
makefileStream << ".SUFFIXES: "
<< "\n";
// have all depend on all outputs
makefileStream << "all: ";
std::map<const cmCustomCommand*, std::string> tname;
int count = 0;
for (auto const& command : commands) {
cmCustomCommandGenerator ccg(command, configName,
this->CurrentLocalGenerator);
if (ccg.GetNumberOfCommands() > 0) {
const std::vector<std::string>& outputs = ccg.GetOutputs();
if (!outputs.empty()) {
for (auto const& output : outputs) {
makefileStream << "\\\n\t"
<< this->ConvertToRelativeForMake(output.c_str());
}
} else {
std::ostringstream str;
str << "_buildpart_" << count++;
2015-10-19 19:23:29 +00:00
tname[&ccg.GetCC()] = std::string(target->GetName()) + str.str();
makefileStream << "\\\n\t" << tname[&ccg.GetCC()];
}
}
}
makefileStream << "\n\n";
for (auto const& command : commands) {
cmCustomCommandGenerator ccg(command, configName,
this->CurrentLocalGenerator);
if (ccg.GetNumberOfCommands() > 0) {
2006-09-29 20:14:34 +00:00
makefileStream << "\n";
const std::vector<std::string>& outputs = ccg.GetOutputs();
if (!outputs.empty()) {
// There is at least one output, start the rule for it
const char* sep = "";
for (auto const& output : outputs) {
makefileStream << sep
<< this->ConvertToRelativeForMake(output.c_str());
sep = " ";
}
makefileStream << ": ";
} else {
// There are no outputs. Use the generated force rule name.
makefileStream << tname[&ccg.GetCC()] << ": ";
}
for (auto const& d : ccg.GetDepends()) {
std::string dep;
if (this->CurrentLocalGenerator->GetRealDependency(d, configName,
dep)) {
makefileStream << "\\\n"
<< this->ConvertToRelativeForMake(dep.c_str());
}
}
makefileStream << "\n";
if (const char* comment = ccg.GetComment()) {
std::string echo_cmd = "echo ";
echo_cmd += (this->CurrentLocalGenerator->EscapeForShell(
comment, ccg.GetCC().GetEscapeAllowMakeVars()));
makefileStream << "\t" << echo_cmd << "\n";
}
// Add each command line to the set of commands.
for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
// Build the command line in a single string.
std::string cmd2 = ccg.GetCommand(c);
cmSystemTools::ReplaceString(cmd2, "/./", "/");
cmd2 = this->ConvertToRelativeForMake(cmd2.c_str());
std::string cmd;
std::string wd = ccg.GetWorkingDirectory();
if (!wd.empty()) {
cmd += "cd ";
cmd += this->ConvertToRelativeForMake(wd.c_str());
cmd += " && ";
}
cmd += cmd2;
ccg.AppendArguments(c, cmd);
makefileStream << "\t" << cmd << "\n";
}
2005-01-27 21:11:44 +00:00
}
}
}
2005-02-01 18:07:42 +00:00
2015-10-19 19:23:29 +00:00
void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
cmXCodeObject* buildSettings,
const std::string& configName)
2005-02-01 18:07:42 +00:00
{
if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
return;
}
std::string defFlags;
bool shared = ((gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) ||
(gtgt->GetType() == cmStateEnums::MODULE_LIBRARY));
bool binary = ((gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) ||
(gtgt->GetType() == cmStateEnums::STATIC_LIBRARY) ||
(gtgt->GetType() == cmStateEnums::EXECUTABLE) || shared);
// Compute the compilation flags for each language.
std::set<std::string> languages;
gtgt->GetLanguages(languages, configName);
std::map<std::string, std::string> cflags;
for (auto const& lang : languages) {
std::string& flags = cflags[lang];
2005-02-02 22:16:07 +00:00
// Add language-specific flags.
this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang,
configName);
2005-02-02 22:16:07 +00:00
// Add shared-library flags if needed.
this->CurrentLocalGenerator->AddCMP0018Flags(flags, gtgt, lang,
configName);
this->CurrentLocalGenerator->AddVisibilityPresetFlags(flags, gtgt, lang);
this->CurrentLocalGenerator->AddCompileOptions(flags, gtgt, lang,
configName);
}
std::string llang = gtgt->GetLinkerLanguage(configName);
if (binary && llang.empty()) {
cmSystemTools::Error(
"CMake can not determine linker language for target: ",
gtgt->GetName().c_str());
return;
}
2005-02-11 19:25:05 +00:00
if (gtgt->IsIPOEnabled(llang, configName)) {
2017-04-27 13:18:26 +00:00
const char* ltoValue =
this->CurrentMakefile->IsOn("_CMAKE_LTO_THIN") ? "YES_THIN" : "YES";
buildSettings->AddAttribute("LLVM_LTO", this->CreateString(ltoValue));
}
2005-02-14 21:46:32 +00:00
// Add define flags
this->CurrentLocalGenerator->AppendFlags(
defFlags, this->CurrentMakefile->GetDefineFlags());
// Add preprocessor definitions for this target and configuration.
BuildObjectListOrString ppDefs(this, true);
this->AppendDefines(
ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"");
if (const char* exportMacro = gtgt->GetExportMacro()) {
// Add the export symbol definition for shared library objects.
this->AppendDefines(ppDefs, exportMacro);
}
std::vector<std::string> targetDefines;
gtgt->GetCompileDefinitions(targetDefines, configName, "C");
this->AppendDefines(ppDefs, targetDefines);
buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS",
ppDefs.CreateList());
std::string extraLinkOptionsVar;
std::string extraLinkOptions;
if (gtgt->GetType() == cmStateEnums::EXECUTABLE) {
extraLinkOptionsVar = "CMAKE_EXE_LINKER_FLAGS";
} else if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
extraLinkOptionsVar = "CMAKE_SHARED_LINKER_FLAGS";
} else if (gtgt->GetType() == cmStateEnums::MODULE_LIBRARY) {
extraLinkOptionsVar = "CMAKE_MODULE_LINKER_FLAGS";
}
if (!extraLinkOptionsVar.empty()) {
this->CurrentLocalGenerator->AddConfigVariableFlags(
extraLinkOptions, extraLinkOptionsVar, configName);
}
if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
gtgt->GetType() == cmStateEnums::STATIC_LIBRARY) {
this->CurrentLocalGenerator->GetStaticLibraryFlags(
extraLinkOptions, cmSystemTools::UpperCase(configName), gtgt);
} else {
const char* targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
if (targetLinkFlags) {
this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,
targetLinkFlags);
}
if (!configName.empty()) {
std::string linkFlagsVar = "LINK_FLAGS_";
linkFlagsVar += cmSystemTools::UpperCase(configName);
if (const char* linkFlags = gtgt->GetProperty(linkFlagsVar)) {
this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, linkFlags);
}
}
}
// Set target-specific architectures.
std::vector<std::string> archs;
gtgt->GetAppleArchs(configName, archs);
if (!archs.empty()) {
// Enable ARCHS attribute.
buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("NO"));
// Store ARCHS value.
if (archs.size() == 1) {
buildSettings->AddAttribute("ARCHS", this->CreateString(archs[0]));
} else {
cmXCodeObject* archObjects =
this->CreateObject(cmXCodeObject::OBJECT_LIST);
for (auto& arch : archs) {
archObjects->AddObject(this->CreateString(arch));
}
buildSettings->AddAttribute("ARCHS", archObjects);
}
}
// Get the product name components.
std::string pnprefix;
std::string pnbase;
std::string pnsuffix;
gtgt->GetFullNameComponents(pnprefix, pnbase, pnsuffix, configName);
const char* version = gtgt->GetProperty("VERSION");
const char* soversion = gtgt->GetProperty("SOVERSION");
if (!gtgt->HasSOName(configName) || gtgt->IsFrameworkOnApple()) {
version = nullptr;
soversion = nullptr;
}
if (version && !soversion) {
soversion = version;
}
if (!version && soversion) {
version = soversion;
}
std::string realName = pnbase;
std::string soName = pnbase;
if (version && soversion) {
realName += ".";
realName += version;
soName += ".";
soName += soversion;
}
// Set attributes to specify the proper name for the target.
std::string pndir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
if (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY ||
gtgt->GetType() == cmStateEnums::SHARED_LIBRARY ||
gtgt->GetType() == cmStateEnums::MODULE_LIBRARY ||
gtgt->GetType() == cmStateEnums::EXECUTABLE) {
if (!gtgt->UsesDefaultOutputDir(configName,
cmStateEnums::RuntimeBinaryArtifact)) {
std::string pncdir = gtgt->GetDirectory(configName);
buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
this->CreateString(pncdir));
}
if (gtgt->IsFrameworkOnApple() || gtgt->IsCFBundleOnApple()) {
pnprefix = "";
}
buildSettings->AddAttribute("EXECUTABLE_PREFIX",
this->CreateString(pnprefix));
buildSettings->AddAttribute("EXECUTABLE_SUFFIX",
this->CreateString(pnsuffix));
} else if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
pnprefix = "lib";
pnbase = gtgt->GetName();
pnsuffix = ".a";
std::string pncdir =
this->GetObjectsNormalDirectory(this->CurrentProject, configName, gtgt);
buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
this->CreateString(pncdir));
}
// Store the product name for all target types.
buildSettings->AddAttribute("PRODUCT_NAME", this->CreateString(realName));
buildSettings->AddAttribute("SYMROOT", this->CreateString(pndir));
// Handle settings for each target type.
switch (gtgt->GetType()) {
case cmStateEnums::STATIC_LIBRARY:
if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
std::string fw_version = gtgt->GetFrameworkVersion();
buildSettings->AddAttribute("FRAMEWORK_VERSION",
this->CreateString(fw_version));
const char* ext = gtgt->GetProperty("BUNDLE_EXTENSION");
if (ext) {
buildSettings->AddAttribute("WRAPPER_EXTENSION",
this->CreateString(ext));
}
std::string plist = this->ComputeInfoPListLocation(gtgt);
// Xcode will create the final version of Info.plist at build time,
// so let it replace the framework name. This avoids creating
// a per-configuration Info.plist file.
this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
gtgt, "$(EXECUTABLE_NAME)", plist.c_str());
buildSettings->AddAttribute("INFOPLIST_FILE",
this->CreateString(plist));
buildSettings->AddAttribute("MACH_O_TYPE",
this->CreateString("staticlib"));
} else {
buildSettings->AddAttribute("LIBRARY_STYLE",
this->CreateString("STATIC"));
}
break;
case cmStateEnums::OBJECT_LIBRARY: {
buildSettings->AddAttribute("LIBRARY_STYLE",
this->CreateString("STATIC"));
break;
2005-11-18 19:12:09 +00:00
}
case cmStateEnums::MODULE_LIBRARY: {
buildSettings->AddAttribute("LIBRARY_STYLE",
this->CreateString("BUNDLE"));
if (gtgt->IsCFBundleOnApple()) {
// It turns out that a BUNDLE is basically the same
// in many ways as an application bundle, as far as
// link flags go
std::string createFlags = this->LookupFlags(
"CMAKE_SHARED_MODULE_CREATE_", llang, "_FLAGS", "-bundle");
if (!createFlags.empty()) {
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
const char* ext = gtgt->GetProperty("BUNDLE_EXTENSION");
if (ext) {
buildSettings->AddAttribute("WRAPPER_EXTENSION",
this->CreateString(ext));
}
std::string plist = this->ComputeInfoPListLocation(gtgt);
// Xcode will create the final version of Info.plist at build time,
// so let it replace the cfbundle name. This avoids creating
// a per-configuration Info.plist file. The cfbundle plist
// is very similar to the application bundle plist
this->CurrentLocalGenerator->GenerateAppleInfoPList(
gtgt, "$(EXECUTABLE_NAME)", plist.c_str());
buildSettings->AddAttribute("INFOPLIST_FILE",
this->CreateString(plist));
} else {
buildSettings->AddAttribute("MACH_O_TYPE",
this->CreateString("mh_bundle"));
buildSettings->AddAttribute("GCC_DYNAMIC_NO_PIC",
this->CreateString("NO"));
// Add the flags to create an executable.
std::string createFlags =
this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
if (!createFlags.empty()) {
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
2005-02-14 21:46:32 +00:00
}
break;
2005-11-18 19:12:09 +00:00
}
case cmStateEnums::SHARED_LIBRARY: {
if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
std::string fw_version = gtgt->GetFrameworkVersion();
buildSettings->AddAttribute("FRAMEWORK_VERSION",
this->CreateString(fw_version));
const char* ext = gtgt->GetProperty("BUNDLE_EXTENSION");
if (ext) {
buildSettings->AddAttribute("WRAPPER_EXTENSION",
this->CreateString(ext));
}
std::string plist = this->ComputeInfoPListLocation(gtgt);
// Xcode will create the final version of Info.plist at build time,
// so let it replace the framework name. This avoids creating
// a per-configuration Info.plist file.
this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
gtgt, "$(EXECUTABLE_NAME)", plist.c_str());
buildSettings->AddAttribute("INFOPLIST_FILE",
this->CreateString(plist));
} else {
// Add the flags to create a shared library.
std::string createFlags = this->LookupFlags(
"CMAKE_SHARED_LIBRARY_CREATE_", llang, "_FLAGS", "-dynamiclib");
if (!createFlags.empty()) {
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
}
buildSettings->AddAttribute("LIBRARY_STYLE",
this->CreateString("DYNAMIC"));
break;
}
case cmStateEnums::EXECUTABLE: {
// Add the flags to create an executable.
std::string createFlags =
this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
if (!createFlags.empty()) {
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
// Handle bundles and normal executables separately.
if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
const char* ext = gtgt->GetProperty("BUNDLE_EXTENSION");
if (ext) {
buildSettings->AddAttribute("WRAPPER_EXTENSION",
this->CreateString(ext));
}
std::string plist = this->ComputeInfoPListLocation(gtgt);
// Xcode will create the final version of Info.plist at build time,
// so let it replace the executable name. This avoids creating
// a per-configuration Info.plist file.
this->CurrentLocalGenerator->GenerateAppleInfoPList(
gtgt, "$(EXECUTABLE_NAME)", plist.c_str());
buildSettings->AddAttribute("INFOPLIST_FILE",
this->CreateString(plist));
}
} break;
default:
2005-02-02 22:16:07 +00:00
break;
}
if (this->XcodeVersion < 40) {
buildSettings->AddAttribute("PREBINDING", this->CreateString("NO"));
}
BuildObjectListOrString dirs(this, true);
BuildObjectListOrString fdirs(this, true);
BuildObjectListOrString sysdirs(this, true);
BuildObjectListOrString sysfdirs(this, true);
const bool emitSystemIncludes = this->XcodeVersion >= 83;
std::vector<std::string> includes;
this->CurrentLocalGenerator->GetIncludeDirectories(includes, gtgt, "C",
configName);
std::set<std::string> emitted;
emitted.insert("/System/Library/Frameworks");
for (auto& include : includes) {
if (this->NameResolvesToFramework(include)) {
std::string frameworkDir = include;
frameworkDir += "/../";
frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
if (emitted.insert(frameworkDir).second) {
std::string incpath = this->XCodeEscapePath(frameworkDir);
if (emitSystemIncludes &&
gtgt->IsSystemIncludeDirectory(frameworkDir, configName)) {
sysfdirs.Add(incpath);
} else {
fdirs.Add(incpath);
}
}
} else {
std::string incpath = this->XCodeEscapePath(include);
if (emitSystemIncludes &&
gtgt->IsSystemIncludeDirectory(include, configName)) {
sysdirs.Add(incpath);
} else {
dirs.Add(incpath);
}
}
}
// Add framework search paths needed for linking.
if (cmComputeLinkInformation* cli = gtgt->GetLinkInformation(configName)) {
for (auto const& fwDir : cli->GetFrameworkPaths()) {
if (emitted.insert(fwDir).second) {
std::string incpath = this->XCodeEscapePath(fwDir);
if (emitSystemIncludes &&
gtgt->IsSystemIncludeDirectory(fwDir, configName)) {
sysfdirs.Add(incpath);
} else {
fdirs.Add(incpath);
}
}
}
}
if (!fdirs.IsEmpty()) {
buildSettings->AddAttribute("FRAMEWORK_SEARCH_PATHS", fdirs.CreateList());
}
if (!dirs.IsEmpty()) {
buildSettings->AddAttribute("HEADER_SEARCH_PATHS", dirs.CreateList());
}
if (!sysfdirs.IsEmpty()) {
buildSettings->AddAttribute("SYSTEM_FRAMEWORK_SEARCH_PATHS",
sysfdirs.CreateList());
}
if (!sysdirs.IsEmpty()) {
buildSettings->AddAttribute("SYSTEM_HEADER_SEARCH_PATHS",
sysdirs.CreateList());
}
if (this->XcodeVersion >= 60 && !emitSystemIncludes) {
// Add those per-language flags in addition to HEADER_SEARCH_PATHS to gain
// system include directory awareness. We need to also keep on setting
// HEADER_SEARCH_PATHS to work around a missing compile options flag for
// GNU assembly files (#16449)
for (auto const& language : languages) {
std::string includeFlags = this->CurrentLocalGenerator->GetIncludeFlags(
includes, gtgt, language, true, false, configName);
if (!includeFlags.empty()) {
cflags[language] += " " + includeFlags;
}
}
}
bool same_gflags = true;
std::map<std::string, std::string> gflags;
std::string const* last_gflag = nullptr;
std::string optLevel = "0";
// Minimal map of flags to build settings.
for (auto const& language : languages) {
std::string& flags = cflags[language];
std::string& gflag = gflags[language];
std::string oflag =
this->ExtractFlagRegex("(^| )(-Ofast|-Os|-O[0-9]*)( |$)", 2, flags);
if (oflag.size() == 2) {
optLevel = "1";
} else if (oflag.size() > 2) {
optLevel = oflag.substr(2);
}
gflag = this->ExtractFlag("-g", flags);
// put back gdwarf-2 if used since there is no way
// to represent it in the gui, but we still want debug yes
if (gflag == "-gdwarf-2") {
flags += " ";
flags += gflag;
}
if (last_gflag && *last_gflag != gflag) {
same_gflags = false;
}
last_gflag = &gflag;
}
2005-09-02 20:29:32 +00:00
const char* debugStr = "YES";
if (!same_gflags) {
// We can't set the Xcode flag differently depending on the language,
// so put them back in this case.
for (auto const& language : languages) {
cflags[language] += " ";
cflags[language] += gflags[language];
}
2005-09-02 20:29:32 +00:00
debugStr = "NO";
} else if (last_gflag && (last_gflag->empty() || *last_gflag == "-g0")) {
debugStr = "NO";
}
buildSettings->AddAttribute("COMBINE_HIDPI_IMAGES",
this->CreateString("YES"));
2005-09-02 20:29:32 +00:00
buildSettings->AddAttribute("GCC_GENERATE_DEBUGGING_SYMBOLS",
this->CreateString(debugStr));
buildSettings->AddAttribute("GCC_OPTIMIZATION_LEVEL",
2005-09-02 20:29:32 +00:00
this->CreateString(optLevel));
buildSettings->AddAttribute("GCC_SYMBOLS_PRIVATE_EXTERN",
this->CreateString("NO"));
buildSettings->AddAttribute("GCC_INLINES_ARE_PRIVATE_EXTERN",
this->CreateString("NO"));
for (auto const& language : languages) {
std::string flags = cflags[language] + " " + defFlags;
if (language == "CXX") {
buildSettings->AddAttribute("OTHER_CPLUSPLUSFLAGS",
this->CreateString(flags));
} else if (language == "Fortran") {
buildSettings->AddAttribute("IFORT_OTHER_FLAGS",
this->CreateString(flags));
} else if (language == "C") {
buildSettings->AddAttribute("OTHER_CFLAGS", this->CreateString(flags));
} else if (language == "Swift") {
buildSettings->AddAttribute("OTHER_SWIFT_FLAGS",
this->CreateString(flags));
}
}
// Add Fortran source format attribute if property is set.
const char* format = nullptr;
const char* tgtfmt = gtgt->GetProperty("Fortran_FORMAT");
switch (cmOutputConverter::GetFortranFormat(tgtfmt)) {
case cmOutputConverter::FortranFormatFixed:
format = "fixed";
break;
case cmOutputConverter::FortranFormatFree:
format = "free";
break;
default:
break;
}
if (format) {
buildSettings->AddAttribute("IFORT_LANG_SRCFMT",
this->CreateString(format));
}
// Create the INSTALL_PATH attribute.
std::string install_name_dir;
if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
// Get the install_name directory for the build tree.
install_name_dir = gtgt->GetInstallNameDirForBuildTree(configName);
// Xcode doesn't create the correct install_name in some cases.
// That is, if the INSTALL_PATH is empty, or if we have versioning
// of dylib libraries, we want to specify the install_name.
// This is done by adding a link flag to create an install_name
// with just the library soname.
std::string install_name;
if (!install_name_dir.empty()) {
// Convert to a path for the native build tool.
cmSystemTools::ConvertToUnixSlashes(install_name_dir);
install_name += install_name_dir;
install_name += "/";
}
install_name += gtgt->GetSOName(configName);
if ((realName != soName) || install_name_dir.empty()) {
install_name_dir = "";
extraLinkOptions += " -install_name ";
extraLinkOptions += XCodeEscapePath(install_name);
}
}
buildSettings->AddAttribute("INSTALL_PATH",
this->CreateString(install_name_dir));
// Create the LD_RUNPATH_SEARCH_PATHS
cmComputeLinkInformation* pcli = gtgt->GetLinkInformation(configName);
if (pcli) {
std::string search_paths;
std::vector<std::string> runtimeDirs;
pcli->GetRPath(runtimeDirs, false);
// runpath dirs needs to be unique to prevent corruption
std::set<std::string> unique_dirs;
for (auto runpath : runtimeDirs) {
runpath = this->ExpandCFGIntDir(runpath, configName);
if (unique_dirs.find(runpath) == unique_dirs.end()) {
unique_dirs.insert(runpath);
if (!search_paths.empty()) {
search_paths += " ";
}
search_paths += this->XCodeEscapePath(runpath);
}
}
if (!search_paths.empty()) {
buildSettings->AddAttribute("LD_RUNPATH_SEARCH_PATHS",
this->CreateString(search_paths));
}
}
2015-10-19 19:23:29 +00:00
buildSettings->AddAttribute(this->GetTargetLinkFlagsVar(gtgt),
this->CreateString(extraLinkOptions));
buildSettings->AddAttribute("OTHER_REZFLAGS", this->CreateString(""));
buildSettings->AddAttribute("SECTORDER_FLAGS", this->CreateString(""));
buildSettings->AddAttribute("USE_HEADERMAP", this->CreateString("NO"));
cmXCodeObject* group = this->CreateObject(cmXCodeObject::OBJECT_LIST);
group->AddObject(this->CreateString("-Wmost"));
group->AddObject(this->CreateString("-Wno-four-char-constants"));
group->AddObject(this->CreateString("-Wno-unknown-pragmas"));
group->AddObject(this->CreateString("$(inherited)"));
buildSettings->AddAttribute("WARNING_CFLAGS", group);
// Runtime version information.
if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
int major;
int minor;
int patch;
// VERSION -> current_version
gtgt->GetTargetVersion(false, major, minor, patch);
std::ostringstream v;
// Xcode always wants at least 1.0.0 or nothing
if (!(major == 0 && minor == 0 && patch == 0)) {
v << major << "." << minor << "." << patch;
}
buildSettings->AddAttribute("DYLIB_CURRENT_VERSION",
this->CreateString(v.str()));
// SOVERSION -> compatibility_version
gtgt->GetTargetVersion(true, major, minor, patch);
std::ostringstream vso;
// Xcode always wants at least 1.0.0 or nothing
if (!(major == 0 && minor == 0 && patch == 0)) {
vso << major << "." << minor << "." << patch;
}
buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
this->CreateString(vso.str()));
}
// put this last so it can override existing settings
// Convert "XCODE_ATTRIBUTE_*" properties directly.
{
for (auto const& prop : gtgt->GetPropertyKeys()) {
if (prop.find("XCODE_ATTRIBUTE_") == 0) {
std::string attribute = prop.substr(16);
this->FilterConfigurationAttribute(configName, attribute);
if (!attribute.empty()) {
cmGeneratorExpression ge;
std::string processed =
ge.Parse(gtgt->GetProperty(prop))
->Evaluate(this->CurrentLocalGenerator, configName);
buildSettings->AddAttribute(attribute,
this->CreateString(processed));
}
}
}
}
2005-02-01 18:07:42 +00:00
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget(
cmGeneratorTarget* gtgt)
2005-02-03 22:42:55 +00:00
{
cmXCodeObject* shellBuildPhase =
this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
shellBuildPhase->AddAttribute("buildActionMask",
2005-11-18 19:12:09 +00:00
this->CreateString("2147483647"));
2005-02-03 22:42:55 +00:00
cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
shellBuildPhase->AddAttribute("files", buildFiles);
cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
shellBuildPhase->AddAttribute("inputPaths", inputPaths);
cmXCodeObject* outputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
shellBuildPhase->AddAttribute("outputPaths", outputPaths);
shellBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
2005-11-18 19:12:09 +00:00
this->CreateString("0"));
shellBuildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
shellBuildPhase->AddAttribute(
"shellScript", this->CreateString("# shell script goes here\nexit 0"));
shellBuildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
cmXCodeObject* target =
2005-02-03 22:42:55 +00:00
this->CreateObject(cmXCodeObject::PBXAggregateTarget);
target->SetComment(gtgt->GetName());
cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST);
std::vector<cmXCodeObject*> emptyContentVector;
this->CreateCustomCommands(buildPhases, nullptr, nullptr, nullptr,
emptyContentVector, nullptr, gtgt);
2005-02-03 22:42:55 +00:00
target->AddAttribute("buildPhases", buildPhases);
this->AddConfigurations(target, gtgt);
cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2005-02-03 22:42:55 +00:00
target->AddAttribute("dependencies", dependencies);
target->AddAttribute("name", this->CreateString(gtgt->GetName()));
target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
2015-10-19 19:23:29 +00:00
target->SetTarget(gtgt);
this->XCodeObjectMap[gtgt] = target;
// Add source files without build rules for editing convenience.
if (gtgt->GetType() == cmStateEnums::UTILITY &&
gtgt->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
std::vector<cmSourceFile*> sources;
if (!gtgt->GetConfigCommonSourceFiles(sources)) {
return nullptr;
}
// Add CMakeLists.txt file for user convenience.
std::string listfile =
gtgt->GetLocalGenerator()->GetCurrentSourceDirectory();
listfile += "/CMakeLists.txt";
sources.push_back(gtgt->Makefile->GetOrCreateSource(listfile));
for (auto sourceFile : sources) {
if (!sourceFile->GetPropertyAsBool("GENERATED")) {
this->CreateXCodeFileReference(sourceFile, gtgt);
}
}
}
target->SetId(this->GetOrCreateId(gtgt->GetName(), target->GetId()));
2005-02-03 22:42:55 +00:00
return target;
}
std::string cmGlobalXCodeGenerator::AddConfigurations(cmXCodeObject* target,
2015-10-19 19:23:29 +00:00
cmGeneratorTarget* gtgt)
2005-09-02 20:29:32 +00:00
{
std::string configTypes =
2006-05-11 19:39:46 +00:00
this->CurrentMakefile->GetRequiredDefinition("CMAKE_CONFIGURATION_TYPES");
2005-09-02 20:29:32 +00:00
std::vector<std::string> configVectorIn;
std::vector<std::string> configVector;
configVectorIn.push_back(configTypes);
cmSystemTools::ExpandList(configVectorIn, configVector);
cmXCodeObject* configlist =
2006-05-11 19:39:46 +00:00
this->CreateObject(cmXCodeObject::XCConfigurationList);
2005-09-02 20:29:32 +00:00
cmXCodeObject* buildConfigurations =
this->CreateObject(cmXCodeObject::OBJECT_LIST);
configlist->AddAttribute("buildConfigurations", buildConfigurations);
std::string comment = "Build configuration list for ";
comment += cmXCodeObject::PBXTypeNames[target->GetIsA()];
comment += " \"";
2015-10-19 19:23:29 +00:00
comment += gtgt->GetName();
comment += "\"";
configlist->SetComment(comment);
target->AddAttribute("buildConfigurationList",
2005-09-02 20:29:32 +00:00
this->CreateObjectReference(configlist));
for (auto const& i : configVector) {
cmXCodeObject* config =
2006-05-11 19:39:46 +00:00
this->CreateObject(cmXCodeObject::XCBuildConfiguration);
2005-09-02 20:29:32 +00:00
buildConfigurations->AddObject(config);
cmXCodeObject* buildSettings =
this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
this->CreateBuildSettings(gtgt, buildSettings, i);
config->AddAttribute("name", this->CreateString(i));
config->SetComment(i);
2005-09-02 20:29:32 +00:00
config->AddAttribute("buildSettings", buildSettings);
}
if (!configVector.empty()) {
configlist->AddAttribute("defaultConfigurationName",
this->CreateString(configVector[0]));
configlist->AddAttribute("defaultConfigurationIsVisible",
2006-05-11 19:39:46 +00:00
this->CreateString("0"));
return configVector[0];
}
return "";
2005-09-02 20:29:32 +00:00
}
2015-10-19 19:23:29 +00:00
const char* cmGlobalXCodeGenerator::GetTargetLinkFlagsVar(
cmGeneratorTarget const* target) const
{
if (this->XcodeVersion >= 60 &&
(target->GetType() == cmStateEnums::STATIC_LIBRARY ||
target->GetType() == cmStateEnums::OBJECT_LIBRARY)) {
return "OTHER_LIBTOOLFLAGS";
}
return "OTHER_LDFLAGS";
}
const char* cmGlobalXCodeGenerator::GetTargetFileType(
cmGeneratorTarget* target)
{
if (const char* e = target->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
return e;
}
switch (target->GetType()) {
case cmStateEnums::OBJECT_LIBRARY:
return "archive.ar";
case cmStateEnums::STATIC_LIBRARY:
return (target->GetPropertyAsBool("FRAMEWORK") ? "wrapper.framework"
: "archive.ar");
case cmStateEnums::MODULE_LIBRARY:
if (target->IsXCTestOnApple()) {
return "wrapper.cfbundle";
}
if (target->IsCFBundleOnApple()) {
return "wrapper.plug-in";
}
return "compiled.mach-o.executable";
case cmStateEnums::SHARED_LIBRARY:
return (target->GetPropertyAsBool("FRAMEWORK")
? "wrapper.framework"
: "compiled.mach-o.dylib");
case cmStateEnums::EXECUTABLE:
return "compiled.mach-o.executable";
default:
break;
}
return nullptr;
}
const char* cmGlobalXCodeGenerator::GetTargetProductType(
cmGeneratorTarget* target)
{
if (const char* e = target->GetProperty("XCODE_PRODUCT_TYPE")) {
return e;
}
switch (target->GetType()) {
case cmStateEnums::OBJECT_LIBRARY:
return "com.apple.product-type.library.static";
case cmStateEnums::STATIC_LIBRARY:
return (target->GetPropertyAsBool("FRAMEWORK")
? "com.apple.product-type.framework"
: "com.apple.product-type.library.static");
case cmStateEnums::MODULE_LIBRARY:
if (target->IsXCTestOnApple()) {
return "com.apple.product-type.bundle.unit-test";
} else if (target->IsCFBundleOnApple()) {
return "com.apple.product-type.bundle";
} else {
return "com.apple.product-type.tool";
}
case cmStateEnums::SHARED_LIBRARY:
return (target->GetPropertyAsBool("FRAMEWORK")
? "com.apple.product-type.framework"
: "com.apple.product-type.library.dynamic");
case cmStateEnums::EXECUTABLE:
return (target->GetPropertyAsBool("MACOSX_BUNDLE")
? "com.apple.product-type.application"
: "com.apple.product-type.tool");
default:
break;
}
return nullptr;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeTarget(
cmGeneratorTarget* gtgt, cmXCodeObject* buildPhases)
2005-02-01 18:07:42 +00:00
{
if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
return nullptr;
}
cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXNativeTarget);
2005-02-01 18:07:42 +00:00
target->AddAttribute("buildPhases", buildPhases);
cmXCodeObject* buildRules = this->CreateObject(cmXCodeObject::OBJECT_LIST);
target->AddAttribute("buildRules", buildRules);
std::string defConfig;
defConfig = this->AddConfigurations(target, gtgt);
cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2005-02-01 18:07:42 +00:00
target->AddAttribute("dependencies", dependencies);
target->AddAttribute("name", this->CreateString(gtgt->GetName()));
target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
if (const char* fileType = this->GetTargetFileType(gtgt)) {
fileRef->AddAttribute("explicitFileType", this->CreateString(fileType));
}
std::string fullName;
if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
fullName = "lib";
fullName += gtgt->GetName();
fullName += ".a";
} else {
fullName = gtgt->GetFullName(defConfig);
}
fileRef->AddAttribute("path", this->CreateString(fullName));
2005-02-07 22:36:34 +00:00
fileRef->AddAttribute("sourceTree",
this->CreateString("BUILT_PRODUCTS_DIR"));
fileRef->SetComment(gtgt->GetName());
target->AddAttribute("productReference",
2005-02-07 22:36:34 +00:00
this->CreateObjectReference(fileRef));
if (const char* productType = this->GetTargetProductType(gtgt)) {
target->AddAttribute("productType", this->CreateString(productType));
}
2015-10-19 19:23:29 +00:00
target->SetTarget(gtgt);
this->XCodeObjectMap[gtgt] = target;
target->SetId(this->GetOrCreateId(gtgt->GetName(), target->GetId()));
2005-02-01 18:07:42 +00:00
return target;
}
cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget(
cmGeneratorTarget const* t)
2005-02-03 22:42:55 +00:00
{
if (!t) {
return nullptr;
}
2015-10-19 19:23:29 +00:00
std::map<cmGeneratorTarget const*, cmXCodeObject*>::const_iterator const i =
this->XCodeObjectMap.find(t);
if (i == this->XCodeObjectMap.end()) {
return nullptr;
}
return i->second;
2005-02-03 22:42:55 +00:00
}
2014-02-06 22:31:47 +00:00
std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name,
const std::string& id)
{
std::string guidStoreName = name;
guidStoreName += "_GUID_CMAKE";
const char* storedGUID =
this->CMakeInstance->GetCacheDefinition(guidStoreName);
if (storedGUID) {
return storedGUID;
}
this->CMakeInstance->AddCacheEntry(guidStoreName, id.c_str(),
"Stored Xcode object GUID",
cmStateEnums::INTERNAL);
return id;
}
2005-02-03 22:42:55 +00:00
void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target,
cmXCodeObject* dependTarget)
{
// This is called once for every edge in the target dependency graph.
cmXCodeObject* container =
this->CreateObject(cmXCodeObject::PBXContainerItemProxy);
container->SetComment("PBXContainerItemProxy");
container->AddAttribute("containerPortal",
this->CreateObjectReference(this->RootObject));
container->AddAttribute("proxyType", this->CreateString("1"));
container->AddAttribute("remoteGlobalIDString",
this->CreateObjectReference(dependTarget));
container->AddAttribute(
"remoteInfo", this->CreateString(dependTarget->GetTarget()->GetName()));
cmXCodeObject* targetdep =
this->CreateObject(cmXCodeObject::PBXTargetDependency);
targetdep->SetComment("PBXTargetDependency");
targetdep->AddAttribute("target", this->CreateObjectReference(dependTarget));
targetdep->AddAttribute("targetProxy",
this->CreateObjectReference(container));
2005-02-03 22:42:55 +00:00
cmXCodeObject* depends = target->GetObject("dependencies");
if (!depends) {
cmSystemTools::Error(
"target does not have dependencies attribute error..");
} else {
depends->AddUniqueObject(targetdep);
}
2005-02-03 22:42:55 +00:00
}
void cmGlobalXCodeGenerator::AppendOrAddBuildSetting(cmXCodeObject* settings,
const char* attribute,
const char* value)
{
if (settings) {
cmXCodeObject* attr = settings->GetObject(attribute);
if (!attr) {
settings->AddAttribute(attribute, this->CreateString(value));
} else {
std::string oldValue = attr->GetString();
oldValue += " ";
oldValue += value;
attr->SetString(oldValue);
}
}
}
void cmGlobalXCodeGenerator::AppendBuildSettingAttribute(
cmXCodeObject* target, const char* attribute, const char* value,
const std::string& configName)
2005-09-02 20:29:32 +00:00
{
// There are multiple configurations. Add the setting to the
// buildSettings of the configuration name given.
cmXCodeObject* configurationList =
target->GetObject("buildConfigurationList")->GetObject();
cmXCodeObject* buildConfigs =
configurationList->GetObject("buildConfigurations");
for (auto obj : buildConfigs->GetObjectList()) {
if (configName.empty() ||
obj->GetObject("name")->GetString() == configName) {
cmXCodeObject* settings = obj->GetObject("buildSettings");
this->AppendOrAddBuildSetting(settings, attribute, value);
2005-09-02 20:29:32 +00:00
}
}
2005-09-02 20:29:32 +00:00
}
void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
2005-02-03 22:42:55 +00:00
{
2015-10-19 19:23:29 +00:00
cmGeneratorTarget* gt = target->GetTarget();
if (!gt) {
2015-10-21 19:14:00 +00:00
cmSystemTools::Error("Error no target on xobject\n");
return;
}
if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
2005-02-03 22:42:55 +00:00
return;
}
// Add dependencies on other CMake targets.
for (const auto& dep : this->GetTargetDirectDepends(gt)) {
if (cmXCodeObject* dptarget = this->FindXCodeTarget(dep)) {
this->AddDependTarget(target, dptarget);
2005-02-03 22:42:55 +00:00
}
}
// Loop over configuration types and set per-configuration info.
for (auto const& configName : this->CurrentConfigurationTypes) {
// Get the current configuration name.
if (this->XcodeVersion >= 50) {
// Add object library contents as link flags.
std::string linkObjs;
const char* sep = "";
std::vector<cmSourceFile const*> objs;
gt->GetExternalObjects(objs, configName);
for (auto sourceFile : objs) {
if (sourceFile->GetObjectLibrary().empty()) {
continue;
}
linkObjs += sep;
sep = " ";
linkObjs += this->XCodeEscapePath(sourceFile->GetFullPath());
}
this->AppendBuildSettingAttribute(
target, this->GetTargetLinkFlagsVar(gt), linkObjs.c_str(), configName);
}
// Skip link information for object libraries.
if (gt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
gt->GetType() == cmStateEnums::STATIC_LIBRARY) {
continue;
}
// Compute the link library and directory information.
cmComputeLinkInformation* pcli = gt->GetLinkInformation(configName);
if (!pcli) {
continue;
}
cmComputeLinkInformation& cli = *pcli;
// Add dependencies directly on library files.
for (auto const& libDep : cli.GetDepends()) {
target->AddDependLibrary(configName, libDep);
}
// add the library search paths
{
std::string linkDirs;
for (auto const& libDir : cli.GetDirectories()) {
if (!libDir.empty() && libDir != "/usr/lib") {
// Now add the same one but append
// $(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) to it:
linkDirs += " ";
linkDirs += this->XCodeEscapePath(
libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)");
linkDirs += " ";
linkDirs += this->XCodeEscapePath(libDir);
}
}
this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS",
linkDirs.c_str(), configName);
}
// now add the link libraries
{
std::string linkLibs;
const char* sep = "";
for (auto const& libName : cli.GetItems()) {
linkLibs += sep;
sep = " ";
if (libName.IsPath) {
linkLibs += this->XCodeEscapePath(libName.Value);
} else if (!libName.Target ||
libName.Target->GetType() !=
cmStateEnums::INTERFACE_LIBRARY) {
linkLibs += libName.Value;
2007-05-08 19:49:54 +00:00
}
if (libName.Target && !libName.Target->IsImported()) {
target->AddDependTarget(configName, libName.Target->GetName());
}
}
this->AppendBuildSettingAttribute(
target, this->GetTargetLinkFlagsVar(gt), linkLibs.c_str(), configName);
2005-02-03 22:42:55 +00:00
}
}
2005-02-03 22:42:55 +00:00
}
2005-02-02 22:16:07 +00:00
bool cmGlobalXCodeGenerator::CreateGroups(
std::vector<cmLocalGenerator*>& generators)
2005-03-17 20:35:44 +00:00
{
for (auto& generator : generators) {
cmMakefile* mf = generator->GetMakefile();
2005-03-17 20:35:44 +00:00
std::vector<cmSourceGroup> sourceGroups = mf->GetSourceGroups();
for (auto gtgt : generator->GetGeneratorTargets()) {
// Same skipping logic here as in CreateXCodeTargets so that we do not
// end up with (empty anyhow) ZERO_CHECK, install, or test source
// groups:
//
if (gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) {
continue;
}
if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
continue;
}
if (gtgt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
continue;
}
2006-05-11 19:39:46 +00:00
// add the soon to be generated Info.plist file as a source for a
// MACOSX_BUNDLE file
if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
2015-10-19 19:23:29 +00:00
std::string plist = this->ComputeInfoPListLocation(gtgt);
mf->GetOrCreateSource(plist, true);
gtgt->AddSource(plist);
}
// Put cmSourceFile instances in proper groups:
for (auto const& si : gtgt->GetAllConfigSources()) {
cmSourceFile const* sf = si.Source;
if (this->XcodeVersion >= 50 && !sf->GetObjectLibrary().empty()) {
// Object library files go on the link line instead.
continue;
}
2005-03-17 20:35:44 +00:00
// Add the file to the list of sources.
std::string const& source = sf->GetFullPath();
cmSourceGroup* sourceGroup =
2005-03-17 20:35:44 +00:00
mf->FindSourceGroup(source.c_str(), sourceGroups);
cmXCodeObject* pbxgroup = this->CreateOrGetPBXGroup(gtgt, sourceGroup);
std::string key = GetGroupMapKeyFromPath(gtgt, source);
this->GroupMap[key] = pbxgroup;
}
// Add CMakeLists.txt file for user convenience.
{
std::string listfile =
gtgt->GetLocalGenerator()->GetCurrentSourceDirectory();
listfile += "/CMakeLists.txt";
cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(listfile);
std::string const& source = sf->GetFullPath();
cmSourceGroup* sourceGroup =
mf->FindSourceGroup(source.c_str(), sourceGroups);
cmXCodeObject* pbxgroup = this->CreateOrGetPBXGroup(gtgt, sourceGroup);
std::string key = GetGroupMapKeyFromPath(gtgt, source);
this->GroupMap[key] = pbxgroup;
}
}
}
return true;
2005-03-17 20:35:44 +00:00
}
cmXCodeObject* cmGlobalXCodeGenerator::CreatePBXGroup(cmXCodeObject* parent,
const std::string& name)
{
cmXCodeObject* parentChildren = nullptr;
if (parent) {
parentChildren = parent->GetObject("children");
}
cmXCodeObject* group = this->CreateObject(cmXCodeObject::PBXGroup);
cmXCodeObject* groupChildren =
this->CreateObject(cmXCodeObject::OBJECT_LIST);
group->AddAttribute("name", this->CreateString(name));
group->AddAttribute("children", groupChildren);
group->AddAttribute("sourceTree", this->CreateString("<group>"));
if (parentChildren) {
parentChildren->AddObject(group);
}
return group;
}
cmXCodeObject* cmGlobalXCodeGenerator::CreateOrGetPBXGroup(
cmGeneratorTarget* gtgt, cmSourceGroup* sg)
2005-03-17 20:35:44 +00:00
{
std::string s;
std::string target;
const std::string targetFolder = gtgt->GetEffectiveFolderName();
if (!targetFolder.empty()) {
target = targetFolder;
target += "/";
}
2015-10-19 19:23:29 +00:00
target += gtgt->GetName();
s = target + "/";
s += sg->GetFullName();
std::map<std::string, cmXCodeObject*>::iterator it =
2006-05-11 19:39:46 +00:00
this->GroupNameMap.find(s);
if (it != this->GroupNameMap.end()) {
return it->second;
}
it = this->TargetGroup.find(target);
cmXCodeObject* tgroup = nullptr;
if (it != this->TargetGroup.end()) {
tgroup = it->second;
} else {
std::vector<std::string> tgt_folders =
cmSystemTools::tokenize(target, "/");
std::string curr_tgt_folder;
for (std::vector<std::string>::size_type i = 0; i < tgt_folders.size();
i++) {
if (i != 0) {
curr_tgt_folder += "/";
}
curr_tgt_folder += tgt_folders[i];
it = this->TargetGroup.find(curr_tgt_folder);
if (it != this->TargetGroup.end()) {
tgroup = it->second;
continue;
}
tgroup = this->CreatePBXGroup(tgroup, tgt_folders[i]);
this->TargetGroup[curr_tgt_folder] = tgroup;
if (i == 0) {
this->MainGroupChildren->AddObject(tgroup);
}
2005-03-17 20:35:44 +00:00
}
}
this->TargetGroup[target] = tgroup;
// If it's the default source group (empty name) then put the source file
// directly in the tgroup...
//
if (sg->GetFullName().empty()) {
this->GroupNameMap[s] = tgroup;
return tgroup;
}
// It's a recursive folder structure, let's find the real parent group
if (sg->GetFullName() != sg->GetName()) {
std::string curr_folder = target;
curr_folder += "/";
for (auto const& folder :
cmSystemTools::tokenize(sg->GetFullName(), "\\")) {
curr_folder += folder;
std::map<std::string, cmXCodeObject*>::iterator i_folder =
this->GroupNameMap.find(curr_folder);
// Create new folder
if (i_folder == this->GroupNameMap.end()) {
cmXCodeObject* group = this->CreatePBXGroup(tgroup, folder);
this->GroupNameMap[curr_folder] = group;
tgroup = group;
} else {
tgroup = i_folder->second;
}
curr_folder = curr_folder + "\\";
}
return tgroup;
}
cmXCodeObject* group = this->CreatePBXGroup(tgroup, sg->GetName());
2006-03-15 16:02:08 +00:00
this->GroupNameMap[s] = group;
2005-03-17 20:35:44 +00:00
return group;
}
2005-02-02 22:16:07 +00:00
bool cmGlobalXCodeGenerator::CreateXCodeObjects(
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
2005-01-24 22:35:54 +00:00
{
this->ClearXCodeObjects();
this->RootObject = nullptr;
this->MainGroupChildren = nullptr;
2005-01-27 21:11:44 +00:00
cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO"));
2006-04-10 15:39:32 +00:00
cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
for (auto& CurrentConfigurationType : this->CurrentConfigurationTypes) {
cmXCodeObject* buildStyle =
this->CreateObject(cmXCodeObject::PBXBuildStyle);
const char* name = CurrentConfigurationType.c_str();
buildStyle->AddAttribute("name", this->CreateString(name));
buildStyle->SetComment(name);
cmXCodeObject* sgroup = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
sgroup->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO"));
buildStyle->AddAttribute("buildSettings", sgroup);
listObjs->AddObject(buildStyle);
}
2005-09-02 20:29:32 +00:00
2005-01-28 21:00:10 +00:00
cmXCodeObject* mainGroup = this->CreateObject(cmXCodeObject::PBXGroup);
this->MainGroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2006-03-15 16:02:08 +00:00
mainGroup->AddAttribute("children", this->MainGroupChildren);
2005-02-02 22:16:07 +00:00
mainGroup->AddAttribute("sourceTree", this->CreateString("<group>"));
2005-01-28 21:00:10 +00:00
// now create the cmake groups
if (!this->CreateGroups(generators)) {
return false;
}
2005-02-04 22:58:58 +00:00
cmXCodeObject* productGroup = this->CreateObject(cmXCodeObject::PBXGroup);
productGroup->AddAttribute("name", this->CreateString("Products"));
productGroup->AddAttribute("sourceTree", this->CreateString("<group>"));
cmXCodeObject* productGroupChildren =
2005-02-04 22:58:58 +00:00
this->CreateObject(cmXCodeObject::OBJECT_LIST);
productGroup->AddAttribute("children", productGroupChildren);
2006-03-15 16:02:08 +00:00
this->MainGroupChildren->AddObject(productGroup);
2006-03-15 16:02:08 +00:00
this->RootObject = this->CreateObject(cmXCodeObject::PBXProject);
this->RootObject->SetComment("Project object");
std::string project_id = "PROJECT_";
project_id += root->GetProjectName();
this->RootObject->SetId(
this->GetOrCreateId(project_id, this->RootObject->GetId()));
2005-01-27 21:11:44 +00:00
group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
this->RootObject->AddAttribute("mainGroup",
this->CreateObjectReference(mainGroup));
2006-03-15 16:02:08 +00:00
this->RootObject->AddAttribute("buildSettings", group);
this->RootObject->AddAttribute("buildStyles", listObjs);
this->RootObject->AddAttribute("hasScannedForEncodings",
this->CreateString("0"));
group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
group->AddAttribute("BuildIndependentTargetsInParallel",
this->CreateString("YES"));
std::ostringstream v;
v << std::setfill('0') << std::setw(4) << XcodeVersion * 10;
group->AddAttribute("LastUpgradeCheck", this->CreateString(v.str()));
this->RootObject->AddAttribute("attributes", group);
if (this->XcodeVersion >= 32) {
this->RootObject->AddAttribute("compatibilityVersion",
this->CreateString("Xcode 3.2"));
} else if (this->XcodeVersion >= 31) {
this->RootObject->AddAttribute("compatibilityVersion",
this->CreateString("Xcode 3.1"));
} else {
this->RootObject->AddAttribute("compatibilityVersion",
this->CreateString("Xcode 3.0"));
}
// Point Xcode at the top of the source tree.
{
std::string pdir =
this->RelativeToBinary(root->GetCurrentSourceDirectory());
this->RootObject->AddAttribute("projectDirPath", this->CreateString(pdir));
this->RootObject->AddAttribute("projectRoot", this->CreateString(""));
}
cmXCodeObject* configlist =
2006-04-10 15:39:32 +00:00
this->CreateObject(cmXCodeObject::XCConfigurationList);
2005-09-02 20:29:32 +00:00
cmXCodeObject* buildConfigurations =
this->CreateObject(cmXCodeObject::OBJECT_LIST);
2017-08-25 21:25:09 +00:00
typedef std::vector<std::pair<std::string, cmXCodeObject*>> Configs;
Configs configs;
std::string defaultConfigName;
for (const auto& name : this->CurrentConfigurationTypes) {
if (defaultConfigName.empty()) {
defaultConfigName = name;
2006-04-10 15:39:32 +00:00
}
cmXCodeObject* config =
this->CreateObject(cmXCodeObject::XCBuildConfiguration);
config->AddAttribute("name", this->CreateString(name));
configs.push_back(std::make_pair(name, config));
}
if (defaultConfigName.empty()) {
defaultConfigName = "Debug";
}
for (auto& config : configs) {
buildConfigurations->AddObject(config.second);
}
2005-09-02 20:29:32 +00:00
configlist->AddAttribute("buildConfigurations", buildConfigurations);
2006-04-10 15:39:32 +00:00
std::string comment = "Build configuration list for PBXProject";
comment += " \"";
2006-03-15 16:02:08 +00:00
comment += this->CurrentProject;
comment += "\"";
configlist->SetComment(comment);
configlist->AddAttribute("defaultConfigurationIsVisible",
2006-05-11 19:39:46 +00:00
this->CreateString("0"));
configlist->AddAttribute("defaultConfigurationName",
this->CreateString(defaultConfigName));
2005-09-02 20:29:32 +00:00
cmXCodeObject* buildSettings =
this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
const char* sysroot =
this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT");
const char* deploymentTarget =
this->CurrentMakefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
if (sysroot) {
buildSettings->AddAttribute("SDKROOT", this->CreateString(sysroot));
}
// recompute this as it may have been changed since enable language
this->ComputeArchitectures(this->CurrentMakefile);
std::string const archs = cmJoin(this->Architectures, " ");
if (archs.empty()) {
// Tell Xcode to use NATIVE_ARCH instead of ARCHS.
buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("YES"));
} else {
// Tell Xcode to use ARCHS (ONLY_ACTIVE_ARCH defaults to NO).
buildSettings->AddAttribute("ARCHS", this->CreateString(archs));
}
if (deploymentTarget && *deploymentTarget) {
buildSettings->AddAttribute("MACOSX_DEPLOYMENT_TARGET",
this->CreateString(deploymentTarget));
}
if (!this->GeneratorToolset.empty()) {
buildSettings->AddAttribute("GCC_VERSION",
this->CreateString(this->GeneratorToolset));
}
if (this->GetLanguageEnabled("Swift")) {
std::string swiftVersion;
if (const char* vers = this->CurrentMakefile->GetDefinition(
"CMAKE_Swift_LANGUAGE_VERSION")) {
swiftVersion = vers;
} else if (this->XcodeVersion >= 83) {
swiftVersion = "3.0";
} else {
swiftVersion = "2.3";
}
buildSettings->AddAttribute("SWIFT_VERSION",
this->CreateString(swiftVersion));
}
std::string symroot = root->GetCurrentBinaryDirectory();
symroot += "/build";
buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot));
for (auto& config : configs) {
cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
// Put this last so it can override existing settings
// Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly.
for (const auto& var : this->CurrentMakefile->GetDefinitions()) {
if (var.find("CMAKE_XCODE_ATTRIBUTE_") == 0) {
std::string attribute = var.substr(22);
this->FilterConfigurationAttribute(config.first, attribute);
if (!attribute.empty()) {
cmGeneratorExpression ge;
std::string processed =
ge.Parse(this->CurrentMakefile->GetDefinition(var))
->Evaluate(this->CurrentLocalGenerator, config.first);
buildSettingsForCfg->AddAttribute(attribute,
this->CreateString(processed));
}
}
}
// store per-config buildSettings into configuration object
config.second->AddAttribute("buildSettings", buildSettingsForCfg);
}
this->RootObject->AddAttribute("buildConfigurationList",
this->CreateObjectReference(configlist));
2005-09-02 20:29:32 +00:00
2005-01-27 21:11:44 +00:00
std::vector<cmXCodeObject*> targets;
for (auto& generator : generators) {
if (!this->CreateXCodeTargets(generator, targets)) {
return false;
2005-01-27 21:11:44 +00:00
}
}
// loop over all targets and add link and depend info
for (auto t : targets) {
2005-02-03 22:42:55 +00:00
this->AddDependAndLinkInformation(t);
}
this->CreateXCodeDependHackTarget(targets);
// now add all targets to the root object
cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST);
for (auto t : targets) {
2005-02-03 22:42:55 +00:00
allTargets->AddObject(t);
2005-02-04 22:58:58 +00:00
cmXCodeObject* productRef = t->GetObject("productReference");
if (productRef) {
2005-02-07 22:36:34 +00:00
productGroupChildren->AddObject(productRef->GetObject());
2005-01-27 21:11:44 +00:00
}
}
2006-03-15 16:02:08 +00:00
this->RootObject->AddAttribute("targets", allTargets);
return true;
2005-01-24 22:35:54 +00:00
}
std::string cmGlobalXCodeGenerator::GetObjectsNormalDirectory(
const std::string& projName, const std::string& configName,
const cmGeneratorTarget* t) const
{
std::string dir = t->GetLocalGenerator()->GetCurrentBinaryDirectory();
dir += "/";
dir += projName;
dir += ".build/";
dir += configName;
dir += "/";
dir += t->GetName();
dir += ".build/Objects-normal/";
return dir;
}
void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf)
{
this->Architectures.clear();
const char* osxArch = mf->GetDefinition("CMAKE_OSX_ARCHITECTURES");
const char* sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT");
if (osxArch && sysroot) {
cmSystemTools::ExpandListArgument(std::string(osxArch),
this->Architectures);
}
if (this->Architectures.empty()) {
// With no ARCHS we use ONLY_ACTIVE_ARCH.
// Look up the arch that Xcode chooses in this case.
if (const char* arch = mf->GetDefinition("CMAKE_XCODE_CURRENT_ARCH")) {
this->ObjectDirArchDefault = arch;
}
}
this->ComputeObjectDirArch(mf);
}
void cmGlobalXCodeGenerator::ComputeObjectDirArch(cmMakefile* mf)
{
if (this->Architectures.size() > 1 || this->UseEffectivePlatformName(mf)) {
this->ObjectDirArch = "$(CURRENT_ARCH)";
} else if (!this->Architectures.empty()) {
this->ObjectDirArch = this->Architectures[0];
} else {
this->ObjectDirArch = this->ObjectDirArchDefault;
}
}
void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget(
std::vector<cmXCodeObject*>& targets)
{
cmGeneratedFileStream makefileStream(this->CurrentXCodeHackMakefile.c_str());
if (!makefileStream) {
cmSystemTools::Error("Could not create",
2006-03-15 16:02:08 +00:00
this->CurrentXCodeHackMakefile.c_str());
return;
}
makefileStream.SetCopyIfDifferent(true);
// one more pass for external depend information not handled
// correctly by xcode
/* clang-format off */
makefileStream << "# DO NOT EDIT\n";
makefileStream << "# This makefile makes sure all linkable targets are\n";
makefileStream << "# up-to-date with anything they link to\n"
"default:\n"
"\techo \"Do not invoke directly\"\n"
"\n";
/* clang-format on */
std::set<std::string> dummyRules;
// Write rules to help Xcode relink things at the right time.
/* clang-format off */
makefileStream <<
"# Rules to remove targets that are older than anything to which they\n"
"# link. This forces Xcode to relink the targets from scratch. It\n"
"# does not seem to check these dependencies itself.\n";
/* clang-format on */
for (const auto& configName : this->CurrentConfigurationTypes) {
for (auto target : targets) {
cmGeneratorTarget* gt = target->GetTarget();
if (gt->GetType() == cmStateEnums::EXECUTABLE ||
gt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
gt->GetType() == cmStateEnums::STATIC_LIBRARY ||
gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
// Declare an entry point for the target post-build phase.
makefileStream << this->PostBuildMakeTarget(gt->GetName(), configName)
<< ":\n";
}
if (gt->GetType() == cmStateEnums::EXECUTABLE ||
gt->GetType() == cmStateEnums::STATIC_LIBRARY ||
gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
2012-10-06 16:30:43 +00:00
std::string tfull = gt->GetFullPath(configName);
std::string trel = this->ConvertToRelativeForMake(tfull.c_str());
// Add this target to the post-build phases of its dependencies.
std::map<std::string, cmXCodeObject::StringVec>::const_iterator y =
target->GetDependTargets().find(configName);
if (y != target->GetDependTargets().end()) {
for (auto const& deptgt : y->second) {
makefileStream << this->PostBuildMakeTarget(deptgt, configName)
<< ": " << trel << "\n";
}
}
std::vector<cmGeneratorTarget*> objlibs;
gt->GetObjectLibrariesCMP0026(objlibs);
for (auto objLib : objlibs) {
makefileStream << this->PostBuildMakeTarget(objLib->GetName(),
configName)
<< ": " << trel << "\n";
}
// Create a rule for this target.
makefileStream << trel << ":";
// List dependencies if any exist.
std::map<std::string, cmXCodeObject::StringVec>::const_iterator x =
target->GetDependLibraries().find(configName);
if (x != target->GetDependLibraries().end()) {
for (auto const& deplib : x->second) {
std::string file = this->ConvertToRelativeForMake(deplib.c_str());
makefileStream << "\\\n\t" << file;
dummyRules.insert(file);
}
}
for (auto objLib : objlibs) {
const std::string objLibName = objLib->GetName();
std::string d = this->GetObjectsNormalDirectory(this->CurrentProject,
configName, objLib);
d += "lib";
d += objLibName;
d += ".a";
std::string dependency = this->ConvertToRelativeForMake(d.c_str());
makefileStream << "\\\n\t" << dependency;
dummyRules.insert(dependency);
}
// Write the action to remove the target if it is out of date.
makefileStream << "\n";
makefileStream << "\t/bin/rm -f "
<< this->ConvertToRelativeForMake(tfull.c_str())
<< "\n";
// if building for more than one architecture
// then remove those exectuables as well
if (this->Architectures.size() > 1) {
std::string universal = this->GetObjectsNormalDirectory(
2015-10-19 19:23:29 +00:00
this->CurrentProject, configName, gt);
for (const auto& architecture : this->Architectures) {
std::string universalFile = universal;
universalFile += architecture;
universalFile += "/";
universalFile += gt->GetFullName(configName);
makefileStream << "\t/bin/rm -f "
<< this->ConvertToRelativeForMake(
universalFile.c_str())
<< "\n";
}
}
makefileStream << "\n\n";
}
}
}
makefileStream << "\n\n"
<< "# For each target create a dummy rule"
<< "so the target does not have to exist\n";
for (auto const& dummyRule : dummyRules) {
makefileStream << dummyRule << ":\n";
}
}
void cmGlobalXCodeGenerator::OutputXCodeProject(
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
2005-01-24 22:35:54 +00:00
{
if (generators.empty()) {
2005-01-24 22:35:54 +00:00
return;
}
// Skip local generators that are excluded from this project.
for (auto generator : generators) {
if (this->IsExcluded(root, generator)) {
continue;
}
}
if (!this->CreateXCodeObjects(root, generators)) {
return;
}
std::string xcodeDir = root->GetCurrentBinaryDirectory();
2005-01-24 22:35:54 +00:00
xcodeDir += "/";
xcodeDir += root->GetProjectName();
xcodeDir += ".xcodeproj";
2005-01-24 22:35:54 +00:00
cmSystemTools::MakeDirectory(xcodeDir.c_str());
std::string xcodeProjFile = xcodeDir + "/project.pbxproj";
cmGeneratedFileStream fout(xcodeProjFile.c_str());
2005-01-24 22:35:54 +00:00
fout.SetCopyIfDifferent(true);
if (!fout) {
2005-01-24 22:35:54 +00:00
return;
}
2005-01-24 22:35:54 +00:00
this->WriteXCodePBXProj(fout, root, generators);
// Since the lowest available Xcode version for testing was 6.4,
// I'm setting this as a limit then
if (this->XcodeVersion >= 64) {
if (root->GetMakefile()->GetCMakeInstance()->GetIsInTryCompile() ||
root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_SCHEME")) {
this->OutputXCodeSharedSchemes(xcodeDir);
this->OutputXCodeWorkspaceSettings(xcodeDir);
}
}
2005-01-24 22:35:54 +00:00
this->ClearXCodeObjects();
// Since this call may have created new cache entries, save the cache:
//
root->GetMakefile()->GetCMakeInstance()->SaveCache(
root->GetBinaryDirectory());
2005-01-24 22:35:54 +00:00
}
void cmGlobalXCodeGenerator::OutputXCodeSharedSchemes(
const std::string& xcProjDir)
{
// collect all tests for the targets
std::map<std::string, cmXCodeScheme::TestObjects> testables;
for (auto obj : this->XCodeObjects) {
if (obj->GetType() != cmXCodeObject::OBJECT ||
obj->GetIsA() != cmXCodeObject::PBXNativeTarget) {
continue;
}
if (!obj->GetTarget()->IsXCTestOnApple()) {
continue;
}
const char* testee = obj->GetTarget()->GetProperty("XCTEST_TESTEE");
if (!testee) {
continue;
}
testables[testee].push_back(obj);
}
// generate scheme
for (auto obj : this->XCodeObjects) {
if (obj->GetType() == cmXCodeObject::OBJECT &&
(obj->GetIsA() == cmXCodeObject::PBXNativeTarget ||
obj->GetIsA() == cmXCodeObject::PBXAggregateTarget)) {
const std::string& targetName = obj->GetTarget()->GetName();
cmXCodeScheme schm(obj, testables[targetName],
this->CurrentConfigurationTypes, this->XcodeVersion);
schm.WriteXCodeSharedScheme(xcProjDir,
this->RelativeToSource(xcProjDir.c_str()));
}
}
}
2017-02-24 15:19:14 +00:00
void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings(
const std::string& xcProjDir)
{
std::string xcodeSharedDataDir = xcProjDir;
xcodeSharedDataDir += "/project.xcworkspace/xcshareddata";
cmSystemTools::MakeDirectory(xcodeSharedDataDir);
std::string workspaceSettingsFile = xcodeSharedDataDir;
workspaceSettingsFile += "/WorkspaceSettings.xcsettings";
cmGeneratedFileStream fout(workspaceSettingsFile.c_str());
fout.SetCopyIfDifferent(true);
if (!fout) {
return;
}
cmXMLWriter xout(fout);
xout.StartDocument();
xout.Doctype("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\""
"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
xout.StartElement("plist");
xout.Attribute("version", "1.0");
xout.StartElement("dict");
xout.Element("key", "IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded");
xout.Element("false");
xout.EndElement(); // dict
xout.EndElement(); // plist
xout.EndDocument();
}
void cmGlobalXCodeGenerator::WriteXCodePBXProj(std::ostream& fout,
cmLocalGenerator*,
std::vector<cmLocalGenerator*>&)
2005-01-24 22:35:54 +00:00
{
SortXCodeObjects();
2005-01-24 22:35:54 +00:00
fout << "// !$*UTF8*$!\n";
fout << "{\n";
cmXCodeObject::Indent(1, fout);
fout << "archiveVersion = 1;\n";
cmXCodeObject::Indent(1, fout);
fout << "classes = {\n";
cmXCodeObject::Indent(1, fout);
fout << "};\n";
cmXCodeObject::Indent(1, fout);
if (this->XcodeVersion >= 32) {
fout << "objectVersion = 46;\n";
} else if (this->XcodeVersion >= 31) {
fout << "objectVersion = 45;\n";
} else {
fout << "objectVersion = 44;\n";
}
cmXCode21Object::PrintList(this->XCodeObjects, fout);
2005-01-24 22:35:54 +00:00
cmXCodeObject::Indent(1, fout);
2015-04-07 17:19:00 +00:00
fout << "rootObject = " << this->RootObject->GetId()
<< " /* Project object */;\n";
2005-01-24 22:35:54 +00:00
fout << "}\n";
}
const char* cmGlobalXCodeGenerator::GetCMakeCFGIntDir() const
{
return "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
}
std::string cmGlobalXCodeGenerator::ExpandCFGIntDir(
const std::string& str, const std::string& config) const
{
std::string replace1 = "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
std::string replace2 = "$(CONFIGURATION)";
std::string tmp = str;
for (std::string::size_type i = tmp.find(replace1); i != std::string::npos;
i = tmp.find(replace1, i)) {
tmp.replace(i, replace1.size(), config);
i += config.size();
}
for (std::string::size_type i = tmp.find(replace2); i != std::string::npos;
i = tmp.find(replace2, i)) {
tmp.replace(i, replace2.size(), config);
i += config.size();
}
return tmp;
}
2005-02-11 19:25:05 +00:00
void cmGlobalXCodeGenerator::GetDocumentation(cmDocumentationEntry& entry)
2005-01-24 22:35:54 +00:00
{
entry.Name = cmGlobalXCodeGenerator::GetActualName();
entry.Brief = "Generate Xcode project files.";
2005-01-24 22:35:54 +00:00
}
2005-02-18 18:32:51 +00:00
2005-02-24 22:46:49 +00:00
std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(const char* p)
2005-02-18 18:32:51 +00:00
{
return cmSystemTools::ConvertToOutputPath(p);
2005-02-24 22:46:49 +00:00
}
std::string cmGlobalXCodeGenerator::RelativeToSource(const char* p)
{
// We force conversion because Xcode breakpoints do not work unless
// they are in a file named relative to the source tree.
return cmOutputConverter::ForceToRelativePath(
cmSystemTools::JoinPath(this->ProjectSourceDirectoryComponents), p);
}
std::string cmGlobalXCodeGenerator::RelativeToBinary(const char* p)
{
return this->CurrentLocalGenerator->ConvertToRelativePath(
cmSystemTools::JoinPath(this->ProjectOutputDirectoryComponents), p);
}
std::string cmGlobalXCodeGenerator::XCodeEscapePath(const std::string& p)
2005-02-24 20:34:14 +00:00
{
2017-05-30 19:37:46 +00:00
if (p.find(' ') != std::string::npos) {
std::string t = "\"";
t += p;
t += "\"";
return t;
}
return p;
2005-02-24 20:34:14 +00:00
}
void cmGlobalXCodeGenerator::AppendDirectoryForConfig(
const std::string& prefix, const std::string& config,
const std::string& suffix, std::string& dir)
{
if (!config.empty()) {
dir += prefix;
dir += config;
dir += suffix;
}
}
std::string cmGlobalXCodeGenerator::LookupFlags(
const std::string& varNamePrefix, const std::string& varNameLang,
const std::string& varNameSuffix, const std::string& default_flags)
{
if (!varNameLang.empty()) {
std::string varName = varNamePrefix;
varName += varNameLang;
varName += varNameSuffix;
if (const char* varValue = this->CurrentMakefile->GetDefinition(varName)) {
if (*varValue) {
return varValue;
}
}
}
return default_flags;
}
void cmGlobalXCodeGenerator::AppendDefines(BuildObjectListOrString& defs,
const char* defines_list,
bool dflag)
{
// Skip this if there are no definitions.
if (!defines_list) {
return;
}
// Expand the list of definitions.
std::vector<std::string> defines;
cmSystemTools::ExpandListArgument(defines_list, defines);
// Store the definitions in the string.
this->AppendDefines(defs, defines, dflag);
}
void cmGlobalXCodeGenerator::AppendDefines(
BuildObjectListOrString& defs, std::vector<std::string> const& defines,
bool dflag)
{
// GCC_PREPROCESSOR_DEFINITIONS is a space-separated list of definitions.
std::string def;
for (auto const& define : defines) {
// Start with -D if requested.
def = dflag ? "-D" : "";
def += define;
// Append the flag with needed escapes.
std::string tmp;
this->AppendFlag(tmp, def);
defs.Add(tmp);
}
}
void cmGlobalXCodeGenerator::AppendFlag(std::string& flags,
std::string const& flag)
{
// Short-circuit for an empty flag.
if (flag.empty()) {
return;
}
// Separate from previous flags.
if (!flags.empty()) {
flags += " ";
}
// Check if the flag needs quoting.
bool quoteFlag =
2017-05-30 19:37:46 +00:00
flag.find_first_of("`~!@#$%^&*()+={}[]|:;\"'<>,.? ") != std::string::npos;
// We escape a flag as follows:
// - Place each flag in single quotes ''
// - Escape a single quote as \'
// - Escape a backslash as \\ since it itself is an escape
// Note that in the code below we need one more level of escapes for
// C string syntax in this source file.
//
// The final level of escaping is done when the string is stored
// into the project file by cmXCodeObject::PrintString.
if (quoteFlag) {
// Open single quote.
flags += "'";
}
// Flag value with escaped quotes and backslashes.
for (auto c : flag) {
if (c == '\'') {
if (this->XcodeVersion >= 40) {
flags += "'\\''";
} else {
flags += "\\'";
}
} else if (c == '\\') {
flags += "\\\\";
} else {
flags += c;
}
}
if (quoteFlag) {
// Close single quote.
flags += "'";
}
}
std::string cmGlobalXCodeGenerator::ComputeInfoPListLocation(
cmGeneratorTarget* target)
{
std::string plist = target->GetLocalGenerator()->GetCurrentBinaryDirectory();
plist += cmake::GetCMakeFilesDirectory();
plist += "/";
2015-10-19 19:23:29 +00:00
plist += target->GetName();
plist += ".dir/Info.plist";
return plist;
}
// Return true if the generated build tree may contain multiple builds.
// i.e. "Can I build Debug and Release in the same tree?"
bool cmGlobalXCodeGenerator::IsMultiConfig() const
{
// Newer Xcode versions are multi config:
return true;
}
bool cmGlobalXCodeGenerator::HasKnownObjectFileLocation(
std::string* reason) const
{
if (this->ObjectDirArch.find('$') != std::string::npos) {
2017-08-22 21:42:36 +00:00
if (reason != nullptr) {
*reason = " under Xcode with multiple architectures";
}
return false;
}
return true;
}
bool cmGlobalXCodeGenerator::UseEffectivePlatformName(cmMakefile* mf) const
{
const char* epnValue =
this->GetCMakeInstance()->GetState()->GetGlobalProperty(
"XCODE_EMIT_EFFECTIVE_PLATFORM_NAME");
if (!epnValue) {
return mf->PlatformIsAppleIos();
}
return cmSystemTools::IsOn(epnValue);
}
bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
{
// Xcode determines Resource location itself
return true;
}
void cmGlobalXCodeGenerator::ComputeTargetObjectDirectory(
cmGeneratorTarget* gt) const
{
std::string configName = this->GetCMakeCFGIntDir();
std::string dir =
this->GetObjectsNormalDirectory("$(PROJECT_NAME)", configName, gt);
dir += this->ObjectDirArch;
dir += "/";
gt->ObjectDirectory = dir;
}