cmNinjaTargetGenerator.cxx 26.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
  Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>

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

  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.
============================================================================*/
#include "cmNinjaTargetGenerator.h"
Brad King's avatar
Brad King committed
14

15
16
17
#include "cmAlgorithms.h"
#include "cmComputeLinkInformation.h"
#include "cmCustomCommandGenerator.h"
18
#include "cmGeneratedFileStream.h"
19
#include "cmGeneratorTarget.h"
20
21
22
#include "cmGlobalNinjaGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmMakefile.h"
23
24
25
#include "cmNinjaNormalTargetGenerator.h"
#include "cmNinjaUtilityTargetGenerator.h"
#include "cmSourceFile.h"
26
#include "cmSystemTools.h"
27
28
29

#include <algorithm>

30
cmNinjaTargetGenerator* cmNinjaTargetGenerator::New(cmGeneratorTarget* target)
31
{
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  switch (target->GetType()) {
    case cmState::EXECUTABLE:
    case cmState::SHARED_LIBRARY:
    case cmState::STATIC_LIBRARY:
    case cmState::MODULE_LIBRARY:
    case cmState::OBJECT_LIBRARY:
      return new cmNinjaNormalTargetGenerator(target);

    case cmState::UTILITY:
      return new cmNinjaUtilityTargetGenerator(target);
      ;

    case cmState::GLOBAL_TARGET: {
      // We only want to process global targets that live in the home
      // (i.e. top-level) directory.  CMake creates copies of these targets
      // in every directory, which we don't need.
      if (strcmp(target->GetLocalGenerator()->GetCurrentSourceDirectory(),
49
                 target->GetLocalGenerator()->GetSourceDirectory()) == 0) {
50
        return new cmNinjaUtilityTargetGenerator(target);
51
      }
52
      // else fallthrough
53
    }
54
55
56
57

    default:
      return 0;
  }
58
59
}

60
cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target)
61
  : cmCommonTargetGenerator(target)
62
63
64
65
66
67
  , MacOSXContentGenerator(0)
  , OSXBundleGenerator(0)
  , MacContentFolders()
  , LocalGenerator(
      static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator()))
  , Objects()
68
{
Peter Kümmel's avatar
Peter Kümmel committed
69
  MacOSXContentGenerator = new MacOSXContentGeneratorType(this);
70
71
72
73
}

cmNinjaTargetGenerator::~cmNinjaTargetGenerator()
{
Nicolas Despres's avatar
Nicolas Despres committed
74
  delete this->MacOSXContentGenerator;
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
}

cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const
{
  return *this->GetGlobalGenerator()->GetBuildFileStream();
}

cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
{
  return *this->GetGlobalGenerator()->GetRulesFileStream();
}

cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
{
  return this->LocalGenerator->GetGlobalNinjaGenerator();
}

92
93
94
95
std::string cmNinjaTargetGenerator::LanguageCompilerRule(
  const std::string& lang) const
{
  return lang + "_COMPILER__" +
96
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName());
97
98
}

99
std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget()
100
101
102
103
{
  return "cmake_order_depends_target_" + this->GetTargetName();
}

104
105
106
107
// TODO: Most of the code is picked up from
// void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
// Refactor it.
108
109
std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
  cmSourceFile const* source, const std::string& language)
110
{
111
  std::string flags = this->GetFlags(language);
112

113
  // Add Fortran format flags.
114
  if (language == "Fortran") {
115
    this->AppendFortranFormatFlags(flags, *source);
116
  }
117

Ben Boeckel's avatar
Ben Boeckel committed
118
119
  // Add source file specific flags.
  this->LocalGenerator->AppendFlags(flags,
120
                                    source->GetProperty("COMPILE_FLAGS"));
121
122
123
124

  return flags;
}

125
126
127
128
void cmNinjaTargetGenerator::AddIncludeFlags(std::string& languageFlags,
                                             std::string const& language)
{
  std::vector<std::string> includes;
129
130
  this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
                                              language, this->GetConfigName());
131
  // Add include directory flags.
132
133
  std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
    includes, this->GeneratorTarget, language,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
134
    language == "RC", // full include paths for RC needed by cmcldeps
135
    false, this->GetConfigName());
136
  if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
137
    std::replace(includeFlags.begin(), includeFlags.end(), '\\', '/');
138
  }
139
140
141
142

  this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
}

