On 17-Nov-14 18:06, Brad King wrote:
Yes, please change to that so a proper error can be produced for the new API when "123xyz" is given as a timeout, for example.
Function added cmSystemTools::StringToInt
Please look at adding a case to the test suite for this. An outer process could take the lock and then execute_process a child that tries to take the lock with a timeout. Then check that it fails as expected.
Done Ruslo
>From 9a3e5728173d2b5e4381d3c65556534298f7bb27 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:32:27 +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 3247f7f..9664983 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -2925,3 +2925,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 9e7e463d51a8944c8951540114e0e34824a239f5 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:33:46 +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 0b2c39c736c5a9e4d7c9bf1cefda8e6682ee5961 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:34:07 +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 857e10281eba52362c58c66f5830836ede2ad9f8 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:34:21 +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 80297a173a3d7f734538d0c8bd6c9bc354c8ba73 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:35:32 +0300 Subject: [PATCH 5/7] New command 'file(LOCK ...)' --- Source/CMakeLists.txt | 6 ++ Source/cmFileCommand.cxx | 197 ++++++++++++++++++++++++++++++++++++++++++++ Source/cmFileCommand.h | 1 + Source/cmGlobalGenerator.h | 10 +++ Source/cmLocalGenerator.cxx | 6 ++ Source/cmMakefile.cxx | 8 ++ 6 files changed, 228 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 b0ddff4..231600a 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,198 @@ 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->SetError("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->SetError(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: \"" << args[i] << "\"."; + this->SetError(e.str()); + return false; + } + } + } + else if (args[i] == "RESULT_VARIABLE") + { + ++i; + if (i >= args.size()) + { + this->SetError(": expected variable name after RESULT_VARIABLE"); + return false; + } + resultVariable = args[i]; + } + else if (args[i] == "TIMEOUT") + { + ++i; + if (i >= args.size()) + { + this->SetError(": 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->SetError(e.str()); + return false; + } + timeout = static_cast<unsigned>(scanned); + } + else + { + cmOStringStream e; + e << ": expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or TIMEOUT"; + e << ", but got: \"" << args[i] << "\"."; + this->SetError(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 \"" << parentDir << "\" creation failed "; + e << "(check permissions)."; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + FILE *file = cmsys::SystemTools::Fopen(path, "w"); + if (!file) + { + cmOStringStream e; + e << ": file \"" << path << "\" creation failed (check permissions)."; + this->SetError(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 \"" << path << "\" (" << result << ")."; + this->SetError(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 ddd7e91..15fdcb8 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 @@ -341,6 +342,10 @@ public: bool GenerateCPackPropertiesFile(); +#if defined(CMAKE_BUILD_WITH_CMAKE) + cmFileLockPool& GetFileLockPool() { return FileLockPool; } +#endif + protected: virtual void Generate(); @@ -488,6 +493,11 @@ private: // track targets to issue CMP0042 warning for. std::set<std::string> CMP0042WarnTargets; + +#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 69b56c6..abafa0f 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 8a8aadc..5484421 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -4426,6 +4426,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() @@ -4463,6 +4467,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 5f296559e3ea87965e9842a463cadf77205f9859 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:36:02 +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 c026af8f576ece2cf3e95eeb5afd8422ca14013d Mon Sep 17 00:00:00 2001 From: Ruslan Baratov <ruslan_bara...@yahoo.com> Date: Tue, 18 Nov 2014 02:37:40 +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 | 6 ++++ .../file/LOCK-error-file-create-fail.cmake | 3 ++ .../file/LOCK-error-guard-incorrect-result.txt | 1 + .../file/LOCK-error-guard-incorrect-stderr.txt | 4 +++ .../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 | 4 +++ 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 | 6 ++++ 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 | 9 +++++ Tests/RunCMake/file/LOCK-error-timeout-stdout.txt | 1 + Tests/RunCMake/file/LOCK-error-timeout.cmake | 22 ++++++++++++ .../file/LOCK-error-unknown-option-result.txt | 1 + .../file/LOCK-error-unknown-option-stderr.txt | 5 +++ .../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 ++++++++ Tests/RunCMake/file/background.bat | 4 +++ Tests/RunCMake/file/background.sh | 4 +++ Tests/RunCMake/file/file-to-lock | 0 .../file/subdir_test_unlock/CMakeLists.txt | 2 ++ Tests/RunCMake/file/timeout-script.cmake | 2 ++ 48 files changed, 194 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 100755 Tests/RunCMake/file/background.bat create mode 100755 Tests/RunCMake/file/background.sh create mode 100644 Tests/RunCMake/file/file-to-lock 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..d0ce86f --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-file-create-fail-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at LOCK-error-file-create-fail\.cmake:[0-9]+ \(file\): + 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..eda0f35 --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-guard-incorrect-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at LOCK-error-guard-incorrect\.cmake:[0-9]+ \(file\): + 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..53c6747 --- /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\): + 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..b3b11b8 --- /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\): + 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..753b063 --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-lock-fail-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at LOCK-error-lock-fail.cmake:[0-9]+ \(file\): + 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..7e08d53 --- /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\): + 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..46ea08e --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-no-function-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at LOCK-error-no-function\.cmake:[0-9]+ \(file\): + 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..91a63e1 --- /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\): + 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..9edbfd7 --- /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\): + 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..35adf64 --- /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\): + 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..a813426 --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-timeout-stderr.txt @@ -0,0 +1,9 @@ +Output:[ ]* +Error: CMake Error at .*.timeout-script\.cmake:[0-9]+ \(file\): + 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..8b05b35 --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-timeout.cmake @@ -0,0 +1,22 @@ +if(WIN32) + set(ext "bat") +else() + set(ext "sh") +endif() + +set(background "${CMAKE_CURRENT_LIST_DIR}/background.${ext}") + +set(script "${CMAKE_CURRENT_LIST_DIR}/timeout-script.cmake") +execute_process( + COMMAND "${background}" "${CMAKE_COMMAND}" -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..ec9d3aa --- /dev/null +++ b/Tests/RunCMake/file/LOCK-error-unknown-option-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at LOCK-error-unknown-option\.cmake:[0-9]+ \(file\): + 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/background.bat b/Tests/RunCMake/file/background.bat new file mode 100755 index 0000000..15e676c --- /dev/null +++ b/Tests/RunCMake/file/background.bat @@ -0,0 +1,4 @@ +@echo OFF + +START /B %1 %2 %3 +%1 %2 %3 diff --git a/Tests/RunCMake/file/background.sh b/Tests/RunCMake/file/background.sh new file mode 100755 index 0000000..1b9f167 --- /dev/null +++ b/Tests/RunCMake/file/background.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +$@ & +$@ diff --git a/Tests/RunCMake/file/file-to-lock b/Tests/RunCMake/file/file-to-lock new file mode 100644 index 0000000..e69de29 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..87865db --- /dev/null +++ b/Tests/RunCMake/file/timeout-script.cmake @@ -0,0 +1,2 @@ +file(LOCK "${CMAKE_CURRENT_LIST_DIR}/file-to-lock" TIMEOUT 5) +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 10) -- 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