78417317266b9f71cf4b28949b09522f652700c9
[platform/upstream/cmake.git] / Source / cmExportInstallFileGenerator.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmExportInstallFileGenerator.h"
13
14 #include "cmExportSet.h"
15 #include "cmExportSetMap.h"
16 #include "cmGeneratedFileStream.h"
17 #include "cmGlobalGenerator.h"
18 #include "cmLocalGenerator.h"
19 #include "cmInstallExportGenerator.h"
20 #include "cmInstallTargetGenerator.h"
21 #include "cmTargetExport.h"
22
23 //----------------------------------------------------------------------------
24 cmExportInstallFileGenerator
25 ::cmExportInstallFileGenerator(cmInstallExportGenerator* iegen):
26   IEGen(iegen)
27 {
28 }
29
30 //----------------------------------------------------------------------------
31 std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
32 {
33   std::string glob = this->FileBase;
34   glob += "-*";
35   glob += this->FileExt;
36   return glob;
37 }
38
39 //----------------------------------------------------------------------------
40 bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
41 {
42   // Create all the imported targets.
43   for(std::vector<cmTargetExport*>::const_iterator
44         tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
45       tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
46     {
47     cmTargetExport const* te = *tei;
48     if(this->ExportedTargets.insert(te->Target).second)
49       {
50       this->GenerateImportTargetCode(os, te->Target);
51       }
52     else
53       {
54       cmOStringStream e;
55       e << "INSTALL(EXPORT \""
56         << this->IEGen->GetExportSet()->GetName()
57         << "\" ...) " << "includes target \"" << te->Target->GetName()
58         << "\" more than once in the export set.";
59       cmSystemTools::Error(e.str().c_str());
60       return false;
61       }
62     }
63
64   // Now load per-configuration properties for them.
65   os << "# Load information for each installed configuration.\n"
66      << "GET_FILENAME_COMPONENT(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"
67      << "FILE(GLOB CONFIG_FILES \"${_DIR}/"
68      << this->GetConfigImportFileGlob() << "\")\n"
69      << "FOREACH(f ${CONFIG_FILES})\n"
70      << "  INCLUDE(${f})\n"
71      << "ENDFOREACH(f)\n"
72      << "\n";
73
74   // Generate an import file for each configuration.
75   bool result = true;
76   for(std::vector<std::string>::const_iterator
77         ci = this->Configurations.begin();
78       ci != this->Configurations.end(); ++ci)
79     {
80     if(!this->GenerateImportFileConfig(ci->c_str()))
81       {
82       result = false;
83       }
84     }
85   return result;
86 }
87
88 //----------------------------------------------------------------------------
89 bool
90 cmExportInstallFileGenerator::GenerateImportFileConfig(const char* config)
91 {
92   // Skip configurations not enabled for this export.
93   if(!this->IEGen->InstallsForConfig(config))
94     {
95     return true;
96     }
97
98   // Construct the name of the file to generate.
99   std::string fileName = this->FileDir;
100   fileName += "/";
101   fileName += this->FileBase;
102   fileName += "-";
103   if(config && *config)
104     {
105     fileName += cmSystemTools::LowerCase(config);
106     }
107   else
108     {
109     fileName += "noconfig";
110     }
111   fileName += this->FileExt;
112
113   // Open the output file to generate it.
114   cmGeneratedFileStream exportFileStream(fileName.c_str(), true);
115   if(!exportFileStream)
116     {
117     std::string se = cmSystemTools::GetLastSystemError();
118     cmOStringStream e;
119     e << "cannot write to file \"" << fileName.c_str()
120       << "\": " << se;
121     cmSystemTools::Error(e.str().c_str());
122     return false;
123     }
124   std::ostream& os = exportFileStream;
125
126   // Start with the import file header.
127   this->GenerateImportHeaderCode(os, config);
128
129   // Generate the per-config target information.
130   this->GenerateImportConfig(os, config);
131
132   // End with the import file footer.
133   this->GenerateImportFooterCode(os);
134
135   // Record this per-config import file.
136   this->ConfigImportFiles[config] = fileName;
137
138   return true;
139 }
140
141 //----------------------------------------------------------------------------
142 void
143 cmExportInstallFileGenerator
144 ::GenerateImportTargetsConfig(std::ostream& os,
145                               const char* config, std::string const& suffix)
146 {
147   // Add code to compute the installation prefix relative to the
148   // import file location.
149   const char* installDest = this->IEGen->GetDestination();
150   if(!cmSystemTools::FileIsFullPath(installDest))
151     {
152     std::string dest = installDest;
153     os << "# Compute the installation prefix relative to this file.\n"
154        << "GET_FILENAME_COMPONENT(_IMPORT_PREFIX "
155        << "\"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
156     while(!dest.empty())
157       {
158       os <<
159         "GET_FILENAME_COMPONENT(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" PATH)\n";
160       dest = cmSystemTools::GetFilenamePath(dest);
161       }
162     os << "\n";
163
164     // Import location properties may reference this variable.
165     this->ImportPrefix = "${_IMPORT_PREFIX}/";
166     }
167
168   // Add each target in the set to the export.
169   for(std::vector<cmTargetExport*>::const_iterator
170         tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
171       tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
172     {
173     // Collect import properties for this target.
174     cmTargetExport const* te = *tei;
175     ImportPropertyMap properties;
176     std::set<std::string> importedLocations;
177     this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
178                                     properties, importedLocations);
179     this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
180                                     properties, importedLocations);
181     this->SetImportLocationProperty(config, suffix,
182                                     te->RuntimeGenerator, properties,
183                                     importedLocations);
184     this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
185                                     properties, importedLocations);
186     this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
187                                     properties, importedLocations);
188
189     // If any file location was set for the target add it to the
190     // import file.
191     if(!properties.empty())
192       {
193       // Get the rest of the target details.
194       std::vector<std::string> missingTargets;
195       this->SetImportDetailProperties(config, suffix,
196                                       te->Target, properties, missingTargets);
197
198       // TOOD: PUBLIC_HEADER_LOCATION
199       // This should wait until the build feature propagation stuff
200       // is done.  Then this can be a propagated include directory.
201       // this->GenerateImportProperty(config, te->HeaderGenerator,
202       //                              properties);
203
204       // Generate code in the export file.
205       this->GenerateMissingTargetsCheckCode(os, missingTargets);
206       this->GenerateImportPropertyCode(os, config, te->Target, properties);
207       this->GenerateImportedFileChecksCode(os, te->Target, properties,
208                                            importedLocations);
209       }
210     }
211
212   this->GenerateImportedFileCheckLoop(os);
213
214   // Cleanup the import prefix variable.
215   if(!this->ImportPrefix.empty())
216     {
217     os << "# Cleanup temporary variables.\n"
218        << "SET(_IMPORT_PREFIX)\n"
219        << "\n";
220     }
221 }
222
223 //----------------------------------------------------------------------------
224 void
225 cmExportInstallFileGenerator
226 ::SetImportLocationProperty(const char* config, std::string const& suffix,
227                             cmInstallTargetGenerator* itgen,
228                             ImportPropertyMap& properties,
229                             std::set<std::string>& importedLocations
230                            )
231 {
232   // Skip rules that do not match this configuration.
233   if(!(itgen && itgen->InstallsForConfig(config)))
234     {
235     return;
236     }
237
238   // Get the target to be installed.
239   cmTarget* target = itgen->GetTarget();
240
241   // Construct the installed location of the target.
242   std::string dest = itgen->GetDestination();
243   std::string value;
244   if(!cmSystemTools::FileIsFullPath(dest.c_str()))
245     {
246     // The target is installed relative to the installation prefix.
247     if(this->ImportPrefix.empty())
248       {
249       this->ComplainAboutImportPrefix(itgen);
250       }
251     value = this->ImportPrefix;
252     }
253   value += dest;
254   value += "/";
255
256   if(itgen->IsImportLibrary())
257     {
258     // Construct the property name.
259     std::string prop = "IMPORTED_IMPLIB";
260     prop += suffix;
261
262     // Append the installed file name.
263     value += itgen->GetInstallFilename(target, config,
264                                        cmInstallTargetGenerator::NameImplib);
265
266     // Store the property.
267     properties[prop] = value;
268     importedLocations.insert(prop);
269     }
270   else
271     {
272     // Construct the property name.
273     std::string prop = "IMPORTED_LOCATION";
274     prop += suffix;
275
276     // Append the installed file name.
277     if(target->IsFrameworkOnApple())
278       {
279       value += itgen->GetInstallFilename(target, config);
280       value += ".framework/";
281       value += itgen->GetInstallFilename(target, config);
282       }
283     else if(target->IsCFBundleOnApple())
284       {
285       const char *ext = target->GetProperty("BUNDLE_EXTENSION");
286       if (!ext)
287         {
288         ext = "bundle";
289         }
290
291       value += itgen->GetInstallFilename(target, config);
292       value += ".";
293       value += ext;
294       value += "/";
295       value += itgen->GetInstallFilename(target, config);
296       }
297     else if(target->IsAppBundleOnApple())
298       {
299       value += itgen->GetInstallFilename(target, config);
300       value += ".app/Contents/MacOS/";
301       value += itgen->GetInstallFilename(target, config);
302       }
303     else
304       {
305       value += itgen->GetInstallFilename(target, config,
306                                          cmInstallTargetGenerator::NameReal);
307       }
308
309     // Store the property.
310     properties[prop] = value;
311     importedLocations.insert(prop);
312     }
313 }
314
315 //----------------------------------------------------------------------------
316 void
317 cmExportInstallFileGenerator::HandleMissingTarget(
318   std::string& link_libs, std::vector<std::string>& missingTargets,
319   cmMakefile* mf, cmTarget* depender, cmTarget* dependee)
320 {
321   std::string name = dependee->GetName();
322   std::vector<std::string> namespaces = this->FindNamespaces(mf, name);
323   int targetOccurrences = (int)namespaces.size();
324   if (targetOccurrences == 1)
325     {
326     std::string missingTarget = namespaces[0];
327     missingTarget += name;
328     link_libs += missingTarget;
329     missingTargets.push_back(missingTarget);
330     }
331   else
332     {
333     // We are not appending, so all exported targets should be
334     // known here.  This is probably user-error.
335     this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
336     }
337 }
338
339 //----------------------------------------------------------------------------
340 std::vector<std::string>
341 cmExportInstallFileGenerator
342 ::FindNamespaces(cmMakefile* mf, const std::string& name)
343 {
344   std::vector<std::string> namespaces;
345   cmGlobalGenerator* gg = mf->GetLocalGenerator()->GetGlobalGenerator();
346   const cmExportSetMap& exportSets = gg->GetExportSets();
347
348   for(cmExportSetMap::const_iterator expIt = exportSets.begin();
349       expIt != exportSets.end();
350       ++expIt)
351     {
352     const cmExportSet* exportSet = expIt->second;
353     std::vector<cmTargetExport*> const* targets =
354                                                  exportSet->GetTargetExports();
355
356     bool containsTarget = false;
357     for(unsigned int i=0; i<targets->size(); i++)
358       {
359       if (name == (*targets)[i]->Target->GetName())
360         {
361         containsTarget = true;
362         break;
363         }
364       }
365
366     if (containsTarget)
367       {
368       std::vector<cmInstallExportGenerator const*> const* installs =
369                                                  exportSet->GetInstallations();
370       for(unsigned int i=0; i<installs->size(); i++)
371         {
372         namespaces.push_back((*installs)[i]->GetNamespace());
373         }
374       }
375     }
376
377   return namespaces;
378 }
379
380
381 //----------------------------------------------------------------------------
382 void
383 cmExportInstallFileGenerator
384 ::ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen)
385 {
386   const char* installDest = this->IEGen->GetDestination();
387   cmOStringStream e;
388   e << "INSTALL(EXPORT \""
389     << this->IEGen->GetExportSet()->GetName()
390     << "\") given absolute "
391     << "DESTINATION \"" << installDest << "\" but the export "
392     << "references an installation of target \""
393     << itgen->GetTarget()->GetName() << "\" which has relative "
394     << "DESTINATION \"" << itgen->GetDestination() << "\".";
395   cmSystemTools::Error(e.str().c_str());
396 }
397
398 //----------------------------------------------------------------------------
399 void
400 cmExportInstallFileGenerator
401 ::ComplainAboutMissingTarget(cmTarget* depender,
402                              cmTarget* dependee,
403                              int occurrences)
404 {
405   cmOStringStream e;
406   e << "INSTALL(EXPORT \""
407     << this->IEGen->GetExportSet()->GetName()
408     << "\" ...) "
409     << "includes target \"" << depender->GetName()
410     << "\" which requires target \"" << dependee->GetName() << "\" ";
411   if (occurrences == 0)
412     {
413     e << "that is not in the export set.";
414     }
415   else
416     {
417     e << "that is not in this export set, but " << occurrences
418     << " times in others.";
419     }
420   cmSystemTools::Error(e.str().c_str());
421 }