# 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 'open3' require 'pathname' require 'rake/clean' STAGES = Dir.glob('boot/stage*') .collect { |stage| File.basename stage } .sort { |a, b| a.delete_prefix('stage').to_i <=> b.delete_prefix('stage').to_i } .drop(1) # First assembly stage does not count. CLEAN.include 'build/boot', 'build/valid' def compile(*arguments) sh(ENV.fetch('CC', 'gcc'), '-fpie', '-g', *arguments) end def run(exe) ENV.fetch('QEMU', '').split << exe end task default: :boot desc 'Final stage' task boot: "build/valid/#{STAGES.last}/cl" task boot: "build/valid/#{STAGES.last}/cl.s" task boot: "boot/#{STAGES.last}/cl.elna" do |t| groupped = t.prerequisites.group_by { |stage| File.extname stage }.transform_values(&:first) exe = groupped[''] expected = groupped['.s'] source = groupped['.elna'] cat_arguments = ['cat', source] diff_arguments = ['diff', '-Nur', '--text', expected, '-'] Open3.pipeline(cat_arguments, run(exe), diff_arguments) end desc 'Convert previous stage language into the current stage language' task :convert do File.open('boot/stage16.elna', 'w') do |current_stage| skip = false File.readlines('boot/stage15.elna').each do |line| if line.strip == 'type' current_stage << <<~SNIP type _elna_tac_declaration = record next: Word; name: Word; length: Word; body: Word end; _integer_literal_node = record kind: Word; value: Word; length: Word end; SNIP elsif line.start_with?('proc _elna_tac_declaration_size') skip = true elsif line.start_with?('proc _elna_tac_declaration_get_') skip = true elsif line.start_with?('proc _elna_tac_declaration_set_') skip = true elsif line.start_with?('proc _integer_literal_node_size') skip = true elsif line.start_with?('proc _integer_literal_node_get_') skip = true elsif line.start_with?('proc _integer_literal_node_set_') skip = true elsif line.start_with?('end') && skip skip = false elsif !skip current_stage << line end end end end rule /^build\/[[:alpha:]]+\/stage[[:digit:]]+\/cl$/ => ->(match) { "#{match}.o" } do |t| arguments_path = Pathname.new('boot') + Pathname.new(t.name).dirname.basename + 'linker.arg' if arguments_path.exist? arguments1 = ['--dynamic-linker', '/lib32/ld-linux-riscv32-ilp32d.so.1', '/usr/lib/crt1.o', '/usr/lib/crti.o', '-lc'] arguments2 = ['/usr/lib/crtn.o'] else arguments1 = arguments2 = [] end sh(ENV.fetch('LD', 'ld'), '-o', t.name, *arguments1, *t.prerequisites, *arguments2) end rule /^build\/[[:alpha:]]+\/stage[[:digit:]]+\/cl.o$/ => ->(match) { match.ext('.s') } do |t| compile('-c', '-o', t.name, *t.prerequisites) end STAGES.each do |stage| previous = stage.delete_prefix('stage').to_i.pred directory "build/valid/#{stage}" directory "build/boot/#{stage}" file "build/valid/#{stage}/cl.s" => ["build/boot/#{stage}/cl", "boot/#{stage}/cl.elna", "build/valid/#{stage}"] do |t| exe, source = t.prerequisites cat_arguments = ['cat', source] last_stdout, wait_threads = Open3.pipeline_r(cat_arguments, run(exe)) IO.copy_stream last_stdout, t.name end file "build/boot/#{stage}/cl.s" => ["build/valid/stage#{previous}/cl", "boot/#{stage}/cl.elna", "build/boot/#{stage}"] do |t| exe, source = t.prerequisites cat_arguments = ['cat', source] last_stdout, wait_threads = Open3.pipeline_r(cat_arguments, run(exe)) IO.copy_stream last_stdout, t.name end end # # Stage 1. # directory 'build/valid/stage1' directory 'build/boot/stage1' file 'build/valid/stage1/cl' => ['build/valid/stage1.s'] do |t| compile('-nostdlib', '-o', t.name, *t.prerequisites) end file 'build/valid/stage1.s' => ['build/boot/stage1/cl', 'boot/stage1.s', 'build/valid/stage1'] do |t| source, exe, = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.s' } cat_arguments = ['cat', *source] last_stdout, wait_threads = Open3.pipeline_r(cat_arguments, run(exe.first)) IO.copy_stream last_stdout, t.name end file 'build/boot/stage1/cl' => ['build/boot/stage1', 'boot/stage1.s'] do |t| source = t.prerequisites.select { |prerequisite| prerequisite.end_with? '.s' } compile('-nostdlib', '-o', t.name, *source) end