ENH: Prevent cmake in source builds (#1091)

* ENH: Prevent cmake in source builds

Building directly inside the root of the source tree
can cause problems where the build intermediate files
overwrite or conflict with the intended source code
files.

This modification identifies this problem and
issues failure messages and suggestions to over
come the problem with more robust build suggestion.

Co-authored-by: Jordan Bayles <jophba@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 51b74fc..f1db5e3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,6 +75,9 @@
 message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
 set(PROJECT_SOVERSION 24)
 
+include(${CMAKE_CURRENT_SOURCE_DIR}/include/PreventInSourceBuilds.cmake)
+include(${CMAKE_CURRENT_SOURCE_DIR}/include/PreventInBuildInstalls.cmake)
+
 option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
 option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
 option(JSONCPP_WITH_WARNING_AS_ERROR "Force compilation to fail if a warning occurs" OFF)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d72fe97..8d992be 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,7 +19,7 @@
     DESTDIR=/path/to/install/dir
 
 Then,
-
+```sh
     cd jsoncpp/
     BUILD_TYPE=debug
     #BUILD_TYPE=release
@@ -35,6 +35,7 @@
     #meson test --no-rebuild --print-errorlogs
 
     sudo ninja install
+```
 
 ## Building and testing with other build systems
 See https://github.com/open-source-parsers/jsoncpp/wiki/Building
diff --git a/appveyor.yml b/appveyor.yml
index 0b9c8fe..cccce42 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,7 @@
 clone_folder: c:\projects\jsoncpp
 
 environment:
+
   matrix:
     - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
       CMAKE_GENERATOR: Visual Studio 14 2015
@@ -13,11 +14,15 @@
 
 build_script:
   - cmake --version
-  - cd c:\projects\jsoncpp
-  - cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX:PATH=%CD:\=/%/install -DBUILD_SHARED_LIBS:BOOL=ON .
+  # The build script starts in root.
+  - set JSONCPP_FOLDER=%cd%
+  - set JSONCPP_BUILD_FOLDER=%JSONCPP_FOLDER%\build\release
+  - mkdir -p %JSONCPP_BUILD_FOLDER%
+  - cd %JSONCPP_BUILD_FOLDER%
+  - cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX:PATH=%CD:\=/%/install -DBUILD_SHARED_LIBS:BOOL=ON %JSONCPP_FOLDER%
   # Use ctest to make a dashboard build:
   # - ctest -D Experimental(Start|Update|Configure|Build|Test|Coverage|MemCheck|Submit)
-  # NOTE: Testing on window is not yet finished:
+  # NOTE: Testing on windows is not yet finished:
   # - ctest -C Release -D ExperimentalStart -D ExperimentalConfigure -D ExperimentalBuild -D ExperimentalTest -D ExperimentalSubmit
   - ctest -C Release -D ExperimentalStart -D ExperimentalConfigure -D ExperimentalBuild -D ExperimentalSubmit
   # Final step is to verify that installation succeeds
diff --git a/include/PreventInBuildInstalls.cmake b/include/PreventInBuildInstalls.cmake
new file mode 100644
index 0000000..accfea6
--- /dev/null
+++ b/include/PreventInBuildInstalls.cmake
@@ -0,0 +1,9 @@
+string(TOLOWER "${CMAKE_INSTALL_PREFIX}" _PREFIX)
+string(TOLOWER "${ITK_BINARY_DIR}" _BUILD)
+if("${_PREFIX}" STREQUAL "${_BUILD}")
+  message(FATAL_ERROR
+    "The current CMAKE_INSTALL_PREFIX points at the build tree:\n"
+    "  ${CMAKE_INSTALL_PREFIX}\n"
+    "This is not supported."
+    )
+endif()
diff --git a/include/PreventInSourceBuilds.cmake b/include/PreventInSourceBuilds.cmake
new file mode 100644
index 0000000..7ddda54
--- /dev/null
+++ b/include/PreventInSourceBuilds.cmake
@@ -0,0 +1,45 @@
+#
+# This function will prevent in-source builds
+function(AssureOutOfSourceBuilds)
+  # make sure the user doesn't play dirty with symlinks
+  get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
+  get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
+
+  # disallow in-source builds
+  if("${srcdir}" STREQUAL "${bindir}")
+    message("######################################################")
+    message("# jsoncpp should not be configured & built in the jsoncpp source directory")
+    message("# You must run cmake in a build directory.")
+    message("# For example:")
+    message("# mkdir jsoncpp-Sandbox ; cd jsoncpp-sandbox")
+    message("# git clone https://github.com/open-source-parsers/jsoncpp.git # or download & unpack the source tarball")
+    message("# mkdir jsoncpp-build")
+    message("# this will create the following directory structure")
+    message("#")
+    message("# jsoncpp-Sandbox")
+    message("#  +--jsoncpp")
+    message("#  +--jsoncpp-build")
+    message("#")
+    message("# Then you can proceed to configure and build")
+    message("# by using the following commands")
+    message("#")
+    message("# cd jsoncpp-build")
+    message("# cmake ../jsoncpp # or ccmake, or cmake-gui ")
+    message("# make")
+    message("#")
+    message("# NOTE: Given that you already tried to make an in-source build")
+    message("#       CMake have already created several files & directories")
+    message("#       in your source tree. run 'git status' to find them and")
+    message("#       remove them by doing:")
+    message("#")
+    message("#       cd jsoncpp-Sandbox/jsoncpp")
+    message("#       git clean -n -d")
+    message("#       git clean -f -d")
+    message("#       git checkout --")
+    message("#")
+    message("######################################################")
+    message(FATAL_ERROR "Quitting configuration")
+  endif()
+endfunction()
+
+AssureOutOfSourceBuilds()