Files
rt-thread/tools/ng/utils.py

339 lines
9.1 KiB
Python

# -*- coding: utf-8 -*-
"""
Utility functions for RT-Thread build system.
This module provides common utility functions used throughout the build system.
"""
import os
import sys
import platform
from typing import List, Tuple, Optional
class PathService:
"""Service for path manipulation and normalization."""
def __init__(self, base_path: str = None):
self.base_path = base_path or os.getcwd()
def normalize_path(self, path: str) -> str:
"""
Normalize path for cross-platform compatibility.
Args:
path: Path to normalize
Returns:
Normalized path
"""
# Convert to absolute path if relative
if not os.path.isabs(path):
path = os.path.abspath(os.path.join(self.base_path, path))
# Normalize separators
path = os.path.normpath(path)
# Convert to forward slashes for consistency
if platform.system() == 'Windows':
path = path.replace('\\', '/')
return path
def make_relative(self, path: str, base: str = None) -> str:
"""
Make path relative to base.
Args:
path: Path to make relative
base: Base path (defaults to self.base_path)
Returns:
Relative path
"""
if base is None:
base = self.base_path
path = self.normalize_path(path)
base = self.normalize_path(base)
try:
rel_path = os.path.relpath(path, base)
# Convert to forward slashes
if platform.system() == 'Windows':
rel_path = rel_path.replace('\\', '/')
return rel_path
except ValueError:
# Different drives on Windows
return path
def split_path(self, path: str) -> List[str]:
"""
Split path into components.
Args:
path: Path to split
Returns:
List of path components
"""
path = self.normalize_path(path)
parts = []
while True:
head, tail = os.path.split(path)
if tail:
parts.insert(0, tail)
if head == path: # Reached root
if head:
parts.insert(0, head)
break
path = head
return parts
def common_prefix(self, paths: List[str]) -> str:
"""
Find common prefix of multiple paths.
Args:
paths: List of paths
Returns:
Common prefix path
"""
if not paths:
return ""
# Normalize all paths
normalized = [self.normalize_path(p) for p in paths]
# Find common prefix
prefix = os.path.commonpath(normalized)
return self.normalize_path(prefix)
class PlatformInfo:
"""Platform and system information."""
@staticmethod
def get_platform() -> str:
"""Get platform name (Windows, Linux, Darwin)."""
return platform.system()
@staticmethod
def get_architecture() -> str:
"""Get system architecture."""
return platform.machine()
@staticmethod
def is_windows() -> bool:
"""Check if running on Windows."""
return platform.system() == 'Windows'
@staticmethod
def is_linux() -> bool:
"""Check if running on Linux."""
return platform.system() == 'Linux'
@staticmethod
def is_macos() -> bool:
"""Check if running on macOS."""
return platform.system() == 'Darwin'
@staticmethod
def get_python_version() -> Tuple[int, int, int]:
"""Get Python version tuple."""
return sys.version_info[:3]
@staticmethod
def check_python_version(min_version: Tuple[int, int]) -> bool:
"""
Check if Python version meets minimum requirement.
Args:
min_version: Minimum version tuple (major, minor)
Returns:
True if version is sufficient
"""
current = sys.version_info[:2]
return current >= min_version
class FileUtils:
"""File operation utilities."""
@staticmethod
def read_file(filepath: str, encoding: str = 'utf-8') -> str:
"""
Read file content.
Args:
filepath: File path
encoding: File encoding
Returns:
File content
"""
with open(filepath, 'r', encoding=encoding) as f:
return f.read()
@staticmethod
def write_file(filepath: str, content: str, encoding: str = 'utf-8') -> None:
"""
Write content to file.
Args:
filepath: File path
content: Content to write
encoding: File encoding
"""
# Ensure directory exists
directory = os.path.dirname(filepath)
if directory:
os.makedirs(directory, exist_ok=True)
with open(filepath, 'w', encoding=encoding) as f:
f.write(content)
@staticmethod
def copy_file(src: str, dst: str) -> None:
"""
Copy file from src to dst.
Args:
src: Source file path
dst: Destination file path
"""
import shutil
# Ensure destination directory exists
dst_dir = os.path.dirname(dst)
if dst_dir:
os.makedirs(dst_dir, exist_ok=True)
shutil.copy2(src, dst)
@staticmethod
def find_files(directory: str, pattern: str, recursive: bool = True) -> List[str]:
"""
Find files matching pattern.
Args:
directory: Directory to search
pattern: File pattern (supports wildcards)
recursive: Search recursively
Returns:
List of matching file paths
"""
import fnmatch
matches = []
if recursive:
for root, dirnames, filenames in os.walk(directory):
for filename in filenames:
if fnmatch.fnmatch(filename, pattern):
matches.append(os.path.join(root, filename))
else:
try:
filenames = os.listdir(directory)
for filename in filenames:
if fnmatch.fnmatch(filename, pattern):
filepath = os.path.join(directory, filename)
if os.path.isfile(filepath):
matches.append(filepath)
except OSError:
pass
return sorted(matches)
class VersionUtils:
"""Version comparison utilities."""
@staticmethod
def parse_version(version_str: str) -> Tuple[int, ...]:
"""
Parse version string to tuple.
Args:
version_str: Version string (e.g., "1.2.3")
Returns:
Version tuple
"""
try:
parts = version_str.split('.')
return tuple(int(p) for p in parts if p.isdigit())
except (ValueError, AttributeError):
return (0,)
@staticmethod
def compare_versions(v1: str, v2: str) -> int:
"""
Compare two version strings.
Args:
v1: First version
v2: Second version
Returns:
-1 if v1 < v2, 0 if equal, 1 if v1 > v2
"""
t1 = VersionUtils.parse_version(v1)
t2 = VersionUtils.parse_version(v2)
# Pad shorter version with zeros
if len(t1) < len(t2):
t1 = t1 + (0,) * (len(t2) - len(t1))
elif len(t2) < len(t1):
t2 = t2 + (0,) * (len(t1) - len(t2))
if t1 < t2:
return -1
elif t1 > t2:
return 1
else:
return 0
@staticmethod
def version_satisfies(version: str, requirement: str) -> bool:
"""
Check if version satisfies requirement.
Args:
version: Version string
requirement: Requirement string (e.g., ">=1.2.0")
Returns:
True if satisfied
"""
import re
# Parse requirement
match = re.match(r'([<>=]+)\s*(.+)', requirement)
if not match:
# Exact match required
return version == requirement
op, req_version = match.groups()
cmp = VersionUtils.compare_versions(version, req_version)
if op == '>=':
return cmp >= 0
elif op == '<=':
return cmp <= 0
elif op == '>':
return cmp > 0
elif op == '<':
return cmp < 0
elif op == '==':
return cmp == 0
elif op == '!=':
return cmp != 0
else:
return False