From 93e43a82b638a5757ead6296295973c61e024825 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 10 Feb 2025 08:34:08 +0100 Subject: [PATCH] Add cross_toolchain.rb --- bin/cross_toolchain.rb | 314 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100755 bin/cross_toolchain.rb diff --git a/bin/cross_toolchain.rb b/bin/cross_toolchain.rb new file mode 100755 index 0000000..b54ee97 --- /dev/null +++ b/bin/cross_toolchain.rb @@ -0,0 +1,314 @@ +#!/usr/bin/env ruby +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at https://mozilla.org/MPL/2.0/. -} + +# frozen_string_literal: true + +require 'uri' +require 'net/http' +require 'open3' +require 'etc' +require 'fileutils' +require 'pathname' + +BINUTILS_VERSION = '2.43.1' +GLIBC_VERSION = '2.40' +KERNEL_VERSION = '5.15.166' +GCC_VERSION = "14.2.0" + +TMP = Pathname.new('./build') + +class BuildTarget + attr_accessor(:build, :gcc, :target, :tmp) + + def gxx + @gcc.gsub 'c', '+' + end + + def sysroot + tmp + 'sysroot' + end + + def rootfs + tmp + 'rootfs' + end + + def tools + tmp + 'tools' + end + + def configuration + case target + when /^riscv[[:digit:]]+-/ + [ + '--with-arch=rv32imafdc', + '--with-abi=ilp32d', + '--with-tune=rocket', + '--with-isa-spec=20191213' + ] + else + [] + end + end +end + +def gcc_verbose(gcc_binary) + read, write = IO.pipe + system({'LANG' => 'C'}, gcc_binary, '--verbose', err: write) + write.close + output = read.read + read.close + output +end + +def find_build_target(gcc_version, target) + gcc_binary = 'gcc' + output = gcc_verbose gcc_binary + + if output.start_with? 'Apple clang' + gcc_binary = "gcc-#{gcc_version.split('.').first}" + output = gcc_verbose gcc_binary + end + result = output + .lines + .each_with_object(BuildTarget.new) do |line, accumulator| + if line.start_with? 'Target: ' + accumulator.build = line.split(' ').last.strip + elsif line.start_with? 'COLLECT_GCC' + accumulator.gcc = line.split('=').last.strip + end + end + result.tmp = TMP + result.target = target + result +end + +def download_and_unarchive(url, target) + case File.extname url.path + when '.bz2' + archive_type = '-j' + root_directory = File.basename url.path, '.tar.bz2' + when '.xz' + archive_type = '-J' + root_directory = File.basename url.path, '.tar.xz' + else + raise "Unsupported archive type #{url.path}." + end + + Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http| + request = Net::HTTP::Get.new url.request_uri + + http.request request do |response| + case response + when Net::HTTPRedirection + download_and_unarchive URI.parse(response['location']) + when Net::HTTPSuccess + Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' do |stdin, stdout, wait_thread| + Thread.new do + stdout.each { |line| puts line } + end + + response.read_body do |chunk| + stdin.write chunk + end + stdin.close + + wait_thread.value + end + else + response.error! + end + end + end + target + root_directory +end + +def configure_make_install(source_directory, configure_options, env, cwd) + configure = source_directory.relative_path_from(cwd) + 'configure' + system env, configure.to_path, *configure_options, chdir: cwd.to_path, exception: true + system 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path, exception: true + system env, 'make', 'install', chdir: cwd.to_path, exception: true +end + +# Build cross binutils. +def binutils(options) + source_directory = download_and_unarchive( + URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"), + options.tools) + + cwd = source_directory.dirname + 'build-binutils' + cwd.mkpath + options.rootfs.mkpath + + env = { + 'CC' => options.gcc, + 'CXX' => options.gxx + } + configure_options = [ + "--prefix=#{options.rootfs.realpath}", + "--target=#{options.target}", + '--disable-nls', + '--enable-gprofng=no', + '--disable-werror', + '--enable-default-hash-style=gnu', + '--disable-libquadmath' + ] + configure_make_install source_directory, configure_options, env, cwd +end + +# Build stage 1 GCC. +def gcc1(options) + source_directory = download_and_unarchive( + URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"), + options.tools) + + cwd = source_directory.dirname + 'build-gcc' + cwd.mkpath + options.rootfs.mkpath + options.sysroot.mkpath + + system 'contrib/download_prerequisites', chdir: source_directory.to_path, exception: true + configure_options = options.configuration + [ + "--prefix=#{options.rootfs.realpath}", + "--with-sysroot=#{options.sysroot.realpath}", + '--enable-languages=c,c++', + '--disable-shared', + '--disable-bootstrap', + '--disable-multilib', + '--disable-libmudflap', + '--disable-libssp', + '--disable-libquadmath', + '--disable-libsanitizer', + '--disable-threads', + '--disable-libatomic', + '--disable-libgomp', + '--disable-libvtv', + '--disable-libstdcxx', + '--disable-nls', + '--with-newlib', + '--without-headers', + "--target=#{options.target}", + "--build=#{options.build}", + "--host=#{options.build}" + ] + flags = '-O2 -fPIC' + env = { + 'CC' => options.gcc, + 'CXX' => options.gxx, + 'CFLAGS' => flags, + 'CXXFLAGS' => flags, + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}" + } + configure_make_install source_directory, configure_options, env, cwd +end + +# Copy glibc headers. +def headers(options) + source_directory = download_and_unarchive( + URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"), + options.tools) + include_directory = options.tools + 'include' + + include_directory.mkpath + FileUtils.cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h') +end + +# Build linux kernel. +def kernel(options) + cwd = download_and_unarchive( + URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"), + options.tools) + + env = { + 'CROSS_COMPILE' => "#{options.target}-", + 'ARCH' => 'riscv', + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}", + 'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}" + } + system env, 'make', 'rv32_defconfig', chdir: cwd.to_path, exception: true + system env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path, exception: true + system env, 'make', 'headers', chdir: cwd.to_path, exception: true + + user_directory = options.sysroot + 'usr' + + user_directory.mkpath + FileUtils.cp_r (cwd + 'usr/include'), (user_directory + 'include') +end + +# Build glibc. +def glibc(options) + source_directory = options.tools + "glibc-#{GLIBC_VERSION}" + configure_options = [ + '--prefix=/usr', + "--host=#{options.target}", + "--target=#{options.target}", + "--build=#{options.build}", + "--enable-kernel=#{KERNEL_VERSION}", + "--with-headers=#{options.sysroot.realpath + 'usr/include'}", + '--disable-nscd', + '--disable-libquadmath', + '--disable-libitm', + '--disable-werror', + 'libc_cv_forced_unwind=yes' + ] + bin = options.rootfs.realpath + 'bin' + env = { + 'PATH' => "#{bin}:#{ENV['PATH']}", + 'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old. + } + cwd = source_directory.dirname + 'build-glibc' + cwd.mkpath + + configure = source_directory.relative_path_from(cwd) +'./configure' + system env, configure.to_path, *configure_options, chdir: cwd.to_path, exception: true + system env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path, exception: true + system env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path, exception: true +end + +# Build stage 2 GCC. +def gcc2(options) + source_directory = options.tools + "gcc-#{GCC_VERSION}" + cwd = options.tools + 'build-gcc' + + FileUtils.rm_rf cwd + cwd.mkpath + + configure_options = options.configuration + [ + "--prefix=#{options.rootfs.realpath}", + "--with-sysroot=#{options.sysroot.realpath}", + '--enable-languages=c,c++,lto', + '--enable-lto', + '--enable-shared', + '--disable-bootstrap', + '--disable-multilib', + '--enable-checking=release', + '--disable-libssp', + '--disable-libquadmath', + '--enable-threads=posix', + '--with-default-libstdcxx-abi=new', + '--disable-nls', + "--target=#{options.target}", + "--build=#{options.build}", + "--host=#{options.build}" + + ] + flags = '-O2 -fPIC' + env = { + 'CFLAGS' => flags, + 'CXXFLAGS' => flags, + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}" + } + configure_make_install source_directory, configure_options, env, cwd +end + +target = ARGV.fetch 0, 'riscv32-unknown-linux-gnu' +options = find_build_target GCC_VERSION, target + +options.tools.mkpath +binutils options +gcc1 options +headers options +kernel options +glibc options +gcc2 options