cmExportInstallFileGenerator.cxx 18.5 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
12
13
#include "cmExportInstallFileGenerator.h"

14
15
#include "cmExportSet.h"
#include "cmExportSetMap.h"
16
#include "cmGeneratedFileStream.h"
17
18
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
19
20
#include "cmInstallExportGenerator.h"
#include "cmInstallTargetGenerator.h"
21
#include "cmTargetExport.h"
22
#include "cmAlgorithms.h"
23
24
25
26

//----------------------------------------------------------------------------
cmExportInstallFileGenerator
::cmExportInstallFileGenerator(cmInstallExportGenerator* iegen):
27
  IEGen(iegen)
28
29
30
{
}

31
32
33
34
35
36
37
38
39
//----------------------------------------------------------------------------
std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
{
  std::string glob = this->FileBase;
  glob += "-*";
  glob += this->FileExt;
  return glob;
}

40
41
42
//----------------------------------------------------------------------------
bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
{
43
  std::vector<cmTargetExport*> allTargets;
44
45
46
47
48
49
50
  {
  std::string expectedTargets;
  std::string sep;
  for(std::vector<cmTargetExport*>::const_iterator
        tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
      tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
    {
Stephen Kelly's avatar
Stephen Kelly committed
51
    expectedTargets += sep + this->Namespace + (*tei)->Target->GetExportName();
52
    sep = " ";
53
    cmTargetExport * te = *tei;
54
55
    if(this->ExportedTargets.insert(te->Target).second)
      {
56
      allTargets.push_back(te);
57
58
59
      }
    else
      {
60
      std::ostringstream e;
61
      e << "install(EXPORT \""
62
63
        << this->IEGen->GetExportSet()->GetName()
        << "\" ...) " << "includes target \"" << te->Target->GetName()
64
65
66
67
        << "\" more than once in the export set.";
      cmSystemTools::Error(e.str().c_str());
      return false;
      }
68
69
    }

70
71
72
  this->GenerateExpectedTargetsCode(os, expectedTargets);
  }

73
74
75
76
  // Set an _IMPORT_PREFIX variable for import location properties
  // to reference if they are relative to the install prefix.
  std::string installPrefix =
    this->IEGen->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
77
78
  std::string const& expDest = this->IEGen->GetDestination();
  if(cmSystemTools::FileIsFullPath(expDest))
79
    {
80
81
82
83
84
85
86
87
88
89
90
    // The export file is being installed to an absolute path so the
    // package is not relocatable.  Use the configured install prefix.
    os <<
      "# The installation prefix configured by this project.\n"
      "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
      "\n";
    }
  else
    {
    // Add code to compute the installation prefix relative to the
    // import file location.
91
    std::string absDest = installPrefix + "/" + expDest;
92
93
94
95
    std::string absDestS = absDest + "/";
    os << "# Compute the installation prefix relative to this file.\n"
       << "get_filename_component(_IMPORT_PREFIX"
       << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
96
97
98
99
    if(cmHasLiteralPrefix(absDestS.c_str(), "/lib/") ||
       cmHasLiteralPrefix(absDestS.c_str(), "/lib64/") ||
       cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib/") ||
       cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib64/"))
100
      {
101
102
103
104
105
106
107
108
109
110
111
      // Handle "/usr move" symlinks created by some Linux distros.
      os <<
        "# Use original install prefix when loaded through a\n"
        "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
        "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
        "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
        "if(_realCurr STREQUAL _realOrig)\n"
        "  set(_IMPORT_PREFIX \"" << absDest << "\")\n"
        "endif()\n"
        "unset(_realOrig)\n"
        "unset(_realCurr)\n";
112
      }
113
    std::string dest = expDest;
114
    while(!dest.empty())
115
      {
116
117
118
      os <<
        "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" PATH)\n";
      dest = cmSystemTools::GetFilenamePath(dest);
119
      }
120
    os << "\n";
121
122
    }

123
124
  std::vector<std::string> missingTargets;

125
  bool require2_8_12 = false;
126
  bool require3_0_0 = false;
127
  bool require3_1_0 = false;
128
  bool requiresConfigFiles = false;
129
  // Create all the imported targets.
130
  for(std::vector<cmTargetExport*>::const_iterator
131
132
133
        tei = allTargets.begin();
      tei != allTargets.end(); ++tei)
    {
134
    cmTarget* te = (*tei)->Target;
135
136
137
138

    requiresConfigFiles = requiresConfigFiles
                              || te->GetType() != cmTarget::INTERFACE_LIBRARY;

139
    this->GenerateImportTargetCode(os, te);
140
141
142

    ImportPropertyMap properties;

143
    this->PopulateIncludeDirectoriesInterface(*tei,
144
145
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
146
147
148
    this->PopulateSourcesInterface(*tei,
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
149
150
151
    this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES",
                                  te,
                                  cmGeneratorExpression::InstallInterface,
152
153
154
155
156
                                  properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS",
                                  te,
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
157
158
159
160
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS",
                                  te,
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
161
162
163
164
    this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS",
                                  te,
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
165
166
167
168
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES",
                                  te,
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
169
170
171
172
173
174
175
176
177
178
179
180
181
182

    const bool newCMP0022Behavior =
                              te->GetPolicyStatusCMP0022() != cmPolicies::WARN
                           && te->GetPolicyStatusCMP0022() != cmPolicies::OLD;
    if (newCMP0022Behavior)
      {
      if (this->PopulateInterfaceLinkLibrariesProperty(te,
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets)
          && !this->ExportOld)
        {
        require2_8_12 = true;
        }
      }
183
184
    if (te->GetType() == cmTarget::INTERFACE_LIBRARY)
      {
185
      require3_0_0 = true;
186
      }
187
188
189
190
191
192
193
    if(te->GetProperty("INTERFACE_SOURCES"))
      {
      // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
      // can consume them.
      require3_1_0 = true;
      }

194
195
    this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
                                  te, properties);
196
    this->PopulateCompatibleInterfaceProperties(te, properties);
197
198

    this->GenerateInterfaceProperties(te, os, properties);
199
200
    }

201
202
203
204
205
  if (require3_1_0)
    {
    this->GenerateRequiredCMakeVersion(os, "3.1.0");
    }
  else if (require3_0_0)
206
    {
Brad King's avatar
Brad King committed
207
    this->GenerateRequiredCMakeVersion(os, "3.0.0");
208
209
    }
  else if (require2_8_12)
210
    {
Brad King's avatar
Brad King committed
211
    this->GenerateRequiredCMakeVersion(os, "2.8.12");
212
    }
213

214
215
  // Now load per-configuration properties for them.
  os << "# Load information for each installed configuration.\n"
216
217
     << "get_filename_component(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"
     << "file(GLOB CONFIG_FILES \"${_DIR}/"
218
     << this->GetConfigImportFileGlob() << "\")\n"
219
220
221
     << "foreach(f ${CONFIG_FILES})\n"
     << "  include(${f})\n"
     << "endforeach()\n"
222
223
     << "\n";

224
  // Cleanup the import prefix variable.
225
226
227
  os << "# Cleanup temporary variables.\n"
     << "set(_IMPORT_PREFIX)\n"
     << "\n";
228
229
  this->GenerateImportedFileCheckLoop(os);

230
  bool result = true;
231
232
233
  // Generate an import file for each configuration.
  // Don't do this if we only export INTERFACE_LIBRARY targets.
  if (requiresConfigFiles)
234
    {
235
236
237
    for(std::vector<std::string>::const_iterator
          ci = this->Configurations.begin();
        ci != this->Configurations.end(); ++ci)
238
      {
Stephen Kelly's avatar
Stephen Kelly committed
239
      if(!this->GenerateImportFileConfig(*ci, missingTargets))
240
241
242
        {
        result = false;
        }
243
244
      }
    }
245
246
247

  this->GenerateMissingTargetsCheckCode(os, missingTargets);

248
249
250
  return result;
}

Stephen Kelly's avatar
Stephen Kelly committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string &input)
{
  std::string::size_type pos = 0;
  std::string::size_type lastPos = pos;

  while((pos = input.find("$<INSTALL_PREFIX>", lastPos)) != input.npos)
    {
    std::string::size_type endPos = pos + sizeof("$<INSTALL_PREFIX>") - 1;
    input.replace(pos, endPos - pos, "${_IMPORT_PREFIX}");
    lastPos = endPos;
    }
}

266
267
//----------------------------------------------------------------------------
bool
268
269
cmExportInstallFileGenerator::GenerateImportFileConfig(
                                    const std::string& config,
270
                                    std::vector<std::string> &missingTargets)
271
272
{
  // Skip configurations not enabled for this export.
273
  if(!this->IEGen->InstallsForConfig(config))
274
275
276
277
278
279
280
281
282
    {
    return true;
    }

  // Construct the name of the file to generate.
  std::string fileName = this->FileDir;
  fileName += "/";
  fileName += this->FileBase;
  fileName += "-";
283
  if(!config.empty())
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    {
    fileName += cmSystemTools::LowerCase(config);
    }
  else
    {
    fileName += "noconfig";
    }
  fileName += this->FileExt;

  // Open the output file to generate it.
  cmGeneratedFileStream exportFileStream(fileName.c_str(), true);
  if(!exportFileStream)
    {
    std::string se = cmSystemTools::GetLastSystemError();
298
    std::ostringstream e;
299
    e << "cannot write to file \"" << fileName
300
301
302
303
304
305
306
307
308
309
      << "\": " << se;
    cmSystemTools::Error(e.str().c_str());
    return false;
    }
  std::ostream& os = exportFileStream;

  // Start with the import file header.
  this->GenerateImportHeaderCode(os, config);

  // Generate the per-config target information.
310
  this->GenerateImportConfig(os, config, missingTargets);
311
312
313
314
315
316
317
318
319
320
321
322
323
324

  // End with the import file footer.
  this->GenerateImportFooterCode(os);

  // Record this per-config import file.
  this->ConfigImportFiles[config] = fileName;

  return true;
}

//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator
::GenerateImportTargetsConfig(std::ostream& os,
325
326
                              const std::string& config,
                              std::string const& suffix,
327
                              std::vector<std::string> &missingTargets)
328
329
{
  // Add each target in the set to the export.
330
  for(std::vector<cmTargetExport*>::const_iterator
331
332
        tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
      tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
333
334
    {
    // Collect import properties for this target.
335
    cmTargetExport const* te = *tei;
336
    if (te->Target->GetType() == cmTarget::INTERFACE_LIBRARY)
337
338
339
      {
      continue;
      }
340
341
342
343

    ImportPropertyMap properties;
    std::set<std::string> importedLocations;

344
345
346
347
    this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
                                    properties, importedLocations);
    this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
                                    properties, importedLocations);
348
    this->SetImportLocationProperty(config, suffix,
349
350
351
352
353
354
                                    te->RuntimeGenerator, properties,
                                    importedLocations);
    this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
                                    properties, importedLocations);
    this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
                                    properties, importedLocations);
