cmExportInstallFileGenerator.cxx 17.3 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3 4
#include "cmExportInstallFileGenerator.h"

5
#include "cmAlgorithms.h"
6 7
#include "cmExportSet.h"
#include "cmExportSetMap.h"
8
#include "cmGeneratedFileStream.h"
9 10
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
11
#include "cmGlobalGenerator.h"
12 13
#include "cmInstallExportGenerator.h"
#include "cmInstallTargetGenerator.h"
14
#include "cmLocalGenerator.h"
15 16
#include "cmMakefile.h"
#include "cmPolicies.h"
17
#include "cmStateTypes.h"
18 19
#include "cmSystemTools.h"
#include "cmTarget.h"
20
#include "cmTargetExport.h"
21

22 23 24
#include <sstream>
#include <utility>

25 26 27
cmExportInstallFileGenerator::cmExportInstallFileGenerator(
  cmInstallExportGenerator* iegen)
  : IEGen(iegen)
28 29 30
{
}

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

39 40
bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
{
41
  std::vector<cmTargetExport*> allTargets;
42
  {
43 44 45 46 47 48 49
    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) {
      expectedTargets +=
50
        sep + this->Namespace + (*tei)->Target->GetExportName();
51 52 53 54 55 56 57 58 59 60 61 62
      sep = " ";
      cmTargetExport* te = *tei;
      if (this->ExportedTargets.insert(te->Target).second) {
        allTargets.push_back(te);
      } else {
        std::ostringstream e;
        e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
          << "\" ...) "
          << "includes target \"" << te->Target->GetName()
          << "\" more than once in the export set.";
        cmSystemTools::Error(e.str().c_str());
        return false;
63
      }
64 65
    }

66
    this->GenerateExpectedTargetsCode(os, expectedTargets);
67 68
  }

69 70
  // Compute the relative import prefix for the file
  this->GenerateImportPrefix(os);
71

72 73
  std::vector<std::string> missingTargets;

74
  bool require2_8_12 = false;
75
  bool require3_0_0 = false;
76
  bool require3_1_0 = false;
77
  bool requiresConfigFiles = false;
78
  // Create all the imported targets.
79 80
  for (std::vector<cmTargetExport*>::const_iterator tei = allTargets.begin();
       tei != allTargets.end(); ++tei) {
81
    cmGeneratorTarget* gt = (*tei)->Target;
82

83
    requiresConfigFiles =
84
      requiresConfigFiles || gt->GetType() != cmStateEnums::INTERFACE_LIBRARY;
85

86
    this->GenerateImportTargetCode(os, gt);
87 88 89

    ImportPropertyMap properties;

90 91 92
    this->PopulateIncludeDirectoriesInterface(
      *tei, cmGeneratorExpression::InstallInterface, properties,
      missingTargets);
93
    this->PopulateSourcesInterface(*tei,
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
                                   cmGeneratorExpression::InstallInterface,
                                   properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt,
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt,
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets);
    this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
                                    cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets);
111 112

    const bool newCMP0022Behavior =
113 114 115 116 117 118 119
      gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
      gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
    if (newCMP0022Behavior) {
      if (this->PopulateInterfaceLinkLibrariesProperty(
            gt, cmGeneratorExpression::InstallInterface, properties,
            missingTargets) &&
          !this->ExportOld) {
120 121
        require2_8_12 = true;
      }
122
    }
123
    if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
124
      require3_0_0 = true;
125 126
    }
    if (gt->GetProperty("INTERFACE_SOURCES")) {
127 128 129
      // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
      // can consume them.
      require3_1_0 = true;
130
    }
131

132 133
    this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
                                    properties);
134

135
    this->PopulateCompatibleInterfaceProperties(gt, properties);
136

137
    this->GenerateInterfaceProperties(gt, os, properties);
138
  }
139

140
  if (require3_1_0) {
141
    this->GenerateRequiredCMakeVersion(os, "3.1.0");
142
  } else if (require3_0_0) {
143
    this->GenerateRequiredCMakeVersion(os, "3.0.0");
144
  } else if (require2_8_12) {
Brad King's avatar
Brad King committed
145
    this->GenerateRequiredCMakeVersion(os, "2.8.12");
146
  }
147

148
  this->LoadConfigFiles(os);
149

150
  this->CleanupTemporaryVariables(os);
151 152
  this->GenerateImportedFileCheckLoop(os);

153
  bool result = true;
154 155
  // Generate an import file for each configuration.
  // Don't do this if we only export INTERFACE_LIBRARY targets.
156 157 158 159 160
  if (requiresConfigFiles) {
    for (std::vector<std::string>::const_iterator ci =
           this->Configurations.begin();
         ci != this->Configurations.end(); ++ci) {
      if (!this->GenerateImportFileConfig(*ci, missingTargets)) {
161
        result = false;
162 163
      }
    }
164
  }
