blob: 1a2780f26934b61ace1f078f65442697296a7c24 [file] [log] [blame]
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2021 The Meson development team
# Copyright © 2021-2023 Intel Corporation
"""Utility functions with platform specific implementations."""
from __future__ import annotations
import enum
import os
import sys
import typing as T
from .. import mlog
from .core import MesonException
__all__ = ['DirectoryLock', 'DirectoryLockAction']
class DirectoryLockAction(enum.Enum):
IGNORE = 0
WAIT = 1
FAIL = 2
class DirectoryLockBase:
lockfile: T.TextIO
def __init__(self, directory: str, lockfile: str, action: DirectoryLockAction, err: str,
optional: bool = False) -> None:
self.action = action
self.err = err
self.lockpath = os.path.join(directory, lockfile)
self.optional = optional
def __enter__(self) -> None:
mlog.debug('Calling the no-op version of DirectoryLock')
def __exit__(self, *args: T.Any) -> None:
pass
if sys.platform == 'win32':
import msvcrt
class DirectoryLock(DirectoryLockBase):
def __enter__(self) -> None:
try:
self.lockfile = open(self.lockpath, 'w+', encoding='utf-8')
except (FileNotFoundError, IsADirectoryError):
# For FileNotFoundError, there is nothing to lock.
# For IsADirectoryError, something is seriously wrong.
raise
except OSError:
if self.action == DirectoryLockAction.IGNORE or self.optional:
return
try:
mode = msvcrt.LK_LOCK
if self.action != DirectoryLockAction.WAIT:
mode = msvcrt.LK_NBLCK
msvcrt.locking(self.lockfile.fileno(), mode, 1)
except BlockingIOError:
self.lockfile.close()
if self.action == DirectoryLockAction.IGNORE:
return
raise MesonException(self.err)
except PermissionError:
self.lockfile.close()
raise MesonException(self.err)
def __exit__(self, *args: T.Any) -> None:
if self.lockfile is None or self.lockfile.closed:
return
msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
self.lockfile.close()
else:
import fcntl
class DirectoryLock(DirectoryLockBase):
def __enter__(self) -> None:
try:
self.lockfile = open(self.lockpath, 'w+', encoding='utf-8')
except (FileNotFoundError, IsADirectoryError):
# For FileNotFoundError, there is nothing to lock.
# For IsADirectoryError, something is seriously wrong.
raise
except OSError:
if self.action == DirectoryLockAction.IGNORE or self.optional:
return
try:
flags = fcntl.LOCK_EX
if self.action != DirectoryLockAction.WAIT:
flags = flags | fcntl.LOCK_NB
fcntl.flock(self.lockfile, flags)
except BlockingIOError:
self.lockfile.close()
if self.action == DirectoryLockAction.IGNORE:
return
raise MesonException(self.err)
except PermissionError:
self.lockfile.close()
raise MesonException(self.err)
except OSError as e:
self.lockfile.close()
raise MesonException(f'Failed to lock directory {self.lockpath}: {e.strerror}')
def __exit__(self, *args: T.Any) -> None:
if self.lockfile is None or self.lockfile.closed:
return
fcntl.flock(self.lockfile, fcntl.LOCK_UN)
self.lockfile.close()