cmLocalGenerator.cxx 94.5 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Ken Martin's avatar
Ken Martin committed
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
Ken Martin's avatar
Ken Martin committed
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.
============================================================================*/
Ken Martin's avatar
Ken Martin committed
12
#include "cmLocalGenerator.h"
13

14
#include "cmAlgorithms.h"
15
#include "cmComputeLinkInformation.h"
16
#include "cmCustomCommandGenerator.h"
17
#include "cmGeneratedFileStream.h"
18
#include "cmGeneratorExpressionEvaluationFile.h"
19
#include "cmGeneratorTarget.h"
Ken Martin's avatar
updates    
Ken Martin committed
20
#include "cmGlobalGenerator.h"
21
#include "cmInstallGenerator.h"
22
23
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
Ken Martin's avatar
Ken Martin committed
24
#include "cmMakefile.h"
25
#include "cmSourceFile.h"
26
27
#include "cmSystemTools.h"
#include "cmTarget.h"
28
#include "cmTestGenerator.h"
29
#include "cmVersion.h"
30
31
#include "cmake.h"

32
#if defined(CMAKE_BUILD_WITH_CMAKE)
33
34
#define CM_LG_ENCODE_OBJECT_NAMES
#include <cmsys/MD5.h>
35
36
#endif

37
#include <algorithm>
38
#include <assert.h>
39
40
41
42
43
44
#include <cmsys/RegularExpression.hxx>
#include <ctype.h>
#include <iterator>
#include <sstream>
#include <stdio.h>
#include <utility>
45

46
#if defined(__HAIKU__)
47
48
#include <FindDirectory.h>
#include <StorageDefs.h>
49
50
#endif

51
52
53
cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
  : cmOutputConverter(makefile->GetStateSnapshot())
  , StateSnapshot(makefile->GetStateSnapshot())
54
  , DirectoryBacktrace(makefile->GetBacktrace())
Ken Martin's avatar
Ken Martin committed
55
{
56
  this->GlobalGenerator = gg;
57

58
  this->Makefile = makefile;
59

60
61
  this->AliasTargets = makefile->GetAliasTargets();

62
  this->EmitUniversalBinaryFlags = true;
63
64
  this->BackwardsCompatibility = 0;
  this->BackwardsCompatibilityFinal = false;
65
66

  this->ComputeObjectMaxPath();
Ken Martin's avatar
Ken Martin committed
67
68
69
70
}

cmLocalGenerator::~cmLocalGenerator()
{
71
72
  cmDeleteAll(this->GeneratorTargets);
  cmDeleteAll(this->OwnedImportedGeneratorTargets);
Ken Martin's avatar
Ken Martin committed
73
74
}

75
76
77
void cmLocalGenerator::IssueMessage(cmake::MessageType t,
                                    std::string const& text) const
{
78
  this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace);
79
80
}

81
82
void cmLocalGenerator::ComputeObjectMaxPath()
{
83
// Choose a maximum object file name length.
84
85
86
87
88
89
#if defined(_WIN32) || defined(__CYGWIN__)
  this->ObjectPathMax = 250;
#else
  this->ObjectPathMax = 1000;
#endif
  const char* plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
90
  if (plen && *plen) {
91
    unsigned int pmax;
92
93
    if (sscanf(plen, "%u", &pmax) == 1) {
      if (pmax >= 128) {
94
        this->ObjectPathMax = pmax;
95
      } else {
96
        std::ostringstream w;
97
98
99
        w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax
          << ", which is less than the minimum of 128.  "
          << "The value will be ignored.";
100
        this->IssueMessage(cmake::AUTHOR_WARNING, w.str());
101
      }
102
    } else {
103
      std::ostringstream w;
104
105
106
      w << "CMAKE_OBJECT_PATH_MAX is set to \"" << plen
        << "\", which fails to parse as a positive integer.  "
        << "The value will be ignored.";
107
      this->IssueMessage(cmake::AUTHOR_WARNING, w.str());
108
    }
109
  }
110
  this->ObjectMaxPathViolations.clear();
111
112
}

