cmMakefileTargetGenerator.cxx 65.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 "cmMakefileTargetGenerator.h"

5
#include <memory> // IWYU pragma: keep
6 7 8 9
#include <sstream>
#include <stdio.h>
#include <utility>

10 11
#include "cmAlgorithms.h"
#include "cmComputeLinkInformation.h"
12
#include "cmCustomCommand.h"
13
#include "cmCustomCommandGenerator.h"
14
#include "cmGeneratedFileStream.h"
15 16
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
17
#include "cmGlobalUnixMakefileGenerator3.h"
18 19 20 21 22
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmMakefile.h"
#include "cmMakefileExecutableTargetGenerator.h"
#include "cmMakefileLibraryTargetGenerator.h"
#include "cmMakefileUtilityTargetGenerator.h"
23
#include "cmOutputConverter.h"
24
#include "cmRulePlaceholderExpander.h"
25
#include "cmSourceFile.h"
26
#include "cmState.h"
27 28
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
29
#include "cmStateTypes.h"
30 31
#include "cmSystemTools.h"
#include "cmake.h"
32

33
cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target)
34
  : cmCommonTargetGenerator(target)
Daniel Pfeifer's avatar
Daniel Pfeifer committed
35 36
  , OSXBundleGenerator(nullptr)
  , MacOSXContentGenerator(nullptr)
37
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
38 39 40
  this->BuildFileStream = nullptr;
  this->InfoFileStream = nullptr;
  this->FlagFileStream = nullptr;
41
  this->CustomCommandDriver = OnBuild;
42
  this->LocalGenerator =
43
    static_cast<cmLocalUnixMakefileGenerator3*>(target->GetLocalGenerator());
44 45
  this->GlobalGenerator = static_cast<cmGlobalUnixMakefileGenerator3*>(
    this->LocalGenerator->GetGlobalGenerator());
46
  cmake* cm = this->GlobalGenerator->GetCMakeInstance();
47
  this->NoRuleMessages = false;
48 49
  if (const char* ruleStatus =
        cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
50
    this->NoRuleMessages = cmSystemTools::IsOff(ruleStatus);
51
  }
Peter Kümmel's avatar
Peter Kümmel committed
52 53 54 55 56 57
  MacOSXContentGenerator = new MacOSXContentGeneratorType(this);
}

cmMakefileTargetGenerator::~cmMakefileTargetGenerator()
{
  delete MacOSXContentGenerator;
58 59
}

60 61
cmMakefileTargetGenerator* cmMakefileTargetGenerator::New(
  cmGeneratorTarget* tgt)
62
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
63
  cmMakefileTargetGenerator* result = nullptr;
64

65
  switch (tgt->GetType()) {
66
    case cmStateEnums::EXECUTABLE:
67
      result = new cmMakefileExecutableTargetGenerator(tgt);
68
      break;
69 70 71 72
    case cmStateEnums::STATIC_LIBRARY:
    case cmStateEnums::SHARED_LIBRARY:
    case cmStateEnums::MODULE_LIBRARY:
    case cmStateEnums::OBJECT_LIBRARY:
73
      result = new cmMakefileLibraryTargetGenerator(tgt);
74
      break;
75
    case cmStateEnums::UTILITY:
76
      result = new cmMakefileUtilityTargetGenerator(tgt);
77 78
      break;
    default:
Ken Martin's avatar
Ken Martin committed
79
      return result;
80
      // break; /* unreachable */
81
  }
82 83 84
  return result;
}

85 86
void cmMakefileTargetGenerator::GetTargetLinkFlags(
  std::string& flags, const std::string& linkLanguage)
87 88 89 90 91 92 93 94
{
  this->LocalGenerator->AppendFlags(
    flags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));

  std::string linkFlagsConfig = "LINK_FLAGS_";
  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
  this->LocalGenerator->AppendFlags(
    flags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
95 96 97 98 99

  std::vector<std::string> opts;
  this->GeneratorTarget->GetLinkOptions(opts, this->ConfigName, linkLanguage);
  // LINK_OPTIONS are escaped.
  this->LocalGenerator->AppendCompileOptions(flags, opts);
100 101
}

102 103 104
void cmMakefileTargetGenerator::CreateRuleFile()
{
  // Create a directory for this target.
Andy Cedilnik's avatar
Andy Cedilnik committed
105
  this->TargetBuildDirectory =
106
    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
Andy Cedilnik's avatar
Andy Cedilnik committed
107
  this->TargetBuildDirectoryFull =
108
    this->LocalGenerator->ConvertToFullPath(this->TargetBuildDirectory);
109
  cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull);
Andy Cedilnik's avatar
Andy Cedilnik committed
110

111 112 113 114 115
  // Construct the rule file name.
  this->BuildFileName = this->TargetBuildDirectory;
  this->BuildFileName += "/build.make";
  this->BuildFileNameFull = this->TargetBuildDirectoryFull;
  this->BuildFileNameFull += "/build.make";
