cmTryRunCommand.cxx 13.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 13
#include "cmTryRunCommand.h"
#include "cmTryCompileCommand.h"
14
#include <cmsys/FStream.hxx>
Ken Martin's avatar
Ken Martin committed
15

Alexander Neundorf's avatar
 
Alexander Neundorf committed
16
// cmTryRunCommand
17 18
bool cmTryRunCommand
::InitialPass(std::vector<std::string> const& argv, cmExecutionStatus &)
Ken Martin's avatar
Ken Martin committed
19 20 21 22 23 24
{
  if(argv.size() < 4)
    {
    return false;
    }

25 26 27
  if(this->Makefile->GetCMakeInstance()->GetWorkingMode() ==
                                                      cmake::FIND_PACKAGE_MODE)
    {
28
    this->Makefile->IssueMessage(cmake::FATAL_ERROR,
29 30 31 32
            "The TRY_RUN() command is not supported in --find-package mode.");
    return false;
    }

33
  // build an arg list for TryCompile and extract the runArgs,
Ken Martin's avatar
Ken Martin committed
34
  std::vector<std::string> tryCompile;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
35 36 37 38 39 40 41

  this->CompileResultVariable = "";
  this->RunResultVariable = "";
  this->OutputVariable = "";
  this->RunOutputVariable = "";
  this->CompileOutputVariable = "";

Ken Martin's avatar
Ken Martin committed
42
  std::string runArgs;
Ken Martin's avatar
Ken Martin committed
43
  unsigned int i;
Ken Martin's avatar
Ken Martin committed
44 45 46 47 48 49
  for (i = 1; i < argv.size(); ++i)
    {
    if (argv[i] == "ARGS")
      {
      ++i;
      while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
50 51
             argv[i] != "CMAKE_FLAGS" &&
             argv[i] != "LINK_LIBRARIES")
Ken Martin's avatar
Ken Martin committed
52 53 54 55 56 57 58 59 60
        {
        runArgs += " ";
        runArgs += argv[i];
        ++i;
        }
      if (i < argv.size())
        {
        tryCompile.push_back(argv[i]);
        }
61 62
      }
    else
Ken Martin's avatar
Ken Martin committed
63
      {
64
      if (argv[i] == "OUTPUT_VARIABLE")
Alexander Neundorf's avatar
 
Alexander Neundorf committed
65
        {
66 67 68
        if ( argv.size() <= (i+1) )
          {
          cmSystemTools::Error(
Bill Hoffman's avatar
Bill Hoffman committed
69
            "OUTPUT_VARIABLE specified but there is no variable");
70 71
          return false;
          }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
        i++;
        this->OutputVariable = argv[i];
        }
      else if (argv[i] == "RUN_OUTPUT_VARIABLE")
        {
        if (argv.size() <= (i + 1))
          {
          cmSystemTools::Error(
            "RUN_OUTPUT_VARIABLE specified but there is no variable");
          return false;
          }
        i++;
        this->RunOutputVariable = argv[i];
        }
      else if (argv[i] == "COMPILE_OUTPUT_VARIABLE")
        {
        if (argv.size() <= (i + 1))
          {
          cmSystemTools::Error(
            "COMPILE_OUTPUT_VARIABLE specified but there is no variable");
          return false;
          }
        i++;
        this->CompileOutputVariable = argv[i];
        }
      else
        {
        tryCompile.push_back(argv[i]);
100
        }
Ken Martin's avatar
Ken Martin committed
101 102
      }
    }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
103 104 105

  // although they could be used together, don't allow it, because
  // using OUTPUT_VARIABLE makes crosscompiling harder
106
  if (this->OutputVariable.size()
107 108
      && (!this->RunOutputVariable.empty()
       || !this->CompileOutputVariable.empty()))
Alexander Neundorf's avatar
 
Alexander Neundorf committed
109 110 111 112 113 114 115 116 117
    {
    cmSystemTools::Error(
      "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
      "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
      "RUN_OUTPUT_VARIABLE.");
    return false;
    }

  bool captureRunOutput = false;
