mirror of
https://github.com/openharmony/third_party_openhitls.git
synced 2026-07-24 05:56:21 -04:00
b67050ce74
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>
392 lines
12 KiB
Python
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'])
|