From 19d86e26e392a80163d90cbc1accaf45412968f8 Mon Sep 17 00:00:00 2001
From: Wouter Klouwen <wouter.klouwen@youview.com>
Date: Fri, 16 Nov 2018 16:06:54 +0000
Subject: ExternalProject: add LOG_OUTPUT_ON_FAILURE option

This option only has an effect if at least one of the other LOG_<step>
options is enabled.  If an error occurs for a step which has logging to
file enabled, that step's output will be printed to the console.  For
cases where a large amount of output is recorded, just the end of that
output may be printed to the console.
---
 Auxiliary/vim/syntax/cmake.vim                     |  1 +
 Modules/ExternalProject.cmake                      | 51 ++++++++++++++++++++--
 .../LogOutputOnFailure-build-result.txt            |  1 +
 .../LogOutputOnFailure-build-stderr.txt            |  1 +
 .../LogOutputOnFailure-build-stdout.txt            |  8 ++++
 .../ExternalProject/LogOutputOnFailure.cmake       | 20 +++++++++
 .../LogOutputOnFailureMerged-build-result.txt      |  1 +
 .../LogOutputOnFailureMerged-build-stderr.txt      |  1 +
 .../LogOutputOnFailureMerged-build-stdout.txt      |  7 +++
 .../ExternalProject/LogOutputOnFailureMerged.cmake | 21 +++++++++
 Tests/RunCMake/ExternalProject/RunCMakeTest.cmake  |  7 +++
 11 files changed, 115 insertions(+), 4 deletions(-)
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-result.txt
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stderr.txt
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stdout.txt
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailure.cmake
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-result.txt
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stderr.txt
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stdout.txt
 create mode 100644 Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged.cmake

diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index d4d0edf..b67ef06 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -877,6 +877,7 @@ syn keyword cmakeKWExternalProject contained
             \ LOG_DOWNLOAD
             \ LOG_INSTALL
             \ LOG_MERGED_STDOUTERR
+            \ LOG_OUTPUT_ON_FAILURE
             \ LOG_PATCH
             \ LOG_TEST
             \ LOG_UPDATE
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 93cd74a..e763bab 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -550,6 +550,14 @@ External Project Definition
     ``LOG_MERGED_STDOUTERR <bool>``
       When enabled, the output the step is not split by stdout and stderr.
 
+    ``LOG_OUTPUT_ON_FAILURE <bool>``
+      This option only has an effect if at least one of the other ``LOG_<step>``
+      options is enabled.  If an error occurs for a step which has logging to
+      file enabled, that step's output will be printed to the console if
+      ``LOG_OUTPUT_ON_FAILURE`` is set to true.  For cases where a large amount
+      of output is recorded, just the end of that output may be printed to the
+      console.
+
   **Terminal Access Options:**
     Steps can be given direct access to the terminal in some cases. Giving a
     step access to the terminal may allow it to receive terminal input if
@@ -1953,6 +1961,7 @@ endif()
   set(script ${stamp_dir}/${name}-${step}-$<CONFIG>.cmake)
   set(logbase ${log_dir}/${name}-${step})
   get_property(log_merged TARGET ${name} PROPERTY _EP_LOG_MERGED_STDOUTERR)
+  get_property(log_output_on_failure TARGET ${name} PROPERTY _EP_LOG_OUTPUT_ON_FAILURE)
   if (log_merged)
     set(stdout_log "${logbase}.log")
     set(stderr_log "${logbase}.log")