118
  if (!this->OutputVariable.empty())
Alexander Neundorf's avatar
 
Alexander Neundorf committed
119 120 121 122 123
    {
    captureRunOutput = true;
    tryCompile.push_back("OUTPUT_VARIABLE");
    tryCompile.push_back(this->OutputVariable);
    }
124
  if (!this->CompileOutputVariable.empty())
Alexander Neundorf's avatar
 
Alexander Neundorf committed
125 126 127 128
    {
    tryCompile.push_back("OUTPUT_VARIABLE");
    tryCompile.push_back(this->CompileOutputVariable);
    }
129
  if (!this->RunOutputVariable.empty())
Alexander Neundorf's avatar
 
Alexander Neundorf committed
130 131 132 133 134 135 136
    {
    captureRunOutput = true;
    }

  this->RunResultVariable = argv[0];
  this->CompileResultVariable = argv[1];

Ken Martin's avatar
Ken Martin committed
137
  // do the try compile
Alexander Neundorf's avatar
 
Alexander Neundorf committed
138
  int res = this->TryCompileCode(tryCompile);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
139

Ken Martin's avatar
Ken Martin committed
140
  // now try running the command if it compiled
Alexander Neundorf's avatar
 
Alexander Neundorf committed
141
  if (!res)
Ken Martin's avatar
Ken Martin committed
142
    {
143
    if (this->OutputFile.empty())
Ken Martin's avatar
Ken Martin committed
144
      {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
145
      cmSystemTools::Error(this->FindErrorMessage.c_str());
146
      }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
147
    else
148
      {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
149 150
      // "run" it and capture the output
      std::string runOutputContents;
151 152
      if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") &&
          !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR"))
Alexander Neundorf's avatar
 
Alexander Neundorf committed
153
        {
154 155
        this->DoNotRunExecutable(runArgs,
                                 argv[3],
Alexander Neundorf's avatar
 
Alexander Neundorf committed
156 157 158
                                 captureRunOutput ? &runOutputContents : 0);
        }
      else
159
        {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
160
        this->RunExecutable(runArgs, &runOutputContents);
161
        }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
162 163

      // now put the output into the variables
164
      if(!this->RunOutputVariable.empty())
Alexander Neundorf's avatar
 
Alexander Neundorf committed
165
        {
Stephen Kelly's avatar
Stephen Kelly committed
166
        this->Makefile->AddDefinition(this->RunOutputVariable,
Alexander Neundorf's avatar
 
Alexander Neundorf committed
167 168 169
                                      runOutputContents.c_str());
        }

170
      if(!this->OutputVariable.empty())
Alexander Neundorf's avatar
 
Alexander Neundorf committed
171 172 173 174
        {
        // if the TryCompileCore saved output in this outputVariable then
        // prepend that output to this output
        const char* compileOutput
Stephen Kelly's avatar
Stephen Kelly committed
175
                 = this->Makefile->GetDefinition(this->OutputVariable);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
176
        if (compileOutput)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
177
          {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
178
          runOutputContents = std::string(compileOutput) + runOutputContents;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
179
          }
Stephen Kelly's avatar
Stephen Kelly committed
180
        this->Makefile->AddDefinition(this->OutputVariable,
Alexander Neundorf's avatar
 
Alexander Neundorf committed
181
                                      runOutputContents.c_str());
Alexander Neundorf's avatar
 
Alexander Neundorf committed
182
        }
Ken Martin's avatar
Ken Martin committed
183
      }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
184 185
    }

Alexander Neundorf's avatar
 
Alexander Neundorf committed
186
  // if we created a directory etc, then cleanup after ourselves
Ken Martin's avatar
Ken Martin committed
187
  if(!this->Makefile->GetCMakeInstance()->GetDebugTryCompile())
Bill Hoffman's avatar
Bill Hoffman committed
188
    {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
189
    this->CleanupFiles(this->BinaryDirectory.c_str());
Bill Hoffman's avatar
Bill Hoffman committed
190
    }
