From c28715b16c7a71cd8ea0416277cc87ee9bc8eaa1 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Tue, 9 Jul 2013 15:26:15 -0400
Subject: try_compile: Add COPY_FILE_ERROR option to capture failure

When the COPY_FILE operation fails optionally capture the error message
with a COPY_FILE_ERROR option instead of reporting the error
immediately.  This gives callers a chance to do something else or report
the error.

Teach the RunCMake.try_compile test to cover bad argument combinations
involving COPY_FILE_ERROR.  Teach the TryCompile test to cover the case
of a COPY_FILE error message captured by COPY_FILE_ERROR.
---
 Source/cmCoreTryCompile.cxx                        | 46 ++++++++++++++++++++--
 Source/cmTryCompileCommand.h                       |  4 +-
 .../try_compile/CopyFileErrorNoCopyFile-result.txt |  1 +
 .../try_compile/CopyFileErrorNoCopyFile-stderr.txt |  4 ++
 .../try_compile/CopyFileErrorNoCopyFile.cmake      |  2 +
 .../try_compile/NoCopyFileError-result.txt         |  1 +
 .../try_compile/NoCopyFileError-stderr.txt         |  4 ++
 Tests/RunCMake/try_compile/NoCopyFileError.cmake   |  2 +
 Tests/RunCMake/try_compile/RunCMakeTest.cmake      |  2 +
 Tests/TryCompile/CMakeLists.txt                    | 17 ++++++++
 10 files changed, 78 insertions(+), 5 deletions(-)
 create mode 100644 Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-result.txt
 create mode 100644 Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt
 create mode 100644 Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile.cmake
 create mode 100644 Tests/RunCMake/try_compile/NoCopyFileError-result.txt
 create mode 100644 Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt
 create mode 100644 Tests/RunCMake/try_compile/NoCopyFileError.cmake

diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index 860417f..91b7215 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -32,18 +32,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
   std::vector<std::string> compileDefs;
   std::string outputVariable;
   std::string copyFile;
+  std::string copyFileError;
   std::vector<cmTarget*> targets;
   std::string libsToLink = " ";
   bool useOldLinkLibs = true;
   char targetNameBuf[64];
   bool didOutputVariable = false;
   bool didCopyFile = false;
+  bool didCopyFileError = false;
   bool useSources = argv[2] == "SOURCES";
   std::vector<std::string> sources;
 
   enum Doing { DoingNone, DoingCMakeFlags, DoingCompileDefinitions,
                DoingLinkLibraries, DoingOutputVariable, DoingCopyFile,
-               DoingSources };
+               DoingCopyFileError, DoingSources };
   Doing doing = useSources? DoingSources : DoingNone;
   for(size_t i=3; i < argv.size(); ++i)
     {
@@ -74,6 +76,11 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
       doing = DoingCopyFile;
       didCopyFile = true;
       }
+    else if(argv[i] == "COPY_FILE_ERROR")
+      {
+      doing = DoingCopyFileError;
+      didCopyFileError = true;
+      }
     else if(doing == DoingCMakeFlags)
       {
       cmakeFlags.push_back(argv[i]);
@@ -121,6 +128,11 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
       copyFile = argv[i].c_str();
       doing = DoingNone;
       }
+    else if(doing == DoingCopyFileError)
+      {
+      copyFileError = argv[i].c_str();
+      doing = DoingNone;
+      }
     else if(doing == DoingSources)
       {
       sources.push_back(argv[i]);
@@ -149,6 +161,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
     return -1;
     }
 
+  if(didCopyFileError && copyFileError.empty())
+    {
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR,
+      "COPY_FILE_ERROR must be followed by a variable name");
+    return -1;
+    }
+
+  if(didCopyFileError && !didCopyFile)
+    {
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR,
+      "COPY_FILE_ERROR may be used only with COPY_FILE");
+    return -1;
+    }
+
   if(didOutputVariable && outputVariable.empty())
     {
     this->Makefile->IssueMessage(cmake::FATAL_ERROR,
@@ -444,6 +470,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
 
   if (this->SrcFileSignature)
     {
+    std::string copyFileErrorMessage;
     this->FindOutputFile(targetName);
 
     if ((res==0) && (copyFile.size()))
@@ -461,10 +488,23 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
           {
           emsg << this->FindErrorMessage.c_str();
           }
-        this->Makefile->IssueMessage(cmake::FATAL_ERROR, emsg.str());
-        return -1;
+        if(copyFileError.empty())
+          {
+          this->Makefile->IssueMessage(cmake::FATAL_ERROR, emsg.str());
+          return -1;
+          }
+        else
+          {
+          copyFileErrorMessage = emsg.str();
+          }
         }
       }
+
+    if(!copyFileError.empty())
+      {
+      this->Makefile->AddDefinition(copyFileError.c_str(),
+                                    copyFileErrorMessage.c_str());
+      }
     }
   return res;
 }
diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h
index 163756d..a20594c 100644
--- a/Source/cmTryCompileCommand.h
+++ b/Source/cmTryCompileCommand.h
@@ -69,14 +69,14 @@ public:
       "              [COMPILE_DEFINITIONS flags...]\n"
       "              [LINK_LIBRARIES libs...]\n"
       "              [OUTPUT_VARIABLE <var>]\n"
-      "              [COPY_FILE <fileName>])\n"
+      "              [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]])\n"
       "Try building an executable from one or more source files.  "
       "In this form the user need only supply one or more source files "
       "that include a definition for 'main'.  "
       "CMake will create a CMakeLists.txt file to build the source(s) "
       "as an executable.  "
       "Specify COPY_FILE to get a copy of the linked executable at the "
-      "given fileName."
+      "given fileName and optionally COPY_FILE_ERROR to capture any error."
       "\n"
       "In this version all files in bindir/CMakeFiles/CMakeTmp "
       "will be cleaned automatically. For debugging, --debug-trycompile can "
diff --git a/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-result.txt b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt
new file mode 100644
index 0000000..5d09c0c
--- /dev/null
+++ b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at CopyFileErrorNoCopyFile.cmake:1 \(try_compile\):
+  COPY_FILE_ERROR may be used only with COPY_FILE
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile.cmake b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile.cmake
new file mode 100644
index 0000000..8d7cb0e
--- /dev/null
+++ b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile.cmake
@@ -0,0 +1,2 @@
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE_ERROR _copied)
diff --git a/Tests/RunCMake/try_compile/NoCopyFileError-result.txt b/Tests/RunCMake/try_compile/NoCopyFileError-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/try_compile/NoCopyFileError-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt b/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt
new file mode 100644
index 0000000..ed552fd
--- /dev/null
+++ b/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at NoCopyFileError.cmake:1 \(try_compile\):
+  COPY_FILE_ERROR must be followed by a variable name
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/NoCopyFileError.cmake b/Tests/RunCMake/try_compile/NoCopyFileError.cmake
new file mode 100644
index 0000000..d4d69ee
--- /dev/null
+++ b/Tests/RunCMake/try_compile/NoCopyFileError.cmake
@@ -0,0 +1,2 @@
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/copied.bin COPY_FILE_ERROR)
diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
index 3494695..c934458 100644
--- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
@@ -1,10 +1,12 @@
 include(RunCMake)
 
+run_cmake(CopyFileErrorNoCopyFile)
 run_cmake(NoArgs)
 run_cmake(OneArg)
 run_cmake(TwoArgs)
 run_cmake(NoCopyFile)
 run_cmake(NoCopyFile2)
+run_cmake(NoCopyFileError)
 run_cmake(NoOutputVariable)
 run_cmake(NoOutputVariable2)
 run_cmake(NoSources)
diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt
index 4540fd0..173929b 100644
--- a/Tests/TryCompile/CMakeLists.txt
+++ b/Tests/TryCompile/CMakeLists.txt
@@ -44,6 +44,23 @@ else()
    file(REMOVE "${TryCompile_BINARY_DIR}/CopyOfPass")
 endif()
 
+# try to compile a file that should compile
+# also check that COPY_FILE_ERROR works
+file(WRITE ${TryCompile_BINARY_DIR}/invalid "")
+try_compile(SHOULD_PASS
+    ${TryCompile_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
+    ${TryCompile_SOURCE_DIR}/pass.c
+    OUTPUT_VARIABLE TRY_OUT
+    COPY_FILE ${TryCompile_BINARY_DIR}/invalid/path
+    COPY_FILE_ERROR _captured
+    )
+if(NOT SHOULD_PASS)
+  message(SEND_ERROR "should pass failed ${TRY_OUT}")
+endif()
+if(NOT _captured MATCHES "Cannot copy output executable.*/invalid/path")
+  message(SEND_ERROR "COPY_FILE_ERROR did not capture expected message")
+endif()
+
 # try to compile a file that should not compile
 try_compile(SHOULD_FAIL
     ${TryCompile_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
-- 
cgit v0.12