diff --git a/crosstool-ng/configure.sh b/crosstool-ng/configure.sh deleted file mode 100755 index 4cc05343e..000000000 --- a/crosstool-ng/configure.sh +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC2001,SC2207 - -set -eo pipefail - -# script to programmatically generate the ct-ng config files -# this sets the GCC and glibc versions, so we don't have to hardcode -# them on every build. - -scriptdir=$(dirname "${BASH_SOURCE[0]}") -scriptdir=$(realpath "${scriptdir}") - -# allow overrides for the default toolchain versions -# we want to support RHEL7 for glibc 2.17.0, and keep -# the same gcc and linux versions as the other images. -if [[ -z "${GCC_VERSION}" ]]; then - GCC_VERSION="8.3.0" -fi -if [[ -z "${GLIBC_VERSION}" ]]; then - GLIBC_VERSION="2.17.0" -fi -if [[ -z "${LINUX_VERSION}" ]]; then - LINUX_VERSION="4.19.21" -fi - -configure_template() { - local gcc_major - local gcc_minor - local gcc_patch - local glibc_major - local glibc_minor - local glibc_patch - local linux_major - local linux_minor - local linux_patch - - gcc_major=$(echo "$GCC_VERSION" | cut -d '.' -f 1) - gcc_minor=$(echo "$GCC_VERSION" | cut -d '.' -f 2) - gcc_patch=$(echo "$GCC_VERSION" | cut -d '.' -f 3) - glibc_major=$(echo "$GLIBC_VERSION" | cut -d '.' -f 1) - glibc_minor=$(echo "$GLIBC_VERSION" | cut -d '.' -f 2) - # shellcheck disable=SC2034 - glibc_patch=$(echo "$GLIBC_VERSION" | cut -d '.' -f 3) - linux_major=$(echo "$LINUX_VERSION" | cut -d '.' -f 1) - linux_minor=$(echo "$LINUX_VERSION" | cut -d '.' -f 2) - linux_patch=$(echo "$LINUX_VERSION" | cut -d '.' -f 3) - - # write out our valid range of gcc values: from 5-10. - local ct_gcc_v - ct_gcc_v="CT_GCC_V_${gcc_major}=y\n" - ct_gcc_v="${ct_gcc_v}# CT_GCC_NO_VERSIONS is not set\n" - ct_gcc_v="${ct_gcc_v}CT_GCC_VERSION=\"${gcc_major}.${gcc_minor}.${gcc_patch}\"\n" - - # write out our gcc version ranges - local ct_gcc="" - local gcc_is_gt_49= - local gcc_is_ge_49= - local gcc_is_gt_48= - local gcc_is_ge_48= - if [[ "${gcc_major}" -gt "7" ]]; then - ct_gcc="${ct_gcc}CT_GCC_later_than_7=y\n" - fi - if [[ "${gcc_major}" -ge "7" ]]; then - ct_gcc="${ct_gcc}CT_GCC_7_or_later=y\n" - fi - if [[ "${gcc_major}" -gt "6" ]]; then - ct_gcc="${ct_gcc}CT_GCC_later_than_6=y\n" - fi - if [[ "${gcc_major}" -ge "6" ]]; then - ct_gcc="${ct_gcc}CT_GCC_6_or_later=y\n" - fi - if [[ "${gcc_major}" -gt "5" ]]; then - ct_gcc="${ct_gcc}CT_GCC_later_than_5=y\n" - fi - if [[ "${gcc_major}" -ge "5" ]]; then - ct_gcc="${ct_gcc}CT_GCC_5_or_later=y\n" - fi - if [[ "${gcc_major}" -ge "5" ]]; then - gcc_is_gt_49=1 - gcc_is_ge_49=1 - gcc_is_gt_48=1 - gcc_is_ge_48=1 - elif [[ "${gcc_major}" == "4" ]]; then - if [[ "${gcc_minor}" -gt "9" ]]; then - gcc_is_gt_49=1 - fi - if [[ "${gcc_minor}" -ge "9" ]]; then - gcc_is_ge_49=1 - fi - if [[ "${gcc_minor}" -gt "8" ]]; then - gcc_is_gt_48=1 - fi - if [[ "${gcc_minor}" -ge "8" ]]; then - gcc_is_ge_48=1 - fi - fi - if [[ -n "${gcc_is_gt_49}" ]]; then - ct_gcc="${ct_gcc}CT_GCC_later_than_4_9=y\n" - fi - if [[ -n "${gcc_is_ge_49}" ]]; then - ct_gcc="${ct_gcc}CT_GCC_4_9_or_later=y\n" - fi - if [[ -n "${gcc_is_gt_48}" ]]; then - ct_gcc="${ct_gcc}CT_GCC_later_than_4_8=y\n" - fi - if [[ -n "${gcc_is_ge_48}" ]]; then - ct_gcc="${ct_gcc}CT_GCC_4_8_or_later=y\n" - fi - - # write out our valid range of glibc values. - if [[ "${glibc_major}" != "2" ]]; then - echo "glibc major versions other than 2 currently unsupported, got ${glibc_major}." 2>&1 - exit 1 - fi - local ct_glibc_v - ct_glibc_v="CT_GLIBC_V_${glibc_major}_${glibc_minor}=y\n" - ct_glibc_v="${ct_glibc_v}# CT_GLIBC_NO_VERSIONS is not set\n" - ct_glibc_v="${ct_glibc_v}CT_GLIBC_VERSION=\"${glibc_major}.${glibc_minor}\"\n" - - # write out our glibc version ranges - local ct_glibc="" - if [[ "${glibc_minor}" -le "29" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_29_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "29" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_29=y\n" - fi - if [[ "${glibc_minor}" -le "27" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_27_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "27" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_27=y\n" - fi - if [[ "${glibc_minor}" -le "26" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_26_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "26" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_26=y\n" - fi - if [[ "${glibc_minor}" -le "25" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_25_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "25" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_25=y\n" - fi - if [[ "${glibc_minor}" -le "24" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_24_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "24" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_24=y\n" - fi - if [[ "${glibc_minor}" -le "23" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_23_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "23" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_23=y\n" - fi - if [[ "${glibc_minor}" -le "20" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_20_or_older=y\n" - fi - if [[ "${glibc_minor}" -lt "20" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_older_than_2_20=y\n" - fi - if [[ "${glibc_minor}" -ge "17" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_17_or_later=y\n" - fi - if [[ "${glibc_minor}" -le "17" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_17_or_older=y\n" - fi - if [[ "${glibc_minor}" -gt "14" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_later_than_2_14=y\n" - fi - if [[ "${glibc_minor}" -ge "14" ]]; then - ct_glibc="${ct_glibc}CT_GLIBC_2_14_or_later=y\n" - fi - - # write out our valid range of linux values. - local ct_linux_v="CT_LINUX_V_${linux_major}_${linux_minor}=y\n" - local ct_linux_v="${ct_linux_v}# CT_LINUX_NO_VERSIONS is not set\n" - local ct_linux_v="${ct_linux_v}CT_LINUX_VERSION=\"${linux_major}.${linux_minor}.${linux_patch}\"\n" - - # write out our linux version ranges - local ct_linux="" - if [[ "${linux_major}" -lt "4" ]]; then - ct_linux="${ct_linux}CT_LINUX_older_than_4_8=y\n" - ct_linux="${ct_linux}CT_LINUX_4_8_or_older=y\n" - elif [[ "${linux_major}" == "4" ]] && [[ "${linux_minor}" -lt "8" ]]; then - ct_linux="${ct_linux}CT_LINUX_older_than_4_8=y\n" - ct_linux="${ct_linux}CT_LINUX_4_8_or_older=y\n" - else - ct_linux="${ct_linux}CT_LINUX_later_than_4_8=y\n" - ct_linux="${ct_linux}CT_LINUX_4_8_or_later=y\n" - fi - if [[ "${linux_major}" -lt "3" ]]; then - ct_linux="${ct_linux}CT_LINUX_older_than_3_7=y\n" - ct_linux="${ct_linux}CT_LINUX_3_7_or_older=y\n" - elif [[ "${linux_major}" == "3" ]] && [[ "${linux_minor}" -lt "7" ]]; then - ct_linux="${ct_linux}CT_LINUX_older_than_3_7=y\n" - ct_linux="${ct_linux}CT_LINUX_3_7_or_older=y\n" - else - ct_linux="${ct_linux}CT_LINUX_later_than_3_7=y\n" - ct_linux="${ct_linux}CT_LINUX_3_7_or_later=y\n" - fi - if [[ "${linux_major}" -lt "3" ]]; then - ct_linux="${ct_linux}CT_LINUX_older_than_3_2=y\n" - ct_linux="${ct_linux}CT_LINUX_3_2_or_older=y\n" - elif [[ "${linux_major}" == "3" ]] && [[ "${linux_minor}" -lt "7" ]]; then - ct_linux="${ct_linux}CT_LINUX_older_than_3_2=y\n" - ct_linux="${ct_linux}CT_LINUX_3_2_or_older=y\n" - else - ct_linux="${ct_linux}CT_LINUX_later_than_3_2=y\n" - ct_linux="${ct_linux}CT_LINUX_3_2_or_later=y\n" - fi - - # now, replace our variables - local template - template=$(cat "${1}") - template=$(echo "${template}" | sed "s/%CT_GCC_V%/${ct_gcc_v}/") - template=$(echo "${template}" | sed "s/%CT_GCC%/${ct_gcc}/") - template=$(echo "${template}" | sed "s/%CT_GLIBC_V%/${ct_glibc_v}/") - template=$(echo "${template}" | sed "s/%CT_GLIBC%/${ct_glibc}/") - template=$(echo "${template}" | sed "s/%CT_LINUX_V%/${ct_linux_v}/") - template=$(echo "${template}" | sed "s/%CT_LINUX%/${ct_linux}/") - - echo "$template" -} - -main() { - local srcdir="${scriptdir}" - local dstdir="${scriptdir}/../docker/crosstool-config" - local filename - local srcfile - local dstfile - local config - - for srcfile in "$srcdir"/*".config.in"; do - filename=$(basename "$srcfile") - dstfile="${dstdir}/${filename//.in/}" - config=$(configure_template "${srcfile}") - echo "$config" > "$dstfile" - done -} - -main diff --git a/xtask/src/crosstool.rs b/xtask/src/crosstool.rs new file mode 100644 index 000000000..2f5ef5812 --- /dev/null +++ b/xtask/src/crosstool.rs @@ -0,0 +1,227 @@ +use std::fmt::Write; +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::util::{project_dir, write_to_string}; +use clap::Args; +use cross::ToUtf8; + +const DEFAULT_GCC_VERSION: &str = "8.3.0"; +const DEFAULT_GLIBC_VERSION: &str = "2.17.0"; +const DEFAULT_LINUX_VERSION: &str = "4.19.21"; +const DOCKER: &str = "docker"; +const CT_NG: &str = "crosstool-ng"; +const TOOLCHAINS: &str = "cross-toolchains"; +const CT_CONFIG: &str = "crosstool-config"; + +#[derive(Args, Debug)] +pub struct ConfigureCrosstool { + /// Provide verbose diagnostic output. + #[clap(short, long)] + pub verbose: bool, + /// The gcc version to configure for. + #[clap(long, env = "GCC_VERSION")] + pub gcc_version: Option, + /// The glibc version to configure for. + #[clap(long, env = "GLIBC_VERSION")] + pub glibc_version: Option, + /// The linux version to configure for. + #[clap(long, env = "LINUX_VERSION")] + pub linux_version: Option, + /// Targets to build for + #[clap()] + targets: Vec, +} + +fn locate_ctng_config( + target: &str, + root: &Path, + cross_toolchains_root: &Path, +) -> cross::Result<(PathBuf, PathBuf)> { + let config_name = format!("{target}.config"); + let template_name = format!("{config_name}.in"); + let ct_ng_root = root.join(CT_NG); + let cross_toolchains_ctng_root = cross_toolchains_root.join(CT_NG); + let (src_root, dst_root) = if cross_toolchains_ctng_root.join(&template_name).exists() { + ( + &cross_toolchains_ctng_root, + cross_toolchains_root.join(DOCKER).join(CT_CONFIG), + ) + } else if ct_ng_root.join(&template_name).exists() { + (&ct_ng_root, root.join(DOCKER).join(CT_CONFIG)) + } else { + eyre::bail!("unable to find config for target \"{target}\""); + }; + Ok((src_root.join(template_name), dst_root.join(config_name))) +} + +fn read_config_dir(dir: &Path) -> cross::Result> { + let mut targets = vec![]; + for entry in fs::read_dir(dir)? { + let file = entry?; + let basename = file.file_name().to_utf8()?.to_string(); + if let Some(target) = basename.strip_suffix(".config.in") { + targets.push(target.to_string()); + } + } + + Ok(targets) +} + +fn configure_target( + src_file: &Path, + gcc_version: &str, + glibc_version: &str, + linux_version: &str, +) -> cross::Result { + let gcc_versions: Vec<&str> = gcc_version.split('.').collect(); + let glibc_versions: Vec<&str> = glibc_version.split('.').collect(); + let linux_versions: Vec<&str> = linux_version.split('.').collect(); + if !matches!(gcc_versions.len(), 2 | 3) { + eyre::bail!("invalid GCC version, got {gcc_version}"); + } + if !matches!(glibc_versions.len(), 2 | 3) { + eyre::bail!("invalid glibc version, got {glibc_version}"); + } + if !matches!(linux_versions.len(), 2 | 3) { + eyre::bail!("invalid linux version, got {linux_version}"); + } + + // configure the `CT_GCC` values + let gcc_major = gcc_versions[0].parse::()?; + let gcc_minor = gcc_versions[1].parse::()?; + let gcc_patch = gcc_versions.get(2).unwrap_or(&"0").parse::()?; + let ct_gcc_v = format!( + "CT_GCC_V_{gcc_major}=y +# CT_GCC_NO_VERSIONS is not set +CT_GCC_VERSION=\"{gcc_major}.{gcc_minor}.{gcc_patch}\"" + ); + let mut ct_gcc = String::new(); + for major in (5..=7).rev() { + if gcc_major > major { + write!(ct_gcc, "\nCT_GCC_later_than_{major}=y")?; + } + if gcc_major >= major { + write!(ct_gcc, "\nCT_GCC_{major}_or_later=y")?; + } + } + if gcc_major > 4 || (gcc_major == 4 && gcc_major > 9) { + ct_gcc.push_str("\nCT_GCC_later_than_4_9=y"); + } + if gcc_major > 4 || (gcc_major == 4 && gcc_major >= 9) { + ct_gcc.push_str("\nCT_GCC_4_9_or_later=y"); + } + if gcc_major > 4 || (gcc_major == 4 && gcc_major > 8) { + ct_gcc.push_str("\nCT_GCC_later_than_4_8=y"); + } + if gcc_major > 4 || (gcc_major == 4 && gcc_major >= 8) { + ct_gcc.push_str("\nCT_GCC_4_8_or_later=y"); + } + + // configure the `CT_GLIBC` values + let glibc_major = glibc_versions[0].parse::()?; + let glibc_minor = glibc_versions[1].parse::()?; + let _glibc_patch = glibc_versions.get(2).unwrap_or(&"0").parse::()?; + if glibc_major != 2 { + eyre::bail!("glibc major versions other than 2 currently unsupported, got {glibc_major}"); + } + let ct_glibc_v = format!( + "CT_GLIBC_V_{glibc_major}_{glibc_minor}=y +# CT_GLIBC_NO_VERSIONS is not set +CT_GLIBC_VERSION=\"{glibc_major}.{glibc_minor}\"" + ); + let mut ct_glibc = String::new(); + let glibc_older = [29, 27, 26, 25, 24, 23, 20]; + for minor in glibc_older { + if glibc_minor <= minor { + write!(ct_glibc, "\nCT_GLIBC_2_{minor}_or_older=y")?; + } + if glibc_minor < minor { + write!(ct_glibc, "\nCT_GLIBC_older_than_2_{minor}=y")?; + } + } + if glibc_minor >= 17 { + ct_glibc.push_str("\nCT_GLIBC_2_17_or_later=y"); + } + if glibc_minor <= 17 { + ct_glibc.push_str("\nCT_GLIBC_2_17_or_older=y"); + } + if glibc_minor > 14 { + ct_glibc.push_str("\nCT_GLIBC_later_than_2_14=y"); + } + if glibc_minor >= 14 { + ct_glibc.push_str("\nCT_GLIBC_2_14_or_later=y"); + } + + // configure the `CT_LINUX` values + let linux_major = linux_versions[0].parse::()?; + let linux_minor = linux_versions[1].parse::()?; + let linux_patch = linux_versions.get(2).unwrap_or(&"0").parse::()?; + let ct_linux_v = format!( + "CT_LINUX_V_{linux_major}_{linux_minor}=y +# CT_LINUX_NO_VERSIONS is not set +CT_LINUX_VERSION=\"{linux_major}.{linux_minor}.{linux_patch}\"" + ); + let mut ct_linux = String::new(); + if linux_major < 4 || (linux_major == 4 && linux_minor < 8) { + ct_linux.push_str("\nCT_LINUX_older_than_4_8=y"); + ct_linux.push_str("\nCT_LINUX_4_8_or_older=y"); + } else { + ct_linux.push_str("\nCT_LINUX_later_than_4_8=y"); + ct_linux.push_str("\nCT_LINUX_4_8_or_later=y"); + } + if linux_major < 3 || (linux_major == 3 && linux_minor < 7) { + ct_linux.push_str("\nCT_LINUX_older_than_3_7=y"); + ct_linux.push_str("\nCT_LINUX_3_7_or_older=y"); + ct_linux.push_str("\nCT_LINUX_older_than_3_2=y"); + ct_linux.push_str("\nCT_LINUX_3_2_or_older=y"); + } else { + ct_linux.push_str("\nCT_LINUX_later_than_3_7=y"); + ct_linux.push_str("\nCT_LINUX_3_7_or_later=y"); + ct_linux.push_str("\nCT_LINUX_later_than_3_2=y"); + ct_linux.push_str("\nCT_LINUX_3_2_or_later=y"); + } + + Ok(fs::read_to_string(src_file)? + .replacen("%CT_GCC_V%", &ct_gcc_v, 1) + .replacen("%CT_GCC%", &ct_gcc, 1) + .replacen("%CT_GLIBC_V%", &ct_glibc_v, 1) + .replacen("%CT_GLIBC%", &ct_glibc, 1) + .replacen("%CT_LINUX_V%", &ct_linux_v, 1) + .replacen("%CT_LINUX%", &ct_linux, 1)) +} + +pub fn configure_crosstool( + ConfigureCrosstool { + verbose, + gcc_version, + glibc_version, + linux_version, + mut targets, + .. + }: ConfigureCrosstool, +) -> cross::Result<()> { + let gcc_version = gcc_version.as_deref().unwrap_or(DEFAULT_GCC_VERSION); + let glibc_version = glibc_version.as_deref().unwrap_or(DEFAULT_GLIBC_VERSION); + let linux_version = linux_version.as_deref().unwrap_or(DEFAULT_LINUX_VERSION); + + let root = project_dir(verbose)?; + let cross_toolchains_root = root.join(DOCKER).join(TOOLCHAINS); + if targets.is_empty() { + targets = read_config_dir(&root.join(CT_NG))?; + let cross_toolchains_ctng_root = cross_toolchains_root.join(CT_NG); + if cross_toolchains_ctng_root.exists() { + targets.append(&mut read_config_dir(&cross_toolchains_ctng_root)?); + } + } + let config_files = targets + .into_iter() + .map(|t| locate_ctng_config(&t, &root, &cross_toolchains_root)) + .collect::>>()?; + for (src_file, dst_file) in config_files { + let configured = configure_target(&src_file, gcc_version, glibc_version, linux_version)?; + write_to_string(&dst_file, &configured)?; + } + + Ok(()) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 55a2b5c8b..79b0ac04f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,6 +2,7 @@ pub mod build_docker_image; pub mod ci; +pub mod crosstool; pub mod hooks; pub mod install_git_hooks; pub mod target_info; @@ -13,6 +14,7 @@ use cross::docker; use util::{cargo_metadata, ImageTarget}; use self::build_docker_image::BuildDockerImage; +use self::crosstool::ConfigureCrosstool; use self::hooks::{Check, Test}; use self::install_git_hooks::InstallGitHooks; use self::target_info::TargetInfo; @@ -49,6 +51,8 @@ enum Commands { /// CI tasks #[clap(subcommand, hide = true)] CiJob(CiJob), + /// Configure crosstool config files. + ConfigureCrosstool(ConfigureCrosstool), } fn is_toolchain(toolchain: &str) -> cross::Result { @@ -85,6 +89,7 @@ pub fn main() -> cross::Result<()> { let metadata = cargo_metadata(true)?; ci::ci(args, metadata)? } + Commands::ConfigureCrosstool(args) => crosstool::configure_crosstool(args)?, } Ok(()) diff --git a/xtask/src/util.rs b/xtask/src/util.rs index 463e417f8..e62fc9f12 100644 --- a/xtask/src/util.rs +++ b/xtask/src/util.rs @@ -1,3 +1,5 @@ +use std::fs; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; @@ -189,3 +191,13 @@ pub fn cargo_metadata(verbose: bool) -> cross::Result { pub fn project_dir(verbose: bool) -> cross::Result { Ok(cargo_metadata(verbose)?.workspace_root) } + +pub fn write_to_string(path: &Path, contents: &str) -> cross::Result<()> { + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path)?; + writeln!(file, "{}", contents)?; + Ok(()) +}