Skip to content

Commit c4aba76

Browse files
authored
Merge pull request #21 from katsyoshi/implements-subcommands
Implements subcommands
2 parents 1934a34 + 7368de1 commit c4aba76

File tree

4 files changed

+90
-19
lines changed

4 files changed

+90
-19
lines changed

exe/vaporware

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,84 @@
33
require "vaporware"
44
require "optparse"
55
opt = OptionParser.new
6-
options = {}
7-
opt.on("-c", "--compiler[=VAL]", "this option is selecting compiler precompiled file, default: \"self\"") { |v| options[:compiler] = v }
8-
opt.on("-a", "--assembler[=VAL]", "this option is selecting assembler assembler file, default: \"as\"") { |v| options[:assembler] = v }
9-
opt.on("-D", "--debug") { |v| options[:debug] = v }
10-
opt.on("-o", "--objects[=VAL]") { |v| options[:dest] = v }
11-
opt.on("--compiler-options[=VAL]", "compiler options") { |v| options[:compiler_options] = v.split(",") }
12-
opt.on("-s", "--shared-library") { |v| options[:shared] = v }
13-
opt.on("-l", "--linker[=VAL]", "selecting linker: gold, lld, and mold, default: \"gold\".") { |v| options[:linker] = v }
6+
options = {
7+
compile: true,
8+
assemble: true,
9+
link: true,
10+
}
11+
12+
subcommands = Hash.new do |hash, key|
13+
$stderr.puts "no such subcommand: #{key}"
14+
exit 1
15+
end
16+
17+
subcommands["compile"] = OptionParser.new do |opt|
18+
opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v }
19+
opt.on("-c", "--assemble-only", "Assembly only, do not link") { options[:link] = false }
20+
opt.on("-S", "--compile-only", "Compile only, do not assemble") do
21+
options[:assemble] = false
22+
options[:link] = false
23+
end
24+
opt.on("-a", "--assembler=ASSEMBLER", "Specify the assembler to use (e.g., as, gas)") { |v| options[:assembler] = v }
25+
opt.on("-l", "--linker=LINKER", "Specify the linker to use (e.g., gold, lld, mold)") { |v| options[:linker] = v }
26+
27+
opt.on("-s", "--shared", "Compile as shared object") { options[:shared] = true }
28+
opt.on("-d", "--debug", "Compile with debug information") { options[:debug] = true }
29+
end
30+
31+
subcommands["assemble"] = OptionParser.new do |opt|
32+
opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v }
33+
opt.on("-a", "--assembler=ASSEMBLER", "Specify the assembler to use (e.g., as, gas)") { |v| options[:assembler] = v }
34+
opt.on("-c", "--assemble-only", "Assemble only, do not link") { options[:link] = false }
35+
36+
opt.on("-s", "--shared", "Compile as shared object") { options[:shared] = true }
37+
opt.on("-d", "--debug", "Compile with debug information") { options[:debug] = true }
38+
end
39+
40+
subcommands["link"] = OptionParser.new do |opt|
41+
opt.on("-o", "--output=FILE", "Output file name") { |v| options[:output] = v }
42+
opt.on("-l", "--linker=LINKER", "Specify the linker to use (e.g., gold, lld, mold)") { |v| options[:linker] = v }
43+
44+
opt.on("-s", "--shared", "Compile as shared object") { options[:shared] = true }
45+
opt.on("-d", "--debug", "Compile with debug information") { options[:debug] = true }
46+
end
1447

1548
begin
16-
opt.parse!(ARGV)
49+
subcommands[ARGV.shift].parse!(ARGV) if ARGV.any?
1750
raise "please compile target file" if ARGV.empty?
1851
rescue => e
1952
STDERR.puts(e.message)
2053
exit 1
2154
end
2255

