On 25-Nov-14 21:29, Brad King wrote:
* The background.(bat|sh) files need to have quoting to work with
   spaces in the path.  Please try running tests with a source/build
   tree with spaces.

* Instead of background scripts, can you have a parent process take
   a lock, run a child with a timed out lock, and then release the
   lock?
Test updated, no need to use 'background.*' scripts indeed.
* The error messages currently use "SetError(...); return false;".
   That is why you need the ": " to get the prompt.  Instead you can
   use IssueMessage as shown in the hunk below for one example.
's,SetError,IssueMessage,g' done
I'm not quite sure about when do I need to use 'return false'/'return true', so I leave 'return false' everywhere.


* Please use shorter timeouts in the test if possible to make it run
   faster.  If they become spurious/problematic then we can lengthen
   them later.

* ... Then the timeout can be just 0.1s in the child and there
   is no race.
I've set timeout to 1 sec (minimal unsigned integer).

Ruslo.
>From 6b874aa940a86f9a789cfae23a8c47d609587d61 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:45:26 +0300
Subject: [PATCH 1/7] Add function cmSystemTools::StringToInt

---
 Source/cmSystemTools.cxx | 7 +++++++
 Source/cmSystemTools.h   | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index baac7b8..e247481 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -2923,3 +2923,10 @@ std::vector<std::string> cmSystemTools::tokenize(const 
std::string& str,
     }
   return tokens;
 }
+
+//----------------------------------------------------------------------------
+bool cmSystemTools::StringToInt(const char* str, int* value) {
+  char unused;
+  const int result = sscanf(str, "%d%c", value, &unused);
+  return (result == 1);
+}
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 4455dd1..763389b 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -458,6 +458,9 @@ public:
   static std::vector<std::string> tokenize(const std::string& str,
                                            const std::string& sep);
 