Alexander Neundorf's avatar
   
Alexander Neundorf committed
113
114
void cmLocalGenerator::TraceDependencies()
{
115
116
  std::vector<std::string> configs;
  this->Makefile->GetConfigurations(configs);
117
  if (configs.empty()) {
118
    configs.push_back("");
119
120
121
  }
  for (std::vector<std::string>::const_iterator ci = configs.begin();
       ci != configs.end(); ++ci) {
122
    this->GlobalGenerator->CreateEvaluationSourceFiles(*ci);
123
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
124
  // Generate the rule files for each target.
125
  std::vector<cmGeneratorTarget*> targets = this->GetGeneratorTargets();
126
127
128
  for (std::vector<cmGeneratorTarget*>::iterator t = targets.begin();
       t != targets.end(); ++t) {
    if ((*t)->GetType() == cmState::INTERFACE_LIBRARY) {
129
      continue;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
130
    }
131
132
    (*t)->TraceDependencies();
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
133
134
}

135
136
void cmLocalGenerator::GenerateTestFiles()
{
137
  if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
138
    return;
139
  }
140
141
142

  // Compute the set of configurations.
  std::vector<std::string> configurationTypes;
143
  const std::string& config =
144
    this->Makefile->GetConfigurations(configurationTypes, false);
145

146
  std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
147
  file += "/";
Ken Martin's avatar
Ken Martin committed
148
149
  file += "CTestTestfile.cmake";

150
151
152
153
  cmGeneratedFileStream fout(file.c_str());
  fout.SetCopyIfDifferent(true);

  fout << "# CMake generated Testfile for " << std::endl
154
       << "# Source directory: "
155
       << this->StateSnapshot.GetDirectory().GetCurrentSource() << std::endl
156
       << "# Build directory: "
157
       << this->StateSnapshot.GetDirectory().GetCurrentBinary() << std::endl
Ken Martin's avatar
Ken Martin committed
158
       << "# " << std::endl
159
       << "# This file includes the relevant testing commands "
Ken Martin's avatar
Ken Martin committed
160
161
162
       << "required for " << std::endl
       << "# testing this directory and lists subdirectories to "
       << "be tested as well." << std::endl;
163
164

  const char* testIncludeFile =
Ken Martin's avatar
Ken Martin committed
165
    this->Makefile->GetProperty("TEST_INCLUDE_FILE");
166
  if (testIncludeFile) {
167
    fout << "include(\"" << testIncludeFile << "\")" << std::endl;
168
  }
169
170

  // Ask each test generator to write its code.
171
172
173
174
  std::vector<cmTestGenerator*> const& testers =
    this->Makefile->GetTestGenerators();
  for (std::vector<cmTestGenerator*>::const_iterator gi = testers.begin();
       gi != testers.end(); ++gi) {
175
    (*gi)->Compute(this);
176
    (*gi)->Generate(fout, config, configurationTypes);
177
  }
178
179
180
  typedef std::vector<cmState::Snapshot> vec_t;
  vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren();
  for (vec_t::const_iterator i = children.begin(); i != children.end(); ++i) {
181
    // TODO: Use add_subdirectory instead?
182
183
    std::string outP = i->GetDirectory().GetCurrentBinary();
    outP = this->ConvertToRelativePath(outP, START_OUTPUT);
184
    outP = cmOutputConverter::EscapeForCMake(outP);
185
    fout << "subdirs(" << outP << ")" << std::endl;
186
  }
187
188
}

189
190
191
void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
{
  std::vector<cmGeneratorExpressionEvaluationFile*> ef =
192
193
194
195
    this->Makefile->GetEvaluationFiles();
  for (std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator li =
         ef.begin();
       li != ef.end(); ++li) {
196
    (*li)->CreateOutputFile(this, config);
197
  }
198
199
200
}

void cmLocalGenerator::ProcessEvaluationFiles(
201
  std::vector<std::string>& generatedFiles)
202
203
{
  std::vector<cmGeneratorExpressionEvaluationFile*> ef =
204
205
206
207
    this->Makefile->GetEvaluationFiles();
  for (std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator li =
         ef.begin();
       li != ef.end(); ++li) {
208
    (*li)->Generate(this);
209
    if (cmSystemTools::GetFatalErrorOccured()) {
210
      return;
211
    }
212
213
214
215
    std::vector<std::string> files = (*li)->GetFiles();
    std::sort(files.begin(), files.end());

    std::vector<std::string> intersection;
216
217
    std::set_intersection(files.begin(), files.end(), generatedFiles.begin(),
                          generatedFiles.end(),
218
                          std::back_inserter(intersection));
219
    if (!intersection.empty()) {
220
      cmSystemTools::Error("Files to be generated by multiple different "
221
222
                           "commands: ",
                           cmWrap('"', intersection, '"', " ").c_str());
223
      return;
224
    }
225
226
227

    generatedFiles.insert(generatedFiles.end(), files.begin(), files.end());
    std::vector<std::string>::iterator newIt =
228
      generatedFiles.end() - files.size();
229
    std::inplace_merge(generatedFiles.begin(), newIt, generatedFiles.end());
230
  }
231
232
}

233
234
void cmLocalGenerator::GenerateInstallRules()
{
235
  // Compute the install prefix.
Ken Martin's avatar
Ken Martin committed
236
  const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
237
238
#if defined(_WIN32) && !defined(__CYGWIN__)
  std::string prefix_win32;
239
240
  if (!prefix) {
    if (!cmSystemTools::GetEnv("SystemDrive", prefix_win32)) {
241
      prefix_win32 = "C:";
242
    }
Ken Martin's avatar
Ken Martin committed
243
    const char* project_name = this->Makefile->GetDefinition("PROJECT_NAME");
244
    if (project_name && project_name[0]) {
245
246
      prefix_win32 += "/Program Files/";
      prefix_win32 += project_name;
247
    } else {
248
249
      prefix_win32 += "/InstalledCMakeProject";
    }
250
251
    prefix = prefix_win32.c_str();
  }
252
#elif defined(__HAIKU__)
253
  char dir[B_PATH_NAME_LENGTH];
254
255
256
  if (!prefix) {
    if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
        B_OK) {
257
      prefix = dir;
258
    } else {
259
      prefix = "/boot/system";
260
    }
261
  }
262
#else
263
  if (!prefix) {
264
    prefix = "/usr/local";
265
  }
266
#endif
267
268
  if (const char* stagingPrefix =
        this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) {
269
    prefix = stagingPrefix;
270
  }
271

272
273
  // Compute the set of configurations.
  std::vector<std::string> configurationTypes;
274
  const std::string& config =
275
    this->Makefile->GetConfigurations(configurationTypes, false);
276

277
  // Choose a default install configuration.
278
  std::string default_config = config;
279
  const char* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO",
Daniel Pfeifer's avatar
Daniel Pfeifer committed
280
                                  "DEBUG", CM_NULLPTR };