Ken Martin's avatar
Ken Martin committed
191 192
  return true;
}
Alexander Neundorf's avatar
 
Alexander Neundorf committed
193 194 195 196 197

void cmTryRunCommand::RunExecutable(const std::string& runArgs,
                                    std::string* out)
{
  int retVal = -1;
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

  std::string finalCommand;
  const std::string emulator =
  this->Makefile->GetSafeDefinition("CMAKE_CROSSCOMPILING_EMULATOR");
  if (!emulator.empty())
    {
    std::vector<std::string> emulatorWithArgs;
    cmSystemTools::ExpandListArgument(emulator, emulatorWithArgs);
    finalCommand += cmSystemTools::ConvertToRunCommandPath(
                                 emulatorWithArgs[0].c_str());
    finalCommand += " ";
    for (std::vector<std::string>::const_iterator ei =
         emulatorWithArgs.begin()+1;
         ei != emulatorWithArgs.end(); ++ei)
      {
      finalCommand += "\"";
      finalCommand += *ei;
      finalCommand += "\"";
      finalCommand += " ";
      }
    }
  finalCommand += cmSystemTools::ConvertToRunCommandPath(
Alexander Neundorf's avatar
 
Alexander Neundorf committed
220
                               this->OutputFile.c_str());
221
  if (!runArgs.empty())
Alexander Neundorf's avatar
 
Alexander Neundorf committed
222 223 224 225 226
    {
    finalCommand += runArgs;
    }
  int timeout = 0;
  bool worked = cmSystemTools::RunSingleCommand(finalCommand.c_str(),
227
                out, out, &retVal,
228
                0, cmSystemTools::OUTPUT_NONE, timeout);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
229 230 231 232 233 234 235 236 237 238
  // set the run var
  char retChar[1000];
  if (worked)
    {
    sprintf(retChar, "%i", retVal);
    }
  else
    {
    strcpy(retChar, "FAILED_TO_RUN");
    }
Stephen Kelly's avatar
Stephen Kelly committed
239
  this->Makefile->AddCacheDefinition(this->RunResultVariable, retChar,
Alexander Neundorf's avatar
 
Alexander Neundorf committed
240
                                     "Result of TRY_RUN",
241
                                     cmState::INTERNAL);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
242 243 244 245
}

/* This is only used when cross compiling. Instead of running the
 executable, two cache variables are created which will hold the results
246
 the executable would have produced.
Alexander Neundorf's avatar
 
Alexander Neundorf committed
247
*/
248
void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
Alexander Neundorf's avatar
 