Andy Cedilnik's avatar
Andy Cedilnik committed
116

117 118 119 120 121 122 123
  // Construct the rule file name.
  this->ProgressFileNameFull = this->TargetBuildDirectoryFull;
  this->ProgressFileNameFull += "/progress.make";

  // reset the progress count
  this->NumberOfProgressActions = 0;

124 125
  // Open the rule file.  This should be copy-if-different because the
  // rules may depend on this file itself.
Andy Cedilnik's avatar
Andy Cedilnik committed
126
  this->BuildFileStream =
127 128
    new cmGeneratedFileStream(this->BuildFileNameFull.c_str(), false,
                              this->GlobalGenerator->GetMakefileEncoding());
129
  this->BuildFileStream->SetCopyIfDifferent(true);
130
  if (!this->BuildFileStream) {
131
    return;
132
  }
133
  this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream);
134
  if (this->GlobalGenerator->AllowDeleteOnError()) {
135 136 137 138 139
    std::vector<std::string> no_depends;
    std::vector<std::string> no_commands;
    this->LocalGenerator->WriteMakeRule(
      *this->BuildFileStream, "Delete rule output on recipe failure.",
      ".DELETE_ON_ERROR", no_depends, no_commands, false);
140
  }
141 142 143
  this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream);
}

144
void cmMakefileTargetGenerator::WriteTargetBuildRules()
145
{
146 147 148
  const std::string& config =
    this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");

149 150
  // write the custom commands for this target
  // Look for files registered for cleaning in this directory.
151 152
  if (const char* additional_clean_files =
        this->Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) {
153
    cmGeneratorExpression ge;
154
    std::unique_ptr<cmCompiledGeneratorExpression> cge =
155 156 157 158
      ge.Parse(additional_clean_files);

    cmSystemTools::ExpandListArgument(
      cge->Evaluate(this->LocalGenerator, config, false, this->GeneratorTarget,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
159
                    nullptr, nullptr),
160 161
      this->CleanFiles);
  }
162 163

  // add custom commands to the clean rules?
164
  const char* clean_no_custom = this->Makefile->GetProperty("CLEAN_NO_CUSTOM");
165
  bool clean = cmSystemTools::IsOff(clean_no_custom);
Andy Cedilnik's avatar
Andy Cedilnik committed
166

167 168
  // First generate the object rule files.  Save a list of all object
  // files for this target.
169
  std::vector<cmSourceFile const*> customCommands;
170
  this->GeneratorTarget->GetCustomCommands(customCommands, config);
171 172
  std::string currentBinDir =
    this->LocalGenerator->GetCurrentBinaryDirectory();
173 174
  for (cmSourceFile const* sf : customCommands) {
    cmCustomCommandGenerator ccg(*sf->GetCustomCommand(), this->ConfigName,
175
                                 this->LocalGenerator);
176
    this->GenerateCustomRuleFile(ccg);
177
    if (clean) {
178
      const std::vector<std::string>& outputs = ccg.GetOutputs();
179
      for (std::string const& output : outputs) {
180
        this->CleanFiles.push_back(
181 182
          this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir,
                                                           output));
183 184
      }
    }
185
  }
186
  std::vector<cmSourceFile const*> headerSources;
187
  this->GeneratorTarget->GetHeaderSources(headerSources, config);
188
  this->OSXBundleGenerator->GenerateMacOSXContentStatements(
189
    headerSources, this->MacOSXContentGenerator);
190
  std::vector<cmSourceFile const*> extraSources;
191
  this->GeneratorTarget->GetExtraSources(extraSources, config);
192
  this->OSXBundleGenerator->GenerateMacOSXContentStatements(
193
    extraSources, this->MacOSXContentGenerator);
194
  std::vector<cmSourceFile const*> externalObjects;
195
  this->GeneratorTarget->GetExternalObjects(externalObjects, config);
196 197
  for (cmSourceFile const* sf : externalObjects) {
    this->ExternalObjects.push_back(sf->GetFullPath());
198
  }
199
  std::vector<cmSourceFile const*> objectSources;
200
  this->GeneratorTarget->GetObjectSources(objectSources, config);
201
  for (cmSourceFile const* sf : objectSources) {
202
    // Generate this object file's rule file.
203
    this->WriteObjectRuleFiles(*sf);
204
  }
205 206 207 208
}

void cmMakefileTargetGenerator::WriteCommonCodeRules()
{
209 210 211
  const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
                        ? "$(CMAKE_BINARY_DIR)/"
                        : "");
212

213 214 215 216 217
  // Include the dependencies for the target.
  std::string dependFileNameFull = this->TargetBuildDirectoryFull;
  dependFileNameFull += "/depend.make";
  *this->BuildFileStream
    << "# Include any dependencies generated for this target.\n"
