# 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()