Alexander Neundorf committed
249 250 251 252
                                    const std::string& srcFile,
                                    std::string* out
                                    )
{
Alexander Neundorf's avatar
 
Alexander Neundorf committed
253 254 255 256
  // copy the executable out of the CMakeFiles/ directory, so it is not
  // removed at the end of TRY_RUN and the user can run it manually
  // on the target platform.
  std::string copyDest =  this->Makefile->GetHomeOutputDirectory();
Alexander Neundorf's avatar
 
Alexander Neundorf committed
257
  copyDest += cmake::GetCMakeFilesDirectory();
Alexander Neundorf's avatar
 
Alexander Neundorf committed
258 259
  copyDest += "/";
  copyDest += cmSystemTools::GetFilenameWithoutExtension(
Stephen Kelly's avatar
Stephen Kelly committed
260
                                                     this->OutputFile);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
261 262
  copyDest += "-";
  copyDest += this->RunResultVariable;
Stephen Kelly's avatar
Stephen Kelly committed
263
  copyDest += cmSystemTools::GetFilenameExtension(this->OutputFile);
264
  cmSystemTools::CopyFileAlways(this->OutputFile, copyDest);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
265 266 267 268 269 270

  std::string resultFileName =  this->Makefile->GetHomeOutputDirectory();
  resultFileName += "/TryRunResults.cmake";

  std::string detailsString = "For details see ";
  detailsString += resultFileName;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
271

Alexander Neundorf's avatar
 
Alexander Neundorf committed
272
  std::string internalRunOutputName=this->RunResultVariable+"__TRYRUN_OUTPUT";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
273 274
  bool error = false;

Stephen Kelly's avatar
Stephen Kelly committed
275
  if (this->Makefile->GetDefinition(this->RunResultVariable) == 0)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
276 277 278 279
    {
    // if the variables doesn't exist, create it with a helpful error text
    // and mark it as advanced
    std::string comment;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
280 281 282
    comment += "Run result of TRY_RUN(), indicates whether the executable "
               "would have been able to run on its target platform.\n";
    comment += detailsString;
Stephen Kelly's avatar
Stephen Kelly committed
283
    this->Makefile->AddCacheDefinition(this->RunResultVariable,
Alexander Neundorf's avatar
 
Alexander Neundorf committed
284 285
                                       "PLEASE_FILL_OUT-FAILED_TO_RUN",
                                       comment.c_str(),
286
                                       cmState::STRING);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
287

Stephen Kelly's avatar
Stephen Kelly committed
288
    cmState* state = this->Makefile->GetState();
289
    const char* existingValue
Stephen Kelly's avatar
Stephen Kelly committed
290
                        = state->GetCacheEntryValue(this->RunResultVariable);
291
    if (existingValue)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
292
      {
Stephen Kelly's avatar
Stephen Kelly committed
293
      state->SetCacheEntryProperty(this->RunResultVariable, "ADVANCED", "1");
Alexander Neundorf's avatar
 
Alexander Neundorf committed
294 295 296 297 298
      }

    error = true;
    }

Alexander Neundorf's avatar
 
Alexander Neundorf committed
299
  // is the output from the executable used ?
Alexander Neundorf's avatar
 
Alexander Neundorf committed
300 301
  if (out!=0)
    {
Stephen Kelly's avatar
Stephen Kelly committed
302
    if (this->Makefile->GetDefinition(internalRunOutputName) == 0)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
303 304 305 306
      {
      // if the variables doesn't exist, create it with a helpful error text
      // and mark it as advanced
      std::string comment;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
307 308 309
      comment+="Output of TRY_RUN(), contains the text, which the executable "
           "would have printed on stdout and stderr on its target platform.\n";
      comment += detailsString;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
310

Stephen Kelly's avatar
Stephen Kelly committed
311
      this->Makefile->AddCacheDefinition(internalRunOutputName,
Alexander Neundorf's avatar
 
Alexander Neundorf committed
312 313
                                         "PLEASE_FILL_OUT-NOTFOUND",
                                         comment.c_str(),
314
                                         cmState::STRING);
Stephen Kelly's avatar
Stephen Kelly committed
315
      cmState* state = this->Makefile->GetState();
316
      const char* existing =
Stephen Kelly's avatar
Stephen Kelly committed
317
          state->GetCacheEntryValue(internalRunOutputName);
318
      if (existing)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
319
        {
Stephen Kelly's avatar
Stephen Kelly committed
320 321
        state->SetCacheEntryProperty(internalRunOutputName,
                                      "ADVANCED", "1");
Alexander Neundorf's avatar
 
Alexander Neundorf committed
322 323 324 325 326 327 328 329
        }

      error = true;
      }
    }

  if (error)
    {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
330
    static bool firstTryRun = true;
331
    cmsys::ofstream file(resultFileName.c_str(),
Alexander Neundorf's avatar
 
Alexander Neundorf committed
332
                                  firstTryRun ? std::ios::out : std::ios::app);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
333 334
    if ( file )
      {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
335 336 337 338 339 340 341 342 343 344 345 346 347
      if (firstTryRun)
        {
        file << "# This file was generated by CMake because it detected "
                "TRY_RUN() commands\n"
                "# in crosscompiling mode. It will be overwritten by the next "
                "CMake run.\n"
                "# Copy it to a safe location, set the variables to "
                "appropriate values\n"
                "# and use it then to preset the CMake cache (using -C).\n\n";
        }

      std::string comment ="\n";
      comment += this->RunResultVariable;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
348
      comment += "\n   indicates whether the executable would have been able "
Alexander Neundorf's avatar
 
Alexander Neundorf committed
349 350
                 "to run on its\n"
                 "   target platform. If so, set ";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
351 352
      comment += this->RunResultVariable;
      comment += " to\n"
Alexander Neundorf's avatar
 
Alexander Neundorf committed
353
                 "   the exit code (in many cases 0 for success), otherwise "
Alexander Neundorf's avatar
 
Alexander Neundorf committed
354
                 "enter \"FAILED_TO_RUN\".\n";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
355 356 357
      if (out!=0)
        {
        comment += internalRunOutputName;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
358
        comment += "\n   contains the text the executable "
Alexander Neundorf's avatar
 
Alexander Neundorf committed
359
                   "would have printed on stdout and stderr.\n"
Alexander Neundorf's avatar
 
Alexander Neundorf committed
360
                  "   If the executable would not have been able to run, set ";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
361 362
        comment += internalRunOutputName;
        comment += " empty.\n"
Alexander Neundorf's avatar
 
Alexander Neundorf committed
363
                   "   Otherwise check if the output is evaluated by the "
Alexander Neundorf's avatar
 
Alexander Neundorf committed
364
                   "calling CMake code. If so,\n"
Alexander Neundorf's avatar
 
Alexander Neundorf committed
365 366
                   "   check what the source file would have printed when "
                   "called with the given arguments.\n";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
367
        }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
368 369 370 371 372 373 374 375 376 377 378
      comment += "The ";
      comment += this->CompileResultVariable;
      comment += " variable holds the build result for this TRY_RUN().\n\n"
                 "Source file   : ";
      comment += srcFile + "\n";
      comment += "Executable    : ";
      comment += copyDest + "\n";
      comment += "Run arguments : ";
      comment += runArgs;
      comment += "\n";
      comment += "   Called from: " + this->Makefile->GetListFileStack();
Alexander Neundorf's avatar
 
Alexander Neundorf committed
379 380 381
      cmsys::SystemTools::ReplaceString(comment, "\n", "\n# ");
      file << comment << "\n\n";

382
      file << "set( " << this->RunResultVariable << " \n     \""
Stephen Kelly's avatar
Stephen Kelly committed
383
           << this->Makefile->GetDefinition(this->RunResultVariable)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
384
           << "\"\n     CACHE STRING \"Result from TRY_RUN\" FORCE)\n\n";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
385

Alexander Neundorf's avatar
 
Alexander Neundorf committed
386 387
      if (out!=0)
        {
388
        file << "set( " << internalRunOutputName << " \n     \""
Stephen Kelly's avatar
Stephen Kelly committed
389
             << this->Makefile->GetDefinition(internalRunOutputName)
Alexander Neundorf's avatar
 
Alexander Neundorf committed
390
             << "\"\n     CACHE STRING \"Output from TRY_RUN\" FORCE)\n\n";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
391 392 393
        }
      file.close();
      }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
394
    firstTryRun = false;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
395

Alexander Neundorf's avatar
 
Alexander Neundorf committed
396 397
    std::string errorMessage = "TRY_RUN() invoked in cross-compiling mode, "
                               "please set the following cache variables "
398
                               "appropriately:\n";
Alexander Neundorf's avatar
 
Alexander Neundorf committed
399 400 401 402 403
    errorMessage += "   " + this->RunResultVariable + " (advanced)\n";
    if (out!=0)
      {
      errorMessage += "   " + internalRunOutputName + " (advanced)\n";
      }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
404
    errorMessage += detailsString;
Alexander Neundorf's avatar
 
Alexander Neundorf committed
405 406 407 408 409 410
    cmSystemTools::Error(errorMessage.c_str());
    return;
    }

  if (out!=0)
    {
Stephen Kelly's avatar
Stephen Kelly committed
411
    (*out) = this->Makefile->GetDefinition(internalRunOutputName);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
412 413
    }
}