23-
Vaporware::Compiler.compile!(input: ARGV.shift, **options)
56+
assembler, linker = nil, nil
57+
assembler = options[:assembler] || "self"
58+
linker = options[:linker] || "mold"
59+
debug = options[:debug] || false
60+
output = options[:output] || "a.out"
61+
input = ARGV.first
62+
shared = options[:shared] || false
63+
64+
if File.extname(input) == ".s"
65+
options[:assemble] = true
66+
options[:compile] = false
67+
end
68+
69+
basename = File.expand_path(File.basename(input, ".*"))
70+
extname = File.extname(output) == ".so"
71+
72+
output = basename + ".s"
73+
Vaporware.compile(input:, output:, debug:) if options[:compile]
74+
input, output = output, basename + ".o"
75+
if options[:assemble]
76+
input = ARGV.first unless options[:compile]
77+
Vaporware.assemble(input:, output:, assembler:, debug:)
78+
File.delete(input) if File.exist?(input) && !debug && File.extname(input) != ".s"
79+
end
80+
if options[:link]
81+
input, output = output, basename
82+
output = options[:output] || output
83+
output += ".so" if options[:shared] && !extname
84+
Vaporware.link(input:, output:, debug:, shared:, linker:) if options[:link]
85+
File.delete(input) if File.exist?(input) && !debug && File.extname(input) != ".o"
86+
end

lib/vaporware.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,17 @@ def compile!(input:, assembler: "as", linker: "ld", output: "tmp", debug: false,
1212
d = File.expand_path(output)
1313
basename = "#{File.dirname(d)}/#{File.basename(d, ".*")}"
1414
execf = "#{basename}#{File.extname(d)}"
15-
compiler = Vaporware::Compiler.compile!(input:, output: basename + ".s", debug:, compiler_options:, shared:)
16-
assembler = Vaporware::Assembler.assemble!(input: basename+".s", output: basename+".o", assembler:, debug:)
17-
linker = Vaporware::Linker.link!(input: basename+".o", output: execf, linker:, debug:, shared:)
15+
compile(input:, output: basename+".s", debug:, shared:)
16+
assemble(input: basename+".s", output: basename+".o", assembler:, debug:, shared:)
17+
link(input: basename+".o", output: execf, linker:, debug:, shared:)
18+
end
19+
def compile(input:, output: "tmp.s", debug: false, shared: false)
20+
Vaporware::Compiler.compile!(input:, output:, debug:)
21+
end
22+
def assemble(input:, output: "tmp.o", debug: false, shared: false, assembler: "as")
23+
Vaporware::Assembler.assemble!(input:, output:, debug:, assembler:, shared:)
24+
end
25+
def link(input:, output: "tmp", linker: "ld", debug: false, shared: false)
26+
Vaporware::Linker.link!(input:, output:, linker:, debug:, shared:)
1827
end
1928
end

lib/vaporware/assembler.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
require_relative "assembler/elf/section_header"
77

88
class Vaporware::Assembler
9-
GCC_ASSEMBLERS = ["gcc", "as"]
10-
CLANG_ASSEMBLERS = ["clang", "llvm"]
11-
ASSEMBLERS = GCC_ASSEMBLERS + CLANG_ASSEMBLERS
9+
GCC_ASSEMBLERS = ["gcc", "as"].freeze
10+
CLANG_ASSEMBLERS = ["clang", "llvm"].freeze
11+
ASSEMBLERS = (GCC_ASSEMBLERS + CLANG_ASSEMBLERS).freeze
1212
class Error < StandardError; end
1313

14-
def self.assemble!(input:, output: File.basename(input, ".*") + ".o", assembler: "as", debug: false) = new(input:, output:, assembler:, debug:).assemble
14+
def self.assemble!(input:, output: File.basename(input, ".*") + ".o", assembler: "as", debug: false, shared:false) = new(input:, output:, assembler:, debug:).assemble
1515

1616
def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocatable, debug: false)
1717
@input, @output = input, output
@@ -20,7 +20,7 @@ def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as
2020
@debug = debug
2121
end
2222

23-
def assemble(assembler: @assembler, assembler_options: [], input: @input, output: @output, debug: false)
23+
def assemble(assembler: @assembler, assembler_options: [], input: @input, output: @output, debug: false, shared: false)
2424
if ASSEMBLERS.include?(assembler)
2525
IO.popen([command(assembler), *assembler_options, "-o", output, input].join(" ")).close
2626
else

sample/assembler/else.s

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ main:
1313
pop rax
1414
mov rsp, rbp
1515
pop rbp
16-
ret
1716
jmp .Lend0
1817
.Lelse0:
1918
push 2

0 commit comments

Comments
 (0)