143
bool cmNinjaTargetGenerator::NeedDepTypeMSVC(const std::string& lang) const
144
{
145
146
147
  return strcmp(this->GetMakefile()->GetSafeDefinition("CMAKE_NINJA_DEPTYPE_" +
                                                       lang),
                "msvc") == 0;
148
149
}

150
151
// TODO: Refactor with
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
152
153
std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source,
                                                   const std::string& language)
154
{
155
  std::set<std::string> defines;
156
157
  this->LocalGenerator->AppendDefines(
    defines, source->GetProperty("COMPILE_DEFINITIONS"));
158
  {
159
160
161
162
    std::string defPropName = "COMPILE_DEFINITIONS_";
    defPropName += cmSystemTools::UpperCase(this->GetConfigName());
    this->LocalGenerator->AppendDefines(defines,
                                        source->GetProperty(defPropName));
163
164
  }

165
  std::string definesString = this->GetDefines(language);
166
  this->LocalGenerator->JoinDefines(defines, definesString, language);
167
168

  return definesString;
169
170
171
172
173
}

cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
{
  // Static libraries never depend on other targets for linking.
174
  if (this->GeneratorTarget->GetType() == cmState::STATIC_LIBRARY ||
175
      this->GeneratorTarget->GetType() == cmState::OBJECT_LIBRARY) {
176
    return cmNinjaDeps();
177
  }
178
179

  cmComputeLinkInformation* cli =
180
    this->GeneratorTarget->GetLinkInformation(this->GetConfigName());
181
  if (!cli) {
182
    return cmNinjaDeps();
183
  }
184

185
  const std::vector<std::string>& deps = cli->GetDepends();
186
187
  cmNinjaDeps result(deps.size());
  std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath());
Peter Kuemmel's avatar
Peter Kuemmel committed
188
189

  // Add a dependency on the link definitions file, if any.
190
191
192
193
  if (this->ModuleDefinitionFile) {
    result.push_back(
      this->ConvertToNinjaPath(this->ModuleDefinitionFile->GetFullPath()));
  }
Peter Kuemmel's avatar
Peter Kuemmel committed
194

195
196
197
198
  // Add a dependency on user-specified manifest files, if any.
  std::vector<cmSourceFile const*> manifest_srcs;
  this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName);
  for (std::vector<cmSourceFile const*>::iterator mi = manifest_srcs.begin();
199
       mi != manifest_srcs.end(); ++mi) {
200
    result.push_back(this->ConvertToNinjaPath((*mi)->GetFullPath()));
201
  }
202

203
  // Add user-specified dependencies.
204
  if (const char* linkDepends =
205
        this->GeneratorTarget->GetProperty("LINK_DEPENDS")) {
206
207
208
209
    std::vector<std::string> linkDeps;
    cmSystemTools::ExpandListArgument(linkDepends, linkDeps);
    std::transform(linkDeps.begin(), linkDeps.end(),
                   std::back_inserter(result), MapToNinjaPath());
210
  }
211

212
213
214
  return result;
}

215
216
std::string cmNinjaTargetGenerator::GetSourceFilePath(
  cmSourceFile const* source) const
217
{
218
  return ConvertToNinjaPath(source->GetFullPath());
219
220
}

221
222
std::string cmNinjaTargetGenerator::GetObjectFilePath(
  cmSourceFile const* source) const
223
224
{
  std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
225
  if (!path.empty()) {
226
    path += "/";
227
  }
228
  std::string const& objectName = this->GeneratorTarget->GetObjectName(source);
229
  path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
230
231
  path += "/";
  path += objectName;
232
233
234
235
236
  return path;
}

std::string cmNinjaTargetGenerator::GetTargetOutputDir() const
{
237
  std::string dir = this->GeneratorTarget->GetDirectory(this->GetConfigName());
238
  return ConvertToNinjaPath(dir);
239
240
}

241
242
std::string cmNinjaTargetGenerator::GetTargetFilePath(
  const std::string& name) const
243
244
{
  std::string path = this->GetTargetOutputDir();
245
  if (path.empty() || path == ".") {
246
    return name;
247
  }
248
249
250
251
252
253
254
  path += "/";
  path += name;
  return path;
}

std::string cmNinjaTargetGenerator::GetTargetName() const
{
255
  return this->GeneratorTarget->GetName();
256
257
}

