Files
OpenROAD/etc/DependencyInstaller.sh
Matt Liberty 1d1582ed42 Merge pull request #10037 from titan73/master
etc: Fixes for DependencyInstaller.sh
2026-04-14 18:44:27 +00:00

1318 lines
60 KiB
Bash
Executable File

#!/usr/bin/env bash
# Strict Mode
set -euo pipefail
# ==============================================================================
# Configuration
# ==============================================================================
# All dependency versions, checksums, and installation prefixes will be
# defined here to provide a single, easy-to-manage location for configuration.
# ------------------------------------------------------------------------------
# Script Configuration
# ------------------------------------------------------------------------------
# Default values for command-line arguments
PREFIX=""
CI="no"
SAVE_DEPS_PREFIXES=""
if [[ "$OSTYPE" == "darwin"* ]]; then
NUM_THREADS=$(sysctl -n hw.logicalcpu)
else
NUM_THREADS=$(nproc)
fi
SKIP_SYSTEM_OR_TOOLS="false"
BASE_DIR=$(mktemp -d /tmp/DependencyInstaller-XXXXXX)
CMAKE_PACKAGE_ROOT_ARGS=""
OR_TOOLS_PATH=""
INSTALL_SUMMARY=()
VERBOSE_MODE="no"
OPT_NOCERT=""
# Colors
if [[ -t 1 ]]; then
export RED=$(tput setaf 1)
export GREEN=$(tput setaf 2)
export YELLOW=$(tput setaf 3)
export BLUE=$(tput setaf 4)
export BOLD=$(tput bold)
export NC=$(tput sgr0) # No Color
else
RED=""
GREEN=""
YELLOW=""
BLUE=""
BOLD=""
NC=""
fi
# ------------------------------------------------------------------------------
# Dependency Versions and Checksums
# ------------------------------------------------------------------------------
YOSYS_VERSION="v0.58"
CMAKE_VERSION_BIG="3.31"
CMAKE_VERSION_SMALL="${CMAKE_VERSION_BIG}.9"
CMAKE_CHECKSUM_AARCH64="8d426361ce8c54494c0a041a0e3bcc5c"
CMAKE_CHECKSUM_X86_64="64797150473dabe9de95459cb1ce0715"
PCRE_VERSION="10.42"
PCRE_CHECKSUM="37d2f77cfd411a3ddf1c64e1d72e43f7"
SWIG_VERSION="4.3.0"
SWIG_CHECKSUM="9f74c7f402aa28d9f75e67d1990ee6fb"
BOOST_VERSION_BIG="1.89"
BOOST_VERSION_SMALL="${BOOST_VERSION_BIG}.0"
BOOST_CHECKSUM="187b577ce9f485314fcf17bcba2fb542"
EIGEN_VERSION="3.4"
CUDD_VERSION="3.0.0"
LEMON_VERSION="1.3.1"
SPDLOG_VERSION="1.15.0"
GTEST_VERSION="1.17.0"
GTEST_CHECKSUM="3471f5011afc37b6555f6619c14169cf"
ABSL_VERSION="20260107.0"
ABSL_CHECKSUM="2a7add2ee848dd4591f41b0f6339d624"
BISON_VERSION="3.8.2"
BISON_CHECKSUM="1e541a097cda9eca675d29dd2832921f"
FLEX_VERSION="2.6.4"
FLEX_CHECKSUM="2882e3179748cc9f9c23ec593d6adc8d"
OR_TOOLS_VERSION_BIG="9.14"
OR_TOOLS_VERSION_SMALL="${OR_TOOLS_VERSION_BIG}.6206"
EQUIVALENCE_DEPS="no"
# ... configuration variables will be added here ...
# ==============================================================================
# Helper Functions
# ==============================================================================
# This section will contain generic helper functions for version comparison,
# ------------------------------------------------------------------------------
# Logging and Error Handling
# ------------------------------------------------------------------------------
log() {
echo "${BLUE}${BOLD}[INFO]${NC} $1"
}
warn() {
echo "${YELLOW}${BOLD}[WARNING]${NC} $1" >&2
}
error() {
echo "${RED}${BOLD}[ERROR]${NC} $1" >&2
exit 1
}
# ------------------------------------------------------------------------------
# Version Comparison
# ------------------------------------------------------------------------------
# Compares two semantic versions.
# Usage: _version_compare "1.2.3" ">=" "1.2.0"
_version_compare() {
local op
case "$2" in
-ge) op=">=" ;;
-gt) op=">" ;;
-le) op="<=" ;;
-lt) op="<" ;;
-eq) op="==" ;;
-ne) op="!=" ;;
*)
error "Invalid operator in _version_compare: $2"
;;
esac
awk -v v1="$1" -v v2="$3" 'BEGIN { exit !(v1 '"$op"' v2) }'
}
# ------------------------------------------------------------------------------
# Check Command Existence
# ------------------------------------------------------------------------------
# Checks if a command is available in the system's PATH.
# Usage: _command_exists "cmake"
_command_exists() {
command -v "$1" &> /dev/null
}
# ------------------------------------------------------------------------------
# Path Truncation
# ------------------------------------------------------------------------------
_truncate_path() {
local path=$1
local max_len=$2
if [[ ${#path} -gt ${max_len} ]]; then
local start_len=$(( (max_len - 3) / 2 ))
local end_len=$(( max_len - 3 - start_len ))
echo "${path:0:$start_len}...${path: -${end_len}}"
else
echo "$path"
fi
}
# ------------------------------------------------------------------------------
# Checksum Verification
# ------------------------------------------------------------------------------
_verify_checksum() {
local checksum=$1
local filename=$2
_execute "Verifying ${filename} checksum..." bash -c "echo '${checksum} ${filename}' | md5sum --quiet -c -"
}
# ------------------------------------------------------------------------------
# Yosys
# ------------------------------------------------------------------------------
_install_yosys() {
local yosys_prefix=${PREFIX:-"/usr/local"}
local yosys_bin=${yosys_prefix}/bin/yosys
local yosys_installed_version="none"
if [[ -f "${yosys_bin}" ]]; then
yosys_installed_version=$("${yosys_bin}" --version | awk '{print $2}')
elif _command_exists "yosys" && [[ -z "${PREFIX}" ]]; then
yosys_installed_version=$(yosys --version | awk '{print $2}')
fi
local required_version="${YOSYS_VERSION#v}"
log "Checking Yosys (System: ${yosys_installed_version}, Required: ${required_version})"
if [[ "${yosys_installed_version}" != "${required_version}" ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning Yosys ${YOSYS_VERSION}..." git clone --depth=1 -b "${YOSYS_VERSION}" --recursive https://github.com/YosysHQ/yosys
cd yosys
_execute "Building Yosys..." make -j "${NUM_THREADS}" PREFIX="${yosys_prefix}" ABC_ARCHFLAGS=-Wno-register
_execute "Installing Yosys..." make install PREFIX="${yosys_prefix}"
)
INSTALL_SUMMARY+=("Yosys: system=${yosys_installed_version}, required=${required_version}, path=${yosys_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Yosys: system=${yosys_installed_version}, required=${required_version}, path=${yosys_prefix}, status=skipped")
fi
}
# ------------------------------------------------------------------------------
# Eqy
# ------------------------------------------------------------------------------
_install_eqy() {
local eqy_prefix=${PREFIX:-"/usr/local"}
local eqy_bin=${eqy_prefix}/bin/eqy
local eqy_installed_version="none"
if [[ -f "${eqy_bin}" ]]; then
eqy_installed_version=$("${eqy_bin}" --version | awk '{print $2}')
elif _command_exists "eqy" && [[ -z "${PREFIX}" ]]; then
eqy_installed_version=$(eqy --version | awk '{print $2}')
fi
local required_version="${YOSYS_VERSION}"
log "Checking Eqy (System: ${eqy_installed_version}, Required: ${required_version})"
if [[ "${eqy_installed_version}" != "${required_version}" ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning Eqy ${YOSYS_VERSION}..." git clone --depth=1 -b "${YOSYS_VERSION}" https://github.com/YosysHQ/eqy
cd eqy
export PATH="${PREFIX:-/usr/local}/bin:${PATH}"
_execute "Building Eqy..." make -j "${NUM_THREADS}" PREFIX="${eqy_prefix}"
_execute "Installing Eqy..." make install PREFIX="${eqy_prefix}"
)
INSTALL_SUMMARY+=("Eqy: system=${eqy_installed_version}, required=${required_version}, path=${eqy_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Eqy: system=${eqy_installed_version}, required=${required_version}, path=${eqy_prefix}, status=skipped")
fi
}
# ------------------------------------------------------------------------------
# Sby
# ------------------------------------------------------------------------------
_install_sby() {
local sby_prefix=${PREFIX:-"/usr/local"}
local sby_bin=${sby_prefix}/bin/sby
local sby_installed_version="none"
if [[ -f "${sby_bin}" ]]; then
sby_installed_version=$("${sby_bin}" --version | awk '{print $2}')
elif _command_exists "sby" && [[ -z "${PREFIX}" ]]; then
sby_installed_version=$(sby --version | awk '{print $2}')
fi
local required_version="${YOSYS_VERSION}"
log "Checking Sby (System: ${sby_installed_version}, Required: ${required_version})"
if [[ "${sby_installed_version}" != "${required_version}" ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning Sby ${YOSYS_VERSION}..." git clone --depth=1 -b "${YOSYS_VERSION}" --recursive https://github.com/YosysHQ/sby
cd sby
export PATH="${PREFIX:-/usr/local}/bin:${PATH}"
_execute "Installing Sby..." make -j "${NUM_THREADS}" PREFIX="${sby_prefix}" install
)
INSTALL_SUMMARY+=("Sby: system=${sby_installed_version}, required=${required_version}, path=${sby_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Sby: system=${sby_installed_version}, required=${required_version}, path=${sby_prefix}, status=skipped")
fi
}
_install_equivalence_deps() {
log "Install equivalence dependencies (-eqy)..."
_install_yosys
_install_eqy
_install_sby
}
# logging, error handling, and other utility tasks.
# ------------------------------------------------------------------------------
# Execute a command, hiding output unless in verbose mode or an error occurs.
# Usage: _execute "Cloning Yosys..." git clone ...
_execute() {
local description=$1
shift
if [[ "${VERBOSE_MODE}" == "yes" ]]; then
log "${description}"
"$@"
return
fi
echo -n "${BLUE}${BOLD}[INFO]${NC} ${description}"
local log_file
log_file=$(mktemp)
if ! "$@" &> "${log_file}"; then
echo -e " ${RED}${NC}"
cat "${log_file}"
rm "${log_file}"
error "Failed to execute: $*"
else
echo -e " ${GREEN}${NC}"
rm "${log_file}"
fi
}
# CMake
# ------------------------------------------------------------------------------
_install_cmake() {
local cmake_prefix=${PREFIX:-"/usr/local"}
local cmake_bin=${cmake_prefix}/bin/cmake
local cmake_installed_version="none"
if [[ -f ${cmake_bin} ]]; then
cmake_installed_version=$(${cmake_bin} --version | head -n1 | awk '{print $3}')
elif _command_exists "cmake" && [[ -z "${PREFIX}" ]]; then
cmake_installed_version=$(cmake --version | head -n1 | awk '{print $3}')
fi
log "Checking CMake (System: ${cmake_installed_version}, Required: ${CMAKE_VERSION_SMALL})"
if [[ "${cmake_installed_version}" != "${CMAKE_VERSION_SMALL}" ]]; then
(
cd "${BASE_DIR}"
local arch
arch=$(uname -m)
local cmake_checksum=""
if [[ "${arch}" == "aarch64" ]]; then
cmake_checksum=${CMAKE_CHECKSUM_AARCH64}
else
cmake_checksum=${CMAKE_CHECKSUM_X86_64}
fi
_execute "Downloading CMake..." wget $OPT_NOCERT "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION_SMALL}/cmake-${CMAKE_VERSION_SMALL}-linux-${arch}.sh"
_verify_checksum "${cmake_checksum}" "cmake-${CMAKE_VERSION_SMALL}-linux-${arch}.sh" || error "CMake checksum failed."
chmod +x "cmake-${CMAKE_VERSION_SMALL}-linux-${arch}.sh"
_execute "Installing CMake..." "./cmake-${CMAKE_VERSION_SMALL}-linux-${arch}.sh" --skip-license --prefix="${cmake_prefix}"
)
INSTALL_SUMMARY+=("CMake: system=${cmake_installed_version}, required=${CMAKE_VERSION_SMALL}, path=${cmake_prefix}, status=installed")
else
INSTALL_SUMMARY+=("CMake: system=${cmake_installed_version}, required=${CMAKE_VERSION_SMALL}, path=${cmake_prefix}, status=skipped")
fi
}
# ------------------------------------------------------------------------------
# Bison
# ------------------------------------------------------------------------------
_install_bison() {
local bison_prefix=${PREFIX:-"/usr/local"}
local bison_bin=${bison_prefix}/bin/bison
local bison_installed_version="none"
if [[ -f ${bison_bin} ]]; then
bison_installed_version=$(${bison_bin} --version | awk 'NR==1 {print $NF}')
elif _command_exists "bison" && [[ -z "${PREFIX}" ]]; then
bison_installed_version=$(bison --version | awk 'NR==1 {print $NF}')
bison_prefix=$(dirname "$(dirname "$(command -v bison)")")
fi
log "Checking Bison (System: ${bison_installed_version}, Required: ${BISON_VERSION})"
if [[ "${bison_installed_version}" != "${BISON_VERSION}" ]]; then
(
cd "${BASE_DIR}"
local mirrors=(
"https://ftp.gnu.org/gnu/bison"
"https://ftpmirror.gnu.org/bison"
"https://mirrors.kernel.org/gnu/bison"
"https://mirrors.dotsrc.org/gnu/bison"
)
local success=0
for mirror in "${mirrors[@]}"; do
local url="${mirror}/bison-${BISON_VERSION}.tar.gz"
log "Trying to download bison from: $url"
if wget $OPT_NOCERT "$url"; then
success=1
break
else
warn "Failed to download from $mirror"
fi
done
if [[ ${success} -ne 1 ]]; then
error "Could not download bison-${BISON_VERSION}.tar.gz from any mirror."
fi
_verify_checksum "${BISON_CHECKSUM}" "bison-${BISON_VERSION}.tar.gz" || error "Bison checksum failed."
_execute "Extracting Bison..." tar xf "bison-${BISON_VERSION}.tar.gz"
cd "bison-${BISON_VERSION}"
_execute "Configuring Bison..." ./configure --prefix="${bison_prefix}"
_execute "Building and installing Bison..." make -j "${NUM_THREADS}" install
)
INSTALL_SUMMARY+=("Bison: system=${bison_installed_version}, required=${BISON_VERSION}, path=${bison_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Bison: system=${bison_installed_version}, required=${BISON_VERSION}, path=${bison_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D bison_ROOT=$(realpath "${bison_prefix}") "
}
# ------------------------------------------------------------------------------
# Flex
# ------------------------------------------------------------------------------
_install_flex() {
local flex_prefix=${PREFIX:-"/usr/local"}
local flex_bin=${flex_prefix}/bin/flex
local flex_installed_version="none"
if [[ -f ${flex_bin} ]]; then
flex_installed_version=$(${flex_bin} --version | awk '{print $2}')
elif _command_exists "flex" && [[ -z "${PREFIX}" ]]; then
flex_installed_version=$(flex --version | awk '{print $2}')
fi
log "Checking Flex (System: ${flex_installed_version}, Required: ${FLEX_VERSION})"
if [[ "${flex_installed_version}" != "${FLEX_VERSION}" ]]; then
(
cd "${BASE_DIR}"
_execute "Downloading Flex..." wget $OPT_NOCERT https://github.com/westes/flex/releases/download/v${FLEX_VERSION}/flex-${FLEX_VERSION}.tar.gz
_verify_checksum "${FLEX_CHECKSUM}" "flex-${FLEX_VERSION}.tar.gz" || error "Flex checksum failed."
_execute "Extracting Flex..." tar xf "flex-${FLEX_VERSION}.tar.gz"
cd "flex-${FLEX_VERSION}"
_execute "Configuring Flex..." ./configure --prefix="${flex_prefix}"
_execute "Building Flex..." make -j "${NUM_THREADS}"
_execute "Installing Flex..." make -j "${NUM_THREADS}" install
)
INSTALL_SUMMARY+=("Flex: system=${flex_installed_version}, required=${FLEX_VERSION}, path=${flex_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Flex: system=${flex_installed_version}, required=${FLEX_VERSION}, path=${flex_prefix}, status=skipped")
fi
}
# ------------------------------------------------------------------------------
# SWIG
# ------------------------------------------------------------------------------
_install_swig() {
local swig_prefix=${PREFIX:-"/usr/local"}
local swig_bin=${swig_prefix}/bin/swig
local swig_installed_version="none"
if [[ -f ${swig_bin} ]]; then
swig_installed_version=$(${swig_bin} -version | grep "SWIG Version" | awk '{print $3}')
elif _command_exists "swig" && [[ -z "${PREFIX}" ]]; then
swig_installed_version=$(swig -version | grep "SWIG Version" | awk '{print $3}')
fi
log "Checking SWIG (System: ${swig_installed_version}, Required: ${SWIG_VERSION})"
if [[ "${swig_installed_version}" != "${SWIG_VERSION}" ]]; then
(
cd "${BASE_DIR}"
local tar_name="v${SWIG_VERSION}.tar.gz"
_execute "Downloading SWIG..." wget $OPT_NOCERT "https://github.com/swig/swig/archive/${tar_name}"
_verify_checksum "${SWIG_CHECKSUM}" "${tar_name}" || error "SWIG checksum failed."
_execute "Extracting SWIG..." tar xfz "${tar_name}"
cd swig-*
_install_pcre
_execute "Generating SWIG configure script..." ./autogen.sh
_execute "Configuring SWIG..." ./configure --prefix="${swig_prefix}"
_execute "Building SWIG..." make -j "${NUM_THREADS}"
_execute "Installing SWIG..." make -j "${NUM_THREADS}" install
)
INSTALL_SUMMARY+=("SWIG: system=${swig_installed_version}, required=${SWIG_VERSION}, path=${swig_prefix}, status=installed")
else
INSTALL_SUMMARY+=("SWIG: system=${swig_installed_version}, required=${SWIG_VERSION}, path=${swig_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D SWIG_ROOT=$(realpath "${swig_prefix}") "
}
# ------------------------------------------------------------------------------
# PCRE
# ------------------------------------------------------------------------------
_install_pcre() {
local pcre_prefix=${PREFIX:-"/usr/local"}
local pcre_config=${pcre_prefix}/bin/pcre2-config
local pcre_installed_version="none"
if [[ -f "${pcre_config}" ]]; then
pcre_installed_version=$(${pcre_config} --version)
elif _command_exists "pcre2-config" && [[ -z "${PREFIX}" ]]; then
pcre_installed_version=$(pcre2-config --version)
fi
log "Checking PCRE (System: ${pcre_installed_version}, Required: ${PCRE_VERSION})"
if [[ "${pcre_installed_version}" == "${PCRE_VERSION}" ]]; then
INSTALL_SUMMARY+=("PCRE: system=${pcre_installed_version}, required=${PCRE_VERSION}, path=${pcre_prefix}, status=skipped")
return
fi
(
cd "${BASE_DIR}"
local pcre_tar_name="pcre2-${PCRE_VERSION}.tar.gz"
_execute "Downloading PCRE..." wget $OPT_NOCERT "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE_VERSION}/${pcre_tar_name}"
_verify_checksum "${PCRE_CHECKSUM}" "${pcre_tar_name}" || error "PCRE checksum failed."
_execute "Extracting PCRE..." tar xf "${pcre_tar_name}"
cd "pcre2-${PCRE_VERSION}"
_execute "Configuring PCRE..." ./configure --prefix="${pcre_prefix}"
_execute "Building PCRE..." make -j "${NUM_THREADS}"
_execute "Installing PCRE..." make -j "${NUM_THREADS}" install
)
INSTALL_SUMMARY+=("PCRE: system=${pcre_installed_version}, required=${PCRE_VERSION}, path=${pcre_prefix}, status=installed")
}
# ------------------------------------------------------------------------------
# Boost
# ------------------------------------------------------------------------------
_install_boost() {
local boost_prefix=${PREFIX:-"/usr/local"}
local boost_installed_version="none"
if [[ -f "${boost_prefix}/include/boost/version.hpp" ]]; then
boost_installed_version=$(grep "^#define BOOST_LIB_VERSION" "${boost_prefix}/include/boost/version.hpp" | sed -e 's/.*"\(.*\)"/\1/' -e 's/_/./g')
fi
local required_version="${BOOST_VERSION_BIG}"
log "Checking Boost (System: ${boost_installed_version}, Required: ${required_version})"
if [[ "${boost_installed_version}" != "${required_version}" ]]; then
(
cd "${BASE_DIR}"
local boost_version_underscore=${BOOST_VERSION_SMALL//./_}
_execute "Downloading Boost..." wget $OPT_NOCERT "https://archives.boost.io/release/${BOOST_VERSION_SMALL}/source/boost_${boost_version_underscore}.tar.gz"
_verify_checksum "${BOOST_CHECKSUM}" "boost_${boost_version_underscore}.tar.gz" || error "Boost checksum failed."
_execute "Extracting Boost..." tar -xf "boost_${boost_version_underscore}.tar.gz"
cd "boost_${boost_version_underscore}"
_execute "Bootstrapping Boost..." ./bootstrap.sh --prefix="${boost_prefix}"
_execute "Installing Boost..." ./b2 install --with-iostreams --with-test --with-serialization --with-system --with-thread -j "${NUM_THREADS}"
)
INSTALL_SUMMARY+=("Boost: system=${boost_installed_version}, required=${required_version}, path=${boost_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Boost: system=${boost_installed_version}, required=${required_version}, path=${boost_prefix}, status=skipped")
fi
local boost_cmake_dir=""
if [[ -d "${boost_prefix}/lib/cmake/Boost-${BOOST_VERSION_SMALL}" ]]; then
boost_cmake_dir="${boost_prefix}/lib/cmake/Boost-${BOOST_VERSION_SMALL}"
elif [[ -d "${boost_prefix}/lib64/cmake/Boost-${BOOST_VERSION_SMALL}" ]]; then
boost_cmake_dir="${boost_prefix}/lib64/cmake/Boost-${BOOST_VERSION_SMALL}"
fi
if [[ -n "${boost_cmake_dir}" ]]; then
CMAKE_PACKAGE_ROOT_ARGS+=" -D Boost_DIR=$(realpath "${boost_cmake_dir}") "
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D Boost_ROOT=$(realpath "${boost_prefix}") "
}
# ------------------------------------------------------------------------------
# Eigen
# ------------------------------------------------------------------------------
_install_eigen() {
local eigen_prefix=${PREFIX:-"/usr/local"}
local eigen_version_file="${eigen_prefix}/include/eigen3/Eigen/src/Core/util/Macros.h"
local eigen_installed_version="none"
if [[ -f "${eigen_version_file}" ]]; then
local world
world=$(grep "#define EIGEN_WORLD_VERSION" "${eigen_version_file}" | awk '{print $3}')
local major
major=$(grep "#define EIGEN_MAJOR_VERSION" "${eigen_version_file}" | awk '{print $3}')
eigen_installed_version="${world}.${major}"
fi
log "Checking Eigen (System: ${eigen_installed_version}, Required: ${EIGEN_VERSION})"
if [[ "${eigen_installed_version}" != "${EIGEN_VERSION}" ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning Eigen..." git clone --depth=1 -b "${EIGEN_VERSION}" https://gitlab.com/libeigen/eigen.git
cd eigen
local cmake_bin=${PREFIX:-/usr/local}/bin/cmake
_execute "Configuring Eigen..." "${cmake_bin}" -DCMAKE_INSTALL_PREFIX="${eigen_prefix}" -B build .
_execute "Building and installing Eigen..." "${cmake_bin}" --build build -j "${NUM_THREADS}" --target install
)
INSTALL_SUMMARY+=("Eigen: system=${eigen_installed_version}, required=${EIGEN_VERSION}, path=${eigen_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Eigen: system=${eigen_installed_version}, required=${EIGEN_VERSION}, path=${eigen_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D Eigen3_ROOT=$(realpath "${eigen_prefix}") "
}
# ------------------------------------------------------------------------------
# CUDD
# ------------------------------------------------------------------------------
_install_cudd() {
local cudd_prefix=${PREFIX:-"/usr/local"}
log "Checking CUDD (Required: ${CUDD_VERSION})"
if [[ ! -f ${cudd_prefix}/include/cudd.h ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning CUDD..." git clone --depth=1 -b "${CUDD_VERSION}" https://github.com/The-OpenROAD-Project/cudd.git
cd cudd
_execute "Generating CUDD configure script..." autoreconf
_execute "Configuring CUDD..." ./configure --prefix="${cudd_prefix}"
_execute "Building and installing CUDD..." make -j "${NUM_THREADS}" install
)
INSTALL_SUMMARY+=("CUDD: system=none, required=${CUDD_VERSION}, path=${cudd_prefix}, status=installed")
else
INSTALL_SUMMARY+=("CUDD: system=found, required=${CUDD_VERSION}, path=${cudd_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D cudd_ROOT=$(realpath "${cudd_prefix}") "
}
# ------------------------------------------------------------------------------
# CUSP
# ------------------------------------------------------------------------------
_install_cusp() {
local cusp_prefix=${PREFIX:-"/usr/local/include"}
log "Checking CUSP"
if [[ ! -f ${cusp_prefix}/cusp/version.h ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning CUSP..." git clone --depth=1 -b cuda9 https://github.com/cusplibrary/cusplibrary.git
cd cusplibrary
_execute "Installing CUSP..." cp -r ./cusp "${cusp_prefix}"
)
INSTALL_SUMMARY+=("CUSP: system=none, required=any, path=${cusp_prefix}, status=installed")
else
INSTALL_SUMMARY+=("CUSP: system=found, required=any, path=${cusp_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D cusp_ROOT=$(realpath "${cusp_prefix}") "
}
# ------------------------------------------------------------------------------
# Lemon
# ------------------------------------------------------------------------------
_install_lemon() {
local lemon_prefix=${PREFIX:-"/usr/local"}
local lemon_installed_version="none"
if [[ -f "${lemon_prefix}/include/lemon/config.h" ]]; then
lemon_installed_version=$(grep "LEMON_VERSION" "${lemon_prefix}/include/lemon/config.h" | awk -F'"' '{print $2}')
fi
log "Checking Lemon (System: ${lemon_installed_version}, Required: ${LEMON_VERSION})"
if [[ "${lemon_installed_version}" != "${LEMON_VERSION}" ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning Lemon..." git clone --depth=1 -b "${LEMON_VERSION}" https://github.com/The-OpenROAD-Project/lemon-graph.git
cd lemon-graph
local cmake_bin=${PREFIX:-/usr/local}/bin/cmake
_execute "Configuring Lemon..." "${cmake_bin}" -DCMAKE_INSTALL_PREFIX="${lemon_prefix}" -B build .
_execute "Building and installing Lemon..." "${cmake_bin}" --build build -j "${NUM_THREADS}" --target install
)
INSTALL_SUMMARY+=("Lemon: system=${lemon_installed_version}, required=${LEMON_VERSION}, path=${lemon_prefix}, status=installed")
else
INSTALL_SUMMARY+=("Lemon: system=${lemon_installed_version}, required=${LEMON_VERSION}, path=${lemon_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D LEMON_ROOT=$(realpath "${lemon_prefix}") "
}
# ------------------------------------------------------------------------------
# spdlog
# ------------------------------------------------------------------------------
_install_spdlog() {
local spdlog_prefix=${PREFIX:-"/usr/local"}
local spdlog_installed_version="none"
if [ -d "${spdlog_prefix}/include/spdlog" ]; then
spdlog_installed_version=$(grep "#define SPDLOG_VER_" "${spdlog_prefix}/include/spdlog/version.h" | sed 's/.*\s//' | tr '\n' '.' | sed 's/\.$//')
fi
log "Checking spdlog (System: ${spdlog_installed_version}, Required: ${SPDLOG_VERSION})"
if [[ "${spdlog_installed_version}" != "${SPDLOG_VERSION}" ]]; then
(
cd "${BASE_DIR}"
_execute "Cloning spdlog..." git clone --depth=1 -b "v${SPDLOG_VERSION}" https://github.com/gabime/spdlog.git
cd spdlog
local cmake_bin=${PREFIX:-/usr/local}/bin/cmake
_execute "Configuring spdlog..." "${cmake_bin}" -DCMAKE_INSTALL_PREFIX="${spdlog_prefix}" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSPDLOG_BUILD_EXAMPLE=OFF -B build .
_execute "Building and installing spdlog..." "${cmake_bin}" --build build -j "${NUM_THREADS}" --target install
)
INSTALL_SUMMARY+=("spdlog: system=${spdlog_installed_version}, required=${SPDLOG_VERSION}, path=${spdlog_prefix}, status=installed")
else
INSTALL_SUMMARY+=("spdlog: system=${spdlog_installed_version}, required=${SPDLOG_VERSION}, path=${spdlog_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D spdlog_ROOT=$(realpath "${spdlog_prefix}") "
}
# ------------------------------------------------------------------------------
# gtest
# ------------------------------------------------------------------------------
_install_gtest() {
local gtest_prefix=${PREFIX:-"/usr/local"}
log "Checking gtest (Required: ${GTEST_VERSION})"
if [[ ! -d ${gtest_prefix}/include/gtest ]]; then
(
cd "${BASE_DIR}"
_execute "Downloading gtest..." wget $OPT_NOCERT "https://github.com/google/googletest/archive/refs/tags/v${GTEST_VERSION}.zip"
_verify_checksum "${GTEST_CHECKSUM}" "v${GTEST_VERSION}.zip" || error "gtest checksum failed."
_execute "Extracting gtest..." unzip "v${GTEST_VERSION}.zip"
cd "googletest-${GTEST_VERSION}"
local cmake_bin=${PREFIX:-/usr/local}/bin/cmake
_execute "Configuring gtest..." "${cmake_bin}" -DCMAKE_INSTALL_PREFIX="${gtest_prefix}" -B build .
_execute "Building and installing gtest..." "${cmake_bin}" --build build --target install
)
INSTALL_SUMMARY+=("gtest: system=none, required=${GTEST_VERSION}, path=${gtest_prefix}, status=installed")
else
INSTALL_SUMMARY+=("gtest: system=found, required=${GTEST_VERSION}, path=${gtest_prefix}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D GTest_ROOT=$(realpath "${gtest_prefix}") "
}
# ------------------------------------------------------------------------------
# Abseil
# ------------------------------------------------------------------------------
_install_abseil() {
local absl_prefix_install=${PREFIX:-"/usr/local"}
local absl_prefix_found=""
local absl_version_file=""
# Check in default/user-specified prefix first
local absl_version_file_default="${absl_prefix_install}/lib/cmake/absl/abslConfigVersion.cmake"
if [[ -f "${absl_version_file_default}" ]]; then
absl_prefix_found="${absl_prefix_install}"
absl_version_file="${absl_version_file_default}"
fi
# If not found, check in or-tools path
if [[ -z "${absl_prefix_found}" && -n "${OR_TOOLS_PATH}" ]]; then
local absl_version_file_or_tools="${OR_TOOLS_PATH}/lib/cmake/absl/abslConfigVersion.cmake"
if [[ -f "${absl_version_file_or_tools}" ]]; then
absl_prefix_found="${OR_TOOLS_PATH}"
absl_version_file="${absl_version_file_or_tools}"
fi
fi
local absl_installed_version="none"
if [[ -n "${absl_version_file}" ]]; then
absl_installed_version=$(grep '^set(PACKAGE_VERSION "' "${absl_version_file}" | head -n 1 | awk -F'"' '{print $2}')
fi
local required_version="${ABSL_VERSION%.*}"
log "Checking Abseil (System: ${absl_installed_version}, Required: ${required_version})"
if [[ "${absl_installed_version}" != "${required_version}" ]]; then
(
cd "${BASE_DIR}"
_execute "Downloading Abseil..." wget $OPT_NOCERT "https://github.com/abseil/abseil-cpp/releases/download/${ABSL_VERSION}/abseil-cpp-${ABSL_VERSION}.tar.gz"
_verify_checksum "${ABSL_CHECKSUM}" "abseil-cpp-${ABSL_VERSION}.tar.gz" || error "Abseil checksum failed."
_execute "Extracting Abseil..." tar xf "abseil-cpp-${ABSL_VERSION}.tar.gz"
cd "abseil-cpp-${ABSL_VERSION}"
local cmake_bin=${PREFIX:-/usr/local}/bin/cmake
_execute "Configuring Abseil..." "${cmake_bin}" -DCMAKE_INSTALL_PREFIX="${absl_prefix_install}" -DCMAKE_CXX_STANDARD=17 -B build .
_execute "Building and installing Abseil..." "${cmake_bin}" --build build --target install
)
absl_prefix_found="${absl_prefix_install}"
INSTALL_SUMMARY+=("Abseil: system=${absl_installed_version}, required=${required_version}, path=${absl_prefix_found}, status=installed")
else
INSTALL_SUMMARY+=("Abseil: system=${absl_installed_version}, required=${required_version}, path=${absl_prefix_found}, status=skipped")
fi
CMAKE_PACKAGE_ROOT_ARGS+=" -D ABSL_ROOT=$(realpath "${absl_prefix_found}") "
}
_install_or_tools() {
local os=$1
local os_version=$2
local arch=$3
local skip_system_or_tools=$4
# Create a temporary directory for downloads
local build_dir
build_dir=$(mktemp -d)
local or_tools_installed_version="none"
# Check if a system-installed version of OR-Tools is present
log "Checking or-tools (System: ${or_tools_installed_version}, Required: ${OR_TOOLS_VERSION_SMALL})"
if [[ "${skip_system_or_tools}" == "false" ]]; then
# Disable exit on error for 'find' command, as it might return non zero
set +euo pipefail
local existing_libs
local search_paths=""
if [[ -n "${PREFIX}" ]]; then
search_paths="${OR_TOOLS_PATH}"
else
search_paths="/usr/local /usr /opt"
fi
existing_libs=$(find ${search_paths} -type f -name "libortools.so.*" 2>/dev/null)
# Bring back exit on error
set -euo pipefail
if [[ -n "${existing_libs}" ]]; then
local first_lib="${existing_libs%%$'\n'*}"
or_tools_installed_version=$(basename "${first_lib}" | sed 's/libortools.so.//')
local or_tools_root
or_tools_root=$(dirname "${first_lib}")
local or_tools_install_dir
or_tools_install_dir=$(realpath "${or_tools_root}/..")
if [[ "${or_tools_installed_version}" == "${OR_TOOLS_VERSION_SMALL}" ]]; then
CMAKE_PACKAGE_ROOT_ARGS+=" -D ortools_ROOT=${or_tools_install_dir} "
OR_TOOLS_PATH=${or_tools_install_dir}
INSTALL_SUMMARY+=("or-tools: system=${or_tools_installed_version}, required=${OR_TOOLS_VERSION_SMALL}, path=${OR_TOOLS_PATH}, status=skipped")
return
else
log "Found old OR-Tools version ${or_tools_installed_version}. Removing it."
rm -rf "${or_tools_install_dir}"
fi
fi
fi
if [[ "$(uname -m)" == "aarch64" ]]; then
(
cd "${build_dir}"
_execute "Cloning or-tools..." git clone --depth=1 -b "v${OR_TOOLS_VERSION_BIG}" https://github.com/google/or-tools.git
cd or-tools
local cmake_bin=${PREFIX:-/usr/local}/bin/cmake
_execute "Configuring or-tools..." "${cmake_bin}" -S. -Bbuild -DBUILD_DEPS:BOOL=ON -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_SAMPLES:BOOL=OFF -DBUILD_TESTING:BOOL=OFF -DCMAKE_INSTALL_PREFIX="${OR_TOOLS_PATH}" -DCMAKE_CXX_FLAGS="-w" -DCMAKE_C_FLAGS="-w"
_execute "Building and installing or-tools..." "${cmake_bin}" --build build --config Release --target install -v -j "${NUM_THREADS}"
)
else
(
cd "${build_dir}"
if [[ "${os_version}" == "rodete" ]]; then
os_version=11
fi
local or_tools_file="or-tools_${arch}_${os}-${os_version}_cpp_v${OR_TOOLS_VERSION_SMALL}.tar.gz"
_execute "Downloading or-tools..." wget $OPT_NOCERT "https://github.com/google/or-tools/releases/download/v${OR_TOOLS_VERSION_BIG}/${or_tools_file}"
mkdir -p "${OR_TOOLS_PATH}"
_execute "Extracting or-tools..." tar --strip 1 --dir "${OR_TOOLS_PATH}" -xf "${or_tools_file}"
)
fi
INSTALL_SUMMARY+=("or-tools: system=${or_tools_installed_version}, required=${OR_TOOLS_VERSION_SMALL}, path=${OR_TOOLS_PATH}, status=installed")
CMAKE_PACKAGE_ROOT_ARGS+=" -D ortools_ROOT=$(realpath "${OR_TOOLS_PATH}") "
rm -rf "${build_dir}"
}
# ==============================================================================
# Dependency Installation Modules
# ==============================================================================
# Each dependency will have its own dedicated function for installation and
# version management. This modular approach makes the script easier to
# maintain and extend.
_install_common_dev() {
log "Install common development dependencies (-common or -all)"
rm -rf "${BASE_DIR}"
mkdir -p "${BASE_DIR}"
if [[ -n "${PREFIX}" ]]; then
mkdir -p "${PREFIX}"
fi
_install_cmake
_install_bison
_install_flex
_install_pcre
_install_swig
_install_boost
_install_eigen
_install_cudd
_install_cusp
_install_lemon
_install_spdlog
_install_gtest
if [[ "${EQUIVALENCE_DEPS}" == "yes" ]]; then
_install_equivalence_deps
fi
if [[ -n ${PREFIX} ]]; then
# Emit an environment setup script
cat > "${PREFIX}/env.sh" <<EOF
if [ -n "\$ZSH_VERSION" ]; then
depRoot="\$(dirname \$(readlink -f "\${(%):-%x}"))"
else
depRoot="\$(dirname \$(readlink -f "\${BASH_SOURCE}"))"
fi
PATH=\${depRoot}/bin:\${PATH}
LD_LIBRARY_PATH=\${depRoot}/lib64:\${depRoot}/lib:\${LD_LIBRARY_PATH}
EOF
fi
}
# ==============================================================================
# Platform-Specific Logic
# ==============================================================================
# This section will contain functions for installing platform-specific packages
# using the system's package manager (e.g., apt, yum, brew).
# ... platform-specific functions will be added here ...
# ==============================================================================
# Main Execution
# ==============================================================================
# The main part of the script will parse command-line arguments, detect the
# platform, and call the appropriate functions to install the dependencies.
# ... main execution logic will be added here ...
# ------------------------------------------------------------------------------
# Ubuntu
# ------------------------------------------------------------------------------
_install_ubuntu_packages() {
log "Install ubuntu base packages using apt (-base or -all)"
export DEBIAN_FRONTEND="noninteractive"
_execute "Updating package lists..." apt-get -y update
_execute "Installing base packages..." apt-get -y install --no-install-recommends \
automake autotools-dev binutils bison build-essential ccache clang \
debhelper devscripts flex g++ gcc git groff lcov libbz2-dev libffi-dev libfl-dev \
libgomp1 libomp-dev libpcre2-dev libreadline-dev pandoc \
pkg-config python3-dev qt5-image-formats-plugins tcl tcl-dev tcl-tclreadline \
tcllib unzip wget libyaml-cpp-dev zlib1g-dev tzdata
local packages=()
if _version_compare "$1" -ge "25.04"; then
packages+=("libtcl8.6")
else
packages+=("libtcl")
fi
if _version_compare "$1" -ge "26.04"; then
packages+=("libpython3.14")
elif _version_compare "$1" -ge "25.04"; then
packages+=("libpython3.13")
elif _version_compare "$1" -ge "24.04"; then
packages+=("libpython3.12")
elif _version_compare "$1" -ge "22.10"; then
packages+=("libpython3.11")
else
packages+=("libpython3.8")
fi
if _version_compare "$1" -ge "22.04"; then
packages+=("qt5-qmake" "qtbase5-dev" "qtbase5-dev-tools" "libqt5charts5-dev" "qtchooser")
else
packages+=("libqt5charts5-dev" "qt5-default")
fi
_execute "Installing version-specific packages..." apt-get install -y --no-install-recommends "${packages[@]}"
_execute "Cleaning up packages..." apt-get autoclean -y
_execute "Removing unused packages..." apt-get autoremove -y
}
# ------------------------------------------------------------------------------
# RHEL
# ------------------------------------------------------------------------------
_install_rhel_packages() {
log "Install rhel base packages using yum (-base or -all)"
local rhel_version=$1
_execute "Updating packages..." yum -y update
_execute "Installing EPEL release..." yum -y install "https://dl.fedoraproject.org/pub/epel/epel-release-latest-${rhel_version}.noarch.rpm"
_execute "Installing base packages..." yum -y install \
autoconf automake clang clang-devel gcc gcc-c++ gdb git glibc-devel \
bzip2-devel libffi-devel libtool llvm llvm-devel llvm-libs make \
pcre2-devel pkg-config pkgconf pkgconf-m4 pkgconf-pkg-config python3 \
python3-devel python3-pip qt5-qtbase-devel qt5-qtcharts-devel \
qt5-qtimageformats readline tcl-devel tcl-tclreadline \
tcl-tclreadline-devel tcl-thread-devel tcllib wget yaml-cpp-devel \
zlib-devel tzdata redhat-rpm-config rpm-build
if [[ "${rhel_version}" == "8" ]]; then
local python_version="3.12"
_execute "Installing Python ${python_version}..." yum install -y gcc-toolset-13 "python${python_version}" "python${python_version}-devel" "python${python_version}-pip"
_execute "Setting Python alternatives..." update-alternatives --install /usr/bin/unversioned-python python "$(command -v "python${python_version}")" 50
_execute "Setting Python3 alternatives..." update-alternatives --install /usr/bin/python3 python3 "$(command -v "python${python_version}")" 50
fi
if [[ "${rhel_version}" == "9" ]]; then
_execute "Installing additional packages for RHEL 9..." yum install -y \
https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/flex-2.6.4-9.el9.x86_64.rpm \
https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/readline-devel-8.1-4.el9.x86_64.rpm \
https://rpmfind.net/linux/centos-stream/9-stream/AppStream/x86_64/os/Packages/tcl-devel-8.6.10-7.el9.x86_64.rpm
fi
local arch=amd64
local pandoc_version="3.1.11.1"
_execute "Downloading pandoc..." wget $OPT_NOCERT "https://github.com/jgm/pandoc/releases/download/${pandoc_version}/pandoc-${pandoc_version}-linux-${arch}.tar.gz"
_execute "Installing pandoc..." tar xvzf "pandoc-${pandoc_version}-linux-${arch}.tar.gz" --strip-components 1 -C /usr/local/
rm -rf "pandoc-${pandoc_version}-linux-${arch}.tar.gz"
_execute "Cleaning up yum cache..." yum clean -y all
rm -rf /var/lib/apt/lists/*
}
# ------------------------------------------------------------------------------
# openSUSE
# ------------------------------------------------------------------------------
_install_opensuse_packages() {
log "Install openSUSE base packages using zypper (-base or -all)"
_execute "Refreshing repositories..." zypper refresh
_execute "Updating packages..." zypper -n update
_execute "Installing development pattern..." zypper -n install -t pattern devel_basis
_execute "Installing base packages..." zypper -n install \
binutils clang gcc gcc11-c++ git groff gzip lcov libbz2-devel libffi-devel \
libgomp1 libomp11-devel libpython3_6m1_0 libqt5-creator libqt5-qtbase \
libqt5-qtstyleplugins libstdc++6-devel-gcc8 llvm pandoc \
pcre2-devel pkg-config python3-devel python3-pip readline-devel tcl \
tcl-devel tcllib wget yaml-cpp-devel zlib-devel
_execute "Setting gcc alternatives..." update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 50
_execute "Setting g++ alternatives..." update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 50
_execute "Cleaning up zypper cache..." zypper -n clean --all
_execute "Removing unneeded packages..." bash -c "zypper -n packages --unneeded | awk -F'|' 'NR > 4 {print \$3}' | grep -v Name | xargs -r zypper -n remove --clean-deps"
}
# ------------------------------------------------------------------------------
# Darwin (macOS)
# ------------------------------------------------------------------------------
_install_darwin_packages() {
if ! _command_exists "brew"; then
error "Homebrew is not found. Please install Homebrew before continuing."
fi
if ! xcode-select -p &> /dev/null; then
cat <<EOF
Xcode command line tools not installed.
Run the following command to install them:
xcode-select --install
Then, rerun this script.
EOF
exit 1
fi
log "Install darwin base packages using homebrew (-base or -all)"
_execute "Installing Homebrew packages..." brew install bison boost bzip2 cmake eigen flex fmt groff googletest icu4c libomp or-tools pandoc pkg-config qt@5 python spdlog tcl-tk@8 zlib swig yaml-cpp
# _execute "Installing pipx..." brew install pipx
_execute "Installing Python click..." pip install click
_execute "Linking libomp..." brew link --force libomp
_execute "Installing lemon-graph..." brew install The-OpenROAD-Project/lemon-graph/lemon-graph
}
# ------------------------------------------------------------------------------
# Debian
# ------------------------------------------------------------------------------
_install_debian_packages() {
log "Install debian base packages using apt (-base or -all)"
local debian_version=$1
export DEBIAN_FRONTEND="noninteractive"
_execute "Updating package lists..." apt-get -y update
local tcl_ver=""
if [[ "${debian_version}" == "rodete" ]] || _version_compare "${debian_version}" -ge "13"; then
tcl_ver="8.6"
fi
_execute "Installing base packages..." apt-get -y install --no-install-recommends \
automake autotools-dev binutils bison build-essential clang debhelper \
devscripts flex g++ gcc git groff lcov libbz2-dev libffi-dev libfl-dev libgomp1 \
libomp-dev libpcre2-dev libreadline-dev "libtcl${tcl_ver}" \
pandoc pkg-config python3-dev qt5-image-formats-plugins tcl-dev tcl-tclreadline \
tcllib unzip wget libyaml-cpp-dev zlib1g-dev tzdata
if [[ "${debian_version}" == "10" ]]; then
_execute "Installing Debian 10 specific packages..." apt-get install -y --no-install-recommends libpython3.7 libqt5charts5-dev qt5-default
else
local python_ver="3.8"
if [[ "${debian_version}" == "rodete" ]]; then
python_ver="3.12"
elif _version_compare "${debian_version}" -ge "13"; then
python_ver="3.13"
fi
_execute "Installing Debian specific packages..." apt-get install -y --no-install-recommends "libpython${python_ver}" libqt5charts5-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
fi
_execute "Cleaning up packages..." apt-get autoclean -y
_execute "Removing unused packages..." apt-get autoremove -y
}
# ------------------------------------------------------------------------------
# CI Dependencies
# ------------------------------------------------------------------------------
_install_ci_packages() {
log "Install CI dependencies (-ci)"
_execute "Updating package lists..." apt-get -y update
_execute "Installing CI packages..." apt-get -y install --no-install-recommends \
apt-transport-https ca-certificates curl gnupg jq lsb-release parallel \
python3 python3-pandas python3-pip software-properties-common \
time unzip zip
_execute "Downloading bazelisk..." curl -Lo bazelisk https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64
chmod +x bazelisk
_execute "Installing bazelisk..." mv bazelisk /usr/local/bin/bazelisk
if _command_exists "docker"; then
log "Docker is already installed, skipping reinstallation."
return 0
fi
_execute "Creating Docker keyring directory..." install -m 0755 -d /etc/apt/keyrings
_execute "Downloading Docker GPG key..." curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
_execute "Adding Docker repository..." bash -c 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo \"${VERSION_CODENAME}\") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null'
_execute "Updating package lists for Docker..." apt-get -y update
_execute "Installing Docker..." apt-get -y install --no-install-recommends docker-ce docker-ce-cli containerd.io docker-buildx-plugin
if _version_compare "${1}" -lt "24.04"; then
_execute "Downloading LLVM install script..." wget $OPT_NOCERT https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
_execute "Installing LLVM 16..." ./llvm.sh 16 all
fi
}
_help() {
cat <<EOF
Usage: $0 [OPTIONS]
Options:
-all Install all dependencies (base and common). Requires privileged access.
-base Install base dependencies using package managers. Requires privileged access.
-common Install common dependencies.
-eqy Install equivalence dependencies (yosys, eqy, sby).
-prefix=DIR Install common dependencies in a user-specified directory.
-local Install common dependencies in \${HOME}/.local.
-ci Install dependencies required for CI.
-nocert Disable certificate checks for downloads.
-skip-system-or-tools Skip searching for a system-installed or-tools library.
-save-deps-prefixes=FILE Save OpenROAD build arguments to FILE.
-constant-build-dir Use a constant build directory instead of a random one.
-threads=<N> Limit the number of compiling threads.
-yosys-ver=<VERSION> Specify a custom Yosys version. Used for ORFS.
-verbose Show all output from build commands.
-h, -help Show this help message.
EOF
exit "${1:-1}"
}
main() {
local option="none"
while [ "$#" -gt 0 ]; do
case "${1}" in
-h|-help) _help 0 ;;
-all) option="all" ;;
-base) option="base" ;;
-common) option="common" ;;
-eqy) EQUIVALENCE_DEPS="yes" ;;
-ci) CI="yes" ;;
-verbose) VERBOSE_MODE="yes" ;;
-local)
if [[ $(id -u) == 0 ]]; then
error "Cannot use -local with root or sudo."
fi
if [[ -n "${PREFIX}" ]]; then
warn "Previous -prefix argument will be overwritten with -local."
fi
PREFIX="${HOME}/.local"
;;
-constant-build-dir)
if [[ -d "${BASE_DIR}" ]]; then
log "Removing old building directory ${BASE_DIR}"
rm -r "${BASE_DIR}"
fi
BASE_DIR="/tmp/DependencyInstaller-OpenROAD"
mkdir -p "${BASE_DIR}"
;;
-prefix=*)
if [[ -n "${PREFIX}" ]]; then
warn "Previous -local argument will be overwritten with -prefix."
fi
PREFIX="${1#*=}"
if [[ ! "${PREFIX}" = /* ]]; then
PREFIX="$(pwd)/${PREFIX}"
fi
;;
-nocert)
warn "Security certificates for downloaded packages will not be checked."
OPT_NOCERT="--no-check-certificate"
export GIT_SSL_NO_VERIFY=true
;;
-skip-system-or-tools) SKIP_SYSTEM_OR_TOOLS="true" ;;
-save-deps-prefixes=*) SAVE_DEPS_PREFIXES="$(realpath "${1#*=}")" ;;
-threads=*) NUM_THREADS="${1#*=}" ;;
-yosys-ver=*) YOSYS_VERSION="${1#*=}" ;;
*)
echo "Unknown option: ${1}" >&2
_help
;;
esac
shift 1
done
if [[ "${option}" == "none" ]]; then
error "You must use one of: -all, -base, or -common."
fi
OR_TOOLS_PATH=${PREFIX:-"/opt/or-tools"}
if [[ -z "${SAVE_DEPS_PREFIXES}" ]]; then
local dir
dir="$(dirname "$(readlink -f "$0")")"
SAVE_DEPS_PREFIXES="${dir}/openroad_deps_prefixes.txt"
fi
local platform
platform="$(uname -s)"
case "${platform}" in
"Linux")
if [[ ! -f /etc/os-release ]]; then
error "Unidentified OS, could not find /etc/os-release."
fi
local os
os=$(awk -F= '/^NAME/{print $2}' /etc/os-release | sed 's/"//g')
;;
"Darwin")
if [[ $(id -u) == 0 ]]; then
error "Cannot install on macOS with root or sudo."
fi
os="Darwin"
;;
*)
error "${platform} is not supported. We only officially support Linux at the moment."
;;
esac
case "${os}" in
"Ubuntu")
local ubuntu_version
ubuntu_version=$(awk -F= '/^VERSION_ID/{print $2}' /etc/os-release | sed 's/"//g')
if [[ "${option}" == "base" || "${option}" == "all" ]]; then
_install_ubuntu_packages "${ubuntu_version}"
fi
if [[ "${CI}" == "yes" ]]; then
_install_ci_packages "${ubuntu_version}"
fi
if [[ "${option}" == "common" || "${option}" == "all" ]]; then
_install_common_dev
local ubuntu_version_normalized=${ubuntu_version}
if _version_compare "${ubuntu_version_normalized}" -ge "25.04"; then
# FIXME make do with or-tools for 24.10 until an official release for 25.04 is available
ubuntu_version_normalized="24.10"
elif _version_compare "${ubuntu_version_normalized}" -ge "24.04"; then
ubuntu_version_normalized="24.04"
elif _version_compare "${ubuntu_version_normalized}" -ge "22.04"; then
ubuntu_version_normalized="22.04"
else
ubuntu_version_normalized="20.04"
fi
_install_or_tools "ubuntu" "${ubuntu_version_normalized}" "amd64" "${SKIP_SYSTEM_OR_TOOLS}"
_install_abseil
fi
;;
"Red Hat Enterprise Linux" | "Rocky Linux" | "AlmaLinux")
local rhel_version
if [[ "${os}" == "Red Hat Enterprise Linux" ]]; then
rhel_version=$(rpm -q --queryformat '%{VERSION}' redhat-release | cut -d. -f1)
elif [[ "${os}" == "Rocky Linux" ]]; then
rhel_version=$(rpm -q --queryformat '%{VERSION}' rocky-release | cut -d. -f1)
elif [[ "${os}" == "AlmaLinux" ]]; then
rhel_version=$(rpm -q --queryformat '%{VERSION}' almalinux-release | cut -d. -f1)
fi
if [[ "${rhel_version}" != "8" && "${rhel_version}" != "9" ]]; then
error "Unsupported ${rhel_version} version. Versions '8' and '9' are supported."
fi
if [[ "${option}" == "base" || "${option}" == "all" ]]; then
_install_rhel_packages "${rhel_version}"
fi
if [[ "${option}" == "common" || "${option}" == "all" ]]; then
_install_common_dev
local os_id
os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release | sed 's/"//g')
local arch
arch=$(uname -m)
local or_tools_arch=${arch}
local or_tools_distro=""
local or_tools_version=""
if [[ "${rhel_version}" == "8" ]]; then
or_tools_distro="AlmaLinux"
or_tools_version="8.10"
if [[ "${os_id}" != "almalinux" ]]; then
warn "Using AlmaLinux or-tools package for RHEL 8 compatible system."
fi
elif [[ "${rhel_version}" == "9" ]]; then
or_tools_version="9"
if [[ "${arch}" == "x86_64" ]]; then
or_tools_arch="amd64"
fi
if [[ "${os_id}" == "almalinux" || "${os_id}" == "rocky" ]]; then
or_tools_distro="${os_id}"
else
or_tools_distro="rockylinux"
warn "Defaulting to rockylinux or-tools package for RHEL 9 compatible system."
fi
fi
_install_or_tools "${or_tools_distro}" "${or_tools_version}" "${or_tools_arch}" "${SKIP_SYSTEM_OR_TOOLS}"
_install_abseil
fi
;;
"Darwin")
_install_darwin_packages
cat <<EOF
To install or run OpenROAD, update your path with:
export PATH="\$(brew --prefix bison)/bin:\$(brew --prefix flex)/bin:\$(brew --prefix tcl-tk@8)/bin:\${PATH}"
export CMAKE_PREFIX_PATH=\$(brew --prefix or-tools)
EOF
;;
"openSUSE Leap")
if [[ "${option}" == "base" || "${option}" == "all" ]]; then
_install_opensuse_packages
fi
if [[ "${option}" == "common" || "${option}" == "all" ]]; then
_install_common_dev
_install_or_tools "opensuse" "leap" "amd64" "${SKIP_SYSTEM_OR_TOOLS}"
_install_abseil
fi
cat <<EOF
To enable GCC-11 you need to run:
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 50
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 50
EOF
;;
"Debian GNU/Linux" | "Debian GNU/Linux rodete")
local debian_version
debian_version=$(awk -F= '/^VERSION_ID/{print $2}' /etc/os-release | sed 's/"//g')
if [[ -z "${debian_version}" ]]; then
debian_version=$(awk -F= '/^VERSION_CODENAME/{print $2}' /etc/os-release | sed 's/"//g')
fi
if [[ "${option}" == "base" || "${option}" == "all" ]]; then
_install_debian_packages "${debian_version}"
fi
if [[ "${option}" == "common" || "${option}" == "all" ]]; then
_install_common_dev
local debian_version_normalized=${debian_version}
if _version_compare "${debian_version_normalized}" -ge "13"; then
# FIXME use debian-13 once or-tools publishes an official release for it
debian_version_normalized="sid"
fi
_install_or_tools "debian" "${debian_version_normalized}" "amd64" "${SKIP_SYSTEM_OR_TOOLS}"
_install_abseil
fi
;;
*)
error "Unsupported system: ${os}"
;;
esac
if [[ -n "${SAVE_DEPS_PREFIXES}" ]]; then
mkdir -p "$(dirname "${SAVE_DEPS_PREFIXES}")"
echo "${CMAKE_PACKAGE_ROOT_ARGS}" > "${SAVE_DEPS_PREFIXES}"
if [[ $(id -u) == 0 && -n "${SUDO_USER+x}" ]]; then
chown "${SUDO_USER}:$(id -gn "${SUDO_USER}")" "${SAVE_DEPS_PREFIXES}" 2>/dev/null || true
fi
fi
_print_summary
rm -rf "${BASE_DIR}"
}
_print_summary() {
if [ ${#INSTALL_SUMMARY[@]} -eq 0 ]; then
return
fi
echo ""
log "Installation Summary"
echo "${BOLD}=============================================================================================${NC}"
printf "%-15s | %-25s | %-15s | %-15s | %-10s\n" "Package" "Path" "Found" "Required" "Status"
printf "%-15s | %-25s | %-15s | %-15s | %-10s\n" "---------------" "-------------------------" "---------------" "---------------" "----------"
for summary_line in "${INSTALL_SUMMARY[@]}"; do
# summary_line is like "Flex: system=2.6.4, required=2.6.4, path=/usr/local, status=skipped"
local package
package=$(echo "$summary_line" | cut -d':' -f1)
local system_version
system_version=$(echo "$summary_line" | sed -n 's/.*system=\([^,]*\).*/\1/p')
local required_version
required_version=$(echo "$summary_line" | sed -n 's/.*required=\([^,]*\).*/\1/p')
local path
path=$(echo "$summary_line" | sed -n 's/.*path=\([^,]*\).*/\1/p')
local truncated_path
truncated_path=$(_truncate_path "$path" 25)
local status
status=$(echo "$summary_line" | sed -n 's/.*status=\([^,]*\).*/\1/p')
local status_color=""
if [[ "${status}" == "installed" ]]; then
status_color="${GREEN}"
elif [[ "${status}" == "skipped" ]]; then
status_color="${YELLOW}"
fi
printf "%-15s | %-25s | %-15s | %-15s | ${status_color}%-10s${NC}\n" "$package" "$truncated_path" "$system_version" "$required_version" "$status"
done
echo "${BOLD}=============================================================================================${NC}"
}
main "$@"