165 166 167

  this->GenerateMissingTargetsCheckCode(os, missingTargets);

168 169 170
  return result;
}

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os)
{
  // Set an _IMPORT_PREFIX variable for import location properties
  // to reference if they are relative to the install prefix.
  std::string installPrefix =
    this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
      "CMAKE_INSTALL_PREFIX");
  std::string const& expDest = this->IEGen->GetDestination();
  if (cmSystemTools::FileIsFullPath(expDest)) {
    // The export file is being installed to an absolute path so the
    // package is not relocatable.  Use the configured install prefix.
    /* clang-format off */
    os <<
      "# The installation prefix configured by this project.\n"
      "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
      "\n";
    /* clang-format on */
  } else {
    // Add code to compute the installation prefix relative to the
    // import file location.
    std::string absDest = installPrefix + "/" + expDest;
    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";
    if (cmHasLiteralPrefix(absDestS.c_str(), "/lib/") ||
        cmHasLiteralPrefix(absDestS.c_str(), "/lib64/") ||
        cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib/") ||
        cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib64/")) {
      // Handle "/usr move" symlinks created by some Linux distros.
      /* clang-format off */
      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";
      /* clang-format on */
    }
    std::string dest = expDest;
    while (!dest.empty()) {
      os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
            "PATH)\n";
      dest = cmSystemTools::GetFilenamePath(dest);
    }
    os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
       << "  set(_IMPORT_PREFIX \"\")\n"
       << "endif()\n"
       << "\n";
  }
}

void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os)
{
  /* clang-format off */
  os << "# Cleanup temporary variables.\n"
     << "set(_IMPORT_PREFIX)\n"
     << "\n";
  /* clang-format on */
}

void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os)
{
  // Now load per-configuration properties for them.
  /* clang-format off */
  os << "# Load information for each installed configuration.\n"
     << "get_filename_component(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"
     << "file(GLOB CONFIG_FILES \"${_DIR}/"
     << this->GetConfigImportFileGlob() << "\")\n"
     << "foreach(f ${CONFIG_FILES})\n"
     << "  include(${f})\n"
     << "endforeach()\n"
     << "\n";
  /* clang-format on */
}

251
void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input)
252 253 254 255
{
  std::string::size_type pos = 0;
  std::string::size_type lastPos = pos;

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

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

  // Construct the name of the file to generate.
  std::string fileName = this->FileDir;
  fileName += "/";
  fileName += this->FileBase;
  fileName += "-";
276
  if (!config.empty()) {
277
    fileName += cmSystemTools::LowerCase(config);
278
  } else {
279
    fileName += "noconfig";
280
  }
281 282 283 284
  fileName += this->FileExt;

  // Open the output file to generate it.
  cmGeneratedFileStream exportFileStream(fileName.c_str(), true);
285
  if (!exportFileStream) {
286
    std::string se = cmSystemTools::GetLastSystemError();
287
    std::ostringstream e;
288
    e << "cannot write to file \"" << fileName << "\": " << se;
289 290
    cmSystemTools::Error(e.str().c_str());
    return false;
291
  }
292 293 294 295 296 297
  std::ostream& os = exportFileStream;

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

  // Generate the per-config target information.
298
  this->GenerateImportConfig(os, config, missingTargets);
299 300 301 302 303 304 305 306 307 308

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

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

  return true;
}

309 310 311
void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
  std::ostream& os, const std::string& config, std::string const& suffix,
  std::vector<std::string>& missingTargets)
312 313
{
  // Add each target in the set to the export.
314 315 316
  for (std::vector<cmTargetExport*>::const_iterator tei =
         this->IEGen->GetExportSet()->GetTargetExports()->begin();
       tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei) {
317
    // Collect import properties for this target.
318
    cmTargetExport const* te = *tei;
319
    if (te->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
320
      continue;
321
    }
322 323 324 325

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

326 327 328 329
    this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
                                    properties, importedLocations);
    this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
                                    properties, importedLocations);
330 331
    this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator,
                                    properties, importedLocations);
332 333 334 335
    this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
                                    properties, importedLocations);
    this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
                                    properties, importedLocations);
336 337 338

    // If any file location was set for the target add it to the
    // import file.
339
    if (!properties.empty()) {
340
      // Get the rest of the target details.
341 342 343
      cmGeneratorTarget* gtgt = te->Target;
      this->SetImportDetailProperties(config, suffix, gtgt, properties,
                                      missingTargets);
344

345 346
      this->SetImportLinkInterface(config, suffix,
                                   cmGeneratorExpression::InstallInterface,
347
                                   gtgt, properties, missingTargets);
348

349
      // TOOD: PUBLIC_HEADER_LOCATION
350 351
      // This should wait until the build feature propagation stuff
      // is done.  Then this can be a propagated include directory.
352 353 354 355
      // this->GenerateImportProperty(config, te->HeaderGenerator,
      //                              properties);

      // Generate code in the export file.
356 357
      this->GenerateImportPropertyCode(os, config, gtgt, properties);
      this->GenerateImportedFileChecksCode(os, gtgt, properties,
358
                                           importedLocations);
359
    }
