1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
# -*- 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
|