218
    << this->GlobalGenerator->IncludeDirective << " " << root
219
    << cmSystemTools::ConvertToOutputPath(
220 221
         this->LocalGenerator->MaybeConvertToRelativePath(
           this->LocalGenerator->GetBinaryDirectory(), dependFileNameFull))
222
    << "\n\n";
223

224
  if (!this->NoRuleMessages) {
225 226 227
    // Include the progress variables for the target.
    *this->BuildFileStream
      << "# Include the progress variables for this target.\n"
228
      << this->GlobalGenerator->IncludeDirective << " " << root
229
      << cmSystemTools::ConvertToOutputPath(
230 231 232
           this->LocalGenerator->MaybeConvertToRelativePath(
             this->LocalGenerator->GetBinaryDirectory(),
             this->ProgressFileNameFull))
233
      << "\n\n";
234
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
235

236
  // make sure the depend file exists
237
  if (!cmSystemTools::FileExists(dependFileNameFull)) {
238
    // Write an empty dependency file.
239 240 241
    cmGeneratedFileStream depFileStream(
      dependFileNameFull.c_str(), false,
      this->GlobalGenerator->GetMakefileEncoding());
242 243 244 245 246
    depFileStream << "# Empty dependencies file for "
                  << this->GeneratorTarget->GetName() << ".\n"
                  << "# This may be replaced when dependencies are built."
                  << std::endl;
  }
247 248 249 250 251

  // Open the flags file.  This should be copy-if-different because the
  // rules may depend on this file itself.
  this->FlagFileNameFull = this->TargetBuildDirectoryFull;
  this->FlagFileNameFull += "/flags.make";
Andy Cedilnik's avatar
Andy Cedilnik committed
252
  this->FlagFileStream =
253 254
    new cmGeneratedFileStream(this->FlagFileNameFull.c_str(), false,
                              this->GlobalGenerator->GetMakefileEncoding());
255
  this->FlagFileStream->SetCopyIfDifferent(true);
256
  if (!this->FlagFileStream) {
257
    return;
258
  }
259
  this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream);
Andy Cedilnik's avatar
Andy Cedilnik committed
260

261 262 263
  // Include the flags for the target.
  *this->BuildFileStream
    << "# Include the compile flags for this target's objects.\n"
264
    << this->GlobalGenerator->IncludeDirective << " " << root
265
    << cmSystemTools::ConvertToOutputPath(
266 267
         this->LocalGenerator->MaybeConvertToRelativePath(
           this->LocalGenerator->GetBinaryDirectory(), this->FlagFileNameFull))
268
    << "\n\n";
269 270 271 272
}

void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
{
273
  // write language flags for target
274
  std::set<std::string> languages;
275 276
  this->GeneratorTarget->GetLanguages(
    languages, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
277
  // put the compiler in the rules.make file so that if it changes
278
  // things rebuild
279
  for (std::string const& language : languages) {
280
    std::string compiler = "CMAKE_";
281
    compiler += language;
282
    compiler += "_COMPILER";
283
    *this->FlagFileStream << "# compile " << language << " with "
284 285 286
                          << this->Makefile->GetSafeDefinition(compiler)
                          << "\n";
  }
287

288 289 290 291
  for (std::string const& language : languages) {
    std::string flags = this->GetFlags(language);
    std::string defines = this->GetDefines(language);
    std::string includes = this->GetIncludes(language);
292 293 294
    // Escape comment characters so they do not terminate assignment.
    cmSystemTools::ReplaceString(flags, "#", "\\#");
    cmSystemTools::ReplaceString(defines, "#", "\\#");
295
    cmSystemTools::ReplaceString(includes, "#", "\\#");
296 297 298
    *this->FlagFileStream << language << "_FLAGS = " << flags << "\n\n";
    *this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n";
    *this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n";
299
  }
300 301
}

302 303
void cmMakefileTargetGenerator::MacOSXContentGeneratorType::operator()(
  cmSourceFile const& source, const char* pkgloc)
304
{
305
  // Skip OS X content when not building a Framework or Bundle.
306
  if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
307
    return;
308
  }
309

310 311
  std::string macdir =
    this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc);
312

313
  // Get the input file location.
314
  std::string const& input = source.GetFullPath();
315 316 317 318 319

  // Get the output file location.
  std::string output = macdir;
  output += "/";
  output += cmSystemTools::GetFilenameName(input);
320
  this->Generator->CleanFiles.push_back(
321
    this->Generator->LocalGenerator->MaybeConvertToRelativePath(
322
      this->Generator->LocalGenerator->GetCurrentBinaryDirectory(), output));
323
  output = this->Generator->LocalGenerator->MaybeConvertToRelativePath(
324
    this->Generator->LocalGenerator->GetBinaryDirectory(), output);
325 326 327 328 329

  // Create a rule to copy the content into the bundle.
  std::vector<std::string> depends;
  std::vector<std::string> commands;
  depends.push_back(input);
