From d93a80b12d08f8ee720ae7156d5c7edb86a4b7d0 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 25 May 2025 18:27:44 +0900 Subject: [PATCH 1/5] set subcommands --- exe/vaporware | 80 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/exe/vaporware b/exe/vaporware index 1d00e94..154fae0 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -3,21 +3,81 @@ 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, + assembly: 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", "--assembly-only", "Assembly only, do not link") { options[:link] = false } + opt.on("-S", "--compile-only", "Compile only, do not assemble") do + options[:assembly] = 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("-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, 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[:assembly] = true + options[:compile] = false +end + +basename = File.expand_path(File.basename(input, ".*")) + +output = basename + ".s" +Vaporware.compile(input:, output:, debug:) if options[:compile] +input, output = output, basename + ".o" +if options[:assembly] + Vaporware.assemble(input:, output:, assembler:, debug:) + File.delete(input) if File.exist?(input) && !debug +end +if options[:link] + input, output = output, basename + output = options[:output] || output + output += ".so" if options[:shared] + Vaporware.link(input:, output:, debug:, shared:, linker:) if options[:link] + File.delete(input) if File.exist?(input) && !debug +end From 57e01c4cecb6f9d968e03c4201e4d46abafb4ace Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 25 May 2025 18:28:05 +0900 Subject: [PATCH 2/5] add subcommands operations --- lib/vaporware.rb | 15 ++++++++++++--- lib/vaporware/assembler.rb | 10 +++++----- 2 files changed, 17 insertions(+), 8 deletions(-) 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 From a450fd182abc5be16f5fc1ec0f2cf667cef263c2 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 3 Jun 2025 21:26:25 +0900 Subject: [PATCH 3/5] fix subcommand for shared libraries --- exe/vaporware | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exe/vaporware b/exe/vaporware index 154fae0..53766ad 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -52,7 +52,7 @@ rescue => e exit 1 end -assembler, linker = nil, nil, nil +assembler, linker = nil, nil assembler = options[:assembler] || "self" linker = options[:linker] || "mold" debug = options[:debug] || false @@ -66,6 +66,7 @@ if File.extname(input) == ".s" end basename = File.expand_path(File.basename(input, ".*")) +extname = File.extname(output) == ".so" output = basename + ".s" Vaporware.compile(input:, output:, debug:) if options[:compile] @@ -77,7 +78,7 @@ end if options[:link] input, output = output, basename output = options[:output] || output - output += ".so" if options[:shared] + output += ".so" if options[:shared] && !extname Vaporware.link(input:, output:, debug:, shared:, linker:) if options[:link] File.delete(input) if File.exist?(input) && !debug end From 3bac181aeeadbcc10fe05b1c7490d20bfe332ecf Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 3 Jun 2025 21:52:24 +0900 Subject: [PATCH 4/5] rename subcommand assembly to assemble --- exe/vaporware | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/exe/vaporware b/exe/vaporware index 53766ad..0f96b96 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -5,7 +5,7 @@ require "optparse" opt = OptionParser.new options = { compile: true, - assembly: true, + assemble: true, link: true, } @@ -16,9 +16,9 @@ end subcommands["compile"] = OptionParser.new do |opt| opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v } - opt.on("-c", "--assembly-only", "Assembly only, do not link") { options[:link] = false } + 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[:assembly] = false + 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 } @@ -31,6 +31,7 @@ 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 } @@ -61,7 +62,7 @@ input = ARGV.first shared = options[:shared] || false if File.extname(input) == ".s" - options[:assembly] = true + options[:assemble] = true options[:compile] = false end @@ -71,14 +72,14 @@ extname = File.extname(output) == ".so" output = basename + ".s" Vaporware.compile(input:, output:, debug:) if options[:compile] input, output = output, basename + ".o" -if options[:assembly] +if options[:assemble] Vaporware.assemble(input:, output:, assembler:, debug:) - File.delete(input) if File.exist?(input) && !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.delete(input) if File.exist?(input) && !debug && File.extname(input) != ".o" end From 7368de171b4351fc0c03b3c674d8a4487a2fcd2d Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 22 Dec 2025 00:06:34 +0900 Subject: [PATCH 5/5] add assemble subcommand --- exe/vaporware | 1 + sample/assembler/else.s | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/exe/vaporware b/exe/vaporware index 0f96b96..6196846 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -73,6 +73,7 @@ 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 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