355
356
357
358
359
360
361

    // If any file location was set for the target add it to the
    // import file.
    if(!properties.empty())
      {
      // Get the rest of the target details.
      this->SetImportDetailProperties(config, suffix,
362
                                      te->Target, properties, missingTargets);
363

364
365
366
367
      this->SetImportLinkInterface(config, suffix,
                                   cmGeneratorExpression::InstallInterface,
                                   te->Target, properties, missingTargets);

368
      // TOOD: PUBLIC_HEADER_LOCATION
369
370
      // This should wait until the build feature propagation stuff
      // is done.  Then this can be a propagated include directory.
371
372
373
374
375
      // this->GenerateImportProperty(config, te->HeaderGenerator,
      //                              properties);

      // Generate code in the export file.
      this->GenerateImportPropertyCode(os, config, te->Target, properties);
376
377
      this->GenerateImportedFileChecksCode(os, te->Target, properties,
                                           importedLocations);
378
379
380
381
382
383
384
      }
    }
}

//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator
385
386
::SetImportLocationProperty(const std::string& config,
                            std::string const& suffix,
387
                            cmInstallTargetGenerator* itgen,
388
389
390
                            ImportPropertyMap& properties,
                            std::set<std::string>& importedLocations
                           )
391
392
393
394
395
396
397
{
  // Skip rules that do not match this configuration.
  if(!(itgen && itgen->InstallsForConfig(config)))
    {
    return;
    }

398
399
  // Get the target to be installed.
  cmTarget* target = itgen->GetTarget();
400
401

  // Construct the installed location of the target.
402
  std::string dest = itgen->GetDestination(config);
403
404
405
406
  std::string value;
  if(!cmSystemTools::FileIsFullPath(dest.c_str()))
    {
    // The target is installed relative to the installation prefix.
407
    value = "${_IMPORT_PREFIX}/";
408
409
410
411
    }
  value += dest;
  value += "/";

412
  if(itgen->IsImportLibrary())
413
    {
414
415
416
417
418
419
420
421
422
423
    // Construct the property name.
    std::string prop = "IMPORTED_IMPLIB";
    prop += suffix;

    // Append the installed file name.
    value += itgen->GetInstallFilename(target, config,
                                       cmInstallTargetGenerator::NameImplib);

    // Store the property.
    properties[prop] = value;
424
    importedLocations.insert(prop);
425
    }
426
  else
427
    {
428
429
430
    // Construct the property name.
    std::string prop = "IMPORTED_LOCATION";
    prop += suffix;
431

432
    // Append the installed file name.
433
    if(target->IsAppBundleOnApple())
434
435
436
437
438
439
440
441
442
443
444
445
446
      {
      value += itgen->GetInstallFilename(target, config);
      value += ".app/Contents/MacOS/";
      value += itgen->GetInstallFilename(target, config);
      }
    else
      {
      value += itgen->GetInstallFilename(target, config,
                                         cmInstallTargetGenerator::NameReal);
      }

    // Store the property.
    properties[prop] = value;
447
    importedLocations.insert(prop);
448
    }
449
450
}

451
452
453
454
455
456
//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator::HandleMissingTarget(
  std::string& link_libs, std::vector<std::string>& missingTargets,
  cmMakefile* mf, cmTarget* depender, cmTarget* dependee)
{
Stephen Kelly's avatar
Stephen Kelly committed
457
  const std::string name = dependee->GetName();
458
459
460
461
462
  std::vector<std::string> namespaces = this->FindNamespaces(mf, name);
  int targetOccurrences = (int)namespaces.size();
  if (targetOccurrences == 1)
    {
    std::string missingTarget = namespaces[0];
Stephen Kelly's avatar
Stephen Kelly committed
463
464

    missingTarget += dependee->GetExportName();
465
466
467
468
469
    link_libs += missingTarget;
    missingTargets.push_back(missingTarget);
    }
  else
    {
470
471
    // All exported targets should be known here and should be unique.
    // This is probably user-error.
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
    this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
    }
}

//----------------------------------------------------------------------------
std::vector<std::string>
cmExportInstallFileGenerator
::FindNamespaces(cmMakefile* mf, const std::string& name)
{
  std::vector<std::string> namespaces;
  cmGlobalGenerator* gg = mf->GetLocalGenerator()->GetGlobalGenerator();
  const cmExportSetMap& exportSets = gg->GetExportSets();

  for(cmExportSetMap::const_iterator expIt = exportSets.begin();
      expIt != exportSets.end();
      ++expIt)
    {
    const cmExportSet* exportSet = expIt->second;
    std::vector<cmTargetExport*> const* targets =
                                                 exportSet->GetTargetExports();

    bool containsTarget = false;
    for(unsigned int i=0; i<targets->size(); i++)
      {
      if (name == (*targets)[i]->Target->GetName())
        {
        containsTarget = true;
        break;
        }
      }

    if (containsTarget)
      {
      std::vector<cmInstallExportGenerator const*> const* installs =
                                                 exportSet->GetInstallations();
      for(unsigned int i=0; i<installs->size(); i++)
        {
        namespaces.push_back((*installs)[i]->GetNamespace());
        }
      }
    }

  return namespaces;
}

517
518
519
//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator
520
521
522
::ComplainAboutMissingTarget(cmTarget* depender,
                             cmTarget* dependee,
                             int occurrences)
523
{
524
  std::ostringstream e;
525
  e << "install(EXPORT \""
526
527
    << this->IEGen->GetExportSet()->GetName()
    << "\" ...) "
528
    << "includes target \"" << depender->GetName()
529
530
531
532
533
534
535
536
537
538
    << "\" which requires target \"" << dependee->GetName() << "\" ";
  if (occurrences == 0)
    {
    e << "that is not in the export set.";
    }
  else
    {
    e << "that is not in this export set, but " << occurrences
    << " times in others.";
    }
539
  cmSystemTools::Error(e.str().c_str());
540
}
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556

std::string
cmExportInstallFileGenerator::InstallNameDir(cmTarget* target,
                                             const std::string&)
{
  std::string install_name_dir;

  cmMakefile* mf = target->GetMakefile();
  if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
    {
    install_name_dir =
      target->GetInstallNameDirForInstallTree();
    }

  return install_name_dir;
}