diff options
author | Alexander Neundorf <neundorf@kde.org> | 2007-06-01 15:16:29 (GMT) |
---|---|---|
committer | Alexander Neundorf <neundorf@kde.org> | 2007-06-01 15:16:29 (GMT) |
commit | eddf1cf39f50f9b06131ead26b5b84b8edfd60da (patch) | |
tree | 1cbc429b0f2006f19ee9c19773f841648405793d /Source | |
parent | 26a5a295eb231dc9fae9ea90f6cf28d3b4eecaa2 (diff) | |
download | CMake-eddf1cf39f50f9b06131ead26b5b84b8edfd60da.zip CMake-eddf1cf39f50f9b06131ead26b5b84b8edfd60da.tar.gz CMake-eddf1cf39f50f9b06131ead26b5b84b8edfd60da.tar.bz2 |
ENH: improve TRY_RUN() for crosscompiling: instead of just failing, it now
creates two cache variables, one for the RUN_RESULT, one for the RUN_OUTPUT
(if required), which can be set or preset by the user. It has now also two
new arguments: RUN_OUTPUT_VARIABLE and COMPILE_OUTPUT_VARIABLE (the old
OUTPUT_VARIABLE merges both), so if only COMPILE_OUTPUT_VARIABLE is used the
run time output of the TRY_RUN is unused and the user doesn't have to care
about the output when crosscompiling. This is now used in FindThreads.cmake,
CheckC/CXXSourceRuns.cmake and TestBigEndian.cmake, which used the output
only for the logfile (compile output is still there). Test/TryCompile/ now
also tests the behaviour of OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE and
COMPILE_OUTPUT_VARIABLE.
Alex
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmTryRunCommand.cxx | 283 | ||||
-rw-r--r-- | Source/cmTryRunCommand.h | 59 |
2 files changed, 288 insertions, 54 deletions
diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index 54e7a2d..1781e9c 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -25,17 +25,16 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv) { return false; } - - if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING")) - { - this->SetError("doesn't work when crosscompiling."); - cmSystemTools::SetFatalErrorOccured(); - return false; - } // build an arg list for TryCompile and extract the runArgs std::vector<std::string> tryCompile; - std::string outputVariable; + + this->CompileResultVariable = ""; + this->RunResultVariable = ""; + this->OutputVariable = ""; + this->RunOutputVariable = ""; + this->CompileOutputVariable = ""; + std::string runArgs; unsigned int i; for (i = 1; i < argv.size(); ++i) @@ -57,19 +56,79 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv) } else { - tryCompile.push_back(argv[i]); if (argv[i] == "OUTPUT_VARIABLE") - { + { if ( argv.size() <= (i+1) ) { cmSystemTools::Error( "OUTPUT_VARIABLE specified but there is no variable"); return false; } - outputVariable = argv[i+1]; + 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]); } } } + + // although they could be used together, don't allow it, because + // using OUTPUT_VARIABLE makes crosscompiling harder + if (this->OutputVariable.size() + && ((this->RunOutputVariable.size()) + || (this->CompileOutputVariable.size()))) + { + 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; + if (this->OutputVariable.size()) + { + captureRunOutput = true; + tryCompile.push_back("OUTPUT_VARIABLE"); + tryCompile.push_back(this->OutputVariable); + } + if (this->CompileOutputVariable.size()) + { + tryCompile.push_back("OUTPUT_VARIABLE"); + tryCompile.push_back(this->CompileOutputVariable); + } + if (this->RunOutputVariable.size()) + { + captureRunOutput = true; + } + + this->RunResultVariable = argv[0]; + this->CompileResultVariable = argv[1]; + // do the try compile int res = this->TryCompileCode(tryCompile); @@ -82,46 +141,42 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv) } else { - int retVal = -1; - std::string output; - std::string finalCommand = cmSystemTools::ConvertToRunCommandPath( - this->OutputFile.c_str()); - if(runArgs.size()) + // "run" it and capture the output + std::string runOutputContents; + if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING")) + { + this->DoNotRunExecutable(runArgs, + argv[3], + captureRunOutput ? &runOutputContents : 0); + } + else { - finalCommand += runArgs; + this->RunExecutable(runArgs, &runOutputContents); } - int timeout = 0; - bool worked = cmSystemTools::RunSingleCommand(finalCommand.c_str(), - &output, &retVal, - 0, false, timeout); - if(outputVariable.size()) + + // now put the output into the variables + if(this->RunOutputVariable.size()) + { + this->Makefile->AddDefinition(this->RunOutputVariable.c_str(), + runOutputContents.c_str()); + } + + if(this->OutputVariable.size()) { // if the TryCompileCore saved output in this outputVariable then // prepend that output to this output const char* compileOutput - = this->Makefile->GetDefinition(outputVariable.c_str()); - if(compileOutput) + = this->Makefile->GetDefinition(this->OutputVariable.c_str()); + if (compileOutput) { - output = std::string(compileOutput) + output; + runOutputContents = std::string(compileOutput) + runOutputContents; } - this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str()); - } - // set the run var - char retChar[1000]; - if(worked) - { - sprintf(retChar,"%i",retVal); - } - else - { - strcpy(retChar, "FAILED_TO_RUN"); + this->Makefile->AddDefinition(this->OutputVariable.c_str(), + runOutputContents.c_str()); } - this->Makefile->AddCacheDefinition(argv[0].c_str(), retChar, - "Result of TRY_RUN", - cmCacheManager::INTERNAL); } - } - + } + // if we created a directory etc, then cleanup after ourselves if(!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { @@ -129,3 +184,149 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv) } return true; } + +void cmTryRunCommand::RunExecutable(const std::string& runArgs, + std::string* out) +{ + int retVal = -1; + std::string finalCommand = cmSystemTools::ConvertToRunCommandPath( + this->OutputFile.c_str()); + if (runArgs.size()) + { + finalCommand += runArgs; + } + int timeout = 0; + bool worked = cmSystemTools::RunSingleCommand(finalCommand.c_str(), + out, &retVal, + 0, false, timeout); + // set the run var + char retChar[1000]; + if (worked) + { + sprintf(retChar, "%i", retVal); + } + else + { + strcpy(retChar, "FAILED_TO_RUN"); + } + this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(), retChar, + "Result of TRY_RUN", + cmCacheManager::INTERNAL); +} + +/* This is only used when cross compiling. Instead of running the + executable, two cache variables are created which will hold the results + the executable would have produced. +*/ +void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string* out + ) +{ + + std::string internalRunOutputName = this->RunResultVariable+"__" + +this->CompileResultVariable+"__TRYRUN_OUTPUT"; + bool error = false; + std::string info = "Source file: "; + info += srcFile + "\n"; + if (runArgs.size()) + { + info += "Run arguments: "; + info += runArgs; + info += "\n"; + } + info += "Current CMake stack: " + this->Makefile->GetListFileStack(); + + if (this->Makefile->GetDefinition(this->RunResultVariable.c_str()) == 0) + { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment; + comment += "Run result of TRY_RUN().\n" + "This variable should indicate whether the executable would " + "have been able to run if it was executed on its target " + "platform.\n" + "If it would have been able to run, enter the exit code here " + "(in many cases 0 for success). If not, enter " + "\"FAILED_TO_RUN\" here."; + if (out!=0) + { + comment += "If it was able to run, also check the variable "; + comment += internalRunOutputName; + comment += " and set it appropriately."; + } + comment += "\n"; + comment += info; + this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(), + "PLEASE_FILL_OUT-FAILED_TO_RUN", + comment.c_str(), + cmCacheManager::STRING); + + cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()-> + GetCacheIterator(this->RunResultVariable.c_str()); + if ( !it.IsAtEnd() ) + { + it.SetProperty("ADVANCED", "1"); + } + + error = true; + } + + if (out!=0) + { + if (this->Makefile->GetDefinition(internalRunOutputName.c_str()) == 0) + { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment; + comment += "Output of TRY_RUN().\n" + "This variable should contain the text, which the executable " + "run by TRY_RUN() would have printed on stdout and stderr, " + "if it was executed on its target platform.\n" + "The accompanying variable "; + comment += this->RunResultVariable; + comment += " indicates whether the executable would have been able to " + "run and its exit code." + "If the executable would not have been able to run, set "; + comment += internalRunOutputName; + comment += " empty. Otherwise check if the output is evaluated by the " + "calling CMake code. If this is the case, check the source " + "file what it would have printed if called with the given " + "arguments.\n"; + comment += info; + + this->Makefile->AddCacheDefinition(internalRunOutputName.c_str(), + "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), + cmCacheManager::STRING); + cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()-> + GetCacheIterator(internalRunOutputName.c_str()); + if ( !it.IsAtEnd() ) + { + it.SetProperty("ADVANCED", "1"); + } + + error = true; + } + } + + if (error) + { + std::string errorMessage = "TRY_RUN() invoked in cross-compiling mode, " + "please set the following cache variables " + "appropriatly:\n"; + errorMessage += " " + this->RunResultVariable + " (advanced)\n"; + if (out!=0) + { + errorMessage += " " + internalRunOutputName + " (advanced)\n"; + } + errorMessage += info; + cmSystemTools::Error(errorMessage.c_str()); + return; + } + + if (out!=0) + { + (*out) = this->Makefile->GetDefinition(internalRunOutputName.c_str()); + } +} diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h index b11a7c4..8c4788c 100644 --- a/Source/cmTryRunCommand.h +++ b/Source/cmTryRunCommand.h @@ -61,22 +61,55 @@ public: { return " TRY_RUN(RUN_RESULT_VAR COMPILE_RESULT_VAR\n" - " bindir srcfile <CMAKE_FLAGS <Flags>>\n" - " <COMPILE_DEFINITIONS <flags>>\n" - " <OUTPUT_VARIABLE var>\n" - " <ARGS <arg1> <arg2>...>)\n" - "Try compiling a srcfile. Return the success or failure in " - "COMPILE_RESULT_VAR. Then if the compile succeeded, run the " - "executable and return the result in RUN_RESULT_VAR. " - "If the executable was built, but failed to run for some" - "reason, then RUN_RESULT_VAR will be set to FAILED_TO_RUN, and " - "the output will be in the COMPILE_RESULT_VAR. OUTPUT_VARIABLE " - "specifies the name of the variable to put all of the standard " - "output and standard error into."; + " bindir srcfile [CMAKE_FLAGS <Flags>]\n" + " [COMPILE_DEFINITIONS <flags>]\n" + " [COMPILE_OUTPUT_VARIABLE comp]\n" + " [RUN_OUTPUT_VARIABLE run]\n" + " [OUTPUT_VARIABLE var]\n" + " [ARGS <arg1> <arg2>...])\n" + "Try compiling a srcfile. Return TRUE or FALSE for success or failure " + "in COMPILE_RESULT_VAR. Then if the compile succeeded, run the " + "executable and return its exit code in RUN_RESULT_VAR. " + "If the executable was built, but failed to run, then RUN_RESULT_VAR " + "will be set to FAILED_TO_RUN. " + "COMPILE_OUTPUT_VARIABLE specifies the variable where the output from " + "the compile step goes. RUN_OUTPUT_VARIABLE specifies the variable " + "where the output from the running executable goes.\n" + "For compatibility reasons OUTPUT_VARIABLE is still supported, which " + "gives you the output from the compile and run step combined.\n\n" + "Cross compiling issues\n" + "When cross compiling, the executable compiled in the first step " + "usually cannot be run on the build host. TRY_RUN() checks the " + "CMAKE_CROSSCOMPILING variable to detect whether CMake is in " + "crosscompiling mode. If that's the case, it will still try to compile " + "the executable, but it will not try to run the executable. Instead it " + "will create cache variables which must be filled by the user or by " + "presetting them in some CMake script file to the values the " + "executable would have produced if it would have been run on its actual " + "target platform. These variables are RUN_RESULT_VAR (explanation see " + "above) and if RUN_OUTPUT_VARIABLE (or OUTPUT_VARIABLE) was used, an " + "additional cache variable " + "RUN_RESULT_VAR__COMPILE_RESULT_VAR__TRYRUN_OUTPUT." + "This is intended to hold stdout and stderr from the executable.\n" + "In order to make cross compiling your project easier, use TRY_RUN " + "only if really required. If you use TRY_RUN, use RUN_OUTPUT_VARIABLE " + "(or OUTPUT_VARIABLE) only if really required. Using them will require " + "that when crosscompiling, the cache variables will have to be set " + "manually to the output of the executable. You can also \"guard\" the " + "calls to TRY_RUN with IF(CMAKE_CROSSCOMPILING) and provide an " + "easy-to-preset alternative for this case.\n"; } - + cmTypeMacro(cmTryRunCommand, cmCoreTryCompile); +private: + void RunExecutable(const std::string& runArgs, std::string* runOutputContents); + void DoNotRunExecutable(const std::string& runArgs, const std::string& srcFile, std::string* runOutputContents); + std::string CompileResultVariable; + std::string RunResultVariable; + std::string OutputVariable; + std::string RunOutputVariable; + std::string CompileOutputVariable; }; |