Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 73 additions & 10 deletions exe/vaporware
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 12 additions & 3 deletions lib/vaporware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 5 additions & 5 deletions lib/vaporware/assembler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion sample/assembler/else.s
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ main:
pop rax
mov rsp, rbp
pop rbp
ret
jmp .Lend0
.Lelse0:
push 2
Expand Down