cmLocalVisualStudioGenerator.cxx 7.06 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmLocalVisualStudioGenerator.h"
Brad King's avatar
Brad King committed
4

5
6
#include "windows.h"

7
#include "cmCustomCommand.h"
8
#include "cmCustomCommandGenerator.h"
9
#include "cmGeneratorTarget.h"
10
#include "cmGlobalGenerator.h"
11
12
13
14
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmSystemTools.h"

15
16
cmLocalVisualStudioGenerator::cmLocalVisualStudioGenerator(
  cmGlobalGenerator* gg, cmMakefile* mf)
17
  : cmLocalGenerator(gg, mf)
18
19
20
21
22
23
24
{
}

cmLocalVisualStudioGenerator::~cmLocalVisualStudioGenerator()
{
}

25
26
27
28
29
30
31
32
cmGlobalVisualStudioGenerator::VSVersion
cmLocalVisualStudioGenerator::GetVersion() const
{
  cmGlobalVisualStudioGenerator* gg =
    static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator);
  return gg->GetVersion();
}

33
void cmLocalVisualStudioGenerator::ComputeObjectFilenames(
34
35
  std::map<cmSourceFile const*, std::string>& mapping,
  cmGeneratorTarget const* gt)
36
{
37
  char const* custom_ext = gt->GetCustomObjectExtension();
38
  std::string dir_max = this->ComputeLongestObjectDirectory(gt);
39
40
41
42
43

  // Count the number of object files with each name.  Note that
  // windows file names are not case sensitive.
  std::map<std::string, int> counts;

44
45
  for (auto const& si : mapping) {
    cmSourceFile const* sf = si.first;
46
47
    std::string objectNameLower = cmSystemTools::LowerCase(
      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath()));
48
49
50
51
52
53
    if (custom_ext) {
      objectNameLower += custom_ext;
    } else {
      objectNameLower +=
        this->GlobalGenerator->GetLanguageOutputExtension(*sf);
    }
54
    counts[objectNameLower] += 1;
55
  }
56
57
58
59

  // For all source files producing duplicate names we need unique
  // object name computation.

60
61
  for (auto& si : mapping) {
    cmSourceFile const* sf = si.first;
62
63
    std::string objectName =
      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath());
64
65
66
67
68
    if (custom_ext) {
      objectName += custom_ext;
    } else {
      objectName += this->GlobalGenerator->GetLanguageOutputExtension(*sf);
    }
69
    if (counts[cmSystemTools::LowerCase(objectName)] > 1) {
70
      const_cast<cmGeneratorTarget*>(gt)->AddExplicitObjectName(sf);
71
72
73
      bool keptSourceExtension;
      objectName = this->GetObjectFileNameWithoutTarget(
        *sf, dir_max, &keptSourceExtension, custom_ext);
74
    }
75
    si.second = objectName;
76
  }
77
78
}

79
std::unique_ptr<cmCustomCommand>
80
cmLocalVisualStudioGenerator::MaybeCreateImplibDir(cmGeneratorTarget* target,
81
                                                   const std::string& config,
82
                                                   bool isFortran)
83
{
84
  std::unique_ptr<cmCustomCommand> pcc;
85
86
87

  // If an executable exports symbols then VS wants to create an
  // import library but forgets to create the output directory.
88
  // The Intel Fortran plugin always forgets to the directory.
89
90
  if (target->GetType() != cmStateEnums::EXECUTABLE &&
      !(isFortran && target->GetType() == cmStateEnums::SHARED_LIBRARY)) {
91
92
    return pcc;
  }
93
94
95
96
  std::string outDir =
    target->GetDirectory(config, cmStateEnums::RuntimeBinaryArtifact);
  std::string impDir =
    target->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
97
98
99
  if (impDir == outDir) {
    return pcc;
  }
100
101
102

  // Add a pre-build event to create the directory.
  std::vector<std::string> no_output;
103
  std::vector<std::string> no_byproducts;
104
  std::vector<std::string> no_depends;
105
  bool stdPipesUTF8 = true;
106
107
  cmCustomCommandLines commands = cmMakeSingleCommandLine(
    { cmSystemTools::GetCMakeCommand(), "-E", "make_directory", impDir });
108
  pcc.reset(new cmCustomCommand(no_output, no_byproducts, no_depends, commands,
109
110
                                cmListFileBacktrace(), nullptr, nullptr,
                                stdPipesUTF8));
111
112
113
114
115
  pcc->SetEscapeOldStyle(false);
  pcc->SetEscapeAllowMakeVars(true);
  return pcc;
}

