1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmOSXBundleGenerator.h"
4
5 #include <cassert>
6
7 #include "cmGeneratorTarget.h"
8 #include "cmLocalGenerator.h"
9 #include "cmMakefile.h"
10 #include "cmStateTypes.h"
11 #include "cmStringAlgorithms.h"
12 #include "cmSystemTools.h"
13 #include "cmTarget.h"
14
15 class cmSourceFile;
16
cmOSXBundleGenerator(cmGeneratorTarget * target)17 cmOSXBundleGenerator::cmOSXBundleGenerator(cmGeneratorTarget* target)
18 : GT(target)
19 , Makefile(target->Target->GetMakefile())
20 , LocalGenerator(target->GetLocalGenerator())
21 , MacContentFolders(nullptr)
22 {
23 if (this->MustSkip()) {
24 return;
25 }
26 }
27
MustSkip()28 bool cmOSXBundleGenerator::MustSkip()
29 {
30 return !this->GT->HaveWellDefinedOutputFiles();
31 }
32
CreateAppBundle(const std::string & targetName,std::string & outpath,const std::string & config)33 void cmOSXBundleGenerator::CreateAppBundle(const std::string& targetName,
34 std::string& outpath,
35 const std::string& config)
36 {
37 if (this->MustSkip()) {
38 return;
39 }
40
41 // Compute bundle directory names.
42 std::string out = cmStrCat(
43 outpath, '/',
44 this->GT->GetAppBundleDirectory(config, cmGeneratorTarget::FullLevel));
45 cmSystemTools::MakeDirectory(out);
46 this->Makefile->AddCMakeOutputFile(out);
47
48 // Configure the Info.plist file. Note that it needs the executable name
49 // to be set.
50 std::string plist = cmStrCat(
51 outpath, '/',
52 this->GT->GetAppBundleDirectory(config, cmGeneratorTarget::ContentLevel),
53 "/Info.plist");
54 this->LocalGenerator->GenerateAppleInfoPList(this->GT, targetName, plist);
55 this->Makefile->AddCMakeOutputFile(plist);
56 outpath = out;
57 }
58
CreateFramework(const std::string & targetName,const std::string & outpath,const std::string & config,const cmOSXBundleGenerator::SkipParts & skipParts)59 void cmOSXBundleGenerator::CreateFramework(
60 const std::string& targetName, const std::string& outpath,
61 const std::string& config, const cmOSXBundleGenerator::SkipParts& skipParts)
62 {
63 if (this->MustSkip()) {
64 return;
65 }
66
67 assert(this->MacContentFolders);
68
69 // Compute the location of the top-level foo.framework directory.
70 std::string contentdir = cmStrCat(
71 outpath, '/',
72 this->GT->GetFrameworkDirectory(config, cmGeneratorTarget::ContentLevel),
73 '/');
74
75 std::string newoutpath = outpath + "/" +
76 this->GT->GetFrameworkDirectory(config, cmGeneratorTarget::FullLevel);
77
78 std::string frameworkVersion = this->GT->GetFrameworkVersion();
79
80 std::string name = cmSystemTools::GetFilenameName(targetName);
81 if (!skipParts.infoPlist) {
82 // Configure the Info.plist file
83 std::string plist = newoutpath;
84 if (!this->Makefile->PlatformIsAppleEmbedded()) {
85 // Put the Info.plist file into the Resources directory.
86 this->MacContentFolders->insert("Resources");
87 plist += "/Resources";
88 }
89 plist += "/Info.plist";
90 this->LocalGenerator->GenerateFrameworkInfoPList(this->GT, name, plist);
91 }
92
93 // Generate Versions directory only for MacOSX frameworks
94 if (this->Makefile->PlatformIsAppleEmbedded()) {
95 return;
96 }
97
98 // TODO: Use the cmMakefileTargetGenerator::ExtraFiles vector to
99 // drive rules to create these files at build time.
100 std::string oldName;
101 std::string newName;
102
103 // Make foo.framework/Versions
104 std::string versions = cmStrCat(contentdir, "Versions");
105 cmSystemTools::MakeDirectory(versions);
106
107 // Make foo.framework/Versions/version
108 cmSystemTools::MakeDirectory(newoutpath);
109
110 // Current -> version
111 oldName = frameworkVersion;
112 newName = cmStrCat(versions, "/Current");
113 cmSystemTools::RemoveFile(newName);
114 cmSystemTools::CreateSymlink(oldName, newName);
115 this->Makefile->AddCMakeOutputFile(newName);
116
117 // foo -> Versions/Current/foo
118 oldName = cmStrCat("Versions/Current/", name);
119 newName = cmStrCat(contentdir, name);
120 cmSystemTools::RemoveFile(newName);
121 cmSystemTools::CreateSymlink(oldName, newName);
122 this->Makefile->AddCMakeOutputFile(newName);
123
124 // Resources -> Versions/Current/Resources
125 if (this->MacContentFolders->find("Resources") !=
126 this->MacContentFolders->end()) {
127 oldName = "Versions/Current/Resources";
128 newName = cmStrCat(contentdir, "Resources");
129 cmSystemTools::RemoveFile(newName);
130 cmSystemTools::CreateSymlink(oldName, newName);
131 this->Makefile->AddCMakeOutputFile(newName);
132 }
133
134 // Headers -> Versions/Current/Headers
135 if (this->MacContentFolders->find("Headers") !=
136 this->MacContentFolders->end()) {
137 oldName = "Versions/Current/Headers";
138 newName = cmStrCat(contentdir, "Headers");
139 cmSystemTools::RemoveFile(newName);
140 cmSystemTools::CreateSymlink(oldName, newName);
141 this->Makefile->AddCMakeOutputFile(newName);
142 }
143
144 // PrivateHeaders -> Versions/Current/PrivateHeaders
145 if (this->MacContentFolders->find("PrivateHeaders") !=
146 this->MacContentFolders->end()) {
147 oldName = "Versions/Current/PrivateHeaders";
148 newName = cmStrCat(contentdir, "PrivateHeaders");
149 cmSystemTools::RemoveFile(newName);
150 cmSystemTools::CreateSymlink(oldName, newName);
151 this->Makefile->AddCMakeOutputFile(newName);
152 }
153 }
154
CreateCFBundle(const std::string & targetName,const std::string & root,const std::string & config)155 void cmOSXBundleGenerator::CreateCFBundle(const std::string& targetName,
156 const std::string& root,
157 const std::string& config)
158 {
159 if (this->MustSkip()) {
160 return;
161 }
162
163 // Compute bundle directory names.
164 std::string out = cmStrCat(
165 root, '/',
166 this->GT->GetCFBundleDirectory(config, cmGeneratorTarget::FullLevel));
167 cmSystemTools::MakeDirectory(out);
168 this->Makefile->AddCMakeOutputFile(out);
169
170 // Configure the Info.plist file. Note that it needs the executable name
171 // to be set.
172 std::string plist = cmStrCat(
173 root, '/',
174 this->GT->GetCFBundleDirectory(config, cmGeneratorTarget::ContentLevel),
175 "/Info.plist");
176 std::string name = cmSystemTools::GetFilenameName(targetName);
177 this->LocalGenerator->GenerateAppleInfoPList(this->GT, name, plist);
178 this->Makefile->AddCMakeOutputFile(plist);
179 }
180
GenerateMacOSXContentStatements(std::vector<cmSourceFile const * > const & sources,MacOSXContentGeneratorType * generator,const std::string & config)181 void cmOSXBundleGenerator::GenerateMacOSXContentStatements(
182 std::vector<cmSourceFile const*> const& sources,
183 MacOSXContentGeneratorType* generator, const std::string& config)
184 {
185 if (this->MustSkip()) {
186 return;
187 }
188
189 for (cmSourceFile const* source : sources) {
190 cmGeneratorTarget::SourceFileFlags tsFlags =
191 this->GT->GetTargetSourceFileFlags(source);
192 if (tsFlags.Type != cmGeneratorTarget::SourceFileTypeNormal) {
193 (*generator)(*source, tsFlags.MacFolder, config);
194 }
195 }
196 }
197
InitMacOSXContentDirectory(const char * pkgloc,const std::string & config)198 std::string cmOSXBundleGenerator::InitMacOSXContentDirectory(
199 const char* pkgloc, const std::string& config)
200 {
201 // Construct the full path to the content subdirectory.
202
203 std::string macdir = cmStrCat(this->GT->GetMacContentDirectory(
204 config, cmStateEnums::RuntimeBinaryArtifact),
205 '/', pkgloc);
206 cmSystemTools::MakeDirectory(macdir);
207
208 // Record use of this content location. Only the first level
209 // directory is needed.
210 {
211 std::string loc = pkgloc;
212 loc = loc.substr(0, loc.find('/'));
213 this->MacContentFolders->insert(loc);
214 }
215
216 return macdir;
217 }
218