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