330
  std::string copyEcho = "Copying OS X content ";
331
  copyEcho += output;
332
  this->Generator->LocalGenerator->AppendEcho(
333
    commands, copyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
334
  std::string copyCommand = "$(CMAKE_COMMAND) -E copy ";
335 336
  copyCommand += this->Generator->LocalGenerator->ConvertToOutputFormat(
    input, cmOutputConverter::SHELL);
337
  copyCommand += " ";
338 339
  copyCommand += this->Generator->LocalGenerator->ConvertToOutputFormat(
    output, cmOutputConverter::SHELL);
340
  commands.push_back(std::move(copyCommand));
341
  this->Generator->LocalGenerator->WriteMakeRule(
Daniel Pfeifer's avatar
Daniel Pfeifer committed
342
    *this->Generator->BuildFileStream, nullptr, output, depends, commands,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
343
    false);
344
  this->Generator->ExtraFiles.insert(output);
345 346
}

347 348
void cmMakefileTargetGenerator::WriteObjectRuleFiles(
  cmSourceFile const& source)
349 350
{
  // Identify the language of the source file.
351 352
  const std::string& lang =
    this->LocalGenerator->GetSourceFileLanguage(source);
353
  if (lang.empty()) {
354
    // don't know anything about this file so skip it
355
    return;
356
  }
357 358

  // Get the full path name of the object file.
359 360
  std::string const& objectName =
    this->GeneratorTarget->GetObjectName(&source);
361
  std::string obj =
362
    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
363 364
  obj += "/";
  obj += objectName;
365 366

  // Avoid generating duplicate rules.
367
  if (this->ObjectFiles.find(obj) == this->ObjectFiles.end()) {
368
    this->ObjectFiles.insert(obj);
369
  } else {
370
    std::ostringstream err;
371
    err << "Warning: Source file \"" << source.GetFullPath()
Andy Cedilnik's avatar
Andy Cedilnik committed
372
        << "\" is listed multiple times for target \""
373
        << this->GeneratorTarget->GetName() << "\".";
374 375
    cmSystemTools::Message(err.str().c_str(), "Warning");
    return;
376
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
377

378 379
  // Create the directory containing the object file.  This may be a
  // subdirectory under the target's directory.
Stephen Kelly's avatar
Stephen Kelly committed
380
  std::string dir = cmSystemTools::GetFilenamePath(obj);
381
  cmSystemTools::MakeDirectory(this->LocalGenerator->ConvertToFullPath(dir));
Andy Cedilnik's avatar
Andy Cedilnik committed
382

383 384
  // Save this in the target's list of object files.
  this->Objects.push_back(obj);
385
  this->CleanFiles.push_back(obj);
386 387

  // TODO: Remove
388
  // std::string relativeObj
Andy Cedilnik's avatar
Andy Cedilnik committed
389
  //= this->LocalGenerator->GetHomeRelativeOutputPath();
390
  // relativeObj += obj;
391

392 393 394
  // we compute some depends when writing the depend.make that we will also
  // use in the build.make, same with depMakeFile
  std::vector<std::string> depends;
Andy Cedilnik's avatar
Andy Cedilnik committed
395

396 397
  // generate the build rule file
  this->WriteObjectBuildFile(obj, lang, source, depends);
Andy Cedilnik's avatar
Andy Cedilnik committed
398

399
  // The object file should be checked for dependency integrity.
400
  std::string objFullPath = this->LocalGenerator->GetCurrentBinaryDirectory();
401 402
  objFullPath += "/";
  objFullPath += obj;
403
  objFullPath = cmSystemTools::CollapseFullPath(objFullPath);
404
  std::string srcFullPath =
405
    cmSystemTools::CollapseFullPath(source.GetFullPath());
406 407
  this->LocalGenerator->AddImplicitDepends(
    this->GeneratorTarget, lang, objFullPath.c_str(), srcFullPath.c_str());
408 409
}

410 411 412
void cmMakefileTargetGenerator::WriteObjectBuildFile(
  std::string& obj, const std::string& lang, cmSourceFile const& source,
  std::vector<std::string>& depends)
413
{
Andy Cedilnik's avatar
Andy Cedilnik committed
414
  this->LocalGenerator->AppendRuleDepend(depends,
Ken Martin's avatar
Ken Martin committed
415
                                         this->FlagFileNameFull.c_str());
416 417
  this->LocalGenerator->AppendRuleDepends(depends,
                                          this->FlagFileDepends[lang]);
418 419 420 421 422 423 424

  // generate the depend scanning rule
  this->WriteObjectDependRules(source, depends);

  std::string relativeObj = this->LocalGenerator->GetHomeRelativeOutputPath();
  relativeObj += obj;
  // Write the build rule.
425

426 427
  // Build the set of compiler flags.
  std::string flags;
428 429 430 431 432

  // Add language-specific flags.
  std::string langFlags = "$(";
  langFlags += lang;
  langFlags += "_FLAGS)";
433
  this->LocalGenerator->AppendFlags(flags, langFlags);
434

435 436
  std::string config = this->LocalGenerator->GetConfigName();
  std::string configUpper = cmSystemTools::UpperCase(config);
437
  cmGeneratorExpressionInterpreter genexInterpreter(
438 439
    this->LocalGenerator, this->GeneratorTarget, config,
    this->GeneratorTarget->GetName(), lang);
440

441
  // Add Fortran format flags.
442
  if (lang == "Fortran") {
443
    this->AppendFortranFormatFlags(flags, source);
444
  }
445

446
  // Add flags from source file properties.
447 448 449 450
  const std::string COMPILE_FLAGS("COMPILE_FLAGS");
  if (const char* cflags = source.GetProperty(COMPILE_FLAGS)) {
    const char* evaluatedFlags =
      genexInterpreter.Evaluate(cflags, COMPILE_FLAGS);
451
    this->LocalGenerator->AppendFlags(flags, evaluatedFlags);
452
    *this->FlagFileStream << "# Custom flags: " << relativeObj
453
                          << "_FLAGS = " << evaluatedFlags << "\n"
454
                          << "\n";
455
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
456

457 458 459 460 461 462 463 464 465 466
  const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
  if (const char* coptions = source.GetProperty(COMPILE_OPTIONS)) {
    const char* evaluatedOptions =
      genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS);
    this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions);
    *this->FlagFileStream << "# Custom options: " << relativeObj
                          << "_OPTIONS = " << evaluatedOptions << "\n"
                          << "\n";
  }

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
  // Add include directories from source file properties.
  std::vector<std::string> includes;

  const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
  if (const char* cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) {
    const char* evaluatedIncludes =
      genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES);
    this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes,
                                                   source);
    *this->FlagFileStream << "# Custom include directories: " << relativeObj
                          << "_INCLUDE_DIRECTORIES = " << evaluatedIncludes
                          << "\n"
                          << "\n";
  }