360
  }
361 362
}

363 364 365 366
void cmExportInstallFileGenerator::SetImportLocationProperty(
  const std::string& config, std::string const& suffix,
  cmInstallTargetGenerator* itgen, ImportPropertyMap& properties,
  std::set<std::string>& importedLocations)
367 368
{
  // Skip rules that do not match this configuration.
369
  if (!(itgen && itgen->InstallsForConfig(config))) {
370
    return;
371
  }
372

373
  // Get the target to be installed.
374
  cmGeneratorTarget* target = itgen->GetTarget();
375 376

  // Construct the installed location of the target.
377
  std::string dest = itgen->GetDestination(config);
378
  std::string value;
379
  if (!cmSystemTools::FileIsFullPath(dest.c_str())) {
380
    // The target is installed relative to the installation prefix.
381
    value = "${_IMPORT_PREFIX}/";
382
  }
383 384 385
  value += dest;
  value += "/";

386
  if (itgen->IsImportLibrary()) {
387 388 389 390 391 392 393 394 395 396
    // 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;
397
    importedLocations.insert(prop);
398
  } else {
399 400 401
    // Construct the property name.
    std::string prop = "IMPORTED_LOCATION";
    prop += suffix;
402

403
    // Append the installed file name.
404
    if (target->IsAppBundleOnApple()) {
405 406 407
      value += itgen->GetInstallFilename(target, config);
      value += ".app/Contents/MacOS/";
      value += itgen->GetInstallFilename(target, config);
408
    } else {
409 410
      value += itgen->GetInstallFilename(target, config,
                                         cmInstallTargetGenerator::NameReal);
411
    }
412 413 414

    // Store the property.
    properties[prop] = value;
415
    importedLocations.insert(prop);
416
  }
417 418
}

419 420
void cmExportInstallFileGenerator::HandleMissingTarget(
  std::string& link_libs, std::vector<std::string>& missingTargets,
421
  cmGeneratorTarget* depender, cmGeneratorTarget* dependee)
422
{
423
  const std::string name = dependee->GetName();
424
  cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator();
425
  std::vector<std::string> namespaces = this->FindNamespaces(gg, name);
426
  int targetOccurrences = (int)namespaces.size();
427
  if (targetOccurrences == 1) {
428
    std::string missingTarget = namespaces[0];
429

430
    missingTarget += dependee->GetExportName();
431 432
    link_libs += missingTarget;
    missingTargets.push_back(missingTarget);
433
  } else {
434 435
    // All exported targets should be known here and should be unique.
    // This is probably user-error.
436
    this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
437
  }
438 439
}

440 441
std::vector<std::string> cmExportInstallFileGenerator::FindNamespaces(
  cmGlobalGenerator* gg, const std::string& name)
442 443 444 445
{
  std::vector<std::string> namespaces;
  const cmExportSetMap& exportSets = gg->GetExportSets();

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

    bool containsTarget = false;
453 454
    for (unsigned int i = 0; i < targets->size(); i++) {
      if (name == (*targets)[i]->TargetName) {
455 456 457
        containsTarget = true;
        break;
      }
458
    }
459

460
    if (containsTarget) {
461
      std::vector<cmInstallExportGenerator const*> const* installs =
462 463
        exportSet->GetInstallations();
      for (unsigned int i = 0; i < installs->size(); i++) {
464 465 466
        namespaces.push_back((*installs)[i]->GetNamespace());
      }
    }
467
  }
468 469 470 471

  return namespaces;
}

472 473
void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
  cmGeneratorTarget* depender, cmGeneratorTarget* dependee, int occurrences)
474
{
475
  std::ostringstream e;
476
  e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
477
    << "\" ...) "
478
    << "includes target \"" << depender->GetName()
479
    << "\" which requires target \"" << dependee->GetName() << "\" ";
480
  if (occurrences == 0) {
481
    e << "that is not in the export set.";
482
  } else {
483
    e << "that is not in this export set, but " << occurrences
484 485
      << " times in others.";
  }
486
  cmSystemTools::Error(e.str().c_str());
487
}
488

489
std::string cmExportInstallFileGenerator::InstallNameDir(
490
  cmGeneratorTarget* target, const std::string& /*config*/)
491 492 493
{
  std::string install_name_dir;

494
  cmMakefile* mf = target->Target->GetMakefile();
495 496 497
  if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
    install_name_dir = target->GetInstallNameDirForInstallTree();
  }
498 499 500

  return install_name_dir;
}