diff --git a/exe/vaporware b/exe/vaporware index 1d00e94..6196846 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -3,21 +3,84 @@ require "vaporware" require "optparse" opt = OptionParser.new -options = {} -opt.on("-c", "--compiler[=VAL]", "this option is selecting compiler precompiled file, default: \"self\"") { |v| options[:compiler] = v } -opt.on("-a", "--assembler[=VAL]", "this option is selecting assembler assembler file, default: \"as\"") { |v| options[:assembler] = v } -opt.on("-D", "--debug") { |v| options[:debug] = v } -opt.on("-o", "--objects[=VAL]") { |v| options[:dest] = v } -opt.on("--compiler-options[=VAL]", "compiler options") { |v| options[:compiler_options] = v.split(",") } -opt.on("-s", "--shared-library") { |v| options[:shared] = v } -opt.on("-l", "--linker[=VAL]", "selecting linker: gold, lld, and mold, default: \"gold\".") { |v| options[:linker] = v } +options = { + compile: true, + assemble: true, + link: true, +} + +subcommands = Hash.new do |hash, key| + $stderr.puts "no such subcommand: #{key}" + exit 1 +end + +subcommands["compile"] = OptionParser.new do |opt| + opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v } + opt.on("-c", "--assemble-only", "Assembly only, do not link") { options[:link] = false } + opt.on("-S", "--compile-only", "Compile only, do not assemble") do + options[:assemble] = false + options[:link] = false + end + opt.on("-a", "--assembler=ASSEMBLER", "Specify the assembler to use (e.g., as, gas)") { |v| options[:assembler] = v } + opt.on("-l", "--linker=LINKER", "Specify the linker to use (e.g., gold, lld, mold)") { |v| options[:linker] = v } + + opt.on("-s", "--shared", "Compile as shared object") { options[:shared] = true } + opt.on("-d", "--debug", "Compile with debug information") { options[:debug] = true } +end + +subcommands["assemble"] = OptionParser.new do |opt| + opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v } + opt.on("-a", "--assembler=ASSEMBLER", "Specify the assembler to use (e.g., as, gas)") { |v| options[:assembler] = v } + opt.on("-c", "--assemble-only", "Assemble only, do not link") { options[:link] = false } + + opt.on("-s", "--shared", "Compile as shared object") { options[:shared] = true } + opt.on("-d", "--debug", "Compile with debug information") { options[:debug] = true } +end + +subcommands["link"] = OptionParser.new do |opt| + opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v } + opt.on("-l", "--linker=LINKER", "Specify the linker to use (e.g., gold, lld, mold)") { |v| options[:linker] = v } + + opt.on("-s", "--shared", "Compile as shared object") { options[:shared] = true } + opt.on("-d", "--debug", "Compile with debug information") { options[:debug] = true } +end begin - opt.parse!(ARGV) + subcommands[ARGV.shift].parse!(ARGV) if ARGV.any? raise "please compile target file" if ARGV.empty? rescue => e STDERR.puts(e.message) exit 1 end -Vaporware::Compiler.compile!(input: ARGV.shift, **options) +assembler, linker = nil, nil +assembler = options[:assembler] || "self" +linker = options[:linker] || "mold" +debug = options[:debug] || false +output = options[:output] || "a.out" +input = ARGV.first +shared = options[:shared] || false + +if File.extname(input) == ".s" + options[:assemble] = true + options[:compile] = false +end + +basename = File.expand_path(File.basename(input, ".*")) +extname = File.extname(output) == ".so" + +output = basename + ".s" +Vaporware.compile(input:, output:, debug:) if options[:compile] +input, output = output, basename + ".o" +if options[:assemble] + input = ARGV.first unless options[:compile] + Vaporware.assemble(input:, output:, assembler:, debug:) + File.delete(input) if File.exist?(input) && !debug && File.extname(input) != ".s" +end +if options[:link] + input, output = output, basename + output = options[:output] || output + output += ".so" if options[:shared] && !extname + Vaporware.link(input:, output:, debug:, shared:, linker:) if options[:link] + File.delete(input) if File.exist?(input) && !debug && File.extname(input) != ".o" +end diff --git a/lib/vaporware.rb b/lib/vaporware.rb index 20ab3b5..b41dc85 100644 --- a/lib/vaporware.rb +++ b/lib/vaporware.rb @@ -12,8 +12,17 @@ def compile!(input:, assembler: "as", linker: "ld", output: "tmp", debug: false, d = File.expand_path(output) basename = "#{File.dirname(d)}/#{File.basename(d, ".*")}" execf = "#{basename}#{File.extname(d)}" - compiler = Vaporware::Compiler.compile!(input:, output: basename + ".s", debug:, compiler_options:, shared:) - assembler = Vaporware::Assembler.assemble!(input: basename+".s", output: basename+".o", assembler:, debug:) - linker = Vaporware::Linker.link!(input: basename+".o", output: execf, linker:, debug:, shared:) + compile(input:, output: basename+".s", debug:, shared:) + assemble(input: basename+".s", output: basename+".o", assembler:, debug:, shared:) + link(input: basename+".o", output: execf, linker:, debug:, shared:) + end + def compile(input:, output: "tmp.s", debug: false, shared: false) + Vaporware::Compiler.compile!(input:, output:, debug:) + end + def assemble(input:, output: "tmp.o", debug: false, shared: false, assembler: "as") + Vaporware::Assembler.assemble!(input:, output:, debug:, assembler:, shared:) + end + def link(input:, output: "tmp", linker: "ld", debug: false, shared: false) + Vaporware::Linker.link!(input:, output:, linker:, debug:, shared:) end end diff --git a/lib/vaporware/assembler.rb b/lib/vaporware/assembler.rb index a459d9c..f3e185a 100644 --- a/lib/vaporware/assembler.rb +++ b/lib/vaporware/assembler.rb @@ -6,12 +6,12 @@ require_relative "assembler/elf/section_header" class Vaporware::Assembler - GCC_ASSEMBLERS = ["gcc", "as"] - CLANG_ASSEMBLERS = ["clang", "llvm"] - ASSEMBLERS = GCC_ASSEMBLERS + CLANG_ASSEMBLERS + GCC_ASSEMBLERS = ["gcc", "as"].freeze + CLANG_ASSEMBLERS = ["clang", "llvm"].freeze + ASSEMBLERS = (GCC_ASSEMBLERS + CLANG_ASSEMBLERS).freeze class Error < StandardError; end - def self.assemble!(input:, output: File.basename(input, ".*") + ".o", assembler: "as", debug: false) = new(input:, output:, assembler:, debug:).assemble + def self.assemble!(input:, output: File.basename(input, ".*") + ".o", assembler: "as", debug: false, shared:false) = new(input:, output:, assembler:, debug:).assemble def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocatable, debug: false) @input, @output = input, output @@ -20,7 +20,7 @@ def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as @debug = debug end - def assemble(assembler: @assembler, assembler_options: [], input: @input, output: @output, debug: false) + def assemble(assembler: @assembler, assembler_options: [], input: @input, output: @output, debug: false, shared: false) if ASSEMBLERS.include?(assembler) IO.popen([command(assembler), *assembler_options, "-o", output, input].join(" ")).close else diff --git a/sample/assembler/else.s b/sample/assembler/else.s index 47334f6..cde0d8e 100644 --- a/sample/assembler/else.s +++ b/sample/assembler/else.s @@ -13,7 +13,6 @@ main: pop rax mov rsp, rbp pop rbp - ret jmp .Lend0 .Lelse0: push 2