@@ -1961,21 +1970,55 @@ endif()
     set(stderr_log "${logbase}-err.log")
   endif()
   set(code "
+cmake_minimum_required(VERSION 3.13)
 ${code_cygpath_make}
 set(command \"${command}\")
+set(log_merged \"${log_merged}\")
+set(log_output_on_failure \"${log_output_on_failure}\")
+set(stdout_log \"${stdout_log}\")
+set(stderr_log \"${stderr_log}\")
 execute_process(
   COMMAND \${command}
   RESULT_VARIABLE result
-  OUTPUT_FILE \"${stdout_log}\"
-  ERROR_FILE \"${stderr_log}\"
+  OUTPUT_FILE \"\${stdout_log}\"
+  ERROR_FILE \"\${stderr_log}\"
   )
+macro(read_up_to_max_size log_file output_var)
+  file(SIZE \${log_file} determined_size)
+  set(max_size 10240)
+  if (determined_size GREATER max_size)
+    math(EXPR seek_position \"\${determined_size} - \${max_size}\")
+    file(READ \${log_file} \${output_var} OFFSET \${seek_position})
+    set(\${output_var} \"...skipping to end...\\n\${\${output_var}}\")
+  else()
+    file(READ \${log_file} \${output_var})
+  endif()
+endmacro()
 if(result)
   set(msg \"Command failed: \${result}\\n\")
   foreach(arg IN LISTS command)
     set(msg \"\${msg} '\${arg}'\")
   endforeach()
-  set(msg \"\${msg}\\nSee also\\n  ${stderr_log}\")
-  message(FATAL_ERROR \"\${msg}\")
+  if (\${log_merged})
+    set(msg \"\${msg}\\nSee also\\n  \${stderr_log}\")
+  else()
+    set(msg \"\${msg}\\nSee also\\n  ${logbase}-*.log\")
+  endif()
+  if (\${log_output_on_failure})
+    message(SEND_ERROR \"\${msg}\")
+    if (\${log_merged})
+      read_up_to_max_size(\"\${stderr_log}\" error_log_contents)
+      message(STATUS \"Log output is:\\n\${error_log_contents}\")
+    else()
+      read_up_to_max_size(\"\${stdout_log}\" out_log_contents)
+      read_up_to_max_size(\"\${stderr_log}\" err_log_contents)
+      message(STATUS \"stdout output is:\\n\${out_log_contents}\")
+      message(STATUS \"stderr output is:\\n\${err_log_contents}\")
+    endif()
+    message(FATAL_ERROR \"Stopping after outputting logs.\")
+  else()
+    message(FATAL_ERROR \"\${msg}\")
+  endif()
 else()
   set(msg \"${name} ${step} command succeeded.  See also ${logbase}-*.log\")
   message(STATUS \"\${msg}\")
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-result.txt b/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-result.txt
new file mode 100644
index 0000000..c20fd86
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-result.txt
@@ -0,0 +1 @@
+^[^0]
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stderr.txt b/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stderr.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stderr.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stdout.txt b/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stdout.txt
new file mode 100644
index 0000000..87e5384
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailure-build-stdout.txt
@@ -0,0 +1,8 @@
+(  )?-- stdout output is:
+(  )?This is some dummy output with some long lines to ensure formatting is preserved
+(  )?    Including lines with leading spaces
+(  )?
+(  )?And also blank lines[
+ ]+
+(  )?-- stderr output is:
+(  )?cmake -E env: no command given
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailure.cmake b/Tests/RunCMake/ExternalProject/LogOutputOnFailure.cmake
new file mode 100644
index 0000000..863bbef
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailure.cmake
@@ -0,0 +1,20 @@
+include(ExternalProject)
+
+set(dummyOutput [[
+This is some dummy output with some long lines to ensure formatting is preserved
+    Including lines with leading spaces
+
+And also blank lines
+]])
+
+ExternalProject_Add(FailsWithOutput
+  SOURCE_DIR            ${CMAKE_CURRENT_LIST_DIR}
+  CONFIGURE_COMMAND     ""
+  BUILD_COMMAND         ${CMAKE_COMMAND} -E echo ${dummyOutput}
+        COMMAND         ${CMAKE_COMMAND} -E env  # missing command, forces fail
+  TEST_COMMAND          ""
+  INSTALL_COMMAND       ""
+  LOG_BUILD             YES
+  LOG_OUTPUT_ON_FAILURE YES
+  USES_TERMINAL_BUILD   YES
+)
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-result.txt b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-result.txt
new file mode 100644
index 0000000..c20fd86
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-result.txt
@@ -0,0 +1 @@
+^[^0]
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stderr.txt b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stderr.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stderr.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stdout.txt b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stdout.txt
new file mode 100644
index 0000000..11c7317
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged-build-stdout.txt
@@ -0,0 +1,7 @@
+(  )?-- Log output is:
+(  )?This is some dummy output with some long lines to ensure formatting is preserved
+(  )?    Including lines with leading spaces
+(  )?
+(  )?And also blank lines[
+ ]+
+(  )?cmake -E env: no command given
diff --git a/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged.cmake b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged.cmake
new file mode 100644
index 0000000..116448b
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/LogOutputOnFailureMerged.cmake
@@ -0,0 +1,21 @@
+include(ExternalProject)
+
+set(dummyOutput [[
+This is some dummy output with some long lines to ensure formatting is preserved
+    Including lines with leading spaces
+
+And also blank lines
+]])
+
+ExternalProject_Add(FailsWithOutput
+  SOURCE_DIR            ${CMAKE_CURRENT_LIST_DIR}
+  CONFIGURE_COMMAND     ""
+  BUILD_COMMAND         ${CMAKE_COMMAND} -E echo ${dummyOutput}
+        COMMAND         ${CMAKE_COMMAND} -E env  # missing command, forces fail
+  TEST_COMMAND          ""
+  INSTALL_COMMAND       ""
+  LOG_BUILD             YES
+  LOG_OUTPUT_ON_FAILURE YES
+  LOG_MERGED_STDOUTERR  YES
+  USES_TERMINAL_BUILD   YES
+)
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
index bf11381..caaf0d2 100644
--- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -29,6 +29,13 @@ endfunction()
 
 __ep_test_with_build(MultiCommand)
 
+# Output is not predictable enough to be able to verify it reliably
+# when using the various different Visual Studio generators
+if(NOT RunCMake_GENERATOR MATCHES "Visual Studio")
+  __ep_test_with_build(LogOutputOnFailure)
+  __ep_test_with_build(LogOutputOnFailureMerged)
+endif()
+
 # We can't test the substitution when using the old MSYS due to
 # make/sh mangling the paths (substitution is performed correctly,
 # but the mangling means we can't reliably test the output).
-- 
cgit v0.12