482
  // Add language-specific defines.
483
  std::set<std::string> defines;
484

485
  // Add source-specific preprocessor definitions.
486 487 488 489
  const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
  if (const char* compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) {
    const char* evaluatedDefs =
      genexInterpreter.Evaluate(compile_defs, COMPILE_DEFINITIONS);
490
    this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
491
    *this->FlagFileStream << "# Custom defines: " << relativeObj
492
                          << "_DEFINES = " << evaluatedDefs << "\n"
493
                          << "\n";
494
  }
495 496
  std::string defPropName = "COMPILE_DEFINITIONS_";
  defPropName += configUpper;
497
  if (const char* config_compile_defs = source.GetProperty(defPropName)) {
498 499
    const char* evaluatedDefs =
      genexInterpreter.Evaluate(config_compile_defs, COMPILE_DEFINITIONS);
500
    this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
501
    *this->FlagFileStream << "# Custom defines: " << relativeObj << "_DEFINES_"
502
                          << configUpper << " = " << evaluatedDefs << "\n"
503 504
                          << "\n";
  }
505

506
  // Get the output paths for source and object files.
507 508
  std::string sourceFile = this->LocalGenerator->ConvertToOutputFormat(
    source.GetFullPath(), cmOutputConverter::SHELL);
Andy Cedilnik's avatar
Andy Cedilnik committed
509

510 511 512
  // Construct the build message.
  std::vector<std::string> no_commands;
  std::vector<std::string> commands;
Andy Cedilnik's avatar
Andy Cedilnik committed
513

Ken Martin's avatar
Ken Martin committed
514
  // add in a progress call if needed
515
  this->NumberOfProgressActions++;
Andy Cedilnik's avatar
Andy Cedilnik committed
516

517
  if (!this->NoRuleMessages) {
518 519
    cmLocalUnixMakefileGenerator3::EchoProgress progress;
    this->MakeEchoProgress(progress);
520 521 522 523
    std::string buildEcho = "Building ";
    buildEcho += lang;
    buildEcho += " object ";
    buildEcho += relativeObj;
524
    this->LocalGenerator->AppendEcho(commands, buildEcho,
525 526 527
                                     cmLocalUnixMakefileGenerator3::EchoBuild,
                                     &progress);
  }
Ken Martin's avatar
Ken Martin committed
528

529
  std::string targetOutPathReal;
530
  std::string targetOutPathPDB;
531
  std::string targetOutPathCompilePDB;
532
  {
533 534
    std::string targetFullPathReal;
    std::string targetFullPathPDB;
535
    std::string targetFullPathCompilePDB = this->ComputeTargetCompilePDB();
536 537 538 539
    if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE ||
        this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
        this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
        this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
540 541
      targetFullPathReal = this->GeneratorTarget->GetFullPath(
        this->ConfigName, cmStateEnums::RuntimeBinaryArtifact, true);
542
      targetFullPathPDB =
543
        this->GeneratorTarget->GetPDBDirectory(this->ConfigName);
544 545
      targetFullPathPDB += "/";
      targetFullPathPDB += this->GeneratorTarget->GetPDBName(this->ConfigName);
546
    }