258
bool cmNinjaTargetGenerator::SetMsvcTargetPdbVariable(cmNinjaVars& vars) const
259
{
260
261
  cmMakefile* mf = this->GetMakefile();
  if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
262
      mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID")) {
263
    std::string pdbPath;
264
    std::string compilePdbPath;
265
266
267
268
    if (this->GeneratorTarget->GetType() == cmState::EXECUTABLE ||
        this->GeneratorTarget->GetType() == cmState::STATIC_LIBRARY ||
        this->GeneratorTarget->GetType() == cmState::SHARED_LIBRARY ||
        this->GeneratorTarget->GetType() == cmState::MODULE_LIBRARY) {
269
      pdbPath = this->GeneratorTarget->GetPDBDirectory(this->GetConfigName());
270
      pdbPath += "/";
271
      pdbPath += this->GeneratorTarget->GetPDBName(this->GetConfigName());
272
273
    }
    if (this->GeneratorTarget->GetType() <= cmState::OBJECT_LIBRARY) {
274
      compilePdbPath =
275
276
        this->GeneratorTarget->GetCompilePDBPath(this->GetConfigName());
      if (compilePdbPath.empty()) {
277
        compilePdbPath = this->GeneratorTarget->GetSupportDirectory() + "/";
278
      }
279
    }
280

281
    vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
282
      ConvertToNinjaPath(pdbPath), cmOutputConverter::SHELL);
283
284
    vars["TARGET_COMPILE_PDB"] =
      this->GetLocalGenerator()->ConvertToOutputFormat(
285
        ConvertToNinjaPath(compilePdbPath), cmOutputConverter::SHELL);
286

287
    EnsureParentDirectoryExists(pdbPath);
288
    EnsureParentDirectoryExists(compilePdbPath);
289
    return true;
290
  }
291
  return false;
292
293
}

294
void cmNinjaTargetGenerator::WriteLanguageRules(const std::string& language)
295
{
296
#ifdef NINJA_GEN_VERBOSE_FILES
297
  this->GetRulesFileStream() << "# Rules for language " << language << "\n\n";
298
#endif
299
300
301
  this->WriteCompileRule(language);
}

