Compare commits
No commits in common. "018197e29036e6137c50019ec78753cb8c48225f" and "1f0fccc86663e92ce1d053ea5d8349561a922ce3" have entirely different histories.
018197e290
...
1f0fccc866
|
|
@ -1,187 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Utility functions for the build system.
|
|
||||||
Provides common operations like process execution,
|
|
||||||
file dependency checking, and tool detection.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import List, Union, Optional
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# Process execution
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def run(cmd: List[Union[str, Path]],
|
|
||||||
cwd: Optional[Path] = None,
|
|
||||||
check: bool = True,
|
|
||||||
capture_output: bool = False,
|
|
||||||
verbose: bool = True) -> subprocess.CompletedProcess:
|
|
||||||
"""
|
|
||||||
Execute a command and optionally capture its output.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
cmd: Command and arguments to execute
|
|
||||||
cwd: Working directory for the command
|
|
||||||
check: If True, raise exception on non-zero exit code
|
|
||||||
capture_output: If True, capture stdout and stderr
|
|
||||||
verbose: If True, print the command before executing
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
CompletedProcess instance with return code and output
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
subprocess.CalledProcessError: If check=True and command fails
|
|
||||||
"""
|
|
||||||
# Convert all Path objects to strings
|
|
||||||
cmd_str = [str(c) for c in cmd]
|
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print(">>", " ".join(cmd_str))
|
|
||||||
|
|
||||||
return subprocess.run(
|
|
||||||
cmd_str,
|
|
||||||
cwd=cwd,
|
|
||||||
check=check,
|
|
||||||
capture_output=capture_output,
|
|
||||||
text=True if capture_output else None
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def run_quiet(cmd: List[Union[str, Path]],
|
|
||||||
cwd: Optional[Path] = None,
|
|
||||||
check: bool = False) -> subprocess.CompletedProcess:
|
|
||||||
"""
|
|
||||||
Execute a command silently (no output, no verbose).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
cmd: Command and arguments to execute
|
|
||||||
cwd: Working directory for the command
|
|
||||||
check: If True, raise exception on non-zero exit code
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
CompletedProcess instance
|
|
||||||
"""
|
|
||||||
cmd_str = [str(c) for c in cmd]
|
|
||||||
return subprocess.run(
|
|
||||||
cmd_str,
|
|
||||||
cwd=cwd,
|
|
||||||
check=check,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.DEVNULL
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# File dependency checking
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def is_up_to_date(source: Path, output: Path, dependency: Optional[Path] = None) -> bool:
|
|
||||||
"""
|
|
||||||
Check if an output file is up to date relative to its source and dependencies.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
source: Source file path
|
|
||||||
output: Output file path
|
|
||||||
dependency: Optional dependency file (e.g., .d file)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if output is up to date, False if it needs rebuilding
|
|
||||||
"""
|
|
||||||
# Files with 'meta' in their name are never up to date (force rebuild)
|
|
||||||
if "meta" in source.stem or "meta" in output.stem:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if dependency and "meta" in dependency.stem:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Output doesn't exist, needs building
|
|
||||||
if not output.exists():
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check dependency file if provided
|
|
||||||
if dependency:
|
|
||||||
if dependency.exists() and dependency.stat().st_mtime > output.stat().st_mtime:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check source file modification time
|
|
||||||
return source.stat().st_mtime < output.stat().st_mtime
|
|
||||||
|
|
||||||
|
|
||||||
def needs_rebuild(sources: List[Path], output: Path) -> bool:
|
|
||||||
"""
|
|
||||||
Check if any source file is newer than the output file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sources: List of source file paths
|
|
||||||
output: Output file path
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if rebuild is needed, False otherwise
|
|
||||||
"""
|
|
||||||
if not output.exists():
|
|
||||||
return True
|
|
||||||
|
|
||||||
output_time = output.stat().st_mtime
|
|
||||||
return any(src.stat().st_mtime > output_time for src in sources if src.exists())
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# Tool detection
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def which(tool: str) -> Optional[Path]:
|
|
||||||
"""
|
|
||||||
Find a tool in the system PATH.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tool: Tool name to find
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Path to the tool if found, None otherwise
|
|
||||||
"""
|
|
||||||
result = shutil.which(tool)
|
|
||||||
return Path(result) if result else None
|
|
||||||
|
|
||||||
|
|
||||||
def check_tool(tool: str) -> bool:
|
|
||||||
"""
|
|
||||||
Check if a tool is available in the system PATH.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tool: Tool name to check
|
|
||||||
error_message: Optional custom error message to print if not found
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if tool is found, False otherwise
|
|
||||||
"""
|
|
||||||
if which(tool):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"ERROR: Required tool '{tool}' not found in PATH")
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def check_tools(tools: List[str]) -> bool:
|
|
||||||
"""
|
|
||||||
Check if multiple tools are available.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tools: List of tool names to check
|
|
||||||
error_messages: Optional list of custom error messages (same length as tools)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if all tools are found, False otherwise
|
|
||||||
"""
|
|
||||||
all_found = True
|
|
||||||
|
|
||||||
for tool in tools:
|
|
||||||
if not check_tool(tool):
|
|
||||||
all_found = False
|
|
||||||
|
|
||||||
return all_found
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue