cmExportInstallFileGenerator.cxx 18.6 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)
    {
51 52
    expectedTargets +=
        sep + this->Namespace + (*tei)->Target->Target->GetExportName();
53
    sep = " ";
54
    cmTargetExport * te = *tei;
55
    if(this->ExportedTargets.insert(te->Target).second)
56
      {
57
      allTargets.push_back(te);
58 59 60
      }
    else
      {
61
      std::ostringstream e;
62
      e << "install(EXPORT \""
63 64
        << this->IEGen->GetExportSet()->GetName()
        << "\" ...) " << "includes target \"" << te->Target->GetName()
65 66 67 68
        << "\" more than once in the export set.";
      cmSystemTools::Error(e.str().c_str());
      return false;
      }
69 70
    }

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

74 75
  // Set an _IMPORT_PREFIX variable for import location properties
  // to reference if they are relative to the install prefix.
76 77
  std::string installPrefix = this->IEGen->GetLocalGenerator()
      ->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
78 79
  std::string const& expDest = this->IEGen->GetDestination();
  if(cmSystemTools::FileIsFullPath(expDest))
80
    {
81 82 83 84 85 86 87 88 89 90 91
    // 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.
92
    std::string absDest = installPrefix + "/" + expDest;
93 94 95 96
    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";
97 98 99 100
    if(cmHasLiteralPrefix(absDestS.c_str(), "/lib/") ||
       cmHasLiteralPrefix(absDestS.c_str(), "/lib64/") ||
       cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib/") ||
       cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib64/"))
101
      {
102 103 104 105 106 107 108 109 110 111 112
      // 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";
113
      }
114
    std::string dest = expDest;
115
    while(!dest.empty())
116
      {
117 118 119
      os <<
        "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" PATH)\n";
      dest = cmSystemTools::GetFilenamePath(dest);
120
      }
121
    os << "\n";
122 123
    }

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

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

    requiresConfigFiles = requiresConfigFiles
138
                              || gt->GetType() != cmState::INTERFACE_LIBRARY;
139

140
    this->GenerateImportTargetCode(os, gt);
141 142 143

    ImportPropertyMap properties;

144
    this->PopulateIncludeDirectoriesInterface(*tei,
145 146
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
147 148 149
    this->PopulateSourcesInterface(*tei,
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
150
    this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES",
151
                                  gt,
152
                                  cmGeneratorExpression::InstallInterface,
153 154
                                  properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS",
155
                                  gt,
156 157
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
158
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS",
159
                                  gt,
160 161
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
162
    this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS",
163
                                  gt,
164 165
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
166
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES",
167
                                  gt,
168 169
                                  cmGeneratorExpression::InstallInterface,
                                  properties, missingTargets);
170 171

    const bool newCMP0022Behavior =
172 173
        gt->Target->GetPolicyStatusCMP0022() != cmPolicies::WARN
        && gt->Target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
174 175
    if (newCMP0022Behavior)
      {
176
      if (this->PopulateInterfaceLinkLibrariesProperty(gt,
177 178 179 180 181 182 183
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets)
          && !this->ExportOld)
        {
        require2_8_12 = true;
        }
      }
184
    if (gt->GetType() == cmState::INTERFACE_LIBRARY)
185
      {
186
      require3_0_0 = true;
187
      }
188
    if(gt->GetProperty("INTERFACE_SOURCES"))
189 190 191 192 193 194
      {
      // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
      // can consume them.
      require3_1_0 = true;
      }

195
    this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
196
                                  gt, properties);
197

198
    this->PopulateCompatibleInterfaceProperties(gt, properties);
199

200
    this->GenerateInterfaceProperties(gt, os, properties);
201 202
    }

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

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

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

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

  this->GenerateMissingTargetsCheckCode(os, missingTargets);

250 251 252
  return result;
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
//----------------------------------------------------------------------------
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;
    }
}

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

  // Construct the name of the file to generate.
  std::string fileName = this->FileDir;
  fileName += "/";
  fileName += this->FileBase;
  fileName += "-";
285
  if(!config.empty())
286 287 288 289 290 291 292 293 294 295 296 297 298 299
    {
    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();
300
    std::ostringstream e;
301
    e << "cannot write to file \"" << fileName
302 303 304 305 306 307 308 309 310 311
      << "\": " << 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.
312
  this->GenerateImportConfig(os, config, missingTargets);
313 314 315 316 317 318 319 320 321 322 323 324 325 326

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

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

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

    // 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.
363
      cmGeneratorTarget *gtgt = te->Target;
364
      this->SetImportDetailProperties(config, suffix,
365
                                      gtgt, properties, missingTargets);
366

367 368
      this->SetImportLinkInterface(config, suffix,
                                   cmGeneratorExpression::InstallInterface,
369
                                   gtgt, properties, missingTargets);
370

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

      // Generate code in the export file.
378 379
      this->GenerateImportPropertyCode(os, config, gtgt, properties);
      this->GenerateImportedFileChecksCode(os, gtgt, properties,
380
                                           importedLocations);
381 382 383 384 385 386 387
      }
    }
}

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

401
  // Get the target to be installed.
402
  cmGeneratorTarget* target = itgen->GetTarget();
403 404

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

415
  if(itgen->IsImportLibrary())
416
    {
417 418 419 420 421 422 423 424 425 426
    // 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;
427
    importedLocations.insert(prop);
428
    }
429
  else
430
    {
431 432 433
    // Construct the property name.
    std::string prop = "IMPORTED_LOCATION";
    prop += suffix;
434

435
    // Append the installed file name.
436
    if(target->Target->IsAppBundleOnApple())
437 438 439 440 441 442 443 444 445 446 447 448 449
      {
      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;
450
    importedLocations.insert(prop);
451
    }
452 453
}

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

468
    missingTarget += dependee->Target->GetExportName();
469 470 471 472 473
    link_libs += missingTarget;
    missingTargets.push_back(missingTarget);
    }
  else
    {
474 475
    // All exported targets should be known here and should be unique.
    // This is probably user-error.
476 477 478 479 480 481 482
    this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
    }
}

//----------------------------------------------------------------------------
std::vector<std::string>
cmExportInstallFileGenerator
483
::FindNamespaces(cmGlobalGenerator* gg, const std::string& name)
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 517 518 519
{
  std::vector<std::string> namespaces;
  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;
}

520 521 522
//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator
523 524
::ComplainAboutMissingTarget(cmGeneratorTarget* depender,
                             cmGeneratorTarget* dependee,
525
                             int occurrences)
526
{
527
  std::ostringstream e;
528
  e << "install(EXPORT \""
529 530
    << this->IEGen->GetExportSet()->GetName()
    << "\" ...) "
531
    << "includes target \"" << depender->GetName()
532 533 534 535 536 537 538 539 540 541
    << "\" 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.";
    }
542
  cmSystemTools::Error(e.str().c_str());
543
}
544 545

std::string
546
cmExportInstallFileGenerator::InstallNameDir(cmGeneratorTarget* target,
547 548 549 550
                                             const std::string&)
{
  std::string install_name_dir;

551
  cmMakefile* mf = target->Target->GetMakefile();
552 553 554 555 556 557 558 559
  if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
    {
    install_name_dir =
      target->GetInstallNameDirForInstallTree();
    }

  return install_name_dir;
}