302
void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang)
303
304
305
{
  cmLocalGenerator::RuleVariables vars;
  vars.RuleLauncher = "RULE_LAUNCH_COMPILE";
306
  vars.CMTarget = this->GetGeneratorTarget();
307
308
309
310
  vars.Language = lang.c_str();
  vars.Source = "$in";
  vars.Object = "$out";
  vars.Defines = "$DEFINES";
311
  vars.Includes = "$INCLUDES";
312
  vars.TargetPDB = "$TARGET_PDB";
313
  vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
314
  vars.ObjectDir = "$OBJECT_DIR";
315
  vars.ObjectFileDir = "$OBJECT_FILE_DIR";
316

317
  cmMakefile* mf = this->GetMakefile();
318

319
320
321
322
323
  std::string flags = "$FLAGS";
  std::string rspfile;
  std::string rspcontent;
  std::string responseFlag;

324
  if (this->ForceResponseFile()) {
325
326
327
328
329
330
    rspfile = "$RSP_FILE";
    responseFlag = "@" + rspfile;
    rspcontent = " $DEFINES $INCLUDES $FLAGS";
    flags = responseFlag;
    vars.Defines = "";
    vars.Includes = "";
331
  }
332

333
334
335
336
  // Tell ninja dependency format so all deps can be loaded into a database
  std::string deptype;
  std::string depfile;
  std::string cldeps;
337
  if (this->NeedDepTypeMSVC(lang)) {
338
339
340
    deptype = "msvc";
    depfile = "";
    flags += " /showIncludes";
341
  } else if (mf->IsOn("CMAKE_NINJA_CMCLDEPS_" + lang)) {
342
343
    // For the MS resource compiler we need cmcldeps, but skip dependencies
    // for source-file try_compile cases because they are always fresh.
344
    if (!mf->GetIsSourceFileTryCompile()) {
345
346
      deptype = "gcc";
      depfile = "$DEP_FILE";
347
348
349
350
      const std::string cl = mf->GetDefinition("CMAKE_C_COMPILER")
        ? mf->GetSafeDefinition("CMAKE_C_COMPILER")
        : mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
      cldeps = "\"";
351
      cldeps += cmSystemTools::GetCMClDepsCommand();
352
353
354
      cldeps += "\" " + lang + " $in \"$DEP_FILE\" $out \"";
      cldeps += mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX");
      cldeps += "\" \"" + cl + "\" ";
355
    }
356
  } else {
357
    deptype = "gcc";
358
    const char* langdeptype = mf->GetDefinition("CMAKE_NINJA_DEPTYPE_" + lang);
359
    if (langdeptype) {
360
      deptype = langdeptype;
361
    }
362
    depfile = "$DEP_FILE";
363
    const std::string flagsName = "CMAKE_DEPFILE_FLAGS_" + lang;
Stephen Kelly's avatar
Stephen Kelly committed
364
    std::string depfileFlags = mf->GetSafeDefinition(flagsName);
365
    if (!depfileFlags.empty()) {
366
      cmSystemTools::ReplaceString(depfileFlags, "<DEPFILE>", "$DEP_FILE");
367
      cmSystemTools::ReplaceString(depfileFlags, "<OBJECT>", "$out");
368
369
370
      cmSystemTools::ReplaceString(depfileFlags, "<CMAKE_C_COMPILER>",
                                   mf->GetDefinition("CMAKE_C_COMPILER"));
      flags += " " + depfileFlags;
371
    }
372
  }
373

374
375
  vars.Flags = flags.c_str();
  vars.DependencyFile = depfile.c_str();
Peter Kuemmel's avatar
Peter Kuemmel committed
376

377
  // Rule for compiling object file.
378
  const std::string cmdVar = std::string("CMAKE_") + lang + "_COMPILE_OBJECT";
Stephen Kelly's avatar
Stephen Kelly committed
379
  std::string compileCmd = mf->GetRequiredDefinition(cmdVar);
380
381
  std::vector<std::string> compileCmds;
  cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
382

383
  // Maybe insert an include-what-you-use runner.
384
  if (!compileCmds.empty() && (lang == "C" || lang == "CXX")) {
385
    std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
386
    const char* iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
387
    std::string const tidy_prop = lang + "_CLANG_TIDY";
388
389
390
    const char* tidy = this->GeneratorTarget->GetProperty(tidy_prop);
    if ((iwyu && *iwyu) || (tidy && *tidy)) {
      std::string run_iwyu = this->GetLocalGenerator()->ConvertToOutputFormat(
391
        cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
392
      run_iwyu += " -E __run_iwyu";
393
      if (iwyu && *iwyu) {
394
395
        run_iwyu += " --iwyu=";
        run_iwyu += this->GetLocalGenerator()->EscapeForShell(iwyu);
396
397
      }
      if (tidy && *tidy) {
398
399
400
        run_iwyu += " --tidy=";
        run_iwyu += this->GetLocalGenerator()->EscapeForShell(tidy);
        run_iwyu += " --source=$in";
401
      }
402
403
404
      run_iwyu += " -- ";
      compileCmds.front().insert(0, run_iwyu);
    }
405
  }
406

407
  // Maybe insert a compiler launcher like ccache or distcc
408
  if (!compileCmds.empty() && (lang == "C" || lang == "CXX")) {
409
    std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
410
411
    const char* clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
    if (clauncher && *clauncher) {
412
413
414
      std::vector<std::string> launcher_cmd;
      cmSystemTools::ExpandListArgument(clauncher, launcher_cmd, true);
      for (std::vector<std::string>::iterator i = launcher_cmd.begin(),
415
416
                                              e = launcher_cmd.end();
           i != e; ++i) {
417
        *i = this->LocalGenerator->EscapeForShell(*i);
418
      }
419
420
421
      std::string const& run_launcher = cmJoin(launcher_cmd, " ") + " ";
      compileCmds.front().insert(0, run_launcher);
    }
422
  }
423

424
  if (!compileCmds.empty()) {
425
    compileCmds.front().insert(0, cldeps);
426
  }
427

428
  for (std::vector<std::string>::iterator i = compileCmds.begin();
429
       i != compileCmds.end(); ++i) {
430
    this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
431
  }
432

433
434
  std::string cmdLine =
    this->GetLocalGenerator()->BuildCommandLine(compileCmds);
435

436
  // Write the rule for compiling file of the given language.
437
  std::ostringstream comment;
438
  comment << "Rule for compiling " << lang << " files.";
439
  std::ostringstream description;
440
  description << "Building " << lang << " object $out";
441
442
443
444
445
  this->GetGlobalGenerator()->AddRule(
    this->LanguageCompilerRule(lang), cmdLine, description.str(),
    comment.str(), depfile, deptype, rspfile, rspcontent,
    /*restat*/ "",
    /*generator*/ false);
446
447
}

448
void cmNinjaTargetGenerator::WriteObjectBuildStatements()
449
450
451
452
453
{
  // Write comments.
  cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
  this->GetBuildFileStream()
    << "# Object build statements for "
454
    << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
455
    << " target " << this->GetTargetName() << "\n\n";
456

457
  std::string config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
458
  std::vector<cmSourceFile const*> customCommands;
459
  this->GeneratorTarget->GetCustomCommands(customCommands, config);
460
461
462
463
464
465
466
467
468
469
  for (std::vector<cmSourceFile const*>::const_iterator si =
         customCommands.begin();
       si != customCommands.end(); ++si) {
    cmCustomCommand const* cc = (*si)->GetCustomCommand();
    this->GetLocalGenerator()->AddCustomCommandTarget(
      cc, this->GetGeneratorTarget());
    // Record the custom commands for this target. The container is used
    // in WriteObjectBuildStatement when called in a loop below.
    this->CustomCommands.push_back(cc);
  }
470
  std::vector<cmSourceFile const*> headerSources;
471
  this->GeneratorTarget->GetHeaderSources(headerSources, config);
472
  this->OSXBundleGenerator->GenerateMacOSXContentStatements(
473
    headerSources, this->MacOSXContentGenerator);
474
  std::vector<cmSourceFile const*> extraSources;
475
  this->GeneratorTarget->GetExtraSources(extraSources, config);
476
  this->OSXBundleGenerator->GenerateMacOSXContentStatements(
477
    extraSources, this->MacOSXContentGenerator);
478
  std::vector<cmSourceFile const*> externalObjects;
479
  this->GeneratorTarget->GetExternalObjects(externalObjects, config);
480
481
482
  for (std::vector<cmSourceFile const*>::const_iterator si =
         externalObjects.begin();
       si != externalObjects.end(); ++si) {
483
    this->Objects.push_back(this->GetSourceFilePath(*si));
484
  }
485
486

  cmNinjaDeps orderOnlyDeps;
487
488
  this->GetLocalGenerator()->AppendTargetDepends(this->GeneratorTarget,
                                                 orderOnlyDeps);
489

490
491
492
493
  // Add order-only dependencies on other files associated with the target.
  orderOnlyDeps.insert(orderOnlyDeps.end(), this->ExtraFiles.begin(),
                       this->ExtraFiles.end());

494
  // Add order-only dependencies on custom command outputs.
495
496
497
  for (std::vector<cmCustomCommand const*>::const_iterator cci =
         this->CustomCommands.begin();
       cci != this->CustomCommands.end(); ++cci) {
498
499
    cmCustomCommand const* cc = *cci;
    cmCustomCommandGenerator ccg(*cc, this->GetConfigName(),
500
                                 this->GetLocalGenerator());
501
    const std::vector<std::string>& ccoutputs = ccg.GetOutputs();
502
    const std::vector<std::string>& ccbyproducts = ccg.GetByproducts();
503
504
    std::transform(ccoutputs.begin(), ccoutputs.end(),
                   std::back_inserter(orderOnlyDeps), MapToNinjaPath());
505
506
    std::transform(ccbyproducts.begin(), ccbyproducts.end(),
                   std::back_inserter(orderOnlyDeps), MapToNinjaPath());
507
  }
508

509
  if (!orderOnlyDeps.empty()) {
510
511
    cmNinjaDeps orderOnlyTarget;
    orderOnlyTarget.push_back(this->OrderDependsTargetForTarget());
512
513
514
515
516
    this->GetGlobalGenerator()->WritePhonyBuild(
      this->GetBuildFileStream(),
      "Order-only phony target for " + this->GetTargetName(), orderOnlyTarget,
      cmNinjaDeps(), cmNinjaDeps(), orderOnlyDeps);
  }
517
  std::vector<cmSourceFile const*> objectSources;
518
  this->GeneratorTarget->GetObjectSources(objectSources, config);
519
520
521
  for (std::vector<cmSourceFile const*>::const_iterator si =
         objectSources.begin();
       si != objectSources.end(); ++si) {
522
    this->WriteObjectBuildStatement(*si, !orderOnlyDeps.empty());
523
  }
524
525
526
527

  this->GetBuildFileStream() << "\n";
}

528
void cmNinjaTargetGenerator::WriteObjectBuildStatement(
529
  cmSourceFile const* source, bool writeOrderDependsTargetForTarget)
530
{
531
532
  std::string const language = source->GetLanguage();
  std::string const sourceFileName =
533
    language == "RC" ? source->GetFullPath() : this->GetSourceFilePath(source);
534
535
536
537
  std::string const objectDir =
    this->ConvertToNinjaPath(this->GeneratorTarget->GetSupportDirectory());
  std::string const objectFileName =
    this->ConvertToNinjaPath(this->GetObjectFilePath(source));
538
539
540
541
542
543
544
  std::string const objectFileDir =
    cmSystemTools::GetFilenamePath(objectFileName);

  cmNinjaVars vars;
  vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
  vars["DEFINES"] = this->ComputeDefines(source, language);
  vars["INCLUDES"] = this->GetIncludes(language);
545
  if (!this->NeedDepTypeMSVC(language)) {
546
547
    vars["DEP_FILE"] =
      cmGlobalNinjaGenerator::EncodeDepfileSpace(objectFileName + ".d");
548
  }
549
550

  this->ExportObjectCompileCommand(
551
552
    language, sourceFileName, objectDir, objectFileName, objectFileDir,
    vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"]);
553

554
555
556
557
558
559
560
561
562
563
564
  std::string comment;
  std::string rule = this->LanguageCompilerRule(language);

  cmNinjaDeps outputs;
  outputs.push_back(objectFileName);
  // Add this object to the list of object files.
  this->Objects.push_back(objectFileName);

  cmNinjaDeps explicitDeps;
  explicitDeps.push_back(sourceFileName);

565
  cmNinjaDeps implicitDeps;
566
  if (const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
567
568
    std::vector<std::string> depList;
    cmSystemTools::ExpandListArgument(objectDeps, depList);
569
570
571
    for (std::vector<std::string>::iterator odi = depList.begin();
         odi != depList.end(); ++odi) {
      if (cmSystemTools::FileIsFullPath(*odi)) {
572
573
        *odi = cmSystemTools::CollapseFullPath(*odi);
      }
574
    }
575
    std::transform(depList.begin(), depList.end(),
576
                   std::back_inserter(implicitDeps), MapToNinjaPath());
577
578
  }

579
  cmNinjaDeps orderOnlyDeps;
580
  if (writeOrderDependsTargetForTarget) {
581
    orderOnlyDeps.push_back(this->OrderDependsTargetForTarget());
582
  }
583

584
585
586
587
588
589
590
591
592
593
  // If the source file is GENERATED and does not have a custom command
  // (either attached to this source file or another one), assume that one of
  // the target dependencies, OBJECT_DEPENDS or header file custom commands
  // will rebuild the file.
  if (source->GetPropertyAsBool("GENERATED") && !source->GetCustomCommand() &&
      !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) {
    this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName,
                                                             orderOnlyDeps);
  }

594
  EnsureParentDirectoryExists(objectFileName);
Peter Kuemmel's avatar
Peter Kuemmel committed
595

596
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
597
    objectDir, cmOutputConverter::SHELL);
598
  vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
599
    objectFileDir, cmOutputConverter::SHELL);
