ad12b5a13c8a2dd80acef6b9a0b95cf0b40c73cf
[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   std::vector<cmTarget*> allTargets;
43   {
44   std::string expectedTargets;
45   std::string sep;
46   for(std::vector<cmTargetExport*>::const_iterator
47         tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
48       tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
49     {
50     expectedTargets += sep + this->Namespace + (*tei)->Target->GetName();
51     sep = " ";
52     cmTargetExport const* te = *tei;
53     if(this->ExportedTargets.insert(te->Target).second)
54       {
55       allTargets.push_back(te->Target);
56       }
57     else
58       {
59       cmOStringStream e;
60       e << "install(EXPORT \""
61         << this->IEGen->GetExportSet()->GetName()
62         << "\" ...) " << "includes target \"" << te->Target->GetName()
63         << "\" more than once in the export set.";
64       cmSystemTools::Error(e.str().c_str());
65       return false;
66       }
67     }
68
69   this->GenerateExpectedTargetsCode(os, expectedTargets);
70   }
71
72   // Add code to compute the installation prefix relative to the
73   // import file location.
74   const char* installDest = this->IEGen->GetDestination();
75   if(!cmSystemTools::FileIsFullPath(installDest))
76     {
77     std::string installPrefix =
78       this->IEGen->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
79     std::string absDest = installPrefix + "/" + installDest;
80     std::string absDestS = absDest + "/";
81     os << "# Compute the installation prefix relative to this file.\n"
82        << "get_filename_component(_IMPORT_PREFIX"
83        << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
84     if(strncmp(absDestS.c_str(), "/lib/", 5) == 0 ||
85        strncmp(absDestS.c_str(), "/lib64/", 7) == 0 ||
86        strncmp(absDestS.c_str(), "/usr/lib/", 9) == 0 ||
87        strncmp(absDestS.c_str(), "/usr/lib64/", 11) == 0)
88       {
89       // Handle "/usr move" symlinks created by some Linux distros.
90       os <<
91         "# Use original install prefix when loaded through a\n"
92         "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
93         "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
94         "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
95         "if(_realCurr STREQUAL _realOrig)\n"
96         "  set(_IMPORT_PREFIX \"" << absDest << "\")\n"
97         "endif()\n"
98         "unset(_realOrig)\n"
99         "unset(_realCurr)\n";
100       }
101     std::string dest = installDest;
102     while(!dest.empty())
103       {
104       os <<
105         "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" PATH)\n";
106       dest = cmSystemTools::GetFilenamePath(dest);
107       }
108     os << "\n";
109
110     // Import location properties may reference this variable.
111     this->ImportPrefix = "${_IMPORT_PREFIX}/";
112     }
113
114   std::vector<std::string> missingTargets;
115
116   // Create all the imported targets.
117   for(std::vector<cmTarget*>::const_iterator
118         tei = allTargets.begin();
119       tei != allTargets.end(); ++tei)
120     {
121     cmTarget* te = *tei;
122     this->GenerateImportTargetCode(os, te);
123
124     ImportPropertyMap properties;
125
126     this->PopulateIncludeDirectoriesInterface(te,
127                                   cmGeneratorExpression::InstallInterface,
128                                   properties, missingTargets);
129     this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS",
130                                   te,
131                                   cmGeneratorExpression::InstallInterface,
132                                   properties, missingTargets);
133     this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
134                                   te, properties);
135     this->PopulateCompatibleInterfaceProperties(te, properties);
136
137     this->GenerateInterfaceProperties(te, os, properties);
138     }
139
140
141   // Now load per-configuration properties for them.
142   os << "# Load information for each installed configuration.\n"
143      << "get_filename_component(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"
144      << "file(GLOB CONFIG_FILES \"${_DIR}/"
145      << this->GetConfigImportFileGlob() << "\")\n"
146      << "foreach(f ${CONFIG_FILES})\n"
147      << "  include(${f})\n"
148      << "endforeach()\n"
149      << "\n";
150
151   // Cleanup the import prefix variable.
152   if(!this->ImportPrefix.empty())
153     {
154     os << "# Cleanup temporary variables.\n"
155        << "set(_IMPORT_PREFIX)\n"
156        << "\n";
157     }
158   this->GenerateImportedFileCheckLoop(os);
159
160   // Generate an import file for each configuration.
161   bool result = true;
162   for(std::vector<std::string>::const_iterator
163         ci = this->Configurations.begin();
164       ci != this->Configurations.end(); ++ci)
165     {
166     if(!this->GenerateImportFileConfig(ci->c_str(), missingTargets))
167       {
168       result = false;
169       }
170     }
171
172   this->GenerateMissingTargetsCheckCode(os, missingTargets);
173
174   return result;
175 }
176
177 //----------------------------------------------------------------------------
178 void
179 cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string &input)
180 {
181   std::string::size_type pos = 0;
182   std::string::size_type lastPos = pos;
183
184   while((pos = input.find("$<INSTALL_PREFIX>", lastPos)) != input.npos)
185     {
186     std::string::size_type endPos = pos + sizeof("$<INSTALL_PREFIX>") - 1;
187     input.replace(pos, endPos - pos, "${_IMPORT_PREFIX}");
188     lastPos = endPos;
189     }
190 }
191
192 //----------------------------------------------------------------------------
193 bool
194 cmExportInstallFileGenerator::GenerateImportFileConfig(const char* config,
195                                     std::vector<std::string> &missingTargets)
196 {
197   // Skip configurations not enabled for this export.
198   if(!this->IEGen->InstallsForConfig(config))
199     {
200     return true;
201     }
202
203   // Construct the name of the file to generate.
204   std::string fileName = this->FileDir;
205   fileName += "/";
206   fileName += this->FileBase;
207   fileName += "-";
208   if(config && *config)
209     {
210     fileName += cmSystemTools::LowerCase(config);
211     }
212   else
213     {
214     fileName += "noconfig";
215     }
216   fileName += this->FileExt;
217
218   // Open the output file to generate it.
219   cmGeneratedFileStream exportFileStream(fileName.c_str(), true);
220   if(!exportFileStream)
221     {
222     std::string se = cmSystemTools::GetLastSystemError();
223     cmOStringStream e;
224     e << "cannot write to file \"" << fileName.c_str()
225       << "\": " << se;
226     cmSystemTools::Error(e.str().c_str());
227     return false;
228     }
229   std::ostream& os = exportFileStream;
230
231   // Start with the import file header.
232   this->GenerateImportHeaderCode(os, config);
233
234   // Generate the per-config target information.
235   this->GenerateImportConfig(os, config, missingTargets);
236
237   // End with the import file footer.
238   this->GenerateImportFooterCode(os);
239
240   // Record this per-config import file.
241   this->ConfigImportFiles[config] = fileName;
242
243   return true;
244 }
245
246 //----------------------------------------------------------------------------
247 void
248 cmExportInstallFileGenerator
249 ::GenerateImportTargetsConfig(std::ostream& os,
250                               const char* config, std::string const& suffix,
251                               std::vector<std::string> &missingTargets)
252 {
253   // Add each target in the set to the export.
254   for(std::vector<cmTargetExport*>::const_iterator
255         tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
256       tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
257     {
258     // Collect import properties for this target.
259     cmTargetExport const* te = *tei;
260     ImportPropertyMap properties;
261     std::set<std::string> importedLocations;
262     this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
263                                     properties, importedLocations);
264     this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
265                                     properties, importedLocations);
266     this->SetImportLocationProperty(config, suffix,
267                                     te->RuntimeGenerator, properties,
268                                     importedLocations);
269     this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
270                                     properties, importedLocations);
271     this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
272                                     properties, importedLocations);
273
274     // If any file location was set for the target add it to the
275     // import file.
276     if(!properties.empty())
277       {
278       // Get the rest of the target details.
279       this->SetImportDetailProperties(config, suffix,
280                                       te->Target, properties, missingTargets);
281
282       this->SetImportLinkInterface(config, suffix,
283                                    cmGeneratorExpression::InstallInterface,
284                                    te->Target, properties, missingTargets);
285
286       // TOOD: PUBLIC_HEADER_LOCATION
287       // This should wait until the build feature propagation stuff
288       // is done.  Then this can be a propagated include directory.
289       // this->GenerateImportProperty(config, te->HeaderGenerator,
290       //                              properties);
291
292       // Generate code in the export file.
293       this->GenerateImportPropertyCode(os, config, te->Target, properties);
294       this->GenerateImportedFileChecksCode(os, te->Target, properties,
295                                            importedLocations);
296       }
297     }
298 }
299
300 //----------------------------------------------------------------------------
301 void
302 cmExportInstallFileGenerator
303 ::SetImportLocationProperty(const char* config, std::string const& suffix,
304                             cmInstallTargetGenerator* itgen,
305                             ImportPropertyMap& properties,
306                             std::set<std::string>& importedLocations
307                            )
308 {
309   // Skip rules that do not match this configuration.
310   if(!(itgen && itgen->InstallsForConfig(config)))
311     {
312     return;
313     }
314
315   // Get the target to be installed.
316   cmTarget* target = itgen->GetTarget();
317
318   // Construct the installed location of the target.
319   std::string dest = itgen->GetDestination();
320   std::string value;
321   if(!cmSystemTools::FileIsFullPath(dest.c_str()))
322     {
323     // The target is installed relative to the installation prefix.
324     if(this->ImportPrefix.empty())
325       {
326       this->ComplainAboutImportPrefix(itgen);
327       }
328     value = this->ImportPrefix;
329     }
330   value += dest;
331   value += "/";
332
333   if(itgen->IsImportLibrary())
334     {
335     // Construct the property name.
336     std::string prop = "IMPORTED_IMPLIB";
337     prop += suffix;
338
339     // Append the installed file name.
340     value += itgen->GetInstallFilename(target, config,
341                                        cmInstallTargetGenerator::NameImplib);
342
343     // Store the property.
344     properties[prop] = value;
345     importedLocations.insert(prop);
346     }
347   else
348     {
349     // Construct the property name.
350     std::string prop = "IMPORTED_LOCATION";
351     prop += suffix;
352
353     // Append the installed file name.
354     if(target->IsFrameworkOnApple())
355       {
356       value += itgen->GetInstallFilename(target, config);
357       value += ".framework/";
358       value += itgen->GetInstallFilename(target, config);
359       }
360     else if(target->IsCFBundleOnApple())
361       {
362       const char *ext = target->GetProperty("BUNDLE_EXTENSION");
363       if (!ext)
364         {
365         ext = "bundle";
366         }
367
368       value += itgen->GetInstallFilename(target, config);
369       value += ".";
370       value += ext;
371       value += "/";
372       value += itgen->GetInstallFilename(target, config);
373       }
374     else if(target->IsAppBundleOnApple())
375       {
376       value += itgen->GetInstallFilename(target, config);
377       value += ".app/Contents/MacOS/";
378       value += itgen->GetInstallFilename(target, config);
379       }
380     else
381       {
382       value += itgen->GetInstallFilename(target, config,
383                                          cmInstallTargetGenerator::NameReal);
384       }
385
386     // Store the property.
387     properties[prop] = value;
388     importedLocations.insert(prop);
389     }
390 }
391
392 //----------------------------------------------------------------------------
393 void
394 cmExportInstallFileGenerator::HandleMissingTarget(
395   std::string& link_libs, std::vector<std::string>& missingTargets,
396   cmMakefile* mf, cmTarget* depender, cmTarget* dependee)
397 {
398   std::string name = dependee->GetName();
399   std::vector<std::string> namespaces = this->FindNamespaces(mf, name);
400   int targetOccurrences = (int)namespaces.size();
401   if (targetOccurrences == 1)
402     {
403     std::string missingTarget = namespaces[0];
404     missingTarget += name;
405     link_libs += missingTarget;
406     missingTargets.push_back(missingTarget);
407     }
408   else
409     {
410     // We are not appending, so all exported targets should be
411     // known here.  This is probably user-error.
412     this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
413     }
414 }
415
416 //----------------------------------------------------------------------------
417 std::vector<std::string>
418 cmExportInstallFileGenerator
419 ::FindNamespaces(cmMakefile* mf, const std::string& name)
420 {
421   std::vector<std::string> namespaces;
422   cmGlobalGenerator* gg = mf->GetLocalGenerator()->GetGlobalGenerator();
423   const cmExportSetMap& exportSets = gg->GetExportSets();
424
425   for(cmExportSetMap::const_iterator expIt = exportSets.begin();
426       expIt != exportSets.end();
427       ++expIt)
428     {
429     const cmExportSet* exportSet = expIt->second;
430     std::vector<cmTargetExport*> const* targets =
431                                                  exportSet->GetTargetExports();
432
433     bool containsTarget = false;
434     for(unsigned int i=0; i<targets->size(); i++)
435       {
436       if (name == (*targets)[i]->Target->GetName())
437         {
438         containsTarget = true;
439         break;
440         }
441       }
442
443     if (containsTarget)
444       {
445       std::vector<cmInstallExportGenerator const*> const* installs =
446                                                  exportSet->GetInstallations();
447       for(unsigned int i=0; i<installs->size(); i++)
448         {
449         namespaces.push_back((*installs)[i]->GetNamespace());
450         }
451       }
452     }
453
454   return namespaces;
455 }
456
457
458 //----------------------------------------------------------------------------
459 void
460 cmExportInstallFileGenerator
461 ::ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen)
462 {
463   const char* installDest = this->IEGen->GetDestination();
464   cmOStringStream e;
465   e << "install(EXPORT \""
466     << this->IEGen->GetExportSet()->GetName()
467     << "\") given absolute "
468     << "DESTINATION \"" << installDest << "\" but the export "
469     << "references an installation of target \""
470     << itgen->GetTarget()->GetName() << "\" which has relative "
471     << "DESTINATION \"" << itgen->GetDestination() << "\".";
472   cmSystemTools::Error(e.str().c_str());
473 }
474
475 //----------------------------------------------------------------------------
476 void
477 cmExportInstallFileGenerator
478 ::ComplainAboutMissingTarget(cmTarget* depender,
479                              cmTarget* dependee,
480                              int occurrences)
481 {
482   cmOStringStream e;
483   e << "install(EXPORT \""
484     << this->IEGen->GetExportSet()->GetName()
485     << "\" ...) "
486     << "includes target \"" << depender->GetName()
487     << "\" which requires target \"" << dependee->GetName() << "\" ";
488   if (occurrences == 0)
489     {
490     e << "that is not in the export set.";
491     }
492   else
493     {
494     e << "that is not in this export set, but " << occurrences
495     << " times in others.";
496     }
497   cmSystemTools::Error(e.str().c_str());
498 }