Updates will be applied on October 27th between 12pm - 12:45pm EDT (UTC-0400). Gitlab may be slow during the maintenance window.

cmLocalGenerator.cxx 93.5 KB
Newer Older
Ken Martin's avatar
Ken Martin committed
1
2
/*=========================================================================

3
  Program:   CMake - Cross-Platform Makefile Generator
Ken Martin's avatar
Ken Martin committed
4
5
6
7
8
  Module:    $RCSfile$
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

9
10
  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
Ken Martin's avatar
Ken Martin committed
11

12
13
     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
Ken Martin's avatar
Ken Martin committed
14
15
16
17
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "cmLocalGenerator.h"
18

19
#include "cmComputeLinkInformation.h"
20
#include "cmGeneratedFileStream.h"
Ken Martin's avatar
updates    
Ken Martin committed
21
#include "cmGlobalGenerator.h"
22
#include "cmInstallGenerator.h"
23
#include "cmInstallFilesGenerator.h"
24
25
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
Ken Martin's avatar
Ken Martin committed
26
#include "cmMakefile.h"
27
#include "cmSourceFile.h"
28
#include "cmTest.h"
29
#include "cmTestGenerator.h"
30
#include "cmVersion.h"
31
32
#include "cmake.h"

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

38
39
#include <cmsys/System.h>

40
#include <ctype.h> // for isalpha
Ken Martin's avatar
Ken Martin committed
41

42
43
#include <assert.h>

Ken Martin's avatar
Ken Martin committed
44
45
cmLocalGenerator::cmLocalGenerator()
{
46
  this->Makefile = 0; // moved to after set on global
Ken Martin's avatar
Ken Martin committed
47
48
  this->Parent = 0;
  this->WindowsShell = false;
49
  this->WindowsVSIDE = false;
50
  this->WatcomWMake = false;
51
  this->MinGWMake = false;
52
  this->NMake = false;
53
  this->MSYSShell = false;
54
  this->LinkScriptShell = false;
Ken Martin's avatar
Ken Martin committed
55
56
  this->IgnoreLibPrefix = false;
  this->UseRelativePaths = false;
57
  this->Configured = false;
58
  this->EmitUniversalBinaryFlags = true;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
59
  this->IsMakefileGenerator = false;
60
  this->RelativePathsConfigured = false;
61
  this->PathConversionsSetup = false;
62
63
  this->BackwardsCompatibility = 0;
  this->BackwardsCompatibilityFinal = false;
Ken Martin's avatar
Ken Martin committed
64
65
66
67
}

cmLocalGenerator::~cmLocalGenerator()
{
Ken Martin's avatar
Ken Martin committed
68
  delete this->Makefile;
Ken Martin's avatar
Ken Martin committed
69
70
}

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//----------------------------------------------------------------------------
class cmLocalGeneratorCurrent
{
  cmGlobalGenerator* GG;
  cmLocalGenerator* LG;
public:
  cmLocalGeneratorCurrent(cmLocalGenerator* lg)
    {
    this->GG = lg->GetGlobalGenerator();
    this->LG = this->GG->GetCurrentLocalGenerator();
    this->GG->SetCurrentLocalGenerator(lg);
    }
  ~cmLocalGeneratorCurrent()
    {
    this->GG->SetCurrentLocalGenerator(this->LG);
    }
};

//----------------------------------------------------------------------------
Ken Martin's avatar
Ken Martin committed
90
91
void cmLocalGenerator::Configure()
{
92
93
94
  // Manage the global generator's current local generator.
  cmLocalGeneratorCurrent clg(this);
  static_cast<void>(clg);
Alexander Neundorf's avatar
   
Alexander Neundorf committed
95

96
  // make sure the CMakeFiles dir is there
Ken Martin's avatar
Ken Martin committed
97
  std::string filesDir = this->Makefile->GetStartOutputDirectory();
98
  filesDir += cmake::GetCMakeFilesDirectory();
99
100
  cmSystemTools::MakeDirectory(filesDir.c_str());
  
Ken Martin's avatar
Ken Martin committed
101
  // find & read the list file
Ken Martin's avatar
Ken Martin committed
102
  std::string currentStart = this->Makefile->GetStartDirectory();
Ken Martin's avatar
Ken Martin committed
103
  currentStart += "/CMakeLists.txt";
Ken Martin's avatar
Ken Martin committed
104
  this->Makefile->ReadListFile(currentStart.c_str());
105

106
107
108
109
110
111
112
113
114
115
  // at the end of the ReadListFile handle any old style subdirs
  // first get all the subdirectories
  std::vector<cmLocalGenerator *> subdirs = this->GetChildren();
  
  // for each subdir recurse
  std::vector<cmLocalGenerator *>::iterator sdi = subdirs.begin();
  for (; sdi != subdirs.end(); ++sdi)
    {
    if (!(*sdi)->Configured)
      {
Ken Martin's avatar
Ken Martin committed
116
      this->Makefile->ConfigureSubDirectory(*sdi);
117
118
      }
    }  
119

120
121
  // Check whether relative paths should be used for optionally
  // relative paths.
Ken Martin's avatar
Ken Martin committed
122
  this->UseRelativePaths = this->Makefile->IsOn("CMAKE_USE_RELATIVE_PATHS");
123

124
125
126
127
128
129
130
131
  this->ComputeObjectMaxPath();

  this->Configured = true;
}

//----------------------------------------------------------------------------
void cmLocalGenerator::ComputeObjectMaxPath()
{
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  // Choose a maximum object file name length.
#if defined(_WIN32) || defined(__CYGWIN__)
  this->ObjectPathMax = 250;
#else
  this->ObjectPathMax = 1000;
#endif
  const char* plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
  if(plen && *plen)
    {
    unsigned int pmax;
    if(sscanf(plen, "%u", &pmax) == 1)
      {
      if(pmax >= 128)
        {
        this->ObjectPathMax = pmax;
        }
      else
        {
        cmOStringStream w;
        w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax
          << ", which is less than the minimum of 128.  "
          << "The value will be ignored.";
        this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
        }
      }
    else
      {
      cmOStringStream w;
      w << "CMAKE_OBJECT_PATH_MAX is set to \"" << plen
        << "\", which fails to parse as a positive integer.  "
        << "The value will be ignored.";
      this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
      }
    }
166
  this->ObjectMaxPathViolations.clear();
167
168
169
170
}

void cmLocalGenerator::SetupPathConversions()
{  
171
  // Setup the current output directory components for use by
172
173
174
  // Convert
  std::string outdir; 
  outdir =
Ken Martin's avatar
Ken Martin committed
175
176
    cmSystemTools::CollapseFullPath(this->Makefile->GetHomeDirectory());
  cmSystemTools::SplitPath(outdir.c_str(), this->HomeDirectoryComponents);
177
  outdir =
Ken Martin's avatar
Ken Martin committed
178
179
    cmSystemTools::CollapseFullPath(this->Makefile->GetStartDirectory());
  cmSystemTools::SplitPath(outdir.c_str(), this->StartDirectoryComponents);
Ken Martin's avatar
Ken Martin committed
180
181
182
183
184
185
186
187
188
189

  outdir = cmSystemTools::CollapseFullPath
    (this->Makefile->GetHomeOutputDirectory());
  cmSystemTools::SplitPath(outdir.c_str(), 
                           this->HomeOutputDirectoryComponents);

  outdir = cmSystemTools::CollapseFullPath
    (this->Makefile->GetStartOutputDirectory());
  cmSystemTools::SplitPath(outdir.c_str(), 
                           this->StartOutputDirectoryComponents);
Ken Martin's avatar
Ken Martin committed
190
}
Ken Martin's avatar
updates    
Ken Martin committed
191

192

Ken Martin's avatar
updates    
Ken Martin committed
193
194
void cmLocalGenerator::SetGlobalGenerator(cmGlobalGenerator *gg)
{
Ken Martin's avatar
Ken Martin committed
195
  this->GlobalGenerator = gg;
196
197
  this->Makefile = new cmMakefile;
  this->Makefile->SetLocalGenerator(this);
Ken Martin's avatar
updates    
Ken Martin committed
198
199

  // setup the home directories
200
  this->Makefile->GetProperties().SetCMakeInstance(gg->GetCMakeInstance());
Ken Martin's avatar
Ken Martin committed
201
  this->Makefile->SetHomeDirectory(
Ken Martin's avatar
updates    
Ken Martin committed
202
    gg->GetCMakeInstance()->GetHomeDirectory());
Ken Martin's avatar
Ken Martin committed
203
  this->Makefile->SetHomeOutputDirectory(
Ken Martin's avatar
updates    
Ken Martin committed
204
    gg->GetCMakeInstance()->GetHomeOutputDirectory());
Brad King's avatar
Brad King committed
205
}
Ken Martin's avatar
Ken Martin committed
206
207

void cmLocalGenerator::ConfigureFinalPass()
208
{
Ken Martin's avatar
Ken Martin committed
209
  this->Makefile->ConfigureFinalPass();
Ken Martin's avatar
Ken Martin committed
210
}
211

Alexander Neundorf's avatar
   
Alexander Neundorf committed
212
213
214
215
216
217
void cmLocalGenerator::TraceDependencies()
{
  // Generate the rule files for each target.
  cmTargets& targets = this->Makefile->GetTargets();
  for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
    {
218
219
    const char* projectFilename = 0;
    if (this->IsMakefileGenerator == false)  // only use of this variable
Alexander Neundorf's avatar
   
Alexander Neundorf committed
220
      {
221
      projectFilename = t->second.GetName();
Alexander Neundorf's avatar
   
Alexander Neundorf committed
222
      }
223
    t->second.TraceDependencies(projectFilename);
Alexander Neundorf's avatar
   
Alexander Neundorf committed
224
225
226
    }
}

227
228
void cmLocalGenerator::GenerateTestFiles()
{
Ken Martin's avatar
Ken Martin committed
229
  if ( !this->Makefile->IsOn("CMAKE_TESTING_ENABLED") )
230
231
232
    {
    return;
    }
233
234
235
236
237
238
239
240
241
242
243
244
245
246

  // Compute the set of configurations.
  std::vector<std::string> configurationTypes;
  if(const char* types =
     this->Makefile->GetDefinition("CMAKE_CONFIGURATION_TYPES"))
    {
    cmSystemTools::ExpandListArgument(types, configurationTypes);
    }
  const char* config = 0;
  if(configurationTypes.empty())
    {
    config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE");
    }

Ken Martin's avatar
Ken Martin committed
247
  std::string file = this->Makefile->GetStartOutputDirectory();
248
  file += "/";
Ken Martin's avatar
Ken Martin committed
249
250
  file += "CTestTestfile.cmake";

251
252
253
254
  cmGeneratedFileStream fout(file.c_str());
  fout.SetCopyIfDifferent(true);

  fout << "# CMake generated Testfile for " << std::endl
Ken Martin's avatar
Ken Martin committed
255
256
257
258
259
       << "# Source directory: " 
       << this->Makefile->GetStartDirectory() << std::endl
       << "# Build directory: " 
       << this->Makefile->GetStartOutputDirectory() << std::endl
       << "# " << std::endl
Ken Martin's avatar
Ken Martin committed
260
261
262
263
       << "# This file includes the relevent testing commands "
       << "required for " << std::endl
       << "# testing this directory and lists subdirectories to "
       << "be tested as well." << std::endl;
Ken Martin's avatar
Ken Martin committed
264
265
266
  
  const char* testIncludeFile = 
    this->Makefile->GetProperty("TEST_INCLUDE_FILE");
267
268
269
270
  if ( testIncludeFile )
    {
    fout << "INCLUDE(\"" << testIncludeFile << "\")" << std::endl;
    }
271
272
273
274
275
276

  // Ask each test generator to write its code.
  std::vector<cmTestGenerator*> const&
    testers = this->Makefile->GetTestGenerators();
  for(std::vector<cmTestGenerator*>::const_iterator gi = testers.begin();
      gi != testers.end(); ++gi)
277
    {
278
    (*gi)->Generate(fout, config, configurationTypes);
279
280
281
282
283
284
    }
  if ( this->Children.size())
    {
    size_t i;
    for(i = 0; i < this->Children.size(); ++i)
      {
285
      fout << "SUBDIRS(";
286
      std::string outP = 
Ken Martin's avatar
Ken Martin committed
287
        this->Children[i]->GetMakefile()->GetStartOutputDirectory();
288
289
      fout << this->Convert(outP.c_str(),START_OUTPUT);
      fout << ")" << std::endl;
290
291
292
293
      }
    }
}

294
//----------------------------------------------------------------------------
295
296
void cmLocalGenerator::GenerateInstallRules()
{
297
  // Compute the install prefix.
Ken Martin's avatar
Ken Martin committed
298
  const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
299
300
301
302
303
304
305
306
#if defined(_WIN32) && !defined(__CYGWIN__)
  std::string prefix_win32;
  if(!prefix)
    {
    if(!cmSystemTools::GetEnv("SystemDrive", prefix_win32))
      {
      prefix_win32 = "C:";
      }
Ken Martin's avatar
Ken Martin committed
307
    const char* project_name = this->Makefile->GetDefinition("PROJECT_NAME");
308
309
310
311
312
313
314
315
316
317
318
319
    if(project_name && project_name[0])
      {
      prefix_win32 += "/Program Files/";
      prefix_win32 += project_name;
      }
    else
      {
      prefix_win32 += "/InstalledCMakeProject";
      }
    prefix = prefix_win32.c_str();
    }
#else
320
321
322
323
  if (!prefix)
    {
    prefix = "/usr/local";
    }
324
#endif
325

326
327
  // Compute the set of configurations.
  std::vector<std::string> configurationTypes;
Ken Martin's avatar
Ken Martin committed
328
329
  if(const char* types = 
     this->Makefile->GetDefinition("CMAKE_CONFIGURATION_TYPES"))
330
331
332
333
334
335
    {
    cmSystemTools::ExpandListArgument(types, configurationTypes);
    }
  const char* config = 0;
  if(configurationTypes.empty())
    {
Ken Martin's avatar
Ken Martin committed
336
    config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE");
337
338
    }

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  // Choose a default install configuration.
  const char* default_config = config;
  const char* default_order[] = {"RELEASE", "MINSIZEREL",
                                 "RELWITHDEBINFO", "DEBUG", 0};
  for(const char** c = default_order; *c && !default_config; ++c)
    {
    for(std::vector<std::string>::iterator i = configurationTypes.begin();
        i != configurationTypes.end(); ++i)
      {
      if(cmSystemTools::UpperCase(*i) == *c)
        {
        default_config = i->c_str();
        }
      }
    }
  if(!default_config && !configurationTypes.empty())
    {
    default_config = configurationTypes[0].c_str();
    }
  if(!default_config)
    {
    default_config = "Release";
    }

363
  // Create the install script file.
Ken Martin's avatar
Ken Martin committed
364
365
366
  std::string file = this->Makefile->GetStartOutputDirectory();
  std::string homedir = this->Makefile->GetHomeOutputDirectory();
  std::string currdir = this->Makefile->GetCurrentOutputDirectory();
367
  cmSystemTools::ConvertToUnixSlashes(file);
368
369
370
371
372
373
374
  cmSystemTools::ConvertToUnixSlashes(homedir);
  cmSystemTools::ConvertToUnixSlashes(currdir);
  int toplevel_install = 0;
  if ( currdir == homedir )
    {
    toplevel_install = 1;
    }
375
  file += "/cmake_install.cmake";
376
377
  cmGeneratedFileStream fout(file.c_str());
  fout.SetCopyIfDifferent(true);
378

379
380
  // Write the header.
  fout << "# Install script for directory: "
Ken Martin's avatar
Ken Martin committed
381
382
       << this->Makefile->GetCurrentDirectory() << std::endl << std::endl;
  fout << "# Set the install prefix" << std::endl
Ken Martin's avatar
Ken Martin committed
383
384
385
386
387
388
       << "IF(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl
       << "  SET(CMAKE_INSTALL_PREFIX \"" << prefix << "\")" << std::endl
       << "ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl
       << "STRING(REGEX REPLACE \"/$\" \"\" CMAKE_INSTALL_PREFIX "
       << "\"${CMAKE_INSTALL_PREFIX}\")" << std::endl
       << std::endl;
389

390
  // Write support code for generating per-configuration install rules.
391
392
  fout <<
    "# Set the install configuration name.\n"
393
    "IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
394
395
396
397
    "  IF(BUILD_TYPE)\n"
    "    STRING(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n"
    "           CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n"
    "  ELSE(BUILD_TYPE)\n"
398
    "    SET(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n"
399
    "  ENDIF(BUILD_TYPE)\n"
Ken Martin's avatar
Ken Martin committed
400
401
    "  MESSAGE(STATUS \"Install configuration: "
    "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n"
402
    "ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
403
    "\n";
404

405
406
407
408
409
410
411
412
413
414
415
416
417
  // Write support code for dealing with component-specific installs.
  fout <<
    "# Set the component getting installed.\n"
    "IF(NOT CMAKE_INSTALL_COMPONENT)\n"
    "  IF(COMPONENT)\n"
    "    MESSAGE(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n"
    "    SET(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n"
    "  ELSE(COMPONENT)\n"
    "    SET(CMAKE_INSTALL_COMPONENT)\n"
    "  ENDIF(COMPONENT)\n"
    "ENDIF(NOT CMAKE_INSTALL_COMPONENT)\n"
    "\n";

418
419
420
421
422
423
424
425
426
427
428
429
  // Copy user-specified install options to the install code.
  if(const char* so_no_exe =
     this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE"))
    {
    fout <<
      "# Install shared libraries without execute permission?\n"
      "IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
      "  SET(CMAKE_INSTALL_SO_NO_EXE \"" << so_no_exe << "\")\n"
      "ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
      "\n";
    }

430
431
  // Ask each install generator to write its code.
  std::vector<cmInstallGenerator*> const& installers =
Ken Martin's avatar
Ken Martin committed
432
    this->Makefile->GetInstallGenerators();
Ken Martin's avatar
Ken Martin committed
433
434
  for(std::vector<cmInstallGenerator*>::const_iterator 
        gi = installers.begin();
435
      gi != installers.end(); ++gi)
436
    {
437
    (*gi)->Generate(fout, config, configurationTypes);
438
439
    }

440
441
  // Write rules from old-style specification stored in targets.
  this->GenerateTargetInstallRules(fout, config, configurationTypes);
442

443
  // Include install scripts from subdirectories.
444
  if(!this->Children.empty())
445
    {
446
447
    fout << "IF(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
    fout << "  # Include the install script for each subdirectory.\n";
448
449
    for(std::vector<cmLocalGenerator*>::const_iterator
          ci = this->Children.begin(); ci != this->Children.end(); ++ci)
450
      {
451
      if(!(*ci)->GetMakefile()->GetPropertyAsBool("EXCLUDE_FROM_ALL"))
452
453
454
        {
        std::string odir = (*ci)->GetMakefile()->GetStartOutputDirectory();
        cmSystemTools::ConvertToUnixSlashes(odir);
455
        fout << "  INCLUDE(\"" <<  odir.c_str()
456
457
             << "/cmake_install.cmake\")" << std::endl;
        }
458
      }
459
    fout << "\n";
Alexander Neundorf's avatar
   
Alexander Neundorf committed
460
    fout << "ENDIF(NOT CMAKE_INSTALL_LOCAL_ONLY)\n\n";
461
    }
462
463

  // Record the install manifest.
464
465
  if ( toplevel_install )
    {
466
467
468
469
470
471
    fout <<
      "IF(CMAKE_INSTALL_COMPONENT)\n"
      "  SET(CMAKE_INSTALL_MANIFEST \"install_manifest_"
      "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
      "ELSE(CMAKE_INSTALL_COMPONENT)\n"
      "  SET(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
Alexander Neundorf's avatar
   
Alexander Neundorf committed
472
      "ENDIF(CMAKE_INSTALL_COMPONENT)\n\n";
473
474
475
476
477
478
479
480
    fout
      << "FILE(WRITE \""
      << homedir.c_str() << "/${CMAKE_INSTALL_MANIFEST}\" "
      << "\"\")" << std::endl;
    fout
      << "FOREACH(file ${CMAKE_INSTALL_MANIFEST_FILES})" << std::endl
      << "  FILE(APPEND \""
      << homedir.c_str() << "/${CMAKE_INSTALL_MANIFEST}\" "
481
482
483
      << "\"${file}\\n\")" << std::endl
      << "ENDFOREACH(file)" << std::endl;
    }
484
485
}

486
//----------------------------------------------------------------------------
487
void cmLocalGenerator::GenerateTargetManifest()
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
{
  // Collect the set of configuration types.
  std::vector<std::string> configNames;
  if(const char* configurationTypes =
     this->Makefile->GetDefinition("CMAKE_CONFIGURATION_TYPES"))
    {
    cmSystemTools::ExpandListArgument(configurationTypes, configNames);
    }
  else if(const char* buildType =
          this->Makefile->GetDefinition("CMAKE_BUILD_TYPE"))
    {
    if(*buildType)
      {
      configNames.push_back(buildType);
      }
    }

  // Add our targets to the manifest for each configuration.
  cmTargets& targets = this->Makefile->GetTargets();
  for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
    {
    cmTarget& target = t->second;
510
    if(configNames.empty())
511
      {
512
513
514
515
516
517
      target.GenerateTargetManifest(0);
      }
    else
      {
      for(std::vector<std::string>::iterator ci = configNames.begin();
          ci != configNames.end(); ++ci)
518
        {
519
520
        const char* config = ci->c_str();
        target.GenerateTargetManifest(config);
521
522
523
524
525
        }
      }
    }
}

526
527
528
void cmLocalGenerator::AddCustomCommandToCreateObject(const char* ofname, 
                                                      const char* lang, 
                                                      cmSourceFile& source,
Bill Hoffman's avatar
Bill Hoffman committed
529
                                                      cmTarget& )
530
{ 
531
  std::string objectDir = cmSystemTools::GetFilenamePath(std::string(ofname));
532
  objectDir = this->Convert(objectDir.c_str(),START_OUTPUT,SHELL);
533
534
535
  std::string objectFile = this->Convert(ofname,START_OUTPUT,SHELL);
  std::string sourceFile = 
    this->Convert(source.GetFullPath().c_str(),START_OUTPUT,SHELL,true);
536
537
538
539
  std::string varString = "CMAKE_";
  varString += lang;
  varString += "_COMPILE_OBJECT";
  std::vector<std::string> rules;
Ken Martin's avatar
Ken Martin committed
540
  rules.push_back(this->Makefile->GetRequiredDefinition(varString.c_str()));
541
542
543
544
  varString = "CMAKE_";
  varString += lang;
  varString += "_FLAGS";
  std::string flags;
Ken Martin's avatar
Ken Martin committed
545
  flags += this->Makefile->GetSafeDefinition(varString.c_str());
546
547
  flags += " ";
  flags += this->GetIncludeFlags(lang);
548
549
550

  // Construct the command lines.
  cmCustomCommandLines commandLines;
551
552
  std::vector<std::string> commands;
  cmSystemTools::ExpandList(rules, commands);
553
554
555
556
  cmLocalGenerator::RuleVariables vars;
  vars.Language = lang;
  vars.Source = sourceFile.c_str();
  vars.Object = objectFile.c_str();
557
  vars.ObjectDir = objectDir.c_str();
558
  vars.Flags = flags.c_str();
559
560
561
  for(std::vector<std::string>::iterator i = commands.begin();
      i != commands.end(); ++i)
    {
562
    // Expand the full command line string.
563
    this->ExpandRuleVariables(*i, vars);
564
565
566
567
568
569
570
571
572
573
574
575

    // Parse the string to get the custom command line.
    cmCustomCommandLine commandLine;
    std::vector<cmStdString> cmd = cmSystemTools::ParseArguments(i->c_str());
    for(std::vector<cmStdString>::iterator a = cmd.begin();
        a != cmd.end(); ++a)
      {
      commandLine.push_back(*a);
      }

    // Store this command line.
    commandLines.push_back(commandLine);
576
    }
577
578

  // Check for extra object-file dependencies.
579
580
581
582
583
  std::vector<std::string> depends;
  const char* additionalDeps = source.GetProperty("OBJECT_DEPENDS");
  if(additionalDeps)
    {
    cmSystemTools::ExpandListArgument(additionalDeps, depends);
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
    }

  // Generate a meaningful comment for the command.
  std::string comment = "Building ";
  comment += lang;
  comment += " object ";
  comment += this->Convert(ofname, START_OUTPUT);

  // Add the custom command to build the object file.
  this->Makefile->AddCustomCommandToOutput(
    ofname,
    depends,
    source.GetFullPath().c_str(),
    commandLines,
    comment.c_str(),
    this->Makefile->GetStartOutputDirectory()
    );
601
602
603
604
605
606
607
}

void cmLocalGenerator::AddBuildTargetRule(const char* llang, cmTarget& target)
{
  cmStdString objs;
  std::vector<std::string> objVector;
  // Add all the sources outputs to the depends of the target
608
609
  std::vector<cmSourceFile*> const& classes = target.GetSourceFiles();
  for(std::vector<cmSourceFile*>::const_iterator i = classes.begin();
610
      i != classes.end(); ++i)
611
612
613
614
615
    {
    cmSourceFile* sf = *i;
    if(!sf->GetCustomCommand() &&
       !sf->GetPropertyAsBool("HEADER_FILE_ONLY") &&
       !sf->GetPropertyAsBool("EXTERNAL_OBJECT"))
616
      {
617
618
619
620
      std::string dir_max;
      dir_max += this->Makefile->GetCurrentOutputDirectory();
      dir_max += "/";
      std::string obj = this->GetObjectFileNameWithoutTarget(*sf, dir_max);
621
      if(!obj.empty())
622
        {
Ken Martin's avatar
Ken Martin committed
623
        std::string ofname = this->Makefile->GetCurrentOutputDirectory();
624
        ofname += "/";
625
        ofname += obj;
626
        objVector.push_back(ofname);
Ken Martin's avatar
Ken Martin committed
627
628
        this->AddCustomCommandToCreateObject(ofname.c_str(), 
                                             llang, *(*i), target);
629
        objs += this->Convert(ofname.c_str(),START_OUTPUT,MAKEFILE);
630
631
632
633
634
635
636
        objs += " ";
        }
      }
    }
  std::string createRule = "CMAKE_";
  createRule += llang;
  createRule += target.GetCreateRuleVariable();
637
  std::string targetName = target.GetFullName();
638
639
640
641
642
643
644
645
  // Executable :
  // Shared Library:
  // Static Library:
  // Shared Module:
  std::string linkLibs; // should be set
  std::string flags; // should be set
  std::string linkFlags; // should be set 
  this->GetTargetFlags(linkLibs, flags, linkFlags, target);
646
647
648
  cmLocalGenerator::RuleVariables vars;
  vars.Language = llang;
  vars.Objects = objs.c_str();
649
  vars.ObjectDir = ".";
650
651
652
653
  vars.Target = targetName.c_str();
  vars.LinkLibraries = linkLibs.c_str();
  vars.Flags = flags.c_str();
  vars.LinkFlags = linkFlags.c_str();
654
655
656
657
658
 
  std::string langFlags;
  this->AddLanguageFlags(langFlags, llang, 0);
  vars.LanguageCompileFlags = langFlags.c_str();
  
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
  cmCustomCommandLines commandLines;
  std::vector<std::string> rules;
  rules.push_back(this->Makefile->GetRequiredDefinition(createRule.c_str()));
  std::vector<std::string> commands;
  cmSystemTools::ExpandList(rules, commands);  
  for(std::vector<std::string>::iterator i = commands.begin();
      i != commands.end(); ++i)
    {
    // Expand the full command line string.
    this->ExpandRuleVariables(*i, vars);
    // Parse the string to get the custom command line.
    cmCustomCommandLine commandLine;
    std::vector<cmStdString> cmd = cmSystemTools::ParseArguments(i->c_str());
    for(std::vector<cmStdString>::iterator a = cmd.begin();
        a != cmd.end(); ++a)
      {
      commandLine.push_back(*a);
      }

    // Store this command line.
    commandLines.push_back(commandLine);
    }
  std::string targetFullPath = target.GetFullPath();
  // Generate a meaningful comment for the command.
  std::string comment = "Linking ";
  comment += llang;
  comment += " target ";
  comment += this->Convert(targetFullPath.c_str(), START_OUTPUT);
  this->Makefile->AddCustomCommandToOutput(
    targetFullPath.c_str(),
    objVector,
    0,
    commandLines,
    comment.c_str(),
    this->Makefile->GetStartOutputDirectory()
    );
695
  target.AddSourceFile
Ken Martin's avatar
Ken Martin committed
696
    (this->Makefile->GetSource(targetFullPath.c_str()));
697
}
698

699
  
Ken Martin's avatar
Ken Martin committed
700
701
void cmLocalGenerator
::CreateCustomTargetsAndCommands(std::set<cmStdString> const& lang)
702
{ 
Ken Martin's avatar
Ken Martin committed
703
  cmTargets &tgts = this->Makefile->GetTargets();
704
705
706
707
708
709
710
711
712
713
714
  for(cmTargets::iterator l = tgts.begin(); 
      l != tgts.end(); l++)
    {
    cmTarget& target = l->second;
    switch(target.GetType())
      { 
      case cmTarget::STATIC_LIBRARY:
      case cmTarget::SHARED_LIBRARY:
      case cmTarget::MODULE_LIBRARY:
      case cmTarget::EXECUTABLE: 
        {
715
        const char* llang = target.GetLinkerLanguage();
716
717
        if(!llang)
          {
Ken Martin's avatar
Ken Martin committed
718
719
720
          cmSystemTools::Error
            ("CMake can not determine linker language for target:",
             target.GetName());
721
722
          return;
          }
723
724
725
726
727
728
729
        // if the language is not in the set lang then create custom
        // commands to build the target
        if(lang.count(llang) == 0)
          {
          this->AddBuildTargetRule(llang, target);
          }
        }
Bill Hoffman's avatar
Bill Hoffman committed
730
        break; 
731
      default:
732
733
734
735
736
737
738
739
740
741
742
743
        break;
      }
    }
}

// List of variables that are replaced when
// rules are expanced.  These variables are
// replaced in the form <var> with GetSafeDefinition(var).
// ${LANG} is replaced in the variable first with all enabled 
// languages.
static const char* ruleReplaceVars[] =
{
Bill Hoffman's avatar
Bill Hoffman committed
744
  "CMAKE_${LANG}_COMPILER",
745
746
747
748
749
750
751
752
753
754
755
  "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS",
  "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS",
  "CMAKE_SHARED_MODULE_${LANG}_FLAGS", 
  "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",
756
  "CMAKE_LINKER",
757
758
759
  0
};

Bill Hoffman's avatar
Bill Hoffman committed
760
761
std::string
cmLocalGenerator::ExpandRuleVariable(std::string const& variable,
762
                                     const RuleVariables& replaceValues)
763
{
764
  if(replaceValues.LinkFlags)
765
    {
Bill Hoffman's avatar
Bill Hoffman committed
766
767
    if(variable == "LINK_FLAGS")
      {
768
      return replaceValues.LinkFlags;
Bill Hoffman's avatar
Bill Hoffman committed
769
      }
770
    }
771
  if(replaceValues.Flags)
772
    {
Bill Hoffman's avatar
Bill Hoffman committed
773
774
    if(variable == "FLAGS")
      {
775
      return replaceValues.Flags;
Bill Hoffman's avatar
Bill Hoffman committed
776
      }
777
778
    }
    
779
  if(replaceValues.Source)
780
    {
Bill Hoffman's avatar
Bill Hoffman committed
781
782
    if(variable == "SOURCE")
      {
783
      return replaceValues.Source;
Bill Hoffman's avatar
Bill Hoffman committed
784
      }
785
    }
786
787
788
789
790
791
792
793
794
795
796
797
798
799
  if(replaceValues.PreprocessedSource)
    {
    if(variable == "PREPROCESSED_SOURCE")
      {
      return replaceValues.PreprocessedSource;
      }
    }
  if(replaceValues.AssemblySource)
    {
    if(variable == "ASSEMBLY_SOURCE")
      {
      return replaceValues.AssemblySource;
      }
    }
800
  if(replaceValues.Object)
801
    {
Bill Hoffman's avatar
Bill Hoffman committed
802
803
    if(variable == "OBJECT")
      {
804
      return replaceValues.Object;
Bill Hoffman's avatar
Bill Hoffman committed
805
      }
806
    }
807
808
809
810
811
812
813
  if(replaceValues.ObjectDir)
    {
    if(variable == "OBJECT_DIR")
      {
      return replaceValues.ObjectDir;
      }
    }
814
  if(replaceValues.Objects)
815
    {
Bill Hoffman's avatar
Bill Hoffman committed
816
817
    if(variable == "OBJECTS")
      {
818
      return replaceValues.Objects;
Bill Hoffman's avatar
Bill Hoffman committed
819
      }
820
    }
821
  if(replaceValues.ObjectsQuoted)
822
    {
Bill Hoffman's avatar
Bill Hoffman committed
823
824
    if(variable == "OBJECTS_QUOTED")
      {
825
      return replaceValues.ObjectsQuoted;
Bill Hoffman's avatar
Bill Hoffman committed
826
      }
827
    }
828
829
830
831
  if(replaceValues.Defines && variable == "DEFINES")
    {
    return replaceValues.Defines;
    }
832
833
834
835
836
837
838
839
  if(replaceValues.TargetPDB )
    {
    if(variable == "TARGET_PDB")
      {
      return replaceValues.TargetPDB;
      }
    }

840
  if(replaceValues.Target)
841
    { 
Bill Hoffman's avatar
Bill Hoffman committed
842
843
    if(variable == "TARGET_QUOTED")
      {
844
      std::string targetQuoted = replaceValues.Target;
Bill Hoffman's avatar
Bill Hoffman committed
845
846
847
      if(targetQuoted.size() && targetQuoted[0] != '\"')
        {
        targetQuoted = '\"';
848
        targetQuoted += replaceValues.Target;
Bill Hoffman's avatar
Bill Hoffman committed
849
850
        targetQuoted += '\"';
        }
851
      return targetQuoted;
Bill Hoffman's avatar
Bill Hoffman committed
852
      }
853
    if(replaceValues.LanguageCompileFlags)
854
      {
855
856
857
858
      if(variable == "LANGUAGE_COMPILE_FLAGS")
        {
        return replaceValues.LanguageCompileFlags;
        }
859
      }
860
    if(replaceValues.Target)
861
      {
862
863
864
865
      if(variable == "TARGET")
        {
        return replaceValues.Target;
        }
866
      }
867
868
    if(variable == "TARGET_IMPLIB")
      {
Ken Martin's avatar
Ken Martin committed
869
      return this->TargetImplib;
870
      }
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
    if(variable == "TARGET_VERSION_MAJOR")
      {
      if(replaceValues.TargetVersionMajor)
        {
        return replaceValues.TargetVersionMajor;
        }
      else
        {
        return "0";
        }
      }
    if(variable == "TARGET_VERSION_MINOR")
      {
      if(replaceValues.TargetVersionMinor)
        {
        return replaceValues.TargetVersionMinor;
        }
      else
        {
        return "0";
        }
      }
893
    if(replaceValues.Target)
Bill Hoffman's avatar
Bill Hoffman committed
894
      {
895
      if(variable == "TARGET_BASE")
Bill Hoffman's avatar
Bill Hoffman committed
896
        {
897
898
899
900
901
        // Strip the last extension off the target name.
        std::string targetBase = replaceValues.Target;
        std::string::size_type pos = targetBase.rfind(".");
        if(pos != targetBase.npos)
          {
902
        return targetBase.substr(0, pos);
903
904
905
906
907
          }
        else
          {
          return targetBase;
          }
Bill Hoffman's avatar
Bill Hoffman committed
908
        }
909
910
      }
    }
911
  if(replaceValues.TargetSOName)
912
    {
Bill Hoffman's avatar
Bill Hoffman committed
913
    if(variable == "TARGET_SONAME")
914
      {
915
      if(replaceValues.Language)
916
        {
Bill Hoffman's avatar
Bill Hoffman committed
917
        std::string name = "CMAKE_SHARED_LIBRARY_SONAME_";
918
        name += replaceValues.Language;
Bill Hoffman's avatar
Bill Hoffman committed
919
        name += "_FLAG";
Ken Martin's avatar
Ken Martin committed
920
        if(this->Makefile->GetDefinition(name.c_str()))
Bill Hoffman's avatar
Bill Hoffman committed
921
          {
922
          return replaceValues.TargetSOName;
Bill Hoffman's avatar
Bill Hoffman committed
923
          }
924
        }
Bill Hoffman's avatar
Bill Hoffman committed
925
      return "";
926
927
      }
    }
928
929
930
931
932
933
934
  if(replaceValues.TargetInstallNameDir)
    {
    if(variable == "TARGET_INSTALLNAME_DIR")
      {
      return replaceValues.TargetInstallNameDir;
      }
    }
935
  if(replaceValues.LinkLibraries)
936
    {
Bill Hoffman's avatar
Bill Hoffman committed
937
938
    if(variable == "LINK_LIBRARIES")
      {
939
      return replaceValues.LinkLibraries;
Bill Hoffman's avatar
Bill Hoffman committed
940
      }
941
    }
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  if(replaceValues.Language)
    {
    if(variable == "LANGUAGE")
      {
      return replaceValues.Language;
      }
    }
  if(replaceValues.CMTarget)
    {
    if(variable == "TARGET_NAME")
      {
      return replaceValues.CMTarget->GetName();
      }
    if(variable == "TARGET_TYPE")
      {
      return cmTarget::TargetTypeNames[replaceValues.CMTarget->GetType()];
      }
    }
  if(replaceValues.Output)
    {
    if(variable == "OUTPUT")
      {
      return replaceValues.Output;
      }
    }
967
968
969
  if(variable == "CMAKE_COMMAND")
    {
    const char* cmcommand =
970
      this->GetMakefile()->GetDefinition("CMAKE_COMMAND");
971
972
    return this->Convert(cmcommand, FULL, SHELL);
    }
Bill Hoffman's avatar
Bill Hoffman committed
973
  std::vector<std::string> enabledLanguages;
Ken Martin's avatar
Ken Martin committed
974
  this->GlobalGenerator->GetEnabledLanguages(enabledLanguages);
975
976
977
978
979
980
981
  // loop over language specific replace variables
  int pos = 0;
  while(ruleReplaceVars[pos])
    {
    for(std::vector<std::string>::iterator i = enabledLanguages.begin();   
        i != enabledLanguages.end(); ++i)   
      { 
982
      const char* lang = i->c_str();
Bill Hoffman's avatar
Bill Hoffman committed
983
      std::string actualReplace = ruleReplaceVars[pos];
984
985
986
987
988
989
990
      // If this is the compiler then look for the extra variable
      // _COMPILER_ARG1 which must be the first argument to the compiler 
      const char* compilerArg1 = 0;
      if(actualReplace == "CMAKE_${LANG}_COMPILER")
        {
        std::string arg1 = actualReplace + "_ARG1";
        cmSystemTools::ReplaceString(arg1, "${LANG}", lang);
Ken Martin's avatar
Ken Martin committed
991
        compilerArg1 = this->Makefile->GetDefinition(arg1.c_str());
992
        }
Bill Hoffman's avatar
Bill Hoffman committed
993
      if(actualReplace.find("${LANG}") != actualReplace.npos)
994
        {
Bill Hoffman's avatar
Bill Hoffman committed
995
        cmSystemTools::ReplaceString(actualReplace, "${LANG}", lang);
996
        }
Bill Hoffman's avatar
Bill Hoffman committed
997
      if(actualReplace == variable)
998
        {
Ken Martin's avatar
Ken Martin committed
999
1000
        std::string replace = 
          this->Makefile->GetSafeDefinition(variable.c_str());
Bill Hoffman's avatar
Bill Hoffman committed
1001
1002
1003
        // if the variable is not a FLAG then treat it like a path
        if(variable.find("_FLAG") == variable.npos)
          {
1004
          std::string ret = this->ConvertToOutputForExisting(replace.c_str());
Ken Martin's avatar
Ken Martin committed
1005
1006
          // if there is a required first argument to the compiler add it
          // to the compiler string
1007
1008
1009
1010
1011
1012
          if(compilerArg1)
            {
            ret += " ";
            ret += compilerArg1;
            }
          return ret;
Bill Hoffman's avatar
Bill Hoffman committed
1013
1014
          }
        return replace;
1015
1016
1017
1018
        }
      }
    pos++;
    }
Bill Hoffman's avatar
Bill Hoffman committed
1019
1020
1021
1022
1023
1024
  return variable;
}


void 
cmLocalGenerator::ExpandRuleVariables(std::string& s,
1025
                                      const RuleVariables& replaceValues)
Bill Hoffman's avatar
Bill Hoffman committed
1026
1027
{
  std::vector<std::string> enabledLanguages;
Ken Martin's avatar
Ken Martin committed
1028
  this->GlobalGenerator->GetEnabledLanguages(enabledLanguages);
1029
1030
  this->InsertRuleLauncher(s, replaceValues.CMTarget,
                           replaceValues.RuleLauncher);
Bill Hoffman's avatar
Bill Hoffman committed
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
  std::string::size_type start = s.find('<');
  // no variables to expand
  if(start == s.npos)
    {
    return;
    }
  std::string::size_type pos = 0;
  std::string expandedInput;
  while(start != s.npos && start < s.size()-2)
    {
    std::string::size_type end = s.find('>', start);
    // if we find a < with no > we are done
    if(end == s.npos)
      {
      return;
      }
    char c = s[start+1];
    // if the next char after the < is not A-Za-z then
    // skip it and try to find the next < in the string
    if(!isalpha(c))
      {
      start = s.find('<', start+1);
      }
    else
      {
      // extract the var
      std::string var = s.substr(start+1,  end - start-1);
1058
1059
      std::string replace = this->ExpandRuleVariable(var,
                                                     replaceValues);
Bill Hoffman's avatar
Bill Hoffman committed
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
      expandedInput += s.substr(pos, start-pos);
      expandedInput += replace;
      // move to next one
      start = s.find('<', start+var.size()+2);
      pos = end+1;
      }
    }
  // add the rest of the input
  expandedInput += s.substr(pos, s.size()-pos);
  s = expandedInput;
1070
1071
}

1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
//----------------------------------------------------------------------------
const char* cmLocalGenerator::GetRuleLauncher(cmTarget* target,
                                              const char* prop)
{
  if(target)
    {
    return target->GetProperty(prop);
    }
  else
    {
    return this->Makefile->GetProperty(prop);
    }
}

//----------------------------------------------------------------------------
void cmLocalGenerator::InsertRuleLauncher(std::string& s, cmTarget* target,
                                          const char* prop)
{
  if(const char* val = this->GetRuleLauncher(target, prop))
    {
    cmOStringStream wrapped;
    wrapped << val << " " << s;
    s = wrapped.str();
    }
}

1098
1099
1100
1101
//----------------------------------------------------------------------------
std::string
cmLocalGenerator::ConvertToOutputForExistingCommon(const char* remote,
                                                   std::string const& result)
1102
{
1103
1104
1105
1106
1107
  // If this is a windows shell, the result has a space, and the path
  // already exists, we can use a short-path to reference it without a
  // space.
  if(this->WindowsShell && result.find(' ') != result.npos &&
     cmSystemTools::FileExists(remote))
1108
    {
1109
1110
    std::string tmp;
    if(cmSystemTools::GetShortPath(remote, tmp))
1111
      {
1112
      return this->Convert(tmp.c_str(), NONE, SHELL, true);
1113
1114
      }
    }
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142

  // Otherwise, leave it unchanged.
  return result;
}

//----------------------------------------------------------------------------
std::string
cmLocalGenerator::ConvertToOutputForExisting(const char* remote,
                                             RelativeRoot local)
{
  // Perform standard conversion.
  std::string result = this->Convert(remote, local, SHELL, true);

  // Consider short-path.
  return this->ConvertToOutputForExistingCommon(remote, result);
}

//----------------------------------------------------------------------------
std::string
cmLocalGenerator::ConvertToOutputForExisting(RelativeRoot remote,
                                             const char* local)
{
  // Perform standard conversion.
  std::string result = this->Convert(remote, local, SHELL, true);

  // Consider short-path.
  const char* remotePath = this->GetRelativeRootPath(remote);
  return this->ConvertToOutputForExistingCommon(remotePath, result);
1143
1144
}

1145
//----------------------------------------------------------------------------
1146
const char* cmLocalGenerator::GetIncludeFlags(const char* lang)
1147
{
1148
1149
1150
1151
  if(!lang)
    {
    return "";
    }
Ken Martin's avatar
Ken Martin committed