600

601
602
  this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
                             vars);
603

604
  this->SetMsvcTargetPdbVariable(vars);
605

606
607
608
  int const commandLineLengthLimit = this->ForceResponseFile() ? -1 : 0;
  std::string const rspfile = objectFileName + ".rsp";

609
610
611
  this->GetGlobalGenerator()->WriteBuild(
    this->GetBuildFileStream(), comment, rule, outputs, explicitDeps,
    implicitDeps, orderOnlyDeps, vars, rspfile, commandLineLengthLimit);
612

613
  if (const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
614
615
616
617
    std::vector<std::string> outputList;
    cmSystemTools::ExpandListArgument(objectOutputs, outputList);
    std::transform(outputList.begin(), outputList.end(), outputList.begin(),
                   MapToNinjaPath());
618
619
    this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
                                                "Additional output files.",
620
                                                outputList, outputs);
621
  }
622
}
Peter Kuemmel's avatar
Peter Kuemmel committed
623

624
625
626
627
628
void cmNinjaTargetGenerator::ExportObjectCompileCommand(
  std::string const& language, std::string const& sourceFileName,
  std::string const& objectDir, std::string const& objectFileName,
  std::string const& objectFileDir, std::string const& flags,
  std::string const& defines, std::string const& includes)
629
{
630
  if (!this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS")) {
631
    return;
632
  }
633
634
635
636
637
638

  cmLocalGenerator::RuleVariables compileObjectVars;
  compileObjectVars.Language = language.c_str();

  std::string escapedSourceFileName = sourceFileName;

639
  if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str())) {
640
    escapedSourceFileName = cmSystemTools::CollapseFullPath(
641
642
643
644
      escapedSourceFileName, this->GetGlobalGenerator()
                               ->GetCMakeInstance()
                               ->GetHomeOutputDirectory());
  }
