Files
third_party_openhitls/script/platform_utils.py
T
tlhc b67050ce74 feat: build system improvements and cross-platform support (macOS)
Major Features:
- Multi-compiler/linker configuration system with toolchain support
- Auto-manage Secure C (libboundscheck) dependency
- Dynamic provider library names for CMVP modes (ISO19790, SM)
- macOS/Darwin platform support with unified POSIX abstractions
- Cross-platform build improvements and optimizations

Build System:
- Add toolchain management for cross-compilation
- Support nested 'common' format in compile config
- with _EXTRA/_REMOVE/_OVERRIDE in common flags, we can add remove by
  per compiler
- Unify toolchain naming convention (arch-vendor-os-abi-compiler)

Platform Support:
- Unify Linux and Darwin sources under POSIX
- Add Darwin support for SAL and build flags
- Add Darwin support for REC wrapper functions (WIP)
- Cross-platform ops count macros for benchmarking
- Platform-specific library extension detection (.dylib vs .so)

Cherry-picked from: https://gitcode.com/openHiTLS/openhitls/merge_requests/652

Signed-off-by: Dongjianwei001 <dongjianwei1@huawei.com>
2026-03-30 19:50:26 +08:00

392 lines
12 KiB
Python

#!/usr/bin/env python3
# coding: utf-8
# This file is part of the openHiTLS project.
#
# openHiTLS is licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
# http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
"""
Platform, compiler, and linker detection utilities for openHiTLS build system.
This module provides independent detection for:
- Compiler type (gcc, clang, apple-clang)
- Linker type (gnu-ld, ld64, lld, gold)
- Operating system (linux, darwin, etc.)
The detection is dimension-independent: compiler choice doesn't imply linker choice,
and OS doesn't dictate compiler.
"""
import platform
import subprocess
import re
import os
from typing import Optional, Dict, Tuple
class CompilerDetector:
"""Detect compiler type by analyzing version output."""
COMPILER_TYPES = {
'gcc': 'gcc',
'clang': 'clang',
'apple-clang': 'apple-clang'
}
@staticmethod
def detect_compiler_type(compiler_cmd: Optional[str] = None) -> str:
"""
Detect compiler type from command.
Args:
compiler_cmd: Compiler command (default: $CC or 'cc')
Returns:
Compiler type: 'gcc', 'clang', or 'apple-clang'
"""
if compiler_cmd is None:
compiler_cmd = os.environ.get('CC', 'cc')
try:
# Run compiler with --version
result = subprocess.run(
[compiler_cmd, '--version'],
capture_output=True,
text=True,
timeout=5
)
version_output = result.stdout.lower()
# Detection logic:
# 1. Apple Clang: contains both 'apple' and 'clang'
# 2. Clang: contains 'clang' but not 'apple'
# 3. GCC: contains 'gcc' or is fallback
if 'apple' in version_output and 'clang' in version_output:
return 'apple-clang'
elif 'clang' in version_output:
return 'clang'
elif 'gcc' in version_output or 'gnu' in version_output:
return 'gcc'
else:
# Fallback: assume gcc for unknown compilers
return 'gcc'
except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError) as e:
print(f"Warning: Failed to detect compiler type for '{compiler_cmd}': {e}")
return 'gcc' # Safe fallback
@staticmethod
def get_compiler_info(compiler_cmd: Optional[str] = None) -> Dict[str, str]:
"""
Get detailed compiler information.
Returns:
Dict with 'type', 'version', 'path', 'output'
"""
if compiler_cmd is None:
compiler_cmd = os.environ.get('CC', 'cc')
info = {
'command': compiler_cmd,
'type': 'unknown',
'version': 'unknown',
'path': 'unknown',
'output': ''
}
try:
# Get full path
which_result = subprocess.run(
['which', compiler_cmd],
capture_output=True,
text=True,
timeout=5
)
if which_result.returncode == 0:
info['path'] = which_result.stdout.strip()
# Get version output
result = subprocess.run(
[compiler_cmd, '--version'],
capture_output=True,
text=True,
timeout=5
)
info['output'] = result.stdout
# Detect type
info['type'] = CompilerDetector.detect_compiler_type(compiler_cmd)
# Extract version
version_match = re.search(r'(\d+\.\d+\.\d+)', result.stdout)
if version_match:
info['version'] = version_match.group(1)
except Exception as e:
print(f"Warning: Failed to get compiler info: {e}")
return info
class LinkerDetector:
"""Detect linker type by analyzing version output and toolchain."""
LINKER_TYPES = {
'gnu-ld': 'gnu-ld',
'ld64': 'ld64',
'lld': 'lld',
'gold': 'gold'
}
@staticmethod
def detect_linker_type(linker_cmd: Optional[str] = None, compiler_cmd: Optional[str] = None) -> str:
"""
Detect linker type.
Strategy:
1. If linker_cmd provided, detect from that command
2. Otherwise, detect from compiler's default linker
3. Fall back to OS-based heuristic
Args:
linker_cmd: Explicit linker command (e.g., 'ld', 'ld64')
compiler_cmd: Compiler command to query for default linker
Returns:
Linker type: 'gnu-ld', 'ld64', 'lld', or 'gold'
"""
# Strategy 1: Explicit linker command
if linker_cmd:
return LinkerDetector._detect_from_command(linker_cmd)
# Strategy 2: Query compiler for linker
if compiler_cmd:
linker_from_compiler = LinkerDetector._detect_from_compiler(compiler_cmd)
if linker_from_compiler:
return linker_from_compiler
# Strategy 3: OS-based heuristic
return LinkerDetector._detect_from_os()
@staticmethod
def _detect_from_command(linker_cmd: str) -> str:
"""Detect linker type from direct command."""
try:
# Try ld --version
result = subprocess.run(
[linker_cmd, '--version'],
capture_output=True,
text=True,
timeout=5
)
version_output = result.stdout.lower()
# Detection patterns
if 'lld' in version_output:
return 'lld'
elif 'gnu' in version_output and 'gold' in version_output:
return 'gold'
elif 'gnu' in version_output:
return 'gnu-ld'
elif 'ld64' in version_output or 'darwin' in version_output:
return 'ld64'
# Try ld -v (macOS style)
result = subprocess.run(
[linker_cmd, '-v'],
capture_output=True,
text=True,
timeout=5
)
version_output = result.stdout.lower() + result.stderr.lower()
if 'ld64' in version_output or 'darwin' in version_output:
return 'ld64'
except Exception as e:
print(f"Warning: Failed to detect linker from command '{linker_cmd}': {e}")
return LinkerDetector._detect_from_os()
@staticmethod
def _detect_from_compiler(compiler_cmd: str) -> Optional[str]:
"""Detect linker by querying compiler."""
try:
# Try: cc -Wl,--version
result = subprocess.run(
[compiler_cmd, '-Wl,--version'],
capture_output=True,
text=True,
timeout=5
)
version_output = result.stdout.lower() + result.stderr.lower()
if 'lld' in version_output:
return 'lld'
elif 'gnu' in version_output and 'gold' in version_output:
return 'gold'
elif 'gnu' in version_output:
return 'gnu-ld'
elif 'ld64' in version_output or 'darwin' in version_output:
return 'ld64'
except Exception:
pass
return None
@staticmethod
def _detect_from_os() -> str:
"""Fallback: detect linker based on OS."""
system = platform.system().lower()
if system == 'darwin':
return 'ld64'
elif system == 'linux':
# Check if lld is available
try:
subprocess.run(['ld.lld', '--version'], capture_output=True, timeout=5)
return 'lld'
except Exception:
pass
# Default to GNU ld on Linux
return 'gnu-ld'
else:
return 'gnu-ld' # Safe fallback
@staticmethod
def get_linker_info(linker_cmd: Optional[str] = None, compiler_cmd: Optional[str] = None) -> Dict[str, str]:
"""
Get detailed linker information.
Returns:
Dict with 'type', 'version', 'path', 'output'
"""
info = {
'command': linker_cmd or 'auto',
'type': 'unknown',
'version': 'unknown',
'path': 'unknown',
'output': ''
}
# Detect type
info['type'] = LinkerDetector.detect_linker_type(linker_cmd, compiler_cmd)
# Try to get more info if explicit linker command
if linker_cmd:
try:
which_result = subprocess.run(
['which', linker_cmd],
capture_output=True,
text=True,
timeout=5
)
if which_result.returncode == 0:
info['path'] = which_result.stdout.strip()
result = subprocess.run(
[linker_cmd, '--version'],
capture_output=True,
text=True,
timeout=5
)
info['output'] = result.stdout
version_match = re.search(r'(\d+\.\d+)', result.stdout)
if version_match:
info['version'] = version_match.group(1)
except Exception as e:
print(f"Warning: Failed to get linker info: {e}")
return info
class PlatformDetector:
"""Detect operating system and architecture."""
@staticmethod
def get_current_platform() -> str:
"""
Get normalized platform name.
Returns:
Platform name: 'linux', 'darwin', 'windows', etc.
"""
system = platform.system().lower()
# Normalize names
if system == 'darwin':
return 'darwin'
elif system == 'linux':
return 'linux'
elif system == 'windows':
return 'windows'
else:
return system
@staticmethod
def get_architecture() -> str:
"""Get CPU architecture."""
machine = platform.machine().lower()
# Normalize architecture names
if machine in ('x86_64', 'amd64'):
return 'x86_64'
elif machine in ('aarch64', 'arm64'):
return 'aarch64'
elif machine.startswith('arm'):
return 'arm'
else:
return machine
@staticmethod
def get_platform_info() -> Dict[str, str]:
"""Get comprehensive platform information."""
return {
'os': PlatformDetector.get_current_platform(),
'arch': PlatformDetector.get_architecture(),
'system': platform.system(),
'release': platform.release(),
'machine': platform.machine(),
'python_version': platform.python_version()
}
class BuildEnvironment:
"""Complete build environment detection and reporting."""
@staticmethod
def detect(compiler_cmd: Optional[str] = None, linker_cmd: Optional[str] = None) -> Dict:
"""
Detect complete build environment.
Returns:
Dict with 'platform', 'compiler', 'linker' information
"""
env = {
'platform': PlatformDetector.get_platform_info(),
'compiler': CompilerDetector.get_compiler_info(compiler_cmd),
'linker': LinkerDetector.get_linker_info(linker_cmd, compiler_cmd)
}
return env
if __name__ == '__main__':
# Test: detect and display build environment
env = BuildEnvironment.detect()
print("Platform:", env['platform']['os'], env['platform']['arch'])
print("Compiler:", env['compiler']['type'], env['compiler']['version'])
print("Linker:", env['linker']['type'])