281
282
283
284
  for (const char** c = default_order; *c && default_config.empty(); ++c) {
    for (std::vector<std::string>::iterator i = configurationTypes.begin();
         i != configurationTypes.end(); ++i) {
      if (cmSystemTools::UpperCase(*i) == *c) {
285
        default_config = *i;
286
287
      }
    }
288
289
  }
  if (default_config.empty() && !configurationTypes.empty()) {
290
    default_config = configurationTypes[0];
291
  }
292

293
  // Create the install script file.
294
  std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
295
  std::string homedir = this->GetState()->GetBinaryDirectory();
296
  int toplevel_install = 0;
297
  if (file == homedir) {
298
    toplevel_install = 1;
299
  }
300
  file += "/cmake_install.cmake";
301
302
  cmGeneratedFileStream fout(file.c_str());
  fout.SetCopyIfDifferent(true);
303

304
305
  // Write the header.
  fout << "# Install script for directory: "
306
307
       << this->StateSnapshot.GetDirectory().GetCurrentSource() << std::endl
       << std::endl;
Ken Martin's avatar
Ken Martin committed
308
  fout << "# Set the install prefix" << std::endl
309
310
311
312
       << "if(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl
       << "  set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")" << std::endl
       << "endif()" << std::endl
       << "string(REGEX REPLACE \"/$\" \"\" CMAKE_INSTALL_PREFIX "
