Add an argument parser
This commit is contained in:
parent
5490f6ce1c
commit
aa6b2504bd
43
Rakefile
43
Rakefile
@ -4,8 +4,12 @@ require 'open3'
|
|||||||
|
|
||||||
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
|
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
|
||||||
BINARY = 'build/bin/elna'
|
BINARY = 'build/bin/elna'
|
||||||
TESTS = FileList['tests/*.elna']
|
TESTS = FileList['tests/*.elna'].flat_map do |test|
|
||||||
.map { |test| (Pathname.new('build') + test).sub_ext('').to_path }
|
build = Pathname.new 'build'
|
||||||
|
asm_test = build + 'asm' + Pathname.new(test).basename('')
|
||||||
|
|
||||||
|
[build + test, asm_test].map { |path| path.sub_ext('').to_path }
|
||||||
|
end
|
||||||
SOURCES = FileList['source/**/*.d']
|
SOURCES = FileList['source/**/*.d']
|
||||||
|
|
||||||
directory 'build'
|
directory 'build'
|
||||||
@ -13,22 +17,24 @@ directory 'build'
|
|||||||
CLEAN.include 'build'
|
CLEAN.include 'build'
|
||||||
CLEAN.include '.dub'
|
CLEAN.include '.dub'
|
||||||
|
|
||||||
rule(/build\/tests\/.+/ => ->(file) { test_for_out(file) }) do |t|
|
rule(/build\/tests\/[^\/\.]+$/ => ->(file) { test_for_out(file) }) do |t|
|
||||||
|
sh 'gcc', '-o', t.name, "#{t.name}.o"
|
||||||
|
end
|
||||||
|
|
||||||
|
rule(/build\/asm\/[^\/\.]+$/ => ->(file) { test_for_object(file) }) do |t|
|
||||||
|
Pathname.new(t.name).dirname.mkpath
|
||||||
|
Open3.pipeline [BINARY, t.source], ['gcc', '-x', 'assembler', '-o', t.name, '-']
|
||||||
|
end
|
||||||
|
|
||||||
|
rule(/build\/tests\/.+\.o$/ => ->(file) { test_for_object(file) }) do |t|
|
||||||
Pathname.new(t.name).dirname.mkpath
|
Pathname.new(t.name).dirname.mkpath
|
||||||
sh BINARY, t.source
|
sh BINARY, t.source
|
||||||
sh 'gcc', '-o', t.name, "#{t.name}.o"
|
|
||||||
# Open3.pipeline [BINARY, t.source], ['gcc', '-x', 'assembler', '-o', t.name, '-']
|
|
||||||
end
|
end
|
||||||
|
|
||||||
file BINARY => SOURCES do |t|
|
file BINARY => SOURCES do |t|
|
||||||
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc-12')
|
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc-12')
|
||||||
end
|
end
|
||||||
|
|
||||||
file 'build/tests/sample' => BINARY do |t|
|
|
||||||
sh t.source
|
|
||||||
sh 'gcc', '-o', t.name, 'build/tests/sample.o'
|
|
||||||
end
|
|
||||||
|
|
||||||
task default: BINARY
|
task default: BINARY
|
||||||
|
|
||||||
desc 'Run all tests and check the results'
|
desc 'Run all tests and check the results'
|
||||||
@ -38,7 +44,7 @@ task test: BINARY do
|
|||||||
expected = Pathname
|
expected = Pathname
|
||||||
.new(test)
|
.new(test)
|
||||||
.sub_ext('.txt')
|
.sub_ext('.txt')
|
||||||
.sub(/^build\/tests\//, 'tests/expectations/')
|
.sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
|
||||||
.read
|
.read
|
||||||
.to_i
|
.to_i
|
||||||
|
|
||||||
@ -48,10 +54,6 @@ task test: BINARY do
|
|||||||
|
|
||||||
fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual
|
fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual
|
||||||
end
|
end
|
||||||
|
|
||||||
# system './build/tests/sample'
|
|
||||||
# actual = $?.exitstatus
|
|
||||||
# fail "./build/tests/sample: Expected 3, got #{actual}" unless 3 == actual
|
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Run unittest blocks'
|
desc 'Run unittest blocks'
|
||||||
@ -59,11 +61,18 @@ task unittest: SOURCES do |t|
|
|||||||
sh('dub', 'test', '--compiler=gdc-12')
|
sh('dub', 'test', '--compiler=gdc-12')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_out(out_file)
|
def test_for_object(out_file)
|
||||||
test_source = Pathname
|
test_source = Pathname
|
||||||
.new(out_file)
|
.new(out_file)
|
||||||
.sub_ext('.elna')
|
.sub_ext('.elna')
|
||||||
.sub(/^build\//, '')
|
.sub(/^build\/[[:alpha:]]+\//, 'tests/')
|
||||||
.to_path
|
.to_path
|
||||||
[test_source, BINARY]
|
[test_source, BINARY]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_for_out(out_file)
|
||||||
|
Pathname
|
||||||
|
.new(out_file)
|
||||||
|
.sub_ext('.o')
|
||||||
|
.to_path
|
||||||
|
end
|
||||||
|
146
source/elna/arguments.d
Normal file
146
source/elna/arguments.d
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* Argument parsing.
|
||||||
|
*/
|
||||||
|
module elna.arguments;
|
||||||
|
|
||||||
|
import std.algorithm;
|
||||||
|
import std.range;
|
||||||
|
import std.sumtype;
|
||||||
|
|
||||||
|
struct ArgumentError
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
expectedOutputFile,
|
||||||
|
noInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type type_;
|
||||||
|
private string argument_;
|
||||||
|
|
||||||
|
@property Type type() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this.type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property string argument() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this.argument_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toString(OR)(OR range)
|
||||||
|
if (isOutputRage!OR)
|
||||||
|
{
|
||||||
|
final switch (Type)
|
||||||
|
{
|
||||||
|
case Type.expectedOutputFile:
|
||||||
|
put(range, "Expected an output filename after -o");
|
||||||
|
break;
|
||||||
|
case Type.noInput:
|
||||||
|
put(range, "No input files specified");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported compiler arguments.
|
||||||
|
*/
|
||||||
|
struct Arguments
|
||||||
|
{
|
||||||
|
private bool assembler_;
|
||||||
|
private string output_;
|
||||||
|
private string[] inFiles_;
|
||||||
|
|
||||||
|
@property string[] inFiles() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this.inFiles_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Whether to generate assembly instead of an object file.
|
||||||
|
*/
|
||||||
|
@property bool assembler() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this.assembler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Output file.
|
||||||
|
*/
|
||||||
|
@property string output() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this.output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse command line arguments.
|
||||||
|
*
|
||||||
|
* The first argument is expected to be the program name (and it is
|
||||||
|
* ignored).
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* arguments = Command line arguments.
|
||||||
|
*
|
||||||
|
* Returns: Parsed arguments or an error.
|
||||||
|
*/
|
||||||
|
static SumType!(ArgumentError, Arguments) parse(string[] arguments)
|
||||||
|
@nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
if (!arguments.empty)
|
||||||
|
{
|
||||||
|
arguments.popFront;
|
||||||
|
}
|
||||||
|
alias ReturnType = typeof(return);
|
||||||
|
|
||||||
|
return parseArguments(arguments).match!(
|
||||||
|
(Arguments parsed) {
|
||||||
|
if (parsed.inFiles.empty)
|
||||||
|
{
|
||||||
|
return ReturnType(ArgumentError(ArgumentError.Type.noInput));
|
||||||
|
}
|
||||||
|
return ReturnType(parsed);
|
||||||
|
},
|
||||||
|
(ArgumentError argumentError) => ReturnType(argumentError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SumType!(ArgumentError, Arguments) parseArguments(ref string[] arguments)
|
||||||
|
@nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
Arguments parsed;
|
||||||
|
|
||||||
|
while (!arguments.empty)
|
||||||
|
{
|
||||||
|
if (arguments.front == "-s")
|
||||||
|
{
|
||||||
|
parsed.assembler_ = true;
|
||||||
|
}
|
||||||
|
else if (arguments.front == "-o")
|
||||||
|
{
|
||||||
|
if (arguments.empty)
|
||||||
|
{
|
||||||
|
return typeof(return)(ArgumentError(
|
||||||
|
ArgumentError.Type.expectedOutputFile,
|
||||||
|
arguments.front
|
||||||
|
));
|
||||||
|
}
|
||||||
|
arguments.popFront;
|
||||||
|
parsed.output_ = arguments.front;
|
||||||
|
}
|
||||||
|
else if (arguments.front == "--")
|
||||||
|
{
|
||||||
|
arguments.popFront;
|
||||||
|
parsed.inFiles_ = arguments;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!arguments.front.startsWith("-"))
|
||||||
|
{
|
||||||
|
parsed.inFiles_ = arguments;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
arguments.popFront;
|
||||||
|
}
|
||||||
|
return typeof(return)(parsed);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user