645

646
  escapedSourceFileName = this->LocalGenerator->ConvertToOutputFormat(
647
    escapedSourceFileName, cmOutputConverter::SHELL);
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666

  compileObjectVars.Source = escapedSourceFileName.c_str();
  compileObjectVars.Object = objectFileName.c_str();
  compileObjectVars.ObjectDir = objectDir.c_str();
  compileObjectVars.ObjectFileDir = objectFileDir.c_str();
  compileObjectVars.Flags = flags.c_str();
  compileObjectVars.Defines = defines.c_str();
  compileObjectVars.Includes = includes.c_str();

  // Rule for compiling object file.
  std::string compileCmdVar = "CMAKE_";
  compileCmdVar += language;
  compileCmdVar += "_COMPILE_OBJECT";
  std::string compileCmd =
    this->GetMakefile()->GetRequiredDefinition(compileCmdVar);
  std::vector<std::string> compileCmds;
  cmSystemTools::ExpandListArgument(compileCmd, compileCmds);

  for (std::vector<std::string>::iterator i = compileCmds.begin();
667
       i != compileCmds.end(); ++i) {
668
    this->GetLocalGenerator()->ExpandRuleVariables(*i, compileObjectVars);
669
  }
670
671
672
673

  std::string cmdLine =
    this->GetLocalGenerator()->BuildCommandLine(compileCmds);