547

548
    targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
549
      this->LocalGenerator->MaybeConvertToRelativePath(
550 551
        this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal),
      cmOutputConverter::SHELL);
552 553
    targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
      targetFullPathPDB, cmOutputConverter::SHELL);
554
    targetOutPathCompilePDB = this->LocalGenerator->ConvertToOutputFormat(
555
      this->LocalGenerator->MaybeConvertToRelativePath(
556 557 558
        this->LocalGenerator->GetCurrentBinaryDirectory(),
        targetFullPathCompilePDB),
      cmOutputConverter::SHELL);
559 560 561 562 563 564 565

    if (this->LocalGenerator->IsMinGWMake() &&
        cmHasLiteralSuffix(targetOutPathCompilePDB, "\\")) {
      // mingw32-make incorrectly interprets 'a\ b c' as 'a b' and 'c'
      // (but 'a\ b "c"' as 'a\', 'b', and 'c'!).  Workaround this by
      // avoiding a trailing backslash in the argument.
      targetOutPathCompilePDB[targetOutPathCompilePDB.size() - 1] = '/';
566
    }
567
  }
568
  cmRulePlaceholderExpander::RuleVariables vars;
569 570 571
  vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
  vars.CMTargetType =
    cmState::GetTargetTypeName(this->GeneratorTarget->GetType());
572
  vars.Language = lang.c_str();
573
  vars.Target = targetOutPathReal.c_str();
574
  vars.TargetPDB = targetOutPathPDB.c_str();
575
  vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
576
  vars.Source = sourceFile.c_str();
577
  std::string shellObj =
578
    this->LocalGenerator->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
579
  vars.Object = shellObj.c_str();
580
  std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
581
  objectDir = this->LocalGenerator->ConvertToOutputFormat(
582
    this->LocalGenerator->MaybeConvertToRelativePath(
583 584
      this->LocalGenerator->GetCurrentBinaryDirectory(), objectDir),
    cmOutputConverter::SHELL);
585
  vars.ObjectDir = objectDir.c_str();
586
  std::string objectFileDir = cmSystemTools::GetFilenamePath(obj);
587
  objectFileDir = this->LocalGenerator->ConvertToOutputFormat(
588
    this->LocalGenerator->MaybeConvertToRelativePath(
589 590
      this->LocalGenerator->GetCurrentBinaryDirectory(), objectFileDir),
    cmOutputConverter::SHELL);
591
  vars.ObjectFileDir = objectFileDir.c_str();
592
  vars.Flags = flags.c_str();
593 594 595 596 597 598 599 600

  std::string definesString = "$(";
  definesString += lang;
  definesString += "_DEFINES)";

  this->LocalGenerator->JoinDefines(defines, definesString, lang);

  vars.Defines = definesString.c_str();
Andy Cedilnik's avatar
Andy Cedilnik committed
601

602 603 604 605
  std::string includesString = this->LocalGenerator->GetIncludeFlags(
    includes, this->GeneratorTarget, lang, true, false, config);
  this->LocalGenerator->AppendFlags(includesString,
                                    "$(" + lang + "_INCLUDES)");
606 607
  vars.Includes = includesString.c_str();

608
  // At the moment, it is assumed that C, C++, Fortran, and CUDA have both
609 610
  // assembly and preprocessor capabilities. The same is true for the
  // ability to export compile commands
611 612
  bool lang_has_preprocessor = ((lang == "C") || (lang == "CXX") ||
                                (lang == "Fortran") || (lang == "CUDA"));
613 614
  bool const lang_has_assembly = lang_has_preprocessor;
  bool const lang_can_export_cmds = lang_has_preprocessor;
Manuel Klimek's avatar
Manuel Klimek committed
615

616
  std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
617 618
    this->LocalGenerator->CreateRulePlaceholderExpander());

