diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..fa7adc7 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.3.5 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..e69de29 diff --git a/rakelib/cross.rake b/rakelib/cross.rake new file mode 100644 index 0000000..3de7d79 --- /dev/null +++ b/rakelib/cross.rake @@ -0,0 +1,307 @@ +require 'pathname' +require 'uri' +require 'net/http' +require 'rake/clean' +require 'open3' +require 'etc' + +GCC_VERSION = "14.2.0" +BINUTILS_VERSION = '2.43.1' +GLIBC_VERSION = '2.40' +KERNEL_VERSION = '5.15.166' + +TMP = Pathname.new('./build') + +CLEAN.include TMP + +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 +end + +def gcc_verbose(gcc_binary) + read, write = IO.pipe + sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write) + write.close + output = read.read + read.close + output +end + +def find_build_target(gcc_version, task) + 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 + task.with_defaults target: 'riscv32-unknown-linux-gnu' + result.target = task[: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| + stdout.close + + response.read_body do |chunk| + stdin.write chunk + end + end + else + response.error! + end + end + end + target + root_directory +end + +namespace :cross do + desc 'Build cross binutils' + task :binutils, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + 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 = source_directory.relative_path_from(cwd) + 'configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'install', chdir: cwd.to_path + end + + desc 'Build stage 1 GCC' + task :gcc1, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + 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 + + sh 'contrib/download_prerequisites', chdir: source_directory.to_path + configure_options = [ + "--prefix=#{options.rootfs.realpath}", + "--with-sysroot=#{options.sysroot.realpath}", + '--enable-languages=c,c++', + '--disable-shared', + '--with-arch=rv32imafdc', + '--with-abi=ilp32d', + '--with-tune=rocket', + '--with-isa-spec=20191213', + '--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 = source_directory.relative_path_from(cwd) + 'configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'install', chdir: cwd.to_path + end + + desc 'Copy glibc headers' + task :headers, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + + 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 + cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h') + end + + desc 'Build linux kernel' + task :kernel, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + + 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'}" + } + sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'headers', chdir: cwd.to_path + + user_directory = options.sysroot + 'usr' + + user_directory.mkpath + cp_r (cwd + 'usr/include'), (user_directory + 'include') + end + + desc 'Build glibc' + task :glibc, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + 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' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path + end + + desc 'Build stage 2 GCC' + task :gcc2, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + source_directory = options.tools + "gcc-#{GCC_VERSION}" + cwd = options.tools + 'build-gcc' + + rm_rf cwd + cwd.mkpath + + configure_options = [ + "--prefix=#{options.rootfs.realpath}", + "--with-sysroot=#{options.sysroot.realpath}", + '--enable-languages=c,c++,lto', + '--enable-lto', + '--enable-shared', + '--with-arch=rv32imafdc', + '--with-abi=ilp32d', + '--with-tune=rocket', + '--with-isa-spec=20191213', + '--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 = source_directory.relative_path_from(cwd) + 'configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'install', chdir: cwd.to_path + end +end + +task cross: ['cross:binutils', 'cross:gcc1', 'cross:headers', 'cross:kernel', 'cross:glibc', 'cross:gcc2'] do +end