+  /** Convert string to int. Expected that the whole string is an integer */
+  static bool StringToInt(const char* str, int* value);
+
 #ifdef _WIN32
   struct WindowsFileRetry
   {
-- 
2.1.1

>From 569bc681c0a980b87cd94184cfb83b44122d92a0 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:46:14 +0300
Subject: [PATCH 2/7] Add class cmFileLockResult

---
 Source/cmFileLockResult.cxx | 111 ++++++++++++++++++++++++++++++++++++++++++++
 Source/cmFileLockResult.h   |  85 +++++++++++++++++++++++++++++++++
 2 files changed, 196 insertions(+)
 create mode 100644 Source/cmFileLockResult.cxx
 create mode 100644 Source/cmFileLockResult.h

diff --git a/Source/cmFileLockResult.cxx b/Source/cmFileLockResult.cxx
new file mode 100644
index 0000000..045e7ee
--- /dev/null
+++ b/Source/cmFileLockResult.cxx
@@ -0,0 +1,111 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#include "cmFileLockResult.h"
+
+#include <errno.h>
+
+cmFileLockResult cmFileLockResult::MakeOk()
+{
+  return cmFileLockResult(OK, 0);
+}
+
+cmFileLockResult cmFileLockResult::MakeSystem()
+{
+#if defined(_WIN32)
+  const Error lastError = GetLastError();
+#else
+  const Error lastError = errno;
+#endif
+  return cmFileLockResult(SYSTEM, lastError);
+}
+
+cmFileLockResult cmFileLockResult::MakeTimeout()
+{
+  return cmFileLockResult(TIMEOUT, 0);
+}
+
+cmFileLockResult cmFileLockResult::MakeAlreadyLocked()
+{
+  return cmFileLockResult(ALREADY_LOCKED, 0);
+}
+
+cmFileLockResult cmFileLockResult::MakeInternal()
+{
+  return cmFileLockResult(INTERNAL, 0);
+}
+
+cmFileLockResult cmFileLockResult::MakeNoFunction()
+{
+  return cmFileLockResult(NO_FUNCTION, 0);
+}
+
+bool cmFileLockResult::IsOk() const
+{
+  return this->Type == OK;
+}
+
+std::string cmFileLockResult::GetOutputMessage() const
+{
+  switch (this->Type)
+    {
+    case OK:
+      return "0";
+    case SYSTEM:
+#if defined(_WIN32)
+      {
+      char* errorText = NULL;
+
+      // http://stackoverflow.com/a/455533/2288008
+      DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM |
+          FORMAT_MESSAGE_ALLOCATE_BUFFER |
+          FORMAT_MESSAGE_IGNORE_INSERTS;
+      ::FormatMessageA(
+          flags,
+          NULL,
+          this->ErrorValue,
+          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+          (LPSTR)&errorText,
+          0,
+          NULL
+      );
+
+      if (errorText != NULL)
+        {
+        const std::string message = errorText;
+        ::LocalFree(errorText);
+        return message;
+        }
+      else
+        {
+        return "Internal error (FormatMessageA failed)";
+        }
+      }
+#else
+      return strerror(this->ErrorValue);
+#endif
+    case TIMEOUT:
+      return "Timeout reached";
+    case ALREADY_LOCKED:
+      return "File already locked";
+    case NO_FUNCTION:
+      return "'GUARD FUNCTION' not used in function definition";
+    case INTERNAL:
+    default:
+      return "Internal error";
+    }
+}
+
+cmFileLockResult::cmFileLockResult(ErrorType typeValue, Error errorValue):
+    Type(typeValue), ErrorValue(errorValue)
+{
+}
diff --git a/Source/cmFileLockResult.h b/Source/cmFileLockResult.h
new file mode 100644
index 0000000..531fb49
--- /dev/null
+++ b/Source/cmFileLockResult.h
@@ -0,0 +1,85 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#ifndef cmFileLockResult_h
+#define cmFileLockResult_h
+
+#include "cmStandardIncludes.h"
+
+#if defined(_WIN32)
+# include <windows.h> // DWORD
+#endif
+
+/**
+  * @brief Result of the locking/unlocking file.
+  * @note See @c cmFileLock
+  */
+class cmFileLockResult
+{
+ public:
+#if defined(_WIN32)
+  typedef DWORD Error;
+#else
+  typedef int Error;
+#endif
+
+  /**
+    * @brief Successful lock/unlock.
+    */
+  static cmFileLockResult MakeOk();
+
+  /**
+    * @brief Lock/Unlock failed. Read error/GetLastError.
+    */
+  static cmFileLockResult MakeSystem();
+
+  /**
+    * @brief Lock/Unlock failed. Timeout reached.
+    */
+  static cmFileLockResult MakeTimeout();
+
+  /**
+    * @brief File already locked.
+    */
+  static cmFileLockResult MakeAlreadyLocked();
+
+  /**
+    * @brief Internal error.
+    */
+  static cmFileLockResult MakeInternal();
+
+  /**
+    * @brief Try to lock with function guard outside of the function
+    */
+  static cmFileLockResult MakeNoFunction();
+
+  bool IsOk() const;
+  std::string GetOutputMessage() const;
+
+ private:
+  enum ErrorType
+  {
+    OK,
+    SYSTEM,
+    TIMEOUT,
+    ALREADY_LOCKED,
+    INTERNAL,
+    NO_FUNCTION
+  };
+
+  cmFileLockResult(ErrorType type, Error errorValue);
+
+  ErrorType Type;
+  Error ErrorValue;
+};
+
+#endif // cmFileLockResult_h
-- 
2.1.1

>From 68b527c3e292a2e8ad63ec2356c3edf3b064742a Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:48:12 +0300
Subject: [PATCH 3/7] Add class cmFileLock

---
 Source/cmFileLock.cxx      |  77 +++++++++++++++++++++++++++
 Source/cmFileLock.h        |  74 ++++++++++++++++++++++++++
 Source/cmFileLockUnix.cxx  | 102 ++++++++++++++++++++++++++++++++++++
 Source/cmFileLockWin32.cxx | 126 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 379 insertions(+)
 create mode 100644 Source/cmFileLock.cxx
 create mode 100644 Source/cmFileLock.h
 create mode 100644 Source/cmFileLockUnix.cxx
 create mode 100644 Source/cmFileLockWin32.cxx

diff --git a/Source/cmFileLock.cxx b/Source/cmFileLock.cxx
new file mode 100644
index 0000000..f69c36a
--- /dev/null
+++ b/Source/cmFileLock.cxx
@@ -0,0 +1,77 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#include "cmFileLock.h"
+
+#include <assert.h>
+#include "cmFileLockResult.h"
+
+// Common implementation
+
+cmFileLock::~cmFileLock()
+{
+  if (!this->Filename.empty())
+    {
+    const cmFileLockResult result = this->Release();
+    assert(result.IsOk());
+    }
+}
+
+cmFileLockResult cmFileLock::Lock(
+    const std::string& filename, unsigned timeout)
+{
+  if (filename.empty())
+    {
+    // Error is internal since all the directories and file must be created
+    // before actual lock called.
+    return cmFileLockResult::MakeInternal();
+    }
+
+  if (!this->Filename.empty())
+    {
+    // Error is internal since double-lock must be checked in class
+    // cmFileLockPool by the cmFileLock::IsLocked method.
+    return cmFileLockResult::MakeInternal();
+    }
+
+  this->Filename = filename;
+  cmFileLockResult result = this->OpenFile();
+  if (result.IsOk())
+    {
+    if (timeout == static_cast<unsigned>(-1))
+      {
+      result = this->LockWithoutTimeout();
+      }
+    else
+      {
+      result = this->LockWithTimeout(timeout);
+      }
+    }
+
+  if (!result.IsOk())
+    {
+    this->Filename = "";
+    }
+
+  return result;
+}
+
+bool cmFileLock::IsLocked(const std::string& filename) const
+{
+  return filename == this->Filename;
+}
+
+#if defined(_WIN32)
+# include "cmFileLockWin32.cxx"
+#else
+# include "cmFileLockUnix.cxx"
+#endif
diff --git a/Source/cmFileLock.h b/Source/cmFileLock.h
new file mode 100644
index 0000000..7c2803b
--- /dev/null
+++ b/Source/cmFileLock.h
@@ -0,0 +1,74 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#ifndef cmFileLock_h
+#define cmFileLock_h
+
+#include "cmStandardIncludes.h"
+
+#if defined(_WIN32)
+# include <windows.h> // HANDLE
+#endif
+
+class cmFileLockResult;
+
+/**
+  * @brief Cross-platform file locking.
+  * @detail Under the hood this class use 'fcntl' for Unix-like platforms and
+  * 'LockFileEx'/'UnlockFileEx' for Win32 platform. Locks are exclusive and
+  * advisory.
+  */
+class cmFileLock
+{
+ public:
+  cmFileLock();
+  ~cmFileLock();
+
+  /**
+    * @brief Lock the file.
+    * @param timeoutSec Lock timeout. If -1 try until success or fatal error.
+    */
+  cmFileLockResult Lock(const std::string& filename, unsigned timeoutSec);
+
+  /**
+    * @brief Unlock the file.
+    */
+  cmFileLockResult Release();
+
+  /**
+    * @brief Check file is locked by this class.
+    * @detail This function helps to find double locks (deadlocks) and to do
+    * explicit unlocks.
+    */
+  bool IsLocked(const std::string& filename) const;
+
+ private:
+  cmFileLock(const cmFileLock&);
+  cmFileLock& operator=(const cmFileLock&);
+
+  cmFileLockResult OpenFile();
+  cmFileLockResult LockWithoutTimeout();
+  cmFileLockResult LockWithTimeout(unsigned timeoutSec);
+
+#if defined(_WIN32)
+  typedef HANDLE FileId;
+  BOOL LockFile(DWORD flags);
+#else
+  typedef int FileId;
+  int LockFile(int cmd, int type);
+#endif
+
+  FileId File;
+  std::string Filename;
+};
+
+#endif // cmFileLock_h
diff --git a/Source/cmFileLockUnix.cxx b/Source/cmFileLockUnix.cxx
new file mode 100644
index 0000000..3c01da8
--- /dev/null
+++ b/Source/cmFileLockUnix.cxx
@@ -0,0 +1,102 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#include "cmFileLock.h"
+
+#include <errno.h> // errno
+#include <stdio.h> // SEEK_SET
+#include <fcntl.h>
+#include "cmSystemTools.h"
+
+cmFileLock::cmFileLock(): File(-1)
+{
+}
+
+cmFileLockResult cmFileLock::Release()
+{
+  if (this->Filename.empty())
+    {
+    return cmFileLockResult::MakeOk();
+    }
+  const int lockResult = this->LockFile(F_SETLK, F_UNLCK);
+
+  this->Filename = "";
+
+  if (lockResult == 0)
+    {
+    return cmFileLockResult::MakeOk();
+    }
+  else
+    {
+    return cmFileLockResult::MakeSystem();
+    }
+}
+
+cmFileLockResult cmFileLock::OpenFile()
+{
+  this->File = ::open(this->Filename.c_str(), O_RDWR);
+  if (this->File == -1)
+    {
+    return cmFileLockResult::MakeSystem();
+    }
+  else
+    {
+    return cmFileLockResult::MakeOk();
+    }
+}
+
+cmFileLockResult cmFileLock::LockWithoutTimeout()
+{
+  if (this->LockFile(F_SETLKW, F_WRLCK) == -1)
+    {
+    return cmFileLockResult::MakeSystem();
+    }
+  else
+    {
+    return cmFileLockResult::MakeOk();
+    }
+}
+
+cmFileLockResult cmFileLock::LockWithTimeout(unsigned seconds)
+{
+  while (true)
+    {
+    if (this->LockFile(F_SETLK, F_WRLCK) == -1)
+      {
+      if (errno != EAGAIN)
+        {
+        return cmFileLockResult::MakeSystem();
+        }
+      }
+    else
+      {
+      return cmFileLockResult::MakeOk();
+      }
+    if (seconds == 0)
+      {
+      return cmFileLockResult::MakeTimeout();
+      }
+    --seconds;
+    cmSystemTools::Delay(1000);
+    }
+}
+
+int cmFileLock::LockFile(int cmd, int type)
+{
+  struct ::flock lock;
+  lock.l_start = 0;
+  lock.l_len = 0; // lock all bytes
+  lock.l_pid = 0; // unused (for F_GETLK only)
+  lock.l_type = type; // exclusive lock
+  lock.l_whence = SEEK_SET;
+  return ::fcntl(this->File, cmd, &lock);
+}
diff --git a/Source/cmFileLockWin32.cxx b/Source/cmFileLockWin32.cxx
new file mode 100644
index 0000000..17231ea
--- /dev/null
+++ b/Source/cmFileLockWin32.cxx
@@ -0,0 +1,126 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#include "cmFileLock.h"
+
+#include <windows.h> // CreateFileW
+#include "cmSystemTools.h"
+
+cmFileLock::cmFileLock(): File(INVALID_HANDLE_VALUE)
+{
+}
+
+cmFileLockResult cmFileLock::Release()
+{
+  if (this->Filename.empty())
+    {
+    return cmFileLockResult::MakeOk();
+    }
+  const unsigned long len = static_cast<unsigned long>(-1);
+  static OVERLAPPED overlapped;
+  const DWORD reserved = 0;
+  const BOOL unlockResult = UnlockFileEx(
+      File,
+      reserved,
+      len,
+      len,
+      &overlapped
+  );
+
+  this->Filename = "";
+
+  if (unlockResult)
+    {
+    return cmFileLockResult::MakeOk();
+    }
+  else
+    {
+    return cmFileLockResult::MakeSystem();
+    }
+}
+
+cmFileLockResult cmFileLock::OpenFile()
+{
+  const DWORD access = GENERIC_READ | GENERIC_WRITE;
+  const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+  const PSECURITY_ATTRIBUTES security = NULL;
+  const DWORD attr = 0;
+  const HANDLE templ = NULL;
+  this->File = CreateFileW(
+      cmSystemTools::ConvertToWindowsExtendedPath(this->Filename).c_str(),
+      access,
+      shareMode,
+      security,
+      OPEN_EXISTING,
+      attr,
+      templ
+  );
+  if (this->File == INVALID_HANDLE_VALUE)
+    {
+    return cmFileLockResult::MakeSystem();
+    }
+  else
+    {
+    return cmFileLockResult::MakeOk();
+    }
+}
+
+cmFileLockResult cmFileLock::LockWithoutTimeout()
+{
+  if (!this->LockFile(LOCKFILE_EXCLUSIVE_LOCK))
+    {
+    return cmFileLockResult::MakeSystem();
+    }
+  else
+    {
+    return cmFileLockResult::MakeOk();
+    }
+}
+
+cmFileLockResult cmFileLock::LockWithTimeout(unsigned seconds)
+{
+  const DWORD flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY;
+  while (true)
+    {
+    const BOOL result = this->LockFile(flags);
+    if (result)
+      {
+      return cmFileLockResult::MakeOk();
+      }
+    const DWORD error = GetLastError();
+    if (error != ERROR_LOCK_VIOLATION)
+      {
+      return cmFileLockResult::MakeSystem();
+      }
+    if (seconds == 0)
+      {
+      return cmFileLockResult::MakeTimeout();
+      }
+    --seconds;
+    cmSystemTools::Delay(1000);
+    }
+}
+
+BOOL cmFileLock::LockFile(DWORD flags)
+{
+  const DWORD reserved = 0;
+  const unsigned long len = static_cast<unsigned long>(-1);
+  static OVERLAPPED overlapped;
+  return LockFileEx(
+      this->File,
+      flags,
+      reserved,
+      len,
+      len,
+      &overlapped
+  );
+}
-- 
2.1.1

>From 7a82d57372450fdf8de5b8f339596a60618ec635 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:48:37 +0300
Subject: [PATCH 4/7] Add class cmFileLockPool

---
 Source/cmFileLockPool.cxx | 198 ++++++++++++++++++++++++++++++++++++++++++++++
 Source/cmFileLockPool.h   | 100 +++++++++++++++++++++++
 2 files changed, 298 insertions(+)
 create mode 100644 Source/cmFileLockPool.cxx
 create mode 100644 Source/cmFileLockPool.h

diff --git a/Source/cmFileLockPool.cxx b/Source/cmFileLockPool.cxx
new file mode 100644
index 0000000..e84e71a
--- /dev/null
+++ b/Source/cmFileLockPool.cxx
@@ -0,0 +1,198 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+
+#include "cmFileLockPool.h"
+
+#include <assert.h>
+
+#include "cmFileLock.h"
+#include "cmFileLockResult.h"
+
+cmFileLockPool::cmFileLockPool()
+{
+}
+
+cmFileLockPool::~cmFileLockPool()
+{
+  for (It i = this->FunctionScopes.begin();
+      i != this->FunctionScopes.end(); ++i)
+    {
+    delete *i;
+    }
+
+  for (It i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
+    {
+    delete *i;
+    }
+}
+
+void cmFileLockPool::PushFunctionScope()
+{
+  this->FunctionScopes.push_back(new ScopePool());
+}
+
+void cmFileLockPool::PopFunctionScope()
+{
+  assert(!this->FunctionScopes.empty());
+  delete this->FunctionScopes.back();
+  this->FunctionScopes.pop_back();
+}
+
+void cmFileLockPool::PushFileScope()
+{
+  this->FileScopes.push_back(new ScopePool());
+}
+
+void cmFileLockPool::PopFileScope()
+{
+  assert(!this->FileScopes.empty());
+  delete this->FileScopes.back();
+  this->FileScopes.pop_back();
+}
+
+cmFileLockResult cmFileLockPool::LockFunctionScope(
+    const std::string& filename, unsigned timeoutSec)
+{
+  if (this->IsAlreadyLocked(filename))
+    {
+    return cmFileLockResult::MakeAlreadyLocked();
+    }
+  if (this->FunctionScopes.empty())
+    {
+    return cmFileLockResult::MakeNoFunction();
+    }
+  return this->FunctionScopes.back()->Lock(filename, timeoutSec);
+}
+
+cmFileLockResult cmFileLockPool::LockFileScope(
+    const std::string& filename, unsigned timeoutSec)
+{
+  if (this->IsAlreadyLocked(filename))
+    {
+    return cmFileLockResult::MakeAlreadyLocked();
+    }
+  assert(!this->FileScopes.empty());
+  return this->FileScopes.back()->Lock(filename, timeoutSec);
+}
+
+cmFileLockResult cmFileLockPool::LockProcessScope(
+    const std::string& filename, unsigned timeoutSec)
+{
+  if (this->IsAlreadyLocked(filename))
+    {
+    return cmFileLockResult::MakeAlreadyLocked();
+    }
+  return this->ProcessScope.Lock(filename, timeoutSec);
+}
+
+cmFileLockResult cmFileLockPool::Release(const std::string& filename)
+{
+  for (It i = this->FunctionScopes.begin();
+      i != this->FunctionScopes.end(); ++i)
+    {
+    const cmFileLockResult result = (*i)->Release(filename);
+    if (!result.IsOk())
+      {
+      return result;
+      }
+    }
+
+  for (It i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
+    {
+    const cmFileLockResult result = (*i)->Release(filename);
+    if (!result.IsOk())
+      {
+      return result;
+      }
+    }
+
+  return this->ProcessScope.Release(filename);
+}
+
+bool cmFileLockPool::IsAlreadyLocked(const std::string& filename) const
+{
+  for (CIt i = this->FunctionScopes.begin();
+      i != this->FunctionScopes.end(); ++i)
+    {
+    const bool result = (*i)->IsAlreadyLocked(filename);
+    if (result)
+      {
+      return true;
+      }
+    }
+
+  for (CIt i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
+    {
+    const bool result = (*i)->IsAlreadyLocked(filename);
+    if (result)
+      {
+      return true;
+      }
+    }
+
+  return this->ProcessScope.IsAlreadyLocked(filename);
+}
+
+cmFileLockPool::ScopePool::ScopePool()
+{
+}
+
+cmFileLockPool::ScopePool::~ScopePool()
+{
+  for (It i = this->Locks.begin(); i != this->Locks.end(); ++i)
+    {
+    delete *i;
+    }
+}
+
+cmFileLockResult cmFileLockPool::ScopePool::Lock(
+    const std::string& filename, unsigned timeoutSec)
+{
+  cmFileLock *lock = new cmFileLock();
+  const cmFileLockResult result = lock->Lock(filename, timeoutSec);
+  if (result.IsOk())
+    {
+    this->Locks.push_back(lock);
+    return cmFileLockResult::MakeOk();
+    }
+  else
+    {
+    delete lock;
+    return result;
+    }
+}
+
+cmFileLockResult cmFileLockPool::ScopePool::Release(
+    const std::string& filename)
+{
+  for (It i = this->Locks.begin(); i != this->Locks.end(); ++i)
+    {
+    if ((*i)->IsLocked(filename))
+      {
+      return (*i)->Release();
+      }
+    }
+  return cmFileLockResult::MakeOk();
+}
+
+bool cmFileLockPool::ScopePool::IsAlreadyLocked(
+    const std::string& filename) const
+{
+  for (CIt i = this->Locks.begin(); i != this->Locks.end(); ++i)
+    {
+    if ((*i)->IsLocked(filename))
+      {
+      return true;
+      }
+    }
+  return false;
+}
diff --git a/Source/cmFileLockPool.h b/Source/cmFileLockPool.h
new file mode 100644
index 0000000..a63540c
--- /dev/null
+++ b/Source/cmFileLockPool.h
@@ -0,0 +1,100 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2014 Ruslan Baratov
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+#ifndef cmFileLockPool_h
+#define cmFileLockPool_h
+
+#include "cmStandardIncludes.h"
+
+class cmFileLockResult;
+class cmFileLock;
+
+class cmFileLockPool
+{
+ public:
+  cmFileLockPool();
+  ~cmFileLockPool();
+
+  //@{
+  /**
+    * @brief Function scope control.
+    */
+  void PushFunctionScope();
+  void PopFunctionScope();
+  //@}
+
+  //@{
+  /**
+    * @brief File scope control.
+    */
+  void PushFileScope();
+  void PopFileScope();
+  //@}
+
+  //@{
+  /**
+    * @brief Lock the file in given scope.
+    * @param timeoutSec Lock timeout. If -1 try until success or fatal error.
+    */
+  cmFileLockResult LockFunctionScope(
+      const std::string& filename, unsigned timeoutSec
+  );
+  cmFileLockResult LockFileScope(
+      const std::string& filename, unsigned timeoutSec
+  );
+  cmFileLockResult LockProcessScope(
+      const std::string& filename, unsigned timeoutSec
+  );
+  //@}
+
+  /**
+    * @brief Unlock the file explicitly.
+    */
+  cmFileLockResult Release(const std::string& filename);
+
+ private:
+  cmFileLockPool(const cmFileLockPool&);
+  cmFileLockPool& operator=(const cmFileLockPool&);
+
+  bool IsAlreadyLocked(const std::string& filename) const;
+
+  class ScopePool
+  {
+   public:
+    ScopePool();
+    ~ScopePool();
+
+    cmFileLockResult Lock(const std::string& filename, unsigned timeoutSec);
+    cmFileLockResult Release(const std::string& filename);
+    bool IsAlreadyLocked(const std::string& filename) const;
+
+   private:
+    ScopePool(const ScopePool&);
+    ScopePool& operator=(const ScopePool&);
+
+    typedef std::list<cmFileLock*> List;
+    typedef List::iterator It;
+    typedef List::const_iterator CIt;
+
+    List Locks;
+  };
+
+  typedef std::list<ScopePool*> List;
+
+  typedef List::iterator It;
+  typedef List::const_iterator CIt;
+
+  List FunctionScopes;
+  List FileScopes;
+  ScopePool ProcessScope;
+};
+
+#endif // cmFileLockPool_h
-- 
2.1.1

>From 38e149c68a538dfeb1dbe6760fa4bcd351685cf0 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:49:25 +0300
Subject: [PATCH 5/7] New command 'file(LOCK ...)'

---
 Source/CMakeLists.txt       |   6 ++
 Source/cmFileCommand.cxx    | 203 ++++++++++++++++++++++++++++++++++++++++++++
 Source/cmFileCommand.h      |   1 +
 Source/cmGlobalGenerator.h  |  10 +++
 Source/cmLocalGenerator.cxx |   6 ++
 Source/cmMakefile.cxx       |   8 ++
 6 files changed, 234 insertions(+)

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 7705683..a4c982f 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -219,6 +219,12 @@ set(SRCS
   cmExtraKateGenerator.h
   cmExtraSublimeTextGenerator.cxx
   cmExtraSublimeTextGenerator.h
+  cmFileLock.cxx
+  cmFileLock.h
+  cmFileLockPool.cxx
+  cmFileLockPool.h
+  cmFileLockResult.cxx
+  cmFileLockResult.h
   cmFileTimeComparison.cxx
   cmFileTimeComparison.h
   cmGeneratedFileStream.cxx
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index aa4c95b..a6eb8c4 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -21,6 +21,7 @@
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #include "cm_curl.h"
+#include "cmFileLockResult.h"
 #endif
 
 #undef GetCurrentDirectory
@@ -202,6 +203,10 @@ bool cmFileCommand
     {
     return this->HandleGenerateCommand(args);
     }
+  else if ( subCommand == "LOCK" )
+    {
+    return this->HandleLockCommand(args);
+    }
 
   std::string e = "does not recognize sub-command "+subCommand;
   this->SetError(e);
@@ -3502,6 +3507,204 @@ bool cmFileCommand::HandleGenerateCommand(
 }
 
 //----------------------------------------------------------------------------
+bool cmFileCommand::HandleLockCommand(
+  std::vector<std::string> const& args)
+{
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  // Default values
+  bool directory = false;
+  bool release = false;
+  enum Guard {
+    GUARD_FUNCTION,
+    GUARD_FILE,
+    GUARD_PROCESS
+  };
+  Guard guard = GUARD_PROCESS;
+  std::string resultVariable;
+  unsigned timeout = static_cast<unsigned>(-1);
+
+  // Parse arguments
+  if(args.size() < 2)
+    {
+    this->Makefile->IssueMessage(
+        cmake::FATAL_ERROR,
+        "sub-command LOCK requires at least two arguments.");
+    return false;
+    }
+
+  std::string path = args[1];
+  for (unsigned i = 2; i < args.size(); ++i)
+    {
+    if (args[i] == "DIRECTORY")
+      {
+      directory = true;
+      }
+    else if (args[i] == "RELEASE")
+      {
+      release = true;
+      }
+    else if (args[i] == "GUARD")
+      {
+      ++i;
+      const char* merr = "expected FUNCTION, FILE or PROCESS after GUARD";
+      if (i >= args.size())
+        {
+        this->Makefile->IssueMessage(cmake::FATAL_ERROR, merr);
+        return false;
+        }
+      else
+        {
+        if (args[i] == "FUNCTION")
+          {
+          guard = GUARD_FUNCTION;
+          }
+        else if (args[i] == "FILE")
+          {
+          guard = GUARD_FILE;
+          }
+        else if (args[i] == "PROCESS")
+          {
+          guard = GUARD_PROCESS;
+          }
+        else
+          {
+          cmOStringStream e;
+          e << merr << ", but got:\n  \"" << args[i] << "\".";
+          this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+          return false;
+          }
+        }
+      }
+    else if (args[i] == "RESULT_VARIABLE")
+      {
+      ++i;
+      if (i >= args.size())
+        {
+        this->Makefile->IssueMessage(
+            cmake::FATAL_ERROR,
+            "expected variable name after RESULT_VARIABLE");
+        return false;
+        }
+      resultVariable = args[i];
+      }
+    else if (args[i] == "TIMEOUT")
+      {
+      ++i;
+      if (i >= args.size())
+        {
+        this->Makefile->IssueMessage(
+            cmake::FATAL_ERROR,
+            "expected timeout value after TIMEOUT");
+        return false;
+        }
+      int scanned;
+      if(!cmSystemTools::StringToInt(args[i].c_str(), &scanned) || scanned < 0)
+        {
+        cmOStringStream e;
+        e << "TIMEOUT value \"" << args[i] << "\" is not an unsigned integer.";
+        this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+        return false;
+        }
+      timeout = static_cast<unsigned>(scanned);
+      }
+    else
+      {
+      cmOStringStream e;
+      e << "expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or TIMEOUT\n";
+      e << "but got: \"" << args[i] << "\".";
+      this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+      return false;
+      }
+    }
+
+  if (directory)
+    {
+    path += "/cmake.lock";
+    }
+
+  if (!cmsys::SystemTools::FileIsFullPath(path))
+    {
+    path = this->Makefile->GetCurrentDirectory() + ("/" + path);
+    }
+
+  // Unify path (remove '//', '/../', ...)
+  path = cmSystemTools::CollapseFullPath(path);
+
+  // Create file and directories if needed
+  std::string parentDir = cmSystemTools::GetParentDirectory(path);
+  if (!cmSystemTools::MakeDirectory(parentDir))
+    {
+    cmOStringStream e;
+    e << "directory\n  \"" << parentDir << "\"\ncreation failed ";
+    e << "(check permissions).";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+    }
+  FILE *file = cmsys::SystemTools::Fopen(path, "w");
+  if (!file)
+    {
+    cmOStringStream e;
+    e << "file\n  \"" << path << "\"\ncreation failed (check permissions).";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+    }
+  fclose(file);
+
+  // Actual lock/unlock
+  cmFileLockPool& lockPool = this->Makefile->GetLocalGenerator()->
+      GetGlobalGenerator()->GetFileLockPool();
+
+  cmFileLockResult fileLockResult(cmFileLockResult::MakeOk());
+  if (release)
+    {
+    fileLockResult = lockPool.Release(path);
+    }
+  else
+    {
+    switch (guard)
+      {
+      case GUARD_FUNCTION:
+        fileLockResult = lockPool.LockFunctionScope(path, timeout);
+        break;
+      case GUARD_FILE:
+        fileLockResult = lockPool.LockFileScope(path, timeout);
+        break;
+      case GUARD_PROCESS:
+        fileLockResult = lockPool.LockProcessScope(path, timeout);
+        break;
+      default:
+        cmSystemTools::SetFatalErrorOccured();
+        return false;
+      }
+    }
+
+  const std::string result = fileLockResult.GetOutputMessage();
+
+  if (resultVariable.empty() && !fileLockResult.IsOk())
+    {
+    cmOStringStream e;
+    e << "error locking file\n  \"" << path << "\"\n" << result << ".";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+    }
+
+  if (!resultVariable.empty())
+    {
+    this->Makefile->AddDefinition(resultVariable, result.c_str());
+    }
+
+  return true;
+#else
+  static_cast<void>(args);
+  this->SetError("sub-command LOCK not implemented in bootstrap cmake");
+  return false;
+#endif
+}
+
+//----------------------------------------------------------------------------
 bool cmFileCommand::HandleTimestampCommand(
   std::vector<std::string> const& args)
 {
diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h
index 8d66fdf..a4d341f 100644
--- a/Source/cmFileCommand.h
+++ b/Source/cmFileCommand.h
@@ -75,6 +75,7 @@ protected:
 
   bool HandleTimestampCommand(std::vector<std::string> const& args);
   bool HandleGenerateCommand(std::vector<std::string> const& args);
+  bool HandleLockCommand(std::vector<std::string> const& args);
 
 private:
   void AddEvaluationFile(const std::string &inputName,
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 926efe7..6b75298 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -23,6 +23,7 @@
 #include "cmGeneratorExpression.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
+# include "cmFileLockPool.h"
 # include <cmsys/hash_map.hxx>
 #endif
 
@@ -348,6 +349,10 @@ public:
   std::set<cmTarget const*> const&
   GetFilenameTargetDepends(cmSourceFile* sf) const;
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  cmFileLockPool& GetFileLockPool() { return FileLockPool; }
+#endif
+
 protected:
   virtual void Generate();
 
@@ -499,6 +504,11 @@ private:
 
   mutable std::map<cmSourceFile*, std::set<cmTarget const*> >
   FilenameTargetDepends;
+
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  // Pool of file locks
+  cmFileLockPool FileLockPool;
+#endif
 };
 
 #endif
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 3fb1e1e..2de6c93 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -79,9 +79,15 @@ public:
     this->GG = lg->GetGlobalGenerator();
     this->LG = this->GG->GetCurrentLocalGenerator();
     this->GG->SetCurrentLocalGenerator(lg);
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+    this->GG->GetFileLockPool().PushFileScope();
+#endif
     }
   ~cmLocalGeneratorCurrent()
     {
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+    this->GG->GetFileLockPool().PopFileScope();
+#endif
     this->GG->SetCurrentLocalGenerator(this->LG);
     }
 };
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 61807b2..a72d410 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -4487,6 +4487,10 @@ void cmMakefile::PushScope()
   this->Internal->VarStack.push(cmDefinitions(parent));
   this->Internal->VarInitStack.push(init);
   this->Internal->VarUsageStack.push(usage);
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  this->GetLocalGenerator()->GetGlobalGenerator()->
+    GetFileLockPool().PushFunctionScope();
+#endif
 }
 
 void cmMakefile::PopScope()
@@ -4524,6 +4528,10 @@ void cmMakefile::PopScope()
     {
     this->Internal->VarUsageStack.top().insert(*it);
     }
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  this->GetLocalGenerator()->GetGlobalGenerator()->
+    GetFileLockPool().PopFunctionScope();
+#endif
 }
 
 void cmMakefile::RaiseScope(const std::string& var, const char *varDef)
-- 
2.1.1

>From 46183c9a6f2eb4bdc2ca9fe8caf646eb62ac91b7 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:50:01 +0300
Subject: [PATCH 6/7] Update documentation of command file (LOCK option)

---
 Help/command/file.rst | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/Help/command/file.rst b/Help/command/file.rst
index dbc4149..600464e 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -305,3 +305,33 @@ status messages (subject to the 
:variable:`CMAKE_INSTALL_MESSAGE` variable),
 and ``NO_SOURCE_PERMISSIONS`` is default.
 Installation scripts generated by the :command:`install` command
 use this signature (with some undocumented options for internal use).
+
+------------------------------------------------------------------------------
+
+::
+
+  file(LOCK <path> [DIRECTORY] [RELEASE]
+       [GUARD <FUNCTION|FILE|PROCESS>]
+       [RESULT_VARIABLE <variable>]
+       [TIMEOUT <seconds>])
+
+Lock a file specified by ``<path>`` if no ``DIRECTORY`` option present and file
+``<path>/cmake.lock`` otherwise. File will be locked for scope defined by
+``GUARD`` option (default value is ``PROCESS``). ``RELEASE`` option can be used
+to unlock file explicitly. If option ``TIMEOUT`` is not specified CMake will
+wait until lock succeed or until fatal error occurs. If ``TIMEOUT`` is set to
+``0`` lock will be tried once and result will be reported immediately. If
+``TIMEOUT`` is not ``0`` CMake will try to lock file for the period specified
+by ``<seconds>`` value. Any errors will be interpreted as fatal if there is no
+``RESULT_VARIABLE`` option. Otherwise result will be stored in ``<variable>``
+and will be ``0`` on success or error message on failure.
+
+Note that lock is advisory - there is no guarantee that other processes will
+respect this lock, i.e. lock synchronize two or more CMake instances sharing
+some modifiable resources. Similar logic applied to ``DIRECTORY`` option -
+locking parent directory doesn't prevent other ``LOCK`` commands to lock any
+child directory or file.
+
+Trying to lock file twice is not allowed.  Any intermediate directories and
+file itself will be created if they not exist.  ``GUARD`` and ``TIMEOUT``
+options ignored on ``RELEASE`` operation.
-- 
2.1.1

>From be707b2971e96b8597e1bc742dfbfeb0a09b491a Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Wed, 26 Nov 2014 01:51:29 +0300
Subject: [PATCH 7/7] Add tests for command 'file(LOCK ...)'

---
 .../file/LOCK-error-file-create-fail-result.txt    |  1 +
 .../file/LOCK-error-file-create-fail-stderr.txt    |  8 +++++
 .../file/LOCK-error-file-create-fail.cmake         |  3 ++
 .../file/LOCK-error-guard-incorrect-result.txt     |  1 +
 .../file/LOCK-error-guard-incorrect-stderr.txt     |  6 ++++
 .../RunCMake/file/LOCK-error-guard-incorrect.cmake |  1 +
 .../file/LOCK-error-incorrect-timeout-result.txt   |  1 +
 .../file/LOCK-error-incorrect-timeout-stderr.txt   |  4 +++
 .../LOCK-error-incorrect-timeout-trail-result.txt  |  1 +
 .../LOCK-error-incorrect-timeout-trail-stderr.txt  |  4 +++
 .../file/LOCK-error-incorrect-timeout-trail.cmake  |  1 +
 .../file/LOCK-error-incorrect-timeout.cmake        |  1 +
 .../RunCMake/file/LOCK-error-lock-fail-result.txt  |  1 +
 .../RunCMake/file/LOCK-error-lock-fail-stderr.txt  |  6 ++++
 Tests/RunCMake/file/LOCK-error-lock-fail.cmake     |  6 ++++
 .../file/LOCK-error-negative-timeout-result.txt    |  1 +
 .../file/LOCK-error-negative-timeout-stderr.txt    |  4 +++
 .../file/LOCK-error-negative-timeout.cmake         |  1 +
 .../file/LOCK-error-no-function-result.txt         |  1 +
 .../file/LOCK-error-no-function-stderr.txt         |  8 +++++
 Tests/RunCMake/file/LOCK-error-no-function.cmake   |  1 +
 Tests/RunCMake/file/LOCK-error-no-guard-result.txt |  1 +
 Tests/RunCMake/file/LOCK-error-no-guard-stderr.txt |  4 +++
 Tests/RunCMake/file/LOCK-error-no-guard.cmake      |  1 +
 Tests/RunCMake/file/LOCK-error-no-path-result.txt  |  1 +
 Tests/RunCMake/file/LOCK-error-no-path-stderr.txt  |  4 +++
 Tests/RunCMake/file/LOCK-error-no-path.cmake       |  1 +
 .../file/LOCK-error-no-result-variable-result.txt  |  1 +
 .../file/LOCK-error-no-result-variable-stderr.txt  |  4 +++
 .../file/LOCK-error-no-result-variable.cmake       |  1 +
 .../RunCMake/file/LOCK-error-no-timeout-result.txt |  1 +
 .../RunCMake/file/LOCK-error-no-timeout-stderr.txt |  4 +++
 Tests/RunCMake/file/LOCK-error-no-timeout.cmake    |  1 +
 Tests/RunCMake/file/LOCK-error-timeout-result.txt  |  1 +
 Tests/RunCMake/file/LOCK-error-timeout-stderr.txt  | 14 ++++++++
 Tests/RunCMake/file/LOCK-error-timeout-stdout.txt  |  1 +
 Tests/RunCMake/file/LOCK-error-timeout.cmake       | 17 +++++++++
 .../file/LOCK-error-unknown-option-result.txt      |  1 +
 .../file/LOCK-error-unknown-option-stderr.txt      |  6 ++++
 .../RunCMake/file/LOCK-error-unknown-option.cmake  |  1 +
 Tests/RunCMake/file/LOCK-stdout.txt                | 11 ++++++
 Tests/RunCMake/file/LOCK.cmake                     | 40 ++++++++++++++++++++++
 Tests/RunCMake/file/RunCMakeTest.cmake             | 14 ++++++++
 .../file/subdir_test_unlock/CMakeLists.txt         |  2 ++
 Tests/RunCMake/file/timeout-script.cmake           |  5 +++
 45 files changed, 198 insertions(+)
 create mode 100644 Tests/RunCMake/file/LOCK-error-file-create-fail-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-file-create-fail-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-file-create-fail.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-guard-incorrect-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-guard-incorrect-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-guard-incorrect.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-incorrect-timeout-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-incorrect-timeout-stderr.txt
 create mode 100644 
Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-result.txt
 create mode 100644 
Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-incorrect-timeout.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-lock-fail-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-lock-fail-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-lock-fail.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-negative-timeout-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-negative-timeout-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-negative-timeout.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-function-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-function-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-function.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-guard-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-guard-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-guard.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-path-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-path-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-path.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-result-variable-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-result-variable-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-result-variable.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-timeout-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-timeout-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-no-timeout.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-timeout-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-timeout-stderr.txt
 create mode 100755 Tests/RunCMake/file/LOCK-error-timeout-stdout.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-timeout.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-error-unknown-option-result.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-unknown-option-stderr.txt
 create mode 100644 Tests/RunCMake/file/LOCK-error-unknown-option.cmake
 create mode 100644 Tests/RunCMake/file/LOCK-stdout.txt
 create mode 100644 Tests/RunCMake/file/LOCK.cmake
 create mode 100644 Tests/RunCMake/file/subdir_test_unlock/CMakeLists.txt
 create mode 100755 Tests/RunCMake/file/timeout-script.cmake

diff --git a/Tests/RunCMake/file/LOCK-error-file-create-fail-result.txt 
b/Tests/RunCMake/file/LOCK-error-file-create-fail-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-file-create-fail-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-file-create-fail-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-file-create-fail-stderr.txt
new file mode 100644
index 0000000..1eaa9e9
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-file-create-fail-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at LOCK-error-file-create-fail\.cmake:[0-9]+ \(file\):
+  file
+
+    \".*\"
+
+  creation failed \(check permissions\)\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-file-create-fail.cmake 
b/Tests/RunCMake/file/LOCK-error-file-create-fail.cmake
new file mode 100644
index 0000000..4868cfe
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-file-create-fail.cmake
@@ -0,0 +1,3 @@
+set(tmp "${CMAKE_CURRENT_BINARY_DIR}/temp-directory")
+file(MAKE_DIRECTORY "${tmp}")
+file(LOCK "${tmp}")
diff --git a/Tests/RunCMake/file/LOCK-error-guard-incorrect-result.txt 
b/Tests/RunCMake/file/LOCK-error-guard-incorrect-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-guard-incorrect-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-guard-incorrect-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-guard-incorrect-stderr.txt
new file mode 100644
index 0000000..f659ec9
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-guard-incorrect-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at LOCK-error-guard-incorrect\.cmake:[0-9]+ \(file\):
+  expected FUNCTION, FILE or PROCESS after GUARD, but got:
+
+    \"FUNCTIO\"\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-guard-incorrect.cmake 
b/Tests/RunCMake/file/LOCK-error-guard-incorrect.cmake
new file mode 100644
index 0000000..51daa7c
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-guard-incorrect.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" GUARD FUNCTIO)
diff --git a/Tests/RunCMake/file/LOCK-error-incorrect-timeout-result.txt 
b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-incorrect-timeout-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-stderr.txt
new file mode 100644
index 0000000..914ae0c
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-incorrect-timeout\.cmake:[0-9]+ \(file\):
+  TIMEOUT value \"qwerty\" is not an unsigned integer\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-result.txt 
b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-stderr.txt
new file mode 100644
index 0000000..6253c5d
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-incorrect-timeout-trail\.cmake:[0-9]+ \(file\):
+  TIMEOUT value \"123xyz\" is not an unsigned integer\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail.cmake 
b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail.cmake
new file mode 100644
index 0000000..c4f1b75
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-incorrect-timeout-trail.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT 123xyz)
diff --git a/Tests/RunCMake/file/LOCK-error-incorrect-timeout.cmake 
b/Tests/RunCMake/file/LOCK-error-incorrect-timeout.cmake
new file mode 100644
index 0000000..d882467
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-incorrect-timeout.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT qwerty)
diff --git a/Tests/RunCMake/file/LOCK-error-lock-fail-result.txt 
b/Tests/RunCMake/file/LOCK-error-lock-fail-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-lock-fail-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-lock-fail-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-lock-fail-stderr.txt
new file mode 100644
index 0000000..47008ad
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-lock-fail-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at LOCK-error-lock-fail.cmake:[0-9]+ \(file\):
+  directory
+
+    \".*\"
+
+  creation failed \(check permissions\)\.
diff --git a/Tests/RunCMake/file/LOCK-error-lock-fail.cmake 
b/Tests/RunCMake/file/LOCK-error-lock-fail.cmake
new file mode 100644
index 0000000..aa7f663
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-lock-fail.cmake
@@ -0,0 +1,6 @@
+set(lfile "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock")
+FILE(WRITE "${lfile}" "")
+
+# Try to lock file '${lfile}/cmake.lock'. Since `lfile` is not a directory
+# expected that operation will fail.
+file(LOCK "${lfile}" DIRECTORY)
diff --git a/Tests/RunCMake/file/LOCK-error-negative-timeout-result.txt 
b/Tests/RunCMake/file/LOCK-error-negative-timeout-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-negative-timeout-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-negative-timeout-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-negative-timeout-stderr.txt
new file mode 100644
index 0000000..6d984d3
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-negative-timeout-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-negative-timeout\.cmake:[0-9]+ \(file\):
+  TIMEOUT value \"-2\" is not an unsigned integer\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-negative-timeout.cmake 
b/Tests/RunCMake/file/LOCK-error-negative-timeout.cmake
new file mode 100644
index 0000000..6a0f190
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-negative-timeout.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT -2)
diff --git a/Tests/RunCMake/file/LOCK-error-no-function-result.txt 
b/Tests/RunCMake/file/LOCK-error-no-function-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-function-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-no-function-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-no-function-stderr.txt
new file mode 100644
index 0000000..f6ad76f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-function-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at LOCK-error-no-function\.cmake:[0-9]+ \(file\):
+  error locking file
+
+    \".*\"
+
+  \'GUARD FUNCTION\' not used in function definition\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-no-function.cmake 
b/Tests/RunCMake/file/LOCK-error-no-function.cmake
new file mode 100644
index 0000000..1b8b06a
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-function.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" GUARD FUNCTION)
diff --git a/Tests/RunCMake/file/LOCK-error-no-guard-result.txt 
b/Tests/RunCMake/file/LOCK-error-no-guard-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-guard-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-no-guard-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-no-guard-stderr.txt
new file mode 100644
index 0000000..41177ce
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-guard-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-no-guard\.cmake:[0-9]+ \(file\):
+  expected FUNCTION, FILE or PROCESS after GUARD
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-no-guard.cmake 
b/Tests/RunCMake/file/LOCK-error-no-guard.cmake
new file mode 100644
index 0000000..48ffc5e
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-guard.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" GUARD)
diff --git a/Tests/RunCMake/file/LOCK-error-no-path-result.txt 
b/Tests/RunCMake/file/LOCK-error-no-path-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-path-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt
new file mode 100644
index 0000000..2247aa6
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-no-path.cmake:[0-9]+ \(file\):
+  file must be called with at least two arguments.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-no-path.cmake 
b/Tests/RunCMake/file/LOCK-error-no-path.cmake
new file mode 100644
index 0000000..12d79b7
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-path.cmake
@@ -0,0 +1 @@
+file(LOCK)
diff --git a/Tests/RunCMake/file/LOCK-error-no-result-variable-result.txt 
b/Tests/RunCMake/file/LOCK-error-no-result-variable-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-result-variable-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-no-result-variable-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-no-result-variable-stderr.txt
new file mode 100644
index 0000000..b38e23c
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-result-variable-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-no-result-variable\.cmake:[0-9]+ \(file\):
+  expected variable name after RESULT_VARIABLE
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-no-result-variable.cmake 
b/Tests/RunCMake/file/LOCK-error-no-result-variable.cmake
new file mode 100644
index 0000000..e6ac18d
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-result-variable.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" RESULT_VARIABLE)
diff --git a/Tests/RunCMake/file/LOCK-error-no-timeout-result.txt 
b/Tests/RunCMake/file/LOCK-error-no-timeout-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-timeout-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-no-timeout-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-no-timeout-stderr.txt
new file mode 100644
index 0000000..f34d46f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-timeout-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at LOCK-error-no-timeout\.cmake:[0-9]+ \(file\):
+  expected timeout value after TIMEOUT
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-no-timeout.cmake 
b/Tests/RunCMake/file/LOCK-error-no-timeout.cmake
new file mode 100644
index 0000000..1618192
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-no-timeout.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT)
diff --git a/Tests/RunCMake/file/LOCK-error-timeout-result.txt 
b/Tests/RunCMake/file/LOCK-error-timeout-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-timeout-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-timeout-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-timeout-stderr.txt
new file mode 100644
index 0000000..7407b6c
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-timeout-stderr.txt
@@ -0,0 +1,14 @@
+Output:[ ]*
+Error: CMake Error at .*.timeout-script\.cmake:[0-9]+ \(file\):
+  error locking file
+
+    \".*\"
+
+  Timeout reached\.
+
+
+
+CMake Error at LOCK-error-timeout\.cmake:[0-9]+ \(message\):
+  Result: 1
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-timeout-stdout.txt 
b/Tests/RunCMake/file/LOCK-error-timeout-stdout.txt
new file mode 100755
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-timeout-stdout.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/file/LOCK-error-timeout.cmake 
b/Tests/RunCMake/file/LOCK-error-timeout.cmake
new file mode 100644
index 0000000..b6b9476
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-timeout.cmake
@@ -0,0 +1,17 @@
+set(script "${CMAKE_CURRENT_LIST_DIR}/timeout-script.cmake")
+set(file_to_lock "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock")
+
+file(LOCK "${file_to_lock}")
+execute_process(
+    COMMAND "${CMAKE_COMMAND}" "-Dfile_to_lock=${file_to_lock}" -P "${script}"
+    RESULT_VARIABLE result
+    OUTPUT_VARIABLE output
+    ERROR_VARIABLE error
+)
+
+message("Output: ${output}")
+message("Error: ${error}")
+
+if(NOT result EQUAL 0)
+  message(FATAL_ERROR "Result: ${result}")
+endif()
diff --git a/Tests/RunCMake/file/LOCK-error-unknown-option-result.txt 
b/Tests/RunCMake/file/LOCK-error-unknown-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-unknown-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/LOCK-error-unknown-option-stderr.txt 
b/Tests/RunCMake/file/LOCK-error-unknown-option-stderr.txt
new file mode 100644
index 0000000..d126d39
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-unknown-option-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at LOCK-error-unknown-option\.cmake:[0-9]+ \(file\):
+  expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or TIMEOUT
+
+  but got: \"UNKNOWN\"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]* \(include\)
diff --git a/Tests/RunCMake/file/LOCK-error-unknown-option.cmake 
b/Tests/RunCMake/file/LOCK-error-unknown-option.cmake
new file mode 100644
index 0000000..88ef002
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-error-unknown-option.cmake
@@ -0,0 +1 @@
+file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/temp-file" UNKNOWN)
diff --git a/Tests/RunCMake/file/LOCK-stdout.txt 
b/Tests/RunCMake/file/LOCK-stdout.txt
new file mode 100644
index 0000000..416126a
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK-stdout.txt
@@ -0,0 +1,11 @@
+-- Simple lock
+-- Directory lock
+-- Release
+-- Lock function scope
+-- Lock file scope
+-- Lock in subdirectory
+-- Lock process scope
+-- Error double lock
+-- Ok
+-- Timeout 0
+-- Timeout not 0
diff --git a/Tests/RunCMake/file/LOCK.cmake b/Tests/RunCMake/file/LOCK.cmake
new file mode 100644
index 0000000..8eff084
--- /dev/null
+++ b/Tests/RunCMake/file/LOCK.cmake
@@ -0,0 +1,40 @@
+set(lfile "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock")
+set(ldir "${CMAKE_CURRENT_BINARY_DIR}/dir-to-lock")
+
+message(STATUS "Simple lock")
+file(LOCK ${lfile})
+
+message(STATUS "Directory lock")
+file(LOCK ${ldir} DIRECTORY)
+
+message(STATUS "Release")
+file(LOCK ${lfile} RELEASE)
+
+function(foo)
+  file(LOCK "${lfile}" GUARD FUNCTION)
+endfunction()
+
+message(STATUS "Lock function scope")
+foo()
+
+message(STATUS "Lock file scope")
+add_subdirectory(subdir_test_unlock)
+
+message(STATUS "Lock process scope")
+file(LOCK "${lfile}" GUARD PROCESS)
+
+message(STATUS "Error double lock")
+file(LOCK "${lfile}" RESULT_VARIABLE lock_result)
+if(lock_result STREQUAL "File already locked")
+  message(STATUS "Ok")
+else()
+  message(STATUS FATAL_ERROR "Expected error message")
+endif()
+
+message(STATUS "Timeout 0")
+file(LOCK "${lfile}" RELEASE)
+file(LOCK "${lfile}" TIMEOUT 0)
+
+message(STATUS "Timeout not 0")
+file(LOCK "${lfile}" RELEASE)
+file(LOCK "${lfile}" TIMEOUT 3)
diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake 
b/Tests/RunCMake/file/RunCMakeTest.cmake
index bf14263..14819e7 100644
--- a/Tests/RunCMake/file/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file/RunCMakeTest.cmake
@@ -3,3 +3,17 @@ include(RunCMake)
 run_cmake(INSTALL-DIRECTORY)
 run_cmake(INSTALL-MESSAGE-bad)
 run_cmake(FileOpenFailRead)
