mindspore/scripts/pre_commit/install_generic_tools.sh

352 lines
11 KiB
Bash
Executable File

#!/bin/bash
# Copyright 2025 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Remove strict error handling to prevent script from stopping on minor errors
# set -euo pipefail
# Color output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $*"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $*" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $*" >&2
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $*"
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if running on Debian/Ubuntu
is_debian_based() {
command -v apt-get >/dev/null 2>&1 && \
([[ -f /etc/debian_version ]] || [[ -f /etc/lsb-release ]])
}
# Check network connectivity to PyPI
check_network() {
log_info "Checking network connectivity to PyPI..."
if python -c "import urllib.request; urllib.request.urlopen('https://pypi.org', timeout=10)" 2>/dev/null; then
log_success "Network connectivity is OK"
return 0
else
log_warning "Cannot reach PyPI directly. This may be due to proxy settings or network restrictions."
return 1
fi
}
# Check Python installation and version
check_python() {
local available_py_versions=("3.9" "3.10" "3.11")
if ! command_exists python; then
log_error "Python is not installed. Please install Python ${available_py_versions[*]}"
return 1
fi
local python_version
python_version=$(python -c "import sys; print('.'.join(str(x) for x in sys.version_info[:2]))" 2>/dev/null)
if [[ -z "$python_version" ]]; then
log_error "Failed to determine Python version"
return 1
fi
local version_ok=false
for version in "${available_py_versions[@]}"; do
if [[ "$python_version" == "$version" ]]; then
version_ok=true
break
fi
done
if ! $version_ok; then
log_error "Python version '$python_version' is not supported. Available versions: [${available_py_versions[*]}]"
return 1
fi
log_info "Found Python version: $python_version"
return 0
}
# Check pip installation
check_pip() {
if ! command_exists pip && ! python -c "import pip" 2>/dev/null; then
log_error "pip is not installed. Please install pip first"
return 1
fi
# Use Python to call pip to avoid potential path issues
if ! python -m pip --version >/dev/null 2>&1; then
log_error "pip is not properly installed or not in PATH"
return 1
fi
log_info "Found pip: $(python -m pip --version 2>/dev/null | head -n1 || echo "pip available")"
return 0
}
# Special handling for Debian/Ubuntu systems
debian_safe_install() {
local package_name="$1"
local package_spec="$2"
if is_debian_based; then
log_info "Debian/Ubuntu system detected, using --break-system-packages to avoid conflicts with system packages"
if python -m pip install --break-system-packages --upgrade --force-reinstall "$package_spec" 2>/dev/null; then
return 0
else
log_warning "Failed with --break-system-packages, trying with --user install"
if python -m pip install --user --upgrade --force-reinstall "$package_spec" 2>/dev/null; then
return 0
fi
fi
fi
return 1
}
# Try multiple installation methods
install_with_retry() {
local package_name="$1"
local package_spec="$2"
local max_retries=2
local retry_count=0
while [[ $retry_count -le $max_retries ]]; do
case $retry_count in
0)
# Method 1: Direct installation
log_info "Attempting to install $package_name directly..."
if python -m pip install --upgrade --force-reinstall "$package_spec" 2>/dev/null; then
return 0
fi
;;
1)
# Method 2: Debian-safe installation
if is_debian_based; then
if debian_safe_install "$package_name" "$package_spec"; then
return 0
fi
fi
;;
2)
# Method 3: Using mirror
log_info "Trying with Tsinghua mirror..."
if python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn --upgrade --force-reinstall "$package_spec" 2>/dev/null; then
return 0
fi
;;
esac
((retry_count++))
if [[ $retry_count -le $max_retries ]]; then
log_warning "Installation attempt $retry_count failed for $package_name, retrying..."
sleep 2
fi
done
return 1
}
# Install package with comprehensive error handling
install_package() {
local package_name="$1"
local package_spec="$2"
log_info "Installing $package_name..."
# Check if already installed
if python -c "import ${package_name%%==*}" 2>/dev/null; then
log_info "$package_name is already installed, reinstalling..."
fi
if install_with_retry "$package_name" "$package_spec"; then
log_success "Successfully installed $package_name"
return 0
else
log_error "All installation attempts failed for $package_name"
# Provide specific advice for Debian systems
if is_debian_based; then
log_info "On Debian/Ubuntu systems, you can try:"
log_info "1. Install using apt: sudo apt-get install python3-${package_name}"
log_info "2. Use Python virtual environment: python -m venv venv && source venv/bin/activate"
log_info "3. Use conda: conda install ${package_name}"
else
log_info "You may need to:"
log_info "1. Check your network connection and proxy settings"
log_info "2. Configure pip proxy: pip config set global.proxy http://proxy:port"
log_info "3. Use Python virtual environment to avoid system package conflicts"
fi
return 1
fi
}
# Check tool version
check_tool_version() {
local tool_name="$1"
local check_cmd="$2"
log_info "Checking $tool_name version..."
if eval "$check_cmd" 2>/dev/null; then
log_success "$tool_name is working properly"
return 0
else
# Try to find the command in Python user directory
local user_bin_dir
user_bin_dir="$(python -m site --user-base 2>/dev/null)/bin"
if [[ -d "$user_bin_dir" && -x "$user_bin_dir/$tool_name" ]]; then
log_info "Found $tool_name in user bin directory, you may need to add to PATH:"
log_info "export PATH=\$PATH:$user_bin_dir"
return 0
else
log_warning "$tool_name not installed or not in PATH!"
return 1
fi
fi
}
# Provide manual installation instructions
show_manual_instructions() {
log_info "=== Manual Installation Instructions ==="
log_info "If automatic installation continues to fail, you can:"
log_info ""
if is_debian_based; then
log_info "For Debian/Ubuntu systems:"
log_info "1. Try system packages:"
log_info " sudo apt-get update"
log_info " sudo apt-get install python3-pylint python3-cpplint codespell"
log_info ""
log_info "2. Use Python virtual environment:"
log_info " python -m venv codecheck-env"
log_info " source codecheck-env/bin/activate"
log_info " pip install pylint cpplint codespell cmakelint lizard clang-format"
log_info ""
fi
log_info "3. Configure pip proxy (if behind corporate firewall):"
log_info " pip config set global.proxy http://your-proxy:port"
log_info ""
log_info "4. Download wheels manually and install offline:"
log_info " pip download package_name -d ./packages/"
log_info " pip install --no-index --find-links ./packages/ package_name"
log_info ""
}
# Main installation function
main() {
log_info "Starting code check tools installation..."
# Detect system type
if is_debian_based; then
log_info "Detected Debian/Ubuntu based system"
fi
# Pre-flight checks
log_info "Checking system requirements..."
if ! check_python; then
exit 1
fi
if ! check_pip; then
exit 1
fi
if ! check_network; then
log_warning "Network issues detected, installation may fail"
fi
# Define tools to install (name package_spec check_command)
declare -a tools=(
"cmakelint|cmakelint|cmakelint --version"
"codespell|codespell|codespell --version"
"cpplint|cpplint|cpplint --version"
"lizard|lizard|lizard --version"
"pylint|pylint==3.3.7|pylint --version"
"clang-format|clang-format==18.1.8|clang-format --version"
)
local failed_installations=0
local successful_installations=0
# Install tools
for tool_info in "${tools[@]}"; do
IFS='|' read -r tool_name package_spec check_cmd <<< "$tool_info"
log_info "Processing tool: $tool_name"
if install_package "$tool_name" "$package_spec"; then
((successful_installations++))
else
((failed_installations++))
fi
echo "----------------------------------------"
done
# Verify installations
log_info "Verifying installations..."
local failed_checks=0
for tool_info in "${tools[@]}"; do
IFS='|' read -r tool_name package_spec check_cmd <<< "$tool_info"
if ! check_tool_version "$tool_name" "$check_cmd"; then
((failed_checks++))
fi
done
# Summary
echo
log_info "=== Installation Summary ==="
if [[ $failed_installations -eq 0 && $failed_checks -eq 0 ]]; then
log_success "All code check tools installed successfully!"
else
log_warning "Installation completed with some issues:"
log_warning "- $successful_installations tool(s) installed successfully"
log_warning "- $failed_installations installation(s) failed"
log_warning "- $failed_checks tool(s) failed version check"
# Show PATH instructions
local user_bin_dir
user_bin_dir="$(python -m site --user-base 2>/dev/null)/bin"
if [[ -d "$user_bin_dir" ]]; then
log_info "You may need to add Python user binaries to your PATH:"
log_info "export PATH=\$PATH:$user_bin_dir"
log_info "Add this to your ~/.bashrc or ~/.profile for permanent effect"
fi
if [[ $failed_installations -gt 0 ]]; then
echo
show_manual_instructions
fi
fi
}
# Run main function
main "$@"