116
const char* cmLocalVisualStudioGenerator::ReportErrorLabel() const
117
{
118
  return ":VCReportError";
119
120
}

121
const char* cmLocalVisualStudioGenerator::GetReportErrorLabel() const
122
{
123
  return this->ReportErrorLabel();
124
125
}

126
127
std::string cmLocalVisualStudioGenerator::ConstructScript(
  cmCustomCommandGenerator const& ccg, const std::string& newline_text)
128
{
129
  bool useLocal = this->CustomCommandUseLocal();
130
  std::string workingDirectory = ccg.GetWorkingDirectory();
131

132
  // Avoid leading or trailing newlines.
133
  std::string newline;
134

135
  // Line to check for error between commands.
136
  std::string check_error = newline_text;
137
  if (useLocal) {
138
    check_error += "if %errorlevel% neq 0 goto :cmEnd";
139
  } else {
140
141
    check_error += "if errorlevel 1 goto ";
    check_error += this->GetReportErrorLabel();
142
  }
143

144
145
  // Store the script in a string.
  std::string script;
146
147

  // Open a local context.
148
  if (useLocal) {
149
150
151
    script += newline;
    newline = newline_text;
    script += "setlocal";
152
  }
153

154
  if (!workingDirectory.empty()) {
155
    // Change the working directory.
156
    script += newline;
157
    newline = newline_text;
158
    script += "cd ";
159
    script += this->ConvertToOutputFormat(workingDirectory, SHELL);
160
    script += check_error;
161
162

    // Change the working drive.
163
    if (workingDirectory.size() > 1 && workingDirectory[1] == ':') {
164
      script += newline;
165
      newline = newline_text;
166
167
      script += workingDirectory[0];
      script += workingDirectory[1];
168
      script += check_error;
169
    }
170
  }
171

172
173
  // for visual studio IDE add extra stuff to the PATH
  // if CMAKE_MSVCIDE_RUN_PATH is set.
174
  if (this->Makefile->GetDefinition("MSVC_IDE")) {
175
    cmProp extraPath = this->Makefile->GetDefinition("CMAKE_MSVCIDE_RUN_PATH");
176
    if (extraPath) {
177
      script += newline;
178
      newline = newline_text;
179
      script += "set PATH=";
180
      script += *extraPath;
181
182
      script += ";%PATH%";
    }
183
  }
184

185
  // Write each command on a single line.
186
  for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
187
188
189
190
191
192
193
    // Add this command line.
    std::string cmd = ccg.GetCommand(c);

    if (cmd.empty()) {
      continue;
    }

194
195
    // Start a new line.
    script += newline;
196
    newline = newline_text;
197

198
199
200
201
    // Use "call " before any invocations of .bat or .cmd files
    // invoked as custom commands.
    //
    std::string suffix;
202
203
204
    if (cmd.size() > 4) {
      suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
      if (suffix == ".bat" || suffix == ".cmd") {
205
206
        script += "call ";
      }
207
    }
208

209
210
    script += this->ConvertToOutputFormat(cmd.c_str(), SHELL);

211
    ccg.AppendArguments(c, script);
212
213
214
215
216

    // After each custom command, check for an error result.
    // If there was an error, jump to the VCReportError label,
    // skipping the run of any subsequent commands in this
    // sequence.
217
    script += check_error;
218
  }
219

220
  // Close the local context.
221
  if (useLocal) {
222
223
224
    script += newline;
    script += ":cmEnd";
    script += newline;
225
226
227
228
229
230
231
    script += "endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone";
    script += newline;
    script += ":cmErrorLevel";
    script += newline;
    script += "exit /b %1";
    script += newline;
    script += ":cmDone";
232
    script += newline;
233
    script += "if %errorlevel% neq 0 goto ";
234
    script += this->GetReportErrorLabel();
235
  }
236

237
238
  return script;
}