Ken Martin's avatar
Ken Martin committed
313
314
       << "\"${CMAKE_INSTALL_PREFIX}\")" << std::endl
       << std::endl;
315

316
  // Write support code for generating per-configuration install rules.
317
  /* clang-format off */
318
319
  fout <<
    "# Set the install configuration name.\n"
320
321
322
    "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
    "  if(BUILD_TYPE)\n"
    "    string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n"
323
    "           CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n"
324
325
326
327
    "  else()\n"
    "    set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n"
    "  endif()\n"
    "  message(STATUS \"Install configuration: "
Ken Martin's avatar
Ken Martin committed
328
    "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n"
329
    "endif()\n"
330
    "\n";
331
  /* clang-format on */
332

333
  // Write support code for dealing with component-specific installs.
334
  /* clang-format off */
335
336
  fout <<
    "# Set the component getting installed.\n"
337
338
339
340
341
342
343
344
    "if(NOT CMAKE_INSTALL_COMPONENT)\n"
    "  if(COMPONENT)\n"
    "    message(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n"
    "    set(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n"
    "  else()\n"
    "    set(CMAKE_INSTALL_COMPONENT)\n"
    "  endif()\n"
    "endif()\n"
345
    "\n";
346
  /* clang-format on */
347

348
  // Copy user-specified install options to the install code.
349
350
  if (const char* so_no_exe =
        this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) {
351
    /* clang-format off */
352
353
    fout <<
      "# Install shared libraries without execute permission?\n"
354
355
356
      "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
      "  set(CMAKE_INSTALL_SO_NO_EXE \"" << so_no_exe << "\")\n"
      "endif()\n"
357
      "\n";
358
    /* clang-format on */
359
  }
360

361
362
  // Ask each install generator to write its code.
  std::vector<cmInstallGenerator*> const& installers =
Ken Martin's avatar
Ken Martin committed
363
    this->Makefile->GetInstallGenerators();
364
365
366
  for (std::vector<cmInstallGenerator*>::const_iterator gi =
         installers.begin();
       gi != installers.end(); ++gi) {
367
    (*gi)->Generate(fout, config, configurationTypes);
368
  }
369

370
371
  // Write rules from old-style specification stored in targets.
  this->GenerateTargetInstallRules(fout, config, configurationTypes);
372

373
  // Include install scripts from subdirectories.
374
375
376
  std::vector<cmState::Snapshot> children =
    this->Makefile->GetStateSnapshot().GetChildren();
  if (!children.empty()) {
377
    fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
378
    fout << "  # Include the install script for each subdirectory.\n";
379
380
381
    for (std::vector<cmState::Snapshot>::const_iterator ci = children.begin();
         ci != children.end(); ++ci) {
      if (!ci->GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
382
        std::string odir = ci->GetDirectory().GetCurrentBinary();
383
        cmSystemTools::ConvertToUnixSlashes(odir);
384
385
        fout << "  include(\"" << odir << "/cmake_install.cmake\")"
             << std::endl;
386
      }
387
    }
388
    fout << "\n";
389
    fout << "endif()\n\n";
390
  }
391
392

  // Record the install manifest.
393
  if (toplevel_install) {
394
    /* clang-format off */
395
    fout <<
396
397
      "if(CMAKE_INSTALL_COMPONENT)\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
398
      "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
399
400
      "else()\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
401
402
403
404
405
406
      "endif()\n"
      "\n"
      "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n"
      "       \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n"
      "file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n"
      "     \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n";
407
    /* clang-format on */
408
  }
409
410
}

411
void cmLocalGenerator::AddGeneratorTarget(cmGeneratorTarget* gt)
412
{
413
  this->GeneratorTargets.push_back(gt);
414
  this->GlobalGenerator->IndexGeneratorTarget(gt);
415
416
}

417
418
419
void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt)
{
  this->ImportedGeneratorTargets.push_back(gt);
420
  this->GlobalGenerator->IndexGeneratorTarget(gt);
421
422
}

423
424
425
426
427
void cmLocalGenerator::AddOwnedImportedGeneratorTarget(cmGeneratorTarget* gt)
{
  this->OwnedImportedGeneratorTargets.push_back(gt);
}

428
429
430
431
432
433
434
435
436
437
438
struct NamedGeneratorTargetFinder
{
  NamedGeneratorTargetFinder(std::string const& name)
    : Name(name)
  {
  }

  bool operator()(cmGeneratorTarget* tgt)
  {
    return tgt->GetName() == this->Name;
  }
439

440
441
442
443
private:
  std::string Name;
};

444
cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget(
445
  const std::string& name) const
446
{
447
  std::vector<cmGeneratorTarget*>::const_iterator ti =
448
449
450
    std::find_if(this->GeneratorTargets.begin(), this->GeneratorTargets.end(),
                 NamedGeneratorTargetFinder(name));
  if (ti != this->GeneratorTargets.end()) {
451
    return *ti;
452
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
453
  return CM_NULLPTR;
454
455
}

456
void cmLocalGenerator::ComputeTargetManifest()
457
458
459
{
  // Collect the set of configuration types.
  std::vector<std::string> configNames;
460
  this->Makefile->GetConfigurations(configNames);
461
  if (configNames.empty()) {
462
    configNames.push_back("");
463
  }
464
465

  // Add our targets to the manifest for each configuration.
466
  std::vector<cmGeneratorTarget*> targets = this->GetGeneratorTargets();
467
468
  for (std::vector<cmGeneratorTarget*>::iterator t = targets.begin();
       t != targets.end(); ++t) {
469
    cmGeneratorTarget* target = *t;
470
    if (target->GetType() == cmState::INTERFACE_LIBRARY) {
471
      continue;
472
473
474
    }
    for (std::vector<std::string>::iterator ci = configNames.begin();
         ci != configNames.end(); ++ci) {
475
      const char* config = ci->c_str();
476
      target->ComputeTargetManifest(config);
477
    }
478
  }
479
480
}

481
482
483
484
485
bool cmLocalGenerator::IsRootMakefile() const
{
  return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid();
}

486
487
488
489
490
491
492
cmState* cmLocalGenerator::GetState() const
{
  return this->GlobalGenerator->GetCMakeInstance()->GetState();
}

cmState::Snapshot cmLocalGenerator::GetStateSnapshot() const
{
493
  return this->Makefile->GetStateSnapshot();
494
495
}

496
497
498
// List of variables that are replaced when
// rules are expanced.  These variables are
// replaced in the form <var> with GetSafeDefinition(var).
499
// ${LANG} is replaced in the variable first with all enabled
500
// languages.
501
static const char* ruleReplaceVars[] = {
Bill Hoffman's avatar
Bill Hoffman committed
502
  "CMAKE_${LANG}_COMPILER",
503
504
  "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS",
  "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS",
505
  "CMAKE_SHARED_MODULE_${LANG}_FLAGS",
506
507
508
509
510
511
512
513
  "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS",
  "CMAKE_${LANG}_LINK_FLAGS",
  "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG",
  "CMAKE_${LANG}_ARCHIVE",
  "CMAKE_AR",
  "CMAKE_CURRENT_SOURCE_DIR",
  "CMAKE_CURRENT_BINARY_DIR",
  "CMAKE_RANLIB",
514
  "CMAKE_LINKER",
515
  "CMAKE_CL_SHOWINCLUDES_PREFIX",
Daniel Pfeifer's avatar
Daniel Pfeifer committed
516
  CM_NULLPTR
517
518
};

519
520
std::string cmLocalGenerator::ExpandRuleVariable(
  std::string const& variable, const RuleVariables& replaceValues)
521
{
522
523
  if (replaceValues.LinkFlags) {
    if (variable == "LINK_FLAGS") {
524
      return replaceValues.LinkFlags;
525
    }
526
527
528
  }
  if (replaceValues.Manifests) {
    if (variable == "MANIFESTS") {
529
530
      return replaceValues.Manifests;
    }
531
532
533
  }
  if (replaceValues.Flags) {
    if (variable == "FLAGS") {
534
      return replaceValues.Flags;
535
    }
536
  }
537

538
539
  if (replaceValues.Source) {
    if (variable == "SOURCE") {
540
      return replaceValues.Source;
541
    }
542
543
544
  }
  if (replaceValues.PreprocessedSource) {
    if (variable == "PREPROCESSED_SOURCE") {
545
546
      return replaceValues.PreprocessedSource;
    }
547
548
549
  }
  if (replaceValues.AssemblySource) {
    if (variable == "ASSEMBLY_SOURCE") {
550
551
      return replaceValues.AssemblySource;
    }
552
553
554
  }
  if (replaceValues.Object) {
    if (variable == "OBJECT") {
555
      return replaceValues.Object;
556
    }
557
558
559
  }
  if (replaceValues.ObjectDir) {
    if (variable == "OBJECT_DIR") {
560
561
      return replaceValues.ObjectDir;
    }
562
563
564
  }
  if (replaceValues.ObjectFileDir) {
    if (variable == "OBJECT_FILE_DIR") {
565
566
      return replaceValues.ObjectFileDir;
    }
567
568
569
  }
  if (replaceValues.Objects) {
    if (variable == "OBJECTS") {
570
      return replaceValues.Objects;
571
    }
572
573
574
  }
  if (replaceValues.ObjectsQuoted) {
    if (variable == "OBJECTS_QUOTED") {
575
      return replaceValues.ObjectsQuoted;
576
    }
577
578
  }
  if (replaceValues.Defines && variable == "DEFINES") {
579
    return replaceValues.Defines;
580
581
  }
  if (replaceValues.Includes && variable == "INCLUDES") {
582
    return replaceValues.Includes;
583
584
585
  }
  if (replaceValues.TargetPDB) {
    if (variable == "TARGET_PDB") {
586
587
      return replaceValues.TargetPDB;
    }
588
589
590
  }
  if (replaceValues.TargetCompilePDB) {
    if (variable == "TARGET_COMPILE_PDB") {
591
592
      return replaceValues.TargetCompilePDB;
    }
593
594
595
  }
  if (replaceValues.DependencyFile) {
    if (variable == "DEP_FILE") {
596
597
      return replaceValues.DependencyFile;
    }
598
  }
599

600
601
  if (replaceValues.Target) {
    if (variable == "TARGET_QUOTED") {
602
      std::string targetQuoted = replaceValues.Target;
603
      if (!targetQuoted.empty() && targetQuoted[0] != '\"') {
Bill Hoffman's avatar
Bill Hoffman committed
604
        targetQuoted = '\"';
605
        targetQuoted += replaceValues.Target;
Bill Hoffman's avatar
Bill Hoffman committed
606
607
        targetQuoted += '\"';
      }
608
609
610
      return targetQuoted;
    }
    if (variable == "TARGET_UNQUOTED") {
611
612
      std::string unquoted = replaceValues.Target;
      std::string::size_type sz = unquoted.size();
613
614
      if (sz > 2 && unquoted[0] == '\"' && unquoted[sz - 1] == '\"') {
        unquoted = unquoted.substr(1, sz - 2);
615
      }
616
617
618
619
      return unquoted;
    }
    if (replaceValues.LanguageCompileFlags) {
      if (variable == "LANGUAGE_COMPILE_FLAGS") {
620
        return replaceValues.LanguageCompileFlags;
621
      }
622
623
624
    }
    if (replaceValues.Target) {
      if (variable == "TARGET") {
625
        return replaceValues.Target;
626
      }
627
628
    }
    if (variable == "TARGET_IMPLIB") {
Ken Martin's avatar
Ken Martin committed
629
      return this->TargetImplib;
630
631
632
    }
    if (variable == "TARGET_VERSION_MAJOR") {
      if (replaceValues.TargetVersionMajor) {
633
634
        return replaceValues.TargetVersionMajor;
      }
635
      return "0";
636
637
638
    }
    if (variable == "TARGET_VERSION_MINOR") {
      if (replaceValues.TargetVersionMinor) {
639
640
        return replaceValues.TargetVersionMinor;
      }
641
      return "0";
642
643
644
    }
    if (replaceValues.Target) {
      if (variable == "TARGET_BASE") {
645
646
        // Strip the last extension off the target name.
        std::string targetBase = replaceValues.Target;
647
        std::string::size_type pos = targetBase.rfind('.');
648
649
        if (pos != targetBase.npos) {
          return targetBase.substr(0, pos);
Bill Hoffman's avatar
Bill Hoffman committed
650
        }
651
        return targetBase;
652
653
      }
    }
654
655
656
  }
  if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" ||
      variable == "TARGET_INSTALLNAME_DIR") {
657
    // All these variables depend on TargetSOName
658
659
    if (replaceValues.TargetSOName) {
      if (variable == "TARGET_SONAME") {
660
        return replaceValues.TargetSOName;
661
662
      }
      if (variable == "SONAME_FLAG" && replaceValues.SONameFlag) {
663
        return replaceValues.SONameFlag;
664
665
666
      }
      if (replaceValues.TargetInstallNameDir &&
          variable == "TARGET_INSTALLNAME_DIR") {
667
        return replaceValues.TargetInstallNameDir;
668
669
      }
    }
670
671
672
673
    return "";
  }
  if (replaceValues.LinkLibraries) {
    if (variable == "LINK_LIBRARIES") {
674
      return replaceValues.LinkLibraries;
675
    }
676
677
678
  }
  if (replaceValues.Language) {
    if (variable == "LANGUAGE") {
679
680
      return replaceValues.Language;
    }
681
682
683
  }
  if (replaceValues.CMTarget) {
    if (variable == "TARGET_NAME") {
684
      return replaceValues.CMTarget->GetName();
685
686
    }
    if (variable == "TARGET_TYPE") {
687
      return cmState::GetTargetTypeName(replaceValues.CMTarget->GetType());
688
    }
689
690
691
  }
  if (replaceValues.Output) {
    if (variable == "OUTPUT") {
692
693
      return replaceValues.Output;
    }
694
695
  }
  if (variable == "CMAKE_COMMAND") {
696
697
698
    return this->ConvertToOutputFormat(
      cmSystemTools::CollapseFullPath(cmSystemTools::GetCMakeCommand()),
      SHELL);
699
  }
700
  std::vector<std::string> enabledLanguages =
701
    this->GetState()->GetEnabledLanguages();
702
703
  // loop over language specific replace variables
  int pos = 0;
704
705
706
  while (ruleReplaceVars[pos]) {
    for (std::vector<std::string>::iterator i = enabledLanguages.begin();
         i != enabledLanguages.end(); ++i) {
707
      const char* lang = i->c_str();
Bill Hoffman's avatar
Bill Hoffman committed
708
      std::string actualReplace = ruleReplaceVars[pos];
709
      // If this is the compiler then look for the extra variable
710
      // _COMPILER_ARG1 which must be the first argument to the compiler
Daniel Pfeifer's avatar
Daniel Pfeifer committed
711
712
713
714
715
716
717
      const char* compilerArg1 = CM_NULLPTR;
      const char* compilerTarget = CM_NULLPTR;
      const char* compilerOptionTarget = CM_NULLPTR;
      const char* compilerExternalToolchain = CM_NULLPTR;
      const char* compilerOptionExternalToolchain = CM_NULLPTR;
      const char* compilerSysroot = CM_NULLPTR;
      const char* compilerOptionSysroot = CM_NULLPTR;
718
      if (actualReplace == "CMAKE_${LANG}_COMPILER") {
719
720
        std::string arg1 = actualReplace + "_ARG1";
        cmSystemTools::ReplaceString(arg1, "${LANG}", lang);
Stephen Kelly's avatar
Stephen Kelly committed
721
        compilerArg1 = this->Makefile->GetDefinition(arg1);
722
723
724
725
726
727
728
729
730
731
732
733
734
735
        compilerTarget = this->Makefile->GetDefinition(
          std::string("CMAKE_") + lang + "_COMPILER_TARGET");
        compilerOptionTarget = this->Makefile->GetDefinition(
          std::string("CMAKE_") + lang + "_COMPILE_OPTIONS_TARGET");
        compilerExternalToolchain = this->Makefile->GetDefinition(
          std::string("CMAKE_") + lang + "_COMPILER_EXTERNAL_TOOLCHAIN");
        compilerOptionExternalToolchain =
          this->Makefile->GetDefinition(std::string("CMAKE_") + lang +
                                        "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN");
        compilerSysroot = this->Makefile->GetDefinition("CMAKE_SYSROOT");
        compilerOptionSysroot = this->Makefile->GetDefinition(
          std::string("CMAKE_") + lang + "_COMPILE_OPTIONS_SYSROOT");
      }
      if (actualReplace.find("${LANG}") != actualReplace.npos) {
Bill Hoffman's avatar
Bill Hoffman committed
736
        cmSystemTools::ReplaceString(actualReplace, "${LANG}", lang);
737
738
739
      }
      if (actualReplace == variable) {
        std::string replace = this->Makefile->GetSafeDefinition(variable);
Bill Hoffman's avatar
Bill Hoffman committed
740
        // if the variable is not a FLAG then treat it like a path
741
        if (variable.find("_FLAG") == variable.npos) {
Stephen Kelly's avatar
Stephen Kelly committed
742
          std::string ret = this->ConvertToOutputForExisting(replace);
Ken Martin's avatar
Ken Martin committed
743
744
          // if there is a required first argument to the compiler add it
          // to the compiler string
745
          if (compilerArg1) {
746
747
            ret += " ";
            ret += compilerArg1;
748
749
          }
          if (compilerTarget && compilerOptionTarget) {
750
751
752
            ret += " ";
            ret += compilerOptionTarget;
            ret += compilerTarget;
753
754
          }
          if (compilerExternalToolchain && compilerOptionExternalToolchain) {
755
756
757
            ret += " ";
            ret += compilerOptionExternalToolchain;
            ret += this->EscapeForShell(compilerExternalToolchain, true);
758
759
          }
          if (compilerSysroot && compilerOptionSysroot) {
760
761
762
            ret += " ";
            ret += compilerOptionSysroot;
            ret += this->EscapeForShell(compilerSysroot, true);
Bill Hoffman's avatar
Bill Hoffman committed
763
          }
764
          return ret;
765
        }
766
        return replace;
767
768
      }
    }
769
770
    pos++;
  }
Bill Hoffman's avatar
Bill Hoffman committed
771
772
773
  return variable;
}