619 620
  // Construct the compile rules.
  {
621
    std::vector<std::string> compileCommands;
622 623
    if (lang == "CUDA") {
      std::string cmdVar;
624 625
      if (this->GeneratorTarget->GetPropertyAsBool(
            "CUDA_SEPARABLE_COMPILATION")) {
626
        cmdVar = std::string("CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION");
627 628 629
      } else if (this->GeneratorTarget->GetPropertyAsBool(
                   "CUDA_PTX_COMPILATION")) {
        cmdVar = std::string("CMAKE_CUDA_COMPILE_PTX_COMPILATION");
630 631 632 633 634 635 636 637 638 639 640
      } else {
        cmdVar = std::string("CMAKE_CUDA_COMPILE_WHOLE_COMPILATION");
      }
      std::string compileRule = this->Makefile->GetRequiredDefinition(cmdVar);
      cmSystemTools::ExpandListArgument(compileRule, compileCommands);
    } else {
      const std::string cmdVar =
        std::string("CMAKE_") + lang + "_COMPILE_OBJECT";
      std::string compileRule = this->Makefile->GetRequiredDefinition(cmdVar);
      cmSystemTools::ExpandListArgument(compileRule, compileCommands);
    }
641 642 643 644

    if (this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS") &&
        lang_can_export_cmds && compileCommands.size() == 1) {
      std::string compileCommand = compileCommands[0];
645

646
      // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS
647 648
      rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                   compileCommand, vars);
649 650
      std::string workingDirectory = cmSystemTools::CollapseFullPath(
        this->LocalGenerator->GetCurrentBinaryDirectory());
651 652 653 654 655 656 657 658 659 660 661 662
      compileCommand.replace(compileCommand.find(langFlags), langFlags.size(),
                             this->GetFlags(lang));
      std::string langDefines = std::string("$(") + lang + "_DEFINES)";
      compileCommand.replace(compileCommand.find(langDefines),
                             langDefines.size(), this->GetDefines(lang));
      std::string langIncludes = std::string("$(") + lang + "_INCLUDES)";
      compileCommand.replace(compileCommand.find(langIncludes),
                             langIncludes.size(), this->GetIncludes(lang));
      this->GlobalGenerator->AddCXXCompileCommand(
        source.GetFullPath(), workingDirectory, compileCommand);
    }

663 664
    // See if we need to use a compiler launcher like ccache or distcc
    std::string compilerLauncher;
665 666 667
    if (!compileCommands.empty() &&
        (lang == "C" || lang == "CXX" || lang == "Fortran" ||
         lang == "CUDA")) {
668 669 670 671 672 673 674 675
      std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
      const char* clauncher =
        this->GeneratorTarget->GetProperty(clauncher_prop);
      if (clauncher && *clauncher) {
        compilerLauncher = clauncher;
      }
    }

676 677 678 679 680 681
    // Maybe insert an include-what-you-use runner.
    if (!compileCommands.empty() && (lang == "C" || lang == "CXX")) {
      std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
      const char* iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
      std::string const tidy_prop = lang + "_CLANG_TIDY";
      const char* tidy = this->GeneratorTarget->GetProperty(tidy_prop);
682 683
      std::string const cpplint_prop = lang + "_CPPLINT";
      const char* cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
684 685 686 687
      std::string const cppcheck_prop = lang + "_CPPCHECK";
      const char* cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
      if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint) ||
          (cppcheck && *cppcheck)) {
688
        std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile";
689 690 691 692 693 694 695
        if (!compilerLauncher.empty()) {
          // In __run_co_compile case the launcher command is supplied
          // via --launcher=<maybe-list> and consumed
          run_iwyu += " --launcher=";
          run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher);
          compilerLauncher.clear();
        }
696 697 698
        if (iwyu && *iwyu) {
          run_iwyu += " --iwyu=";
          run_iwyu += this->LocalGenerator->EscapeForShell(iwyu);
699
        }
700 701 702
        if (tidy && *tidy) {
          run_iwyu += " --tidy=";
          run_iwyu += this->LocalGenerator->EscapeForShell(tidy);
703 704 705 706 707
        }
        if (cpplint && *cpplint) {
          run_iwyu += " --cpplint=";
          run_iwyu += this->LocalGenerator->EscapeForShell(cpplint);
        }
708 709 710 711 712 713
        if (cppcheck && *cppcheck) {
          run_iwyu += " --cppcheck=";
          run_iwyu += this->LocalGenerator->EscapeForShell(cppcheck);
        }
        if ((tidy && *tidy) || (cpplint && *cpplint) ||
            (cppcheck && *cppcheck)) {
714 715
          run_iwyu += " --source=";
          run_iwyu += sourceFile;
716
        }
717 718
        run_iwyu += " -- ";
        compileCommands.front().insert(0, run_iwyu);
719 720 721
      }
    }

722 723 724 725 726 727 728
    // If compiler launcher was specified and not consumed above, it
    // goes to the beginning of the command line.
    if (!compileCommands.empty() && !compilerLauncher.empty()) {
      std::vector<std::string> args;
      cmSystemTools::ExpandListArgument(compilerLauncher, args, true);
      for (std::string& i : args) {
        i = this->LocalGenerator->EscapeForShell(i);
729
      }
730
      compileCommands.front().insert(0, cmJoin(args, " ") + " ");
731 732
    }

733 734 735 736 737 738 739 740 741 742
    std::string launcher;
    {
      const char* val = this->LocalGenerator->GetRuleLauncher(
        this->GeneratorTarget, "RULE_LAUNCH_COMPILE");
      if (val && *val) {
        launcher = val;
        launcher += " ";
      }
    }