674
  this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine, sourceFileName);
675
676
}

677
678
void cmNinjaTargetGenerator::EnsureDirectoryExists(
  const std::string& path) const
679
{
680
  if (cmSystemTools::FileIsFullPath(path.c_str())) {
681
    cmSystemTools::MakeDirectory(path.c_str());
682
  } else {
683
684
685
686
687
688
    cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
    std::string fullPath =
      std::string(gg->GetCMakeInstance()->GetHomeOutputDirectory());
    // Also ensures their is a trailing slash.
    gg->StripNinjaOutputPathPrefixAsSuffix(fullPath);
    fullPath += path;
689
    cmSystemTools::MakeDirectory(fullPath.c_str());
690
  }
691
692
}

693
694
void cmNinjaTargetGenerator::EnsureParentDirectoryExists(
  const std::string& path) const
695
{
696
  EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path));
697
}
698

699
void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()(
700
  cmSourceFile const& source, const char* pkgloc)
701
702
{
  // Skip OS X content when not building a Framework or Bundle.
703
  if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
704
    return;
705
  }
706

707
  std::string macdir =
708
    this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc);
709
710
711

  // Get the input file location.
  std::string input = source.GetFullPath();
712
  input = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(input);
713
714
715
716
717

  // Get the output file location.
  std::string output = macdir;
  output += "/";
  output += cmSystemTools::GetFilenameName(input);
718
  output = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(output);
719
720

  // Write a build statement to copy the content into the bundle.
721
722
  this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild(input,
                                                                 output);
723

724
725
  // Add as a dependency to the target so that it gets called.
  this->Generator->ExtraFiles.push_back(output);
726
}
727

Ben Boeckel's avatar
Ben Boeckel committed
728
void cmNinjaTargetGenerator::addPoolNinjaVariable(
729
730
  const std::string& pool_property, cmGeneratorTarget* target,
  cmNinjaVars& vars)
731
{
732
733
734
735
  const char* pool = target->GetProperty(pool_property);
  if (pool) {
    vars["pool"] = pool;
  }
736
}
737
738
739
740
741
742
743

bool cmNinjaTargetGenerator::ForceResponseFile()
{
  static std::string const forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE";
  return (this->GetMakefile()->IsDefinitionSet(forceRspFile) ||
          cmSystemTools::GetEnv(forceRspFile) != 0);
}