+run_cmake(LOCK)
+run_cmake(LOCK-error-file-create-fail)
+run_cmake(LOCK-error-guard-incorrect)
+run_cmake(LOCK-error-incorrect-timeout)
+run_cmake(LOCK-error-incorrect-timeout-trail)
+run_cmake(LOCK-error-lock-fail)
+run_cmake(LOCK-error-negative-timeout)
+run_cmake(LOCK-error-no-function)
+run_cmake(LOCK-error-no-guard)
+run_cmake(LOCK-error-no-path)
+run_cmake(LOCK-error-no-result-variable)
+run_cmake(LOCK-error-no-timeout)
+run_cmake(LOCK-error-timeout)
+run_cmake(LOCK-error-unknown-option)
diff --git a/Tests/RunCMake/file/subdir_test_unlock/CMakeLists.txt 
b/Tests/RunCMake/file/subdir_test_unlock/CMakeLists.txt
new file mode 100644
index 0000000..c167cd7
--- /dev/null
+++ b/Tests/RunCMake/file/subdir_test_unlock/CMakeLists.txt
@@ -0,0 +1,2 @@
+message(STATUS "Lock in subdirectory")
+file(LOCK "${lfile}" GUARD FILE)
diff --git a/Tests/RunCMake/file/timeout-script.cmake 
b/Tests/RunCMake/file/timeout-script.cmake
new file mode 100755
index 0000000..e07dbf0
--- /dev/null
+++ b/Tests/RunCMake/file/timeout-script.cmake
@@ -0,0 +1,5 @@
+if(NOT file_to_lock)
+  message(FATAL_ERROR "file_to_lock is empty")
+endif()
+
+file(LOCK "${file_to_lock}" TIMEOUT 1)
-- 
2.1.1

-- 

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more 
information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake-developers

Reply via email to