743
    // Expand placeholders in the commands.
744 745 746 747
    for (std::string& compileCommand : compileCommands) {
      compileCommand = launcher + compileCommand;
      rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                   compileCommand, vars);
748 749
    }

750 751 752
    // Change the command working directory to the local build tree.
    this->LocalGenerator->CreateCDCommand(
      compileCommands, this->LocalGenerator->GetCurrentBinaryDirectory(),
753
      this->LocalGenerator->GetBinaryDirectory());
754 755
    commands.insert(commands.end(), compileCommands.begin(),
                    compileCommands.end());
756
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
757

758
  // Check for extra outputs created by the compilation.
759
  std::vector<std::string> outputs(1, relativeObj);
760
  if (const char* extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) {
761
    // Register these as extra files to clean.
762
    cmSystemTools::ExpandListArgument(extra_outputs_str, outputs);
763 764 765
    this->CleanFiles.insert(this->CleanFiles.end(), outputs.begin() + 1,
                            outputs.end());
  }
766

767
  // Write the rule.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
768
  this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
769
                      commands);
770

771
  bool do_preprocess_rules = lang_has_preprocessor &&
772
    this->LocalGenerator->GetCreatePreprocessedSourceRules();
773 774 775
  bool do_assembly_rules =
    lang_has_assembly && this->LocalGenerator->GetCreateAssemblySourceRules();
  if (do_preprocess_rules || do_assembly_rules) {
776 777
    std::vector<std::string> force_depends;
    force_depends.push_back("cmake_force");
778
    std::string::size_type dot_pos = relativeObj.rfind('.');
779
    std::string relativeObjBase = relativeObj.substr(0, dot_pos);
780
    dot_pos = obj.rfind('.');
781
    std::string objBase = obj.substr(0, dot_pos);
782

783
    if (do_preprocess_rules) {
784
      commands.clear();
785
      std::string relativeObjI = relativeObjBase + ".i";
786
      std::string objI = objBase + ".i";
787 788 789 790

      std::string preprocessEcho = "Preprocessing ";
      preprocessEcho += lang;
      preprocessEcho += " source to ";
791
      preprocessEcho += objI;
792
      this->LocalGenerator->AppendEcho(
793
        commands, preprocessEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
794 795 796 797

      std::string preprocessRuleVar = "CMAKE_";
      preprocessRuleVar += lang;
      preprocessRuleVar += "_CREATE_PREPROCESSED_SOURCE";
798 799
      if (const char* preprocessRule =
            this->Makefile->GetDefinition(preprocessRuleVar)) {
800 801
        std::vector<std::string> preprocessCommands;
        cmSystemTools::ExpandListArgument(preprocessRule, preprocessCommands);
802

803 804
        std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat(
          objI, cmOutputConverter::SHELL);
805 806
        vars.PreprocessedSource = shellObjI.c_str();

807
        // Expand placeholders in the commands.
808
        for (std::string& preprocessCommand : preprocessCommands) {
809
          // no launcher for preprocessor commands
810 811
          rulePlaceholderExpander->ExpandRuleVariables(
            this->LocalGenerator, preprocessCommand, vars);
812
        }
813 814 815 816

        this->LocalGenerator->CreateCDCommand(
          preprocessCommands,
          this->LocalGenerator->GetCurrentBinaryDirectory(),
817
          this->LocalGenerator->GetBinaryDirectory());
818 819 820
        commands.insert(commands.end(), preprocessCommands.begin(),
                        preprocessCommands.end());
      } else {
821 822
        std::string cmd = "$(CMAKE_COMMAND) -E cmake_unimplemented_variable ";
        cmd += preprocessRuleVar;
823
        commands.push_back(std::move(cmd));
824
      }
825

Daniel Pfeifer's avatar
Daniel Pfeifer committed
826
      this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
827 828 829
                                          relativeObjI, force_depends,
                                          commands, false);
    }
830

831
    if (do_assembly_rules) {
832
      commands.clear();
833
      std::string relativeObjS = relativeObjBase + ".s";
834
      std::string objS = objBase + ".s";
835 836 837 838

      std::string assemblyEcho = "Compiling ";
      assemblyEcho += lang;
      assemblyEcho += " source to assembly ";
839
      assemblyEcho += objS;
840
      this->LocalGenerator->AppendEcho(
841
        commands, assemblyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
842 843 844 845

      std::string assemblyRuleVar = "CMAKE_";
      assemblyRuleVar += lang;
      assemblyRuleVar += "_CREATE_ASSEMBLY_SOURCE";
846 847
      if (const char* assemblyRule =
            this->Makefile->GetDefinition(assemblyRuleVar)) {
848 849
        std::vector<std::string> assemblyCommands;
        cmSystemTools::ExpandListArgument(assemblyRule, assemblyCommands);
850

851 852
        std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat(
          objS, cmOutputConverter::SHELL);