774
775
void cmLocalGenerator::ExpandRuleVariables(std::string& s,
                                           const RuleVariables& replaceValues)
Bill Hoffman's avatar
Bill Hoffman committed
776
{
777
  if (replaceValues.RuleLauncher) {
778
779
    this->InsertRuleLauncher(s, replaceValues.CMTarget,
                             replaceValues.RuleLauncher);
780
  }
Bill Hoffman's avatar
Bill Hoffman committed
781
782
  std::string::size_type start = s.find('<');
  // no variables to expand
783
  if (start == s.npos) {
Bill Hoffman's avatar
Bill Hoffman committed
784
    return;
785
  }
Bill Hoffman's avatar
Bill Hoffman committed
786
787
  std::string::size_type pos = 0;
  std::string expandedInput;
788
  while (start != s.npos && start < s.size() - 2) {
Bill Hoffman's avatar
Bill Hoffman committed
789
790
    std::string::size_type end = s.find('>', start);
    // if we find a < with no > we are done
791
    if (end == s.npos) {
Bill Hoffman's avatar
Bill Hoffman committed
792
      return;
793
794
    }
    char c = s[start + 1];
Bill Hoffman's avatar
Bill Hoffman committed
795
796
    // if the next char after the < is not A-Za-z then
    // skip it and try to find the next < in the string
797
798
799
    if (!isalpha(c)) {
      start = s.find('<', start + 1);
    } else {