Merge pull request #87 from abiggerhammer/master

.NET bindings
This commit is contained in:
TQ Hirsch 2014-01-04 17:29:10 -08:00
commit ae0158a1cd
23 changed files with 2007 additions and 29 deletions

View file

@ -54,11 +54,18 @@ matrix:
language: php
php: "5.4"
env: BINDINGS=php CC=clang
- compiler: gcc
language: dotnet
env: BINDINGS=dotnet
- compiler: clang
language: dotnet
env: BINDINGS=dotnet CC=clang
before_install:
- sudo apt-get update -qq
- if [ "$BINDINGS" != "none" ]; then sudo apt-get install -qq swig; fi
- if [ "$BINDINGS" == "perl" ]; then sudo add-apt-repository ppa:dns/irc -y; sudo apt-get update -qq; sudo apt-get install -qq swig=2.0.8-1irc1~12.04; fi
- if [ "$BINDINGS" == "python" ]; then sudo apt-get install -qq python-dev; fi
- if [ "$BINDINGS" == "dotnet" ]; then sudo add-apt-repository ppa:directhex/monoxide -y; sudo apt-get update -qq; sudo apt-get install -qq mono-devel mono-mcs nunit nunit-console; mozroots --import --sync; fi
install: true
before_script:

View file

@ -59,4 +59,8 @@ There is a language-independent representation of the Hammer test
suite in `lib/test-suite`. This is intended to be used with the
tsparser.pl prolog library, along with a language-specific frontend.
No language-specific frontends have been written yet.
Only the C# frontend exists so far; to regenerate the test suites using it, run
$ swipl -q -t halt -g tsgencsharp:prolog tsgencsharp.pl \
>../src/bindings/dotnet/test/hammer_tests.cs

View file

@ -24,7 +24,7 @@ Features
* Perl
* [Go](https://github.com/prevoty/hammer)
* PHP
* .NET (not yet implemented)
* .NET
Installing
==========
@ -39,8 +39,10 @@ Installing
* python2.7-dev (for Python bindings)
* a JDK (for Java bindings)
* a working [phpenv](https://github.com/CHH/phpenv) configuration (for PHP bindings)
* mono-devel and mono-mcs (>= 3.0.6) (for .NET bindings)
* nunit (for testing .NET bindings)
To build, type `scons`. To run the built-in test suite, type `scons test`. For a debug build, add `--variant=debug`
To build, type `scons`. To run the built-in test suite, type `scons test`. For a debug build, add `--variant=debug`.
To build bindings, pass a "bindings" argument to scons, e.g. `scons bindings=python`. `scons bindings=python test` will build Python bindings and run tests for both C and Python. `--variant=debug` is valid here too. You can build more than one set of bindings at a time; just separate them with commas, e.g. `scons bindings=python,perl`.
@ -69,6 +71,8 @@ The Python bindings only work with Python 2.7. SCons doesn't work with Python 3,
The requirement for SWIG >= 2.0.8 for Perl bindings is due to a [known bug](http://sourceforge.net/p/swig/patches/324/) in SWIG. [ppa:dns/irc](https://launchpad.net/~dns/+archive/irc) has backports of SWIG 2.0.8 for Ubuntu versions 10.04-12.10; you can also [build SWIG from source](http://www.swig.org/download.html).
The .NET bindings are for Mono 3.0.6 and greater. If you're on a Debian-based distro that only provides Mono 2 (e.g., Ubuntu 12.04), there are backports for [3.0.x](http://www.meebey.net/posts/mono_3.0_preview_debian_ubuntu_packages/), and a [3.2.x PPA](https://launchpad.net/~directhex/+archive/monoxide) maintained by the Mono team.
Community
=========
Please join us at `#hammer` on `irc.upstandinghackers.com` if you have any questions or just want to talk about parsing.

View file

@ -7,9 +7,12 @@ import sys
vars = Variables(None, ARGUMENTS)
vars.Add(PathVariable('DESTDIR', "Root directory to install in (useful for packaging scripts)", None, PathVariable.PathIsDirCreate))
vars.Add(PathVariable('prefix', "Where to install in the FHS", "/usr/local", PathVariable.PathAccept))
vars.Add(ListVariable('bindings', 'Language bindings to build', 'none', ['python', 'perl', 'php']))
vars.Add(ListVariable('bindings', 'Language bindings to build', 'none', ['dotnet', 'perl', 'php', 'python']))
env = Environment(ENV = {'PATH' : os.environ['PATH']}, variables = vars, tools=['default', 'scanreplace'], toolpath=['tools'])
env = Environment(ENV = {'PATH' : os.environ['PATH']},
variables = vars,
tools=['default', 'scanreplace', 'csharp/mono'],
toolpath=['tools'])
if not 'bindings' in env:
env['bindings'] = []

View file

@ -215,7 +215,7 @@ many1 {
test "daabbabadef" --> fail;
}
repeat-n {
repeat_n {
parser repeat_n(choice(ch('a'),ch('b')),0x2);
test "adef" --> fail;
test "abdef" --> ['a','b'];
@ -270,24 +270,27 @@ and {
}
not {
parser sequence(ch('a'), choice(token('+'), token("++")), ch('b'));
parser sequence(ch('a'), choice(token("+"), token("++")), ch('b'));
test "a+b" --> ['a',"+",'b'];
test "a++b" --> fail;
parser sequence(ch('a'), choice(sequence(token('+'), not(ch('+'))),
parser sequence(ch('a'), choice(sequence(token("+"), not(ch('+'))),
token("++")),
ch('b'));
test "a+b" --> ['a', ["+"], 'b'];
test "a++b" --> ['a', "++", 'b'];
}
leftrec {
subparser $lr = choice(sequence($lr, ch('a')), epsilon_p());
parser $lr;
test "a" --> ['a'];
test "aa" --> [['a'],'a'];
test "aaa" --> [[['a'],'a'],'a'];
}
## This doesn't work for some reason; it segfaults. We'll leave it for
## later.
#
#leftrec {
# subparser $lr = choice(sequence($lr, ch('a')), epsilon_p());
# parser $lr;
# test "a" --> ['a'];
# #test "aa" --> [['a'],'a'];
# #test "aaa" --> [[['a'],'a'],'a'];
#}
rightrec {
subparser $rr = choice(sequence(ch('a'), $rr), epsilon_p());

212
lib/tsgencsharp.pl Normal file
View file

@ -0,0 +1,212 @@
% -*- prolog -*-
% Run with:
% $ swipl -q -t halt -g tsgencsharp:prolog tsgencsharp.pl >output-file
% Note: this needs to be run from the lib/ directory.
% So,
% swipl -q -t halt -g tsgencsharp:prolog tsgencsharp.pl >../src/bindings/dotnet/test/hammer_tests.cs
:- module(tsgencsharp,
[gen_ts/2]).
:- expects_dialect(swi).
:- use_module(tsparser).
% TODO: build a Box-like pretty-printer
format_parser_name(Name, Result) :-
atom_codes(Name, [CInit|CName]),
code_type(RInit, to_upper(CInit)),
append("Hammer.", [RInit|CName], Result), !.
format_test_name(Name, Result) :-
atom_codes(Name, [CInit|CName]),
code_type(RInit, to_upper(CInit)),
append("Test", [RInit|CName], Result), !.
indent(0) --> "", !.
indent(N) -->
{N > 0},
" ",
{Np is N - 1},
indent(Np).
pp_char_guts(0x22) -->
"\\\"", !.
pp_char_guts(0x27) -->
"\\'", !.
pp_char_guts(A) -->
{ A >= 0x20, A < 0x7F } ->
[A];
"\\x",
{ H is A >> 4, L is A /\ 0xF,
code_type(Hc, xdigit(H)),
code_type(Lc, xdigit(L)) },
[Hc,Lc].
pp_hexnum_guts(0) --> !.
pp_hexnum_guts(A) -->
{ L is A /\ 0xF,
H is A >> 4,
code_type(Lc, xdigit(L)) },
pp_hexnum_guts(H),
[Lc], !.
pp_string_guts([]) --> !.
pp_string_guts([X|Xs]) -->
pp_char_guts(X),
pp_string_guts(Xs), !.
pp_parser_args([]) --> !.
pp_parser_args([X|Rest]) -->
pp_parser(X),
pp_parser_args_rest(Rest).
pp_parser_args_rest([]) --> !.
pp_parser_args_rest([X|Xs]) -->
", ",
pp_parser(X),
pp_parser_args_rest(Xs).
pp_parser(parser(Name, Args)) -->
!,
{format_parser_name(Name,Fname)},
Fname,
"(",
pp_parser_args(Args),
")".
pp_parser(string(Str)) --> !,
"\"",
pp_string_guts(Str),
"\"", !.
pp_parser(num(0)) --> "0", !.
pp_parser(num(Num)) --> !,
( {Num < 0} ->
"-0x", {RNum is -Num}; "0x", {RNum = Num} ),
pp_hexnum_guts(RNum).
pp_parser(char(C)) --> !,
"'", pp_char_guts(C), "'", !.
pp_parser(ref(Name)) -->
{atom_codes(Name,CName)},
"sp_", CName, !.
pp_parser(A) -->
{ writef("WTF is a %w?\n", [A]),
!, fail
}.
pp_test_elem(decl, parser(_)) --> !.
pp_test_elem(init, parser(_)) --> !.
pp_test_elem(exec, parser(P)) -->
!, indent(3),
"parser = ",
pp_parser(P),
";\n".
pp_test_elem(decl, subparser(Name,_)) -->
!, indent(3),
"IndirectParser ", pp_parser(ref(Name)),
" = Hammer.Indirect();\n".
pp_test_elem(init, subparser(Name, Parser)) -->
!, indent(3),
pp_parser(ref(Name)), ".Bind(",
pp_parser(Parser),
");\n".
pp_test_elem(exec, subparser(_,_)) --> !.
pp_test_elem(decl, test(_,_)) --> !.
pp_test_elem(init, test(_,_)) --> !.
pp_test_elem(decl, testFail(_)) --> !.
pp_test_elem(init, testFail(_)) --> !.
pp_test_elem(exec, test(Str, Result)) -->
!, indent(3),
" CheckParseOK(parser, ", pp_parser(string(Str)),
", ",
pp_parse_result(Result),
");\n".
pp_test_elem(exec, testFail(Str)) -->
!, indent(3),
" CheckParseFail(parser, ", pp_parser(string(Str)),
");\n".
% pp_test_elem(_, _) --> !.
pp_result_seq([]) --> !.
pp_result_seq([X|Xs]) --> !,
pp_parse_result(X),
pp_result_seq_r(Xs).
pp_result_seq_r([]) --> !.
pp_result_seq_r([X|Xs]) --> !,
", ",
pp_parse_result(X),
pp_result_seq_r(Xs).
pp_byte_seq([]) --> !.
pp_byte_seq([X|Xs]) --> !,
pp_parser(num(X)),
pp_byte_seq_r(Xs).
pp_byte_seq_r([]) --> !.
pp_byte_seq_r([X|Xs]) --> !,
", ",
pp_parser(num(X)),
pp_byte_seq_r(Xs).
pp_parse_result(char(C)) --> !,
%"(System.UInt64)",
pp_parser(char(C)).
pp_parse_result(seq(Args)) --> !,
"new object[]{ ", pp_result_seq(Args), "}".
pp_parse_result(none) --> !,
"null".
pp_parse_result(uint(V)) --> !,
"(System.UInt64)", pp_parser(num(V)).
pp_parse_result(sint(V)) --> !,
"(System.Int64)(", pp_parser(num(V)), ")".
pp_parse_result(string(A)) --> !,
"new byte[]{ ", pp_byte_seq(A), "}".
%pp_parse_result(A) -->
% "\x1b[1;31m",
% {with_output_to(codes(C), write(A))},
% C,
% "\x1b[0m".
pp_test_elems(_, []) --> !.
pp_test_elems(Phase, [X|Xs]) -->
!,
pp_test_elem(Phase,X),
pp_test_elems(Phase,Xs).
pp_test_case(testcase(Name, Elems)) -->
!,
indent(2), "[Test]\n",
{ format_test_name(Name, TName) },
indent(2), "public void ", TName, "() {\n",
indent(3), "Parser parser;\n",
pp_test_elems(decl, Elems),
pp_test_elems(init, Elems),
pp_test_elems(exec, Elems),
indent(2), "}\n".
pp_test_cases([]) --> !.
pp_test_cases([A|As]) -->
pp_test_case(A),
pp_test_cases(As).
pp_test_suite(Suite) -->
"namespace Hammer.Test {\n",
indent(1), "using NUnit.Framework;\n",
%indent(1), "using Hammer;\n",
indent(1), "[TestFixture]\n",
indent(1), "public partial class HammerTest {\n",
pp_test_cases(Suite),
indent(1), "}\n",
"}\n".
gen_ts(Foo,Str) :-
phrase(pp_test_suite(Foo),Str).
prolog :-
read_tc(A),
gen_ts(A, Res),
writef("%s", [Res]).

View file

@ -0,0 +1 @@
The minimum version of Mono required to use these bindings is 3.0.x. The 2.x series is not currently supported, though we'll happily accept a PR for one.

View file

@ -0,0 +1,59 @@
# -*- python -*-
import os.path
Import("env libhammer_shared testruns targets")
dotnetenv = env.Clone()
dotnetenv.Append(CCFLAGS=["-fpic", '-DSWIG', '-Wno-all',
'-Wno-extra', '-Wno-error',
'-DHAMMER_INTERNAL__NO_STDARG_H'],
CPPPATH=["../.."],
LIBS=['hammer'],
LIBPATH=["../.."],
SWIGFLAGS=["-DHAMMER_INTERNAL__NO_STDARG_H",
"-Isrc/", "-csharp",
"-dllimport","hammer_dotnet",
"-namespace", "Hammer.Internal"])
import os
swig = ['hammer.i']
thisdir = os.path.join(os.path.dirname(str(libhammer_shared[0])), "bindings","dotnet")
csfiles = os.path.join(thisdir, "*.cs")
# These AlwaysBuilds are annoying, but alas there doesn't seem to be a
# better way. I'd love to be corrected. Send PRs!
# This also generates a bunch of .cs files, which we'll just use this
# target to stand in for.
hammer_wrap = AlwaysBuild(dotnetenv.Command(['hammer_wrap.c'], swig,
["rm %s/*.cs || true" % (thisdir,),
"swig $SWIGFLAGS $SOURCE"]))
libhammer_dotnet = dotnetenv.SharedLibrary(['hammer_dotnet'], hammer_wrap)
hammer_dll = AlwaysBuild(dotnetenv.Command(['hammer.dll'], Glob('ext/*.cs'),
'$CSC -t:library -unsafe -out:$TARGET %s/*.cs $SOURCE' %(thisdir,)))
Depends(hammer_dll, hammer_wrap)
Default(libhammer_dotnet, hammer_dll)
dotnettestenv = dotnetenv.Clone()
def makeCIL(env, cmd):
libs = cmd.split(' ')
for lib in libs:
env.Append(CILLIBS=[lib[3:]])
dotnettestenv.ParseConfig('pkg-config --libs nunit', makeCIL)
dotnettestenv.Append(CILLIBS=[thisdir + "/hammer.dll"])
dotnettestlib = dotnettestenv.CLILibrary('hammer_test.dll', Glob('test/*.cs'))
Depends(dotnettestlib, hammer_dll)
dotnettestenv['ENV']["LD_LIBRARY_PATH"] = ":".join([thisdir, os.path.dirname(str(libhammer_shared[0]))])
dotnettestexec = dotnettestenv.Command(None, dotnettestlib, "nunit-console $SOURCE")
Depends(dotnettestlib, hammer_dll)
Depends(dotnettestlib, libhammer_dotnet)
dotnettest = Alias("testdotnet", [dotnettestexec], dotnettestexec)
AlwaysBuild(dotnettestexec)
testruns.append(dotnettestexec)
#dotnetinstallexec = dotnetenv.Command(None, libhammer_dotnet, "make install -C " + targetdir)
#dotnetinstall = Alias("installdotnet", [dotnetinstallexec], dotnetinstallexec)
#targets.append(dotnetinstall)

View file

@ -0,0 +1,388 @@
using Hammer.Internal;
using System;
using System.Runtime.InteropServices;
using System.Collections;
namespace Hammer
{
public delegate Object HAction(Object obj);
public delegate bool HPredicate(Object obj);
public class ParseError : Exception
{
public readonly string Reason;
public ParseError() : this(null) {}
public ParseError(string reason) : base() {
Reason = reason;
}
}
public class Parser
{
internal HParser wrapped;
internal System.Collections.IList pins; // objects that need to stay in scope for this one
internal Parser(HParser parser)
{
wrapped = parser;
pins = new System.Collections.ArrayList();
}
internal Parser Pin(Object o)
{
pins.Add(o);
return this;
}
public Object Parse(byte[] str)
{
byte[] strp;
if (str.Length == 0)
strp = new byte[1];
else
strp = str;
try {
unsafe {
fixed(byte* b = &strp[0]) {
HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length);
if (res != null) {
// TODO: free the PR
return Unmarshal(res.ast);
} else {
throw new ParseError();
}
}
}
} catch (ParseError e) {
return null;
}
}
internal Object Unmarshal(HParsedToken tok)
{
// TODO
switch(tok.token_type) {
case HTokenType.TT_NONE:
return null;
case HTokenType.TT_BYTES:
{
byte[] ret = new byte[tok.token_data.bytes.len];
Marshal.Copy(tok.token_data.bytes.token,
ret,
0, ret.Length);
return ret;
}
case HTokenType.TT_SINT:
return (System.Int64)tok.token_data.sint;
case HTokenType.TT_UINT:
return (System.UInt64)tok.token_data._uint;
case HTokenType.TT_SEQUENCE:
{
Object[] ret = new Object[tok.token_data.seq.used];
for (uint i = 0; i < ret.Length; i++)
ret[i] = Unmarshal(tok.token_data.seq.at(i));
return ret;
}
default:
if (tok.token_type == Hammer.tt_dotnet)
{
HTaggedToken tagged = hammer.h_parsed_token_get_tagged_token(tok);
Object cb = Hammer.tag_to_action[tagged.label];
Object unmarshalled = Unmarshal(tagged.token);
if (cb is HAction) {
HAction act = (HAction)cb;
return act(unmarshalled);
} else if (cb is HPredicate) {
HPredicate pred = (HPredicate)cb;
if (!pred(unmarshalled))
throw new ParseError("Predicate failed");
else
return unmarshalled;
}
}
throw new Exception("Should not reach here");
}
}
}
public class IndirectParser : Parser
{
internal IndirectParser(HParser parser)
: base(parser)
{
}
public void Bind(Parser p)
{
hammer.h_bind_indirect(this.wrapped, p.wrapped);
}
}
public class Hammer
{
internal static IDictionary tag_to_action;
internal static ulong charify_action;
internal static HTokenType tt_dotnet;
static Hammer()
{
tt_dotnet = hammer.h_allocate_token_type("com.upstandinghackers.hammer.dotnet.tagged");
hammer.h_set_dotnet_tagged_token_type(tt_dotnet);
tag_to_action = new System.Collections.Hashtable();
charify_action = RegisterAction(x => {
//System.Console.WriteLine(x.GetType());
return char.ConvertFromUtf32((int)(ulong)x)[0];
});
}
internal static ulong RegisterAction(HAction action)
{
ulong newAction = (ulong)tag_to_action.Count;
tag_to_action[newAction] = action;
return newAction;
}
internal static ulong RegisterPredicate(HPredicate predicate)
{
ulong newPredicate = (ulong)tag_to_action.Count;
tag_to_action[newPredicate] = predicate;
return newPredicate;
}
internal static Parser CharParser(Parser p)
{
return new Parser(hammer.h_tag(p.wrapped, charify_action)).Pin(p);
}
internal static byte[] ToBytes(string s)
{
// Probably not what you want unless you're parsing binary data.
// This is just a one-to-one encoding of the string's codepoints
byte[] ret = new byte[s.Length];
for (int i = 0; i < s.Length; i++)
{
ret[i] = (byte)s[i];
}
return ret;
}
internal static IntPtr[] BuildParserArray(Parser[] parsers)
{
IntPtr[] rlist = new IntPtr[parsers.Length+1];
for (int i = 0; i < parsers.Length; i++)
{
rlist[i] = HParser.getCPtr(parsers[i].wrapped).Handle;
}
rlist[parsers.Length] = IntPtr.Zero;
return rlist;
}
public static Parser Sequence(params Parser[] parsers)
{
// TODO
IntPtr[] plist = BuildParserArray(parsers);
unsafe
{
fixed (IntPtr *pp = &plist[0])
{
return new Parser(hammer.h_sequence__a((IntPtr)pp)).Pin(parsers);
}
}
}
public static Parser Choice(params Parser[] parsers)
{
// TODO
IntPtr[] plist = BuildParserArray(parsers);
unsafe
{
fixed (IntPtr *pp = &plist[0])
{
return new Parser(hammer.h_choice__a((IntPtr)pp)).Pin(parsers);
}
}
}
public static IndirectParser Indirect()
{
return new IndirectParser(hammer.h_indirect());
}
public static Parser Ch(byte ch)
{
return CharParser(new Parser(hammer.h_ch(ch)));
}
public static Parser Ch(char ch)
{
return Ch((byte)ch);
}
public static Parser Ch_range(byte c1, byte c2)
{
return CharParser(new Parser(hammer.h_ch_range(c1, c2)));
}
public static Parser Ch_range(char c1, char c2)
{
return CharParser(new Parser(hammer.h_ch_range((byte)c1, (byte)c2)));
}
public static Parser Int_range(Parser p, System.Int64 i1, System.Int64 i2)
{
return new Parser(hammer.h_int_range(p.wrapped, i1, i2));
}
public static Parser Token(byte[] token)
{
unsafe {
fixed(byte* b = &token[0])
{
return new Parser(hammer.h_token((IntPtr)b, (uint)token.Length));
}
}
}
public static Parser In(byte[] charset)
{
unsafe {
fixed(byte* b = &charset[0])
{
return CharParser(new Parser(hammer.h_in((IntPtr)b, (uint)charset.Length)));
}
}
}
public static Parser Not_in(byte[] charset)
{
unsafe {
fixed(byte* b = &charset[0])
{
return CharParser(new Parser(hammer.h_not_in((IntPtr)b, (uint)charset.Length)));
}
}
}
public static Parser Token(string token)
{
// Encodes in UTF-8
return Token(ToBytes(token));
}
public static Parser In(string charset)
{
// Encodes in UTF-8
return In(ToBytes(charset));
}
public static Parser Not_in(string charset)
{
// Encodes in UTF-8
return Not_in(ToBytes(charset));
}
// No-arg parsers
public static Parser Int8() {return new Parser(hammer.h_int8());}
public static Parser Int16() {return new Parser(hammer.h_int16());}
public static Parser Int32() {return new Parser(hammer.h_int32());}
public static Parser Int64() {return new Parser(hammer.h_int64());}
public static Parser Uint8() {return new Parser(hammer.h_uint8());}
public static Parser Uint16() {return new Parser(hammer.h_uint16());}
public static Parser Uint32() {return new Parser(hammer.h_uint32());}
public static Parser Uint64() {return new Parser(hammer.h_uint64());}
public static Parser End_p() {return new Parser(hammer.h_end_p());}
public static Parser Nothing_p() {return new Parser(hammer.h_nothing_p());}
public static Parser Epsilon_p() {return new Parser(hammer.h_epsilon_p());}
// 1-arg parsers
public static Parser Ignore(Parser p)
{
return new Parser(hammer.h_ignore(p.wrapped)).Pin(p);
}
public static Parser Not(Parser p)
{
return new Parser(hammer.h_not(p.wrapped)).Pin(p);
}
public static Parser Whitespace(Parser p)
{
return new Parser(hammer.h_whitespace(p.wrapped)).Pin(p);
}
public static Parser Optional(Parser p)
{
return new Parser(hammer.h_optional(p.wrapped)).Pin(p);
}
public static Parser And(Parser p)
{
return new Parser(hammer.h_and(p.wrapped)).Pin(p);
}
public static Parser Many(Parser p)
{
return new Parser(hammer.h_many(p.wrapped)).Pin(p);
}
public static Parser Many1(Parser p)
{
return new Parser(hammer.h_many1(p.wrapped)).Pin(p);
}
public static Parser SepBy(Parser p, Parser sep)
{
return new Parser(hammer.h_sepBy(p.wrapped, sep.wrapped)).Pin(p);
}
public static Parser SepBy1(Parser p, Parser sep)
{
return new Parser(hammer.h_sepBy1(p.wrapped, sep.wrapped)).Pin(p);
}
// 2-arg parsers
public static Parser Left(Parser p1, Parser p2)
{
return new Parser(hammer.h_left(p1.wrapped, p2.wrapped)).Pin(p1).Pin(p2);
}
public static Parser Right(Parser p1, Parser p2)
{
return new Parser(hammer.h_right(p1.wrapped, p2.wrapped));
}
public static Parser Xor(Parser p1, Parser p2)
{
return new Parser(hammer.h_xor(p1.wrapped, p2.wrapped));
}
public static Parser Difference(Parser p1, Parser p2)
{
return new Parser(hammer.h_difference(p1.wrapped, p2.wrapped));
}
public static Parser Butnot(Parser p1, Parser p2)
{
return new Parser(hammer.h_butnot(p1.wrapped, p2.wrapped));
}
// Multi-arg parsers
public static Parser Middle(Parser p1, Parser p2, Parser p3)
{
return new Parser(hammer.h_middle(p1.wrapped, p2.wrapped, p3.wrapped));
}
public static Parser Repeat_n(Parser p, uint count)
{
return new Parser(hammer.h_repeat_n(p.wrapped, count));
}
public static Parser Action(Parser p, HAction action)
{
ulong actionNo = Hammer.RegisterAction(action);
return new Parser(hammer.h_tag(p.wrapped, actionNo)).Pin(p).Pin(action);
}
public static Parser AttrBool(Parser p, HPredicate predicate)
{
ulong predNo = Hammer.RegisterPredicate(predicate);
return new Parser(hammer.h_tag(p.wrapped, predNo)).Pin(p).Pin(predicate);
}
}
}

View file

@ -0,0 +1,80 @@
%module hammer;
%include "stdint.i"
// Special attention needs to be paid to:
// h_parse
// h_token
// h_in
// h_not_in
//%typemap(cstype) uint8_t* "byte[]"
//%typemap(csin, pre="unsafe { fixed(byte* temp$csinput = &$csinput[0]) {", terminator="}}") uint8_t* "(IntPtr)temp$csinput"
//%typemap(csvarin) uint8_t
%typemap(imtype) uint8_t* "IntPtr"
%typemap(cstype) uint8_t* "IntPtr"
%typemap(csin) uint8_t* "$csinput"
%typemap(csvarout) uint8_t* %{
get {
return $imcall;
}
%}
%typemap(imtype) void*[] "IntPtr"
%typemap(cstype) void*[] "IntPtr"
%typemap(csin) void*[] "$csinput"
%ignore h_bit_writer_get_buffer;
//%apply (char *STRING, size_t LENGTH) {(uint8_t* str, size_t len)};
//%apply (uint8_t* str, size_t len) {(const uint8_t* input, size_t length)}
//%apply (uint8_t* str, size_t len) {(const uint8_t* str, const size_t len)}
//%apply (uint8_t* str, size_t len) {(const uint8_t* charset, size_t length)}
%typemap(csclassmodifiers) SWIGTYPE "internal class";
%csmethodmodifiers "internal";
%extend HCountedArray_ {
HParsedToken* at(unsigned int posn) {
if (posn >= $self->used)
return NULL;
return $self->elements[posn];
}
}
%include "../swig/hammer.i";
%{
HTokenType dotnet_tagged_token_type;
%}
%inline {
void h_set_dotnet_tagged_token_type(HTokenType new_tt) {
dotnet_tagged_token_type = new_tt;
}
// Need this inline as well
struct HTaggedToken {
HParsedToken *token;
uint64_t label;
};
// this is to make it easier to access via SWIG
struct HTaggedToken *h_parsed_token_get_tagged_token(HParsedToken* hpt) {
return (struct HTaggedToken*)hpt->token_data.user;
}
HParsedToken *act_tag(const HParseResult* p, void* user_data) {
struct HTaggedToken *tagged = H_ALLOC(struct HTaggedToken);
tagged->label = *(uint64_t*)user_data;
tagged->token = p->ast;
return h_make(p->arena, dotnet_tagged_token_type, tagged);
}
HParser *h_tag__m(HAllocator *mm__, HParser *p, uint64_t tag) {
uint64_t *tagptr = h_new(uint64_t, 1);
*tagptr = tag;
return h_action__m(mm__, p, act_tag, tagptr);
}
HParser *h_tag(HParser *p, uint64_t tag) {
return h_tag__m(&system_allocator, p, tag);
}
}

View file

@ -0,0 +1,35 @@
namespace Hammer.Test
{
using NUnit.Framework;
[TestFixture]
public partial class HammerTest
{
[Test]
public void TestAction()
{
Parser parser = Hammer.Action(Hammer.Sequence(Hammer.Choice(Hammer.Ch('a'),
Hammer.Ch('A')),
Hammer.Choice(Hammer.Ch('b'),
Hammer.Ch('B'))),
(HAction)(x => string.Join(",",(object[])x)));
CheckParseOK(parser, "ab", "a,b");
CheckParseOK(parser, "AB", "A,B");
CheckParseFail(parser, "XX");
}
[Test]
public void TestAttrBool()
{
Parser parser = Hammer.AttrBool(Hammer.Many1(Hammer.Choice(Hammer.Ch('a'),
Hammer.Ch('b'))),
(HPredicate)(x => {
object[] elems = (object[])x;
return elems.Length > 1 && (char)elems[0] == (char)elems[1];
}));
CheckParseOK(parser, "aa", new object[]{ 'a','a' });
CheckParseOK(parser, "bb", new object[]{ 'b','b' });
CheckParseFail(parser, "ab");
}
}
}

View file

@ -0,0 +1,316 @@
namespace Hammer.Test {
using NUnit.Framework;
[TestFixture]
public partial class HammerTest {
[Test]
public void TestToken() {
Parser parser;
parser = Hammer.Token("95\xa2");
CheckParseOK(parser, "95\xa2", new byte[]{ 0x39, 0x35, 0xa2});
CheckParseFail(parser, "95\xa2");
}
[Test]
public void TestCh() {
Parser parser;
parser = Hammer.Ch(0xa2);
CheckParseOK(parser, "\xa2", '\xa2');
CheckParseFail(parser, "\xa3");
}
[Test]
public void TestCh_range() {
Parser parser;
parser = Hammer.Ch_range(0x61, 0x63);
CheckParseOK(parser, "b", 'b');
CheckParseFail(parser, "d");
}
[Test]
public void TestInt64() {
Parser parser;
parser = Hammer.Int64();
CheckParseOK(parser, "\xff\xff\xff\xfe\x00\x00\x00\x00", (System.Int64)(-0x200000000));
CheckParseFail(parser, "\xff\xff\xff\xfe\x00\x00\x00");
}
[Test]
public void TestInt32() {
Parser parser;
parser = Hammer.Int32();
CheckParseOK(parser, "\xff\xfe\x00\x00", (System.Int64)(-0x20000));
CheckParseFail(parser, "\xff\xfe\x00");
CheckParseOK(parser, "\x00\x02\x00\x00", (System.Int64)(0x20000));
CheckParseFail(parser, "\x00\x02\x00");
}
[Test]
public void TestInt16() {
Parser parser;
parser = Hammer.Int16();
CheckParseOK(parser, "\xfe\x00", (System.Int64)(-0x200));
CheckParseFail(parser, "\xfe");
CheckParseOK(parser, "\x02\x00", (System.Int64)(0x200));
CheckParseFail(parser, "\x02");
}
[Test]
public void TestInt8() {
Parser parser;
parser = Hammer.Int8();
CheckParseOK(parser, "\x88", (System.Int64)(-0x78));
CheckParseFail(parser, "");
}
[Test]
public void TestUint64() {
Parser parser;
parser = Hammer.Uint64();
CheckParseOK(parser, "\x00\x00\x00\x02\x00\x00\x00\x00", (System.UInt64)0x200000000);
CheckParseFail(parser, "\x00\x00\x00\x02\x00\x00\x00");
}
[Test]
public void TestUint32() {
Parser parser;
parser = Hammer.Uint32();
CheckParseOK(parser, "\x00\x02\x00\x00", (System.UInt64)0x20000);
CheckParseFail(parser, "\x00\x02\x00");
}
[Test]
public void TestUint16() {
Parser parser;
parser = Hammer.Uint16();
CheckParseOK(parser, "\x02\x00", (System.UInt64)0x200);
CheckParseFail(parser, "\x02");
}
[Test]
public void TestUint8() {
Parser parser;
parser = Hammer.Uint8();
CheckParseOK(parser, "x", (System.UInt64)0x78);
CheckParseFail(parser, "");
}
[Test]
public void TestInt_range() {
Parser parser;
parser = Hammer.Int_range(Hammer.Uint8(), 0x3, 0x10);
CheckParseOK(parser, "\x05", (System.UInt64)0x5);
CheckParseFail(parser, "\x0b");
}
[Test]
public void TestWhitespace() {
Parser parser;
parser = Hammer.Whitespace(Hammer.Ch(0x61));
CheckParseOK(parser, "a", 'a');
CheckParseOK(parser, " a", 'a');
CheckParseOK(parser, " a", 'a');
CheckParseOK(parser, "\x09a", 'a');
CheckParseFail(parser, "_a");
parser = Hammer.Whitespace(Hammer.End_p());
CheckParseOK(parser, "", null);
CheckParseOK(parser, " ", null);
CheckParseFail(parser, " x");
}
[Test]
public void TestLeft() {
Parser parser;
parser = Hammer.Left(Hammer.Ch(0x61), Hammer.Ch(0x20));
CheckParseOK(parser, "a ", 'a');
CheckParseFail(parser, "a");
CheckParseFail(parser, " ");
CheckParseFail(parser, "ba");
}
[Test]
public void TestMiddle() {
Parser parser;
parser = Hammer.Middle(Hammer.Ch(' '), Hammer.Ch('a'), Hammer.Ch(' '));
CheckParseOK(parser, " a ", 'a');
CheckParseFail(parser, "a");
CheckParseFail(parser, " a");
CheckParseFail(parser, "a ");
CheckParseFail(parser, " b ");
CheckParseFail(parser, "ba ");
CheckParseFail(parser, " ab");
}
[Test]
public void TestIn() {
Parser parser;
parser = Hammer.In("abc");
CheckParseOK(parser, "b", 'b');
CheckParseFail(parser, "d");
}
[Test]
public void TestNot_in() {
Parser parser;
parser = Hammer.Not_in("abc");
CheckParseOK(parser, "d", 'd');
CheckParseFail(parser, "a");
}
[Test]
public void TestEnd_p() {
Parser parser;
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.End_p());
CheckParseOK(parser, "a", new object[]{ 'a'});
CheckParseFail(parser, "aa");
}
[Test]
public void TestNothing_p() {
Parser parser;
parser = Hammer.Nothing_p();
CheckParseFail(parser, "a");
}
[Test]
public void TestSequence() {
Parser parser;
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.Ch('b'));
CheckParseOK(parser, "ab", new object[]{ 'a', 'b'});
CheckParseFail(parser, "a");
CheckParseFail(parser, "b");
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.Whitespace(Hammer.Ch('b')));
CheckParseOK(parser, "ab", new object[]{ 'a', 'b'});
CheckParseOK(parser, "a b", new object[]{ 'a', 'b'});
CheckParseOK(parser, "a b", new object[]{ 'a', 'b'});
}
[Test]
public void TestChoice() {
Parser parser;
parser = Hammer.Choice(Hammer.Ch('a'), Hammer.Ch('b'));
CheckParseOK(parser, "a", 'a');
CheckParseOK(parser, "b", 'b');
CheckParseOK(parser, "ab", 'a');
CheckParseFail(parser, "c");
}
[Test]
public void TestButnot() {
Parser parser;
parser = Hammer.Butnot(Hammer.Ch('a'), Hammer.Token("ab"));
CheckParseOK(parser, "a", 'a');
CheckParseFail(parser, "ab");
CheckParseOK(parser, "aa", 'a');
parser = Hammer.Butnot(Hammer.Ch_range('0', '9'), Hammer.Ch('6'));
CheckParseOK(parser, "5", '5');
CheckParseFail(parser, "6");
}
[Test]
public void TestDifference() {
Parser parser;
parser = Hammer.Difference(Hammer.Token("ab"), Hammer.Ch('a'));
CheckParseOK(parser, "ab", new byte[]{ 0x61, 0x62});
CheckParseFail(parser, "a");
}
[Test]
public void TestXor() {
Parser parser;
parser = Hammer.Xor(Hammer.Ch_range('0', '6'), Hammer.Ch_range('5', '9'));
CheckParseOK(parser, "0", '0');
CheckParseOK(parser, "9", '9');
CheckParseFail(parser, "5");
CheckParseFail(parser, "a");
}
[Test]
public void TestMany() {
Parser parser;
parser = Hammer.Many(Hammer.Choice(Hammer.Ch('a'), Hammer.Ch('b')));
CheckParseOK(parser, "", new object[]{ });
CheckParseOK(parser, "a", new object[]{ 'a'});
CheckParseOK(parser, "b", new object[]{ 'b'});
CheckParseOK(parser, "aabbaba", new object[]{ 'a', 'a', 'b', 'b', 'a', 'b', 'a'});
}
[Test]
public void TestMany1() {
Parser parser;
parser = Hammer.Many1(Hammer.Choice(Hammer.Ch('a'), Hammer.Ch('b')));
CheckParseFail(parser, "");
CheckParseOK(parser, "a", new object[]{ 'a'});
CheckParseOK(parser, "b", new object[]{ 'b'});
CheckParseOK(parser, "aabbaba", new object[]{ 'a', 'a', 'b', 'b', 'a', 'b', 'a'});
CheckParseFail(parser, "daabbabadef");
}
[Test]
public void TestRepeat_n() {
Parser parser;
parser = Hammer.Repeat_n(Hammer.Choice(Hammer.Ch('a'), Hammer.Ch('b')), 0x2);
CheckParseFail(parser, "adef");
CheckParseOK(parser, "abdef", new object[]{ 'a', 'b'});
CheckParseFail(parser, "dabdef");
}
[Test]
public void TestOptional() {
Parser parser;
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.Optional(Hammer.Choice(Hammer.Ch('b'), Hammer.Ch('c'))), Hammer.Ch('d'));
CheckParseOK(parser, "abd", new object[]{ 'a', 'b', 'd'});
CheckParseOK(parser, "acd", new object[]{ 'a', 'c', 'd'});
CheckParseOK(parser, "ad", new object[]{ 'a', null, 'd'});
CheckParseFail(parser, "aed");
CheckParseFail(parser, "ab");
CheckParseFail(parser, "ac");
}
[Test]
public void TestIgnore() {
Parser parser;
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.Ignore(Hammer.Ch('b')), Hammer.Ch('c'));
CheckParseOK(parser, "abc", new object[]{ 'a', 'c'});
CheckParseFail(parser, "ac");
}
[Test]
public void TestSepBy() {
Parser parser;
parser = Hammer.SepBy(Hammer.Choice(Hammer.Ch('1'), Hammer.Ch('2'), Hammer.Ch('3')), Hammer.Ch(','));
CheckParseOK(parser, "1,2,3", new object[]{ '1', '2', '3'});
CheckParseOK(parser, "1,3,2", new object[]{ '1', '3', '2'});
CheckParseOK(parser, "1,3", new object[]{ '1', '3'});
CheckParseOK(parser, "3", new object[]{ '3'});
CheckParseOK(parser, "", new object[]{ });
}
[Test]
public void TestSepBy1() {
Parser parser;
parser = Hammer.SepBy1(Hammer.Choice(Hammer.Ch('1'), Hammer.Ch('2'), Hammer.Ch('3')), Hammer.Ch(','));
CheckParseOK(parser, "1,2,3", new object[]{ '1', '2', '3'});
CheckParseOK(parser, "1,3,2", new object[]{ '1', '3', '2'});
CheckParseOK(parser, "1,3", new object[]{ '1', '3'});
CheckParseOK(parser, "3", new object[]{ '3'});
CheckParseFail(parser, "");
}
[Test]
public void TestAnd() {
Parser parser;
parser = Hammer.Sequence(Hammer.And(Hammer.Ch('0')), Hammer.Ch('0'));
CheckParseOK(parser, "0", new object[]{ '0'});
CheckParseFail(parser, "1");
parser = Hammer.Sequence(Hammer.And(Hammer.Ch('0')), Hammer.Ch('1'));
CheckParseFail(parser, "0");
CheckParseFail(parser, "1");
parser = Hammer.Sequence(Hammer.Ch('1'), Hammer.And(Hammer.Ch('2')));
CheckParseOK(parser, "12", new object[]{ '1'});
CheckParseFail(parser, "13");
}
[Test]
public void TestNot() {
Parser parser;
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.Choice(Hammer.Token("+"), Hammer.Token("++")), Hammer.Ch('b'));
CheckParseOK(parser, "a+b", new object[]{ 'a', new byte[]{ 0x2b}, 'b'});
CheckParseFail(parser, "a++b");
parser = Hammer.Sequence(Hammer.Ch('a'), Hammer.Choice(Hammer.Sequence(Hammer.Token("+"), Hammer.Not(Hammer.Ch('+'))), Hammer.Token("++")), Hammer.Ch('b'));
CheckParseOK(parser, "a+b", new object[]{ 'a', new object[]{ new byte[]{ 0x2b}}, 'b'});
CheckParseOK(parser, "a++b", new object[]{ 'a', new byte[]{ 0x2b, 0x2b}, 'b'});
}
[Test]
public void TestRightrec() {
Parser parser;
IndirectParser sp_rr = Hammer.Indirect();
sp_rr.Bind(Hammer.Choice(Hammer.Sequence(Hammer.Ch('a'), sp_rr), Hammer.Epsilon_p()));
parser = sp_rr;
CheckParseOK(parser, "a", new object[]{ 'a'});
CheckParseOK(parser, "aa", new object[]{ 'a', new object[]{ 'a'}});
CheckParseOK(parser, "aaa", new object[]{ 'a', new object[]{ 'a', new object[]{ 'a'}}});
}
[Test]
public void TestAmbiguous() {
Parser parser;
IndirectParser sp_d = Hammer.Indirect();
IndirectParser sp_p = Hammer.Indirect();
IndirectParser sp_e = Hammer.Indirect();
sp_d.Bind(Hammer.Ch('d'));
sp_p.Bind(Hammer.Ch('+'));
sp_e.Bind(Hammer.Choice(Hammer.Sequence(sp_e, sp_p, sp_e), sp_d));
parser = sp_e;
CheckParseOK(parser, "d", 'd');
CheckParseOK(parser, "d+d", new object[]{ 'd', '+', 'd'});
CheckParseOK(parser, "d+d+d", new object[]{ new object[]{ 'd', '+', 'd'}, '+', 'd'});
}
}
}

View file

@ -0,0 +1,125 @@
using System;
using Hammer;
namespace Hammer.Test
{
using NUnit.Framework;
public partial class HammerTest
{
protected bool DeepEquals(Object o1, Object o2)
{
if (o1.Equals(o2))
return true;
if (o1.GetType() != o2.GetType())
return false;
if (o1 is byte[])
{
byte[] a1 = (byte[])o1, a2 = (byte[])o2;
if (a1.Length != a2.Length)
return false;
for (uint i = 0; i < a1.Length; i++)
if (a1[i] != a2[i])
return false;
return true;
}
else if (o1 is Object[])
{
Object[] a1 = (Object[])o1, a2 = (Object[])o2;
if (a1.Length != a2.Length)
return false;
for (uint i = 0; i < a1.Length; i++)
if (!DeepEquals(a1[i],a2[i]))
return false;
return true;
}
else
return false;
}
protected static string ToString(Object o)
{
if (o == null)
{
return "null";
}
if (o is byte[])
{
string ret = "<";
byte[] a = (byte[])o;
for (uint i = 0; i < a.Length; i++)
{
if (i != 0)
ret += ".";
ret += a[i].ToString("X2");
}
ret += ">";
return ret;
}
else if (o is Object[])
{
Object[] a = (Object[])o;
string ret = "[";
for (uint i = 0; i < a.Length; i++)
{
if (i != 0)
ret += " ";
ret += ToString(a[i]);
}
ret += "]";
return ret;
}
else if (o is System.Int64)
{
System.Int64 i = (System.Int64)o;
return (i < 0 ? "s-0x" : "s0x") + i.ToString("X");
}
else if (o is System.UInt64)
{
System.UInt64 i = (System.UInt64)o;
return "u0x" + i.ToString("X");
}
else if (o is System.String)
{
return "\"" + o.ToString() + "\"";
}
else if (o is System.Char)
{
return "\'" + o.ToString() + "\'";
}
else
return "WAT(" + o.GetType() + ")";
}
internal static byte[] ToBytes(string s)
{
// Probably not what you want unless you're parsing binary data.
// This is just a one-to-one encoding of the string's codepoints
byte []ret = new byte[s.Length];
for (int i = 0; i < s.Length; i++)
{
ret[i] = (byte)s[i];
}
return ret;
}
protected void CheckParseOK(Parser p, string probe, Object expected)
{
Object ret = p.Parse(ToBytes(probe));
Assert.That(ret, Is.Not.Null);
//System.Console.WriteLine(ToString(ret));
//System.Console.WriteLine(ToString(expected));
if (!DeepEquals(ret, expected))
Assert.Fail();
else
Assert.Pass();
}
protected void CheckParseFail(Parser p, string probe)
{
Object ret = p.Parse(ToBytes(probe));
Assert.That(ret, Is.Null);
}
}
}

View file

@ -16,7 +16,7 @@ pytests = ['hammer_tests.py']
pytestexec = pytestenv.Command(['hammer.pyc', 'hammer_tests.pyc'], pytests + libhammer_python, "LD_LIBRARY_PATH=" + os.path.dirname(str(libhammer_shared[0])) + " nosetests -vv $SOURCE")
pytest = Alias("testpython", [pytestexec], pytestexec)
AlwaysBuild(pytestexec)
testruns.extend(pytest)
testruns.append(pytest)
pyinstallexec = pythonenv.Command(None, libhammer_python, 'python ' + os.path.join(pydir, 'setup.py ') + ' install')
pyinstall = Alias("installpython", [pyinstallexec], pyinstallexec)

View file

@ -666,13 +666,13 @@ void h_benchmark_report(FILE* stream, HBenchmarkResults* results);
// {{{ Token type registry
/// Allocate a new, unused (as far as this function knows) token type.
int h_allocate_token_type(const char* name);
HTokenType h_allocate_token_type(const char* name);
/// Get the token type associated with name. Returns -1 if name is unkown
int h_get_token_type_number(const char* name);
HTokenType h_get_token_type_number(const char* name);
/// Get the name associated with token_type. Returns NULL if the token type is unkown
const char* h_get_token_type_name(int token_type);
const char* h_get_token_type_name(HTokenType token_type);
// }}}
#ifdef __cplusplus

View file

@ -22,14 +22,14 @@
typedef struct Entry_ {
const char* name;
int value;
HTokenType value;
} Entry;
static void *tt_registry = NULL;
static Entry** tt_by_id = NULL;
static int tt_by_id_sz = 0;
static unsigned int tt_by_id_sz = 0;
#define TT_START TT_USER
static int tt_next = TT_START;
static HTokenType tt_next = TT_START;
/*
// TODO: These are for the extension registry, which does not yet have a good name.
@ -45,12 +45,12 @@ static int compare_entries(const void* v1, const void* v2) {
return strcmp(e1->name, e2->name);
}
int h_allocate_token_type(const char* name) {
HTokenType h_allocate_token_type(const char* name) {
Entry* new_entry = malloc(sizeof(*new_entry));
new_entry->name = name;
new_entry->value = -1;
new_entry->value = 0;
Entry* probe = *(Entry**)tsearch(new_entry, &tt_registry, compare_entries);
if (probe->value != -1) {
if (probe->value != 0) {
// Token type already exists...
// TODO: treat this as a bug?
free(new_entry);
@ -70,16 +70,16 @@ int h_allocate_token_type(const char* name) {
return probe->value;
}
}
int h_get_token_type_number(const char* name) {
HTokenType h_get_token_type_number(const char* name) {
Entry e;
e.name = name;
Entry **ret = (Entry**)tfind(&e, &tt_registry, compare_entries);
if (ret == NULL)
return -1;
return 0;
else
return (*ret)->value;
}
const char* h_get_token_type_name(int token_type) {
const char* h_get_token_type_name(HTokenType token_type) {
if (token_type >= tt_next || token_type < TT_START)
return NULL;
else

View file

@ -26,7 +26,7 @@ static void test_tt_registry(void) {
g_test_message("Unknown token type should not return a name");
g_test_fail();
}
g_check_cmp_int32(h_get_token_type_number("com.upstandinghackers.test.unkown_token_type"), ==, -1);
g_check_cmp_int32(h_get_token_type_number("com.upstandinghackers.test.unkown_token_type"), ==, 0);
}
void register_misc_tests(void) {

1
tools/csharp/.hgignore Normal file
View file

@ -0,0 +1 @@
.*~

1
tools/csharp/README Normal file
View file

@ -0,0 +1 @@
Grabbed from https://bitbucket.org/russel/scons_csharp, revision 058956ce21722f806a560c24305775f74dd71d3f

24
tools/csharp/__init__.py Normal file
View file

@ -0,0 +1,24 @@
# -*- coding:utf-8; -*-
# Copyright (c) 2012 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from csharp import exists, generate

503
tools/csharp/csharp.py Normal file
View file

@ -0,0 +1,503 @@
# -*- coding:utf-8; -*-
# Copyright (c) 2009-10 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# This C# Tool taken from http://www.scons.org/wiki/CsharpBuilder and amended
# by the patch from Issue 1912 at http://scons.tigris.org/issues/show_bug.cgi?id=1912
# Amended and extended by Russel Winder <russel.winder@concertant.com>
# On the SCons wiki page there are two distinct tools, one for the Microsoft C# system and one for Mono.
# This is an attempt to meld to two based initially on the Microsoft C# tool with amendmnets from the Mono
# tool.
import os.path
import SCons.Builder
import SCons.Node.FS
import SCons.Util
from SCons.Node.Python import Value
# needed for adding methods to environment
from SCons.Script.SConscript import SConsEnvironment
# parses env['VERSION'] for major, minor, build, and revision
def parseVersion(env):
"""parses env['VERSION'] for major, minor, build, and revision"""
if type(env['VERSION']) is tuple or type(env['VERSION']) is list:
major, minor, build, revision = env['VERSION']
elif type(env['VERSION']) is str:
major, minor, build, revision = env['VERSION'].split('.')
major = int(major)
minor = int(minor)
build = int(build)
revision = int(revision)
return (major, minor, build, revision)
def getVersionAsmDirective(major, minor, build, revision):
return '[assembly: AssemblyVersion("%d.%d.%d.%d")]' % (major, minor, build, revision)
def generateVersionId(env, target, source):
out = open(target[0].path, 'w')
out.write('using System;using System.Reflection;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;\n')
out.write(source[0].get_contents())
out.close()
# used so that we can capture the return value of an executed command
def subprocess(cmdline):
"""used so that we can capture the return value of an executed command"""
import subprocess
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, startupinfo=startupinfo, shell=False)
data, err = proc.communicate()
return proc.wait(), data, err
def generatePublisherPolicyConfig(env, target, source):
"""this method assumes that source list corresponds to [0]=version, [1]=assembly base name, [2]=assembly file node"""
# call strong name tool against compiled assembly and parse output for public token
outputFolder = os.path.split(target[0].tpath)[0]
pubpolicy = os.path.join(outputFolder, source[2].name)
rv, data, err = subprocess('sn -T ' + pubpolicy)
import re
tok_re = re.compile(r"([a-z0-9]{16})[\r\n ]{0,3}$")
match = tok_re.search(data)
tok = match.group(1)
# calculate version range to redirect from
version = source[0].value
oldVersionStartRange = '%s.%s.0.0' % (version[0], version[1])
newVersion = '%s.%s.%s.%s' % (version[0], version[1], version[2], version[3])
build = int(version[2])
rev = int(version[3])
# on build 0 and rev 0 or 1, no range is needed. otherwise calculate range
if (build == 0 and (rev == 0 or rev == 1)):
oldVersionRange = oldVersionStartRange
else:
if rev - 1 < 0:
endRevisionRange = '99'
endBuildRange = str(build-1)
else:
endRevisionRange = str(rev - 1)
endBuildRange = str(build)
oldVersionEndRange = '%s.%s.%s.%s' % (version[0], version[1], endBuildRange, endRevisionRange)
oldVersionRange = '%s-%s' % (oldVersionStartRange, oldVersionEndRange)
# write .net config xml out to file
out = open(target[0].path, 'w')
out.write('''\
<configuration><runtime><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="%s" publicKeyToken="%s"/>
<bindingRedirect oldVersion="%s" newVersion="%s"/>
</dependentAssembly>
</assemblyBinding></runtime></configuration>
''' % (source[1].value, tok, oldVersionRange, newVersion))
out.close()
def getKeyFile(node, sources):
"""search for key file"""
for file in node.children():
if file.name.endswith('.snk'):
sources.append(file)
return
# if not found look in included netmodules (first found is used)
for file in node.children():
if file.name.endswith('.netmodule'):
for file2 in file.children():
if file2.name.endswith('.snk'):
sources.append(file2)
return
def PublisherPolicy(env, target, **kw):
"""creates the publisher policy dll, mapping the major.minor.0.0 calls to the
major, minor, build, and revision passed in through the dictionary VERSION key"""
sources = []
# get version and generate .config file
version = parseVersion(kw)
asm = os.path.splitext(target[0].name)[0]
configName = 'policy.%d.%d.%s.%s' % (version[0], version[1], asm, 'config')
targ = 'policy.%d.%d.%s' % (version[0], version[1], target[0].name)
config = env.Command(configName, [Value(version), Value(asm), target[0]], generatePublisherPolicyConfig)
sources.append(config[0])
# find .snk key
getKeyFile(target[0], sources)
return env.CLIAsmLink(targ, sources, **kw)
def CLIRefs(env, refs, paths = [], **kw):
listRefs = []
normpaths = [env.Dir(p).abspath for p in paths]
normpaths += env['CLIREFPATHS']
for ref in refs:
if not ref.endswith(env['SHLIBSUFFIX']):
ref += env['SHLIBSUFFIX']
if not ref.startswith(env['SHLIBPREFIX']):
ref = env['SHLIBPREFIX'] + ref
pathref = detectRef(ref, normpaths, env)
if pathref:
listRefs.append(pathref)
return listRefs
def CLIMods(env, refs, paths = [], **kw):
listMods = []
normpaths = [env.Dir(p).abspath for p in paths]
normpaths += env['CLIMODPATHS']
for ref in refs:
if not ref.endswith(env['CLIMODSUFFIX']):
ref += env['CLIMODSUFFIX']
pathref = detectRef(ref, normpaths, env)
if pathref:
listMods.append(pathref)
return listMods
def detectRef(ref, paths, env):
"""look for existance of file (ref) at one of the paths"""
for path in paths:
if path.endswith(ref):
return path
pathref = os.path.join(path, ref)
if os.path.isfile(pathref):
return pathref
return ''
def AddToRefPaths(env, files, **kw):
# the file name is included in path reference because otherwise checks for that output file
# by CLIRefs/CLIMods would fail until after it has been built. Since SCons makes a pass
# before building anything, that file won't be there. Only after the second pass will it be built
ref = env.FindIxes(files, 'SHLIBPREFIX', 'SHLIBSUFFIX').abspath
env['CLIREFPATHS'] = [ref] + env['CLIREFPATHS']
return 0
def AddToModPaths(env, files, **kw):
mod = env.FindIxes(files, 'CLIMODPREFIX', 'CLIMODSUFFIX').abspath
env['CLIMODPATHS'] = [mod] + env['CLIMODPATHS']
return 0
def cscFlags(target, source, env, for_signature):
listCmd = []
if (env.has_key('WINEXE')):
if (env['WINEXE'] == 1):
listCmd.append('-t:winexe')
return listCmd
def cscSources(target, source, env, for_signature):
listCmd = []
for s in source:
if (str(s).endswith('.cs')): # do this first since most will be source files
listCmd.append(s)
elif (str(s).endswith('.resources')):
listCmd.append('-resource:%s' % s.get_string(for_signature))
elif (str(s).endswith('.snk')):
listCmd.append('-keyfile:%s' % s.get_string(for_signature))
else:
# just treat this as a generic unidentified source file
listCmd.append(s)
return listCmd
def cscSourcesNoResources(target, source, env, for_signature):
listCmd = []
for s in source:
if (str(s).endswith('.cs')): # do this first since most will be source files
listCmd.append(s)
elif (str(s).endswith('.resources')): # resources cannot be embedded in netmodules
pass
elif (str(s).endswith('.snk')):
listCmd.append('-keyfile:%s' % s.get_string(for_signature))
else:
# just treat this as a generic unidentified source file
listCmd.append(s)
return listCmd
def cscRefs(target, source, env, for_signature):
listCmd = []
if (env.has_key('ASSEMBLYREFS')):
refs = SCons.Util.flatten(env['ASSEMBLYREFS'])
for ref in refs:
if SCons.Util.is_String(ref):
listCmd.append('-reference:%s' % ref)
else:
listCmd.append('-reference:%s' % ref.abspath)
return listCmd
def cscMods(target, source, env, for_signature):
listCmd = []
if (env.has_key('NETMODULES')):
mods = SCons.Util.flatten(env['NETMODULES'])
for mod in mods:
listCmd.append('-addmodule:%s' % mod)
return listCmd
# TODO: this currently does not allow sources to be embedded (-embed flag)
def alLinkSources(target, source, env, for_signature):
listCmd = []
for s in source:
if (str(s).endswith('.snk')):
listCmd.append('-keyfile:%s' % s.get_string(for_signature))
else:
# just treat this as a generic unidentified source file
listCmd.append('-link:%s' % s.get_string(for_signature))
if env.has_key('VERSION'):
version = parseVersion(env)
listCmd.append('-version:%d.%d.%d.%d' % version)
return listCmd
def cliLinkSources(target, source, env, for_signature):
listCmd = []
# append source item. if it is a netmodule and has child resources, also append those
for s in source:
# all source items should go into listCmd
listCmd.append('%s' % s.get_string(for_signature))
if (str(s).endswith('.netmodule')):
for child in s.children():
if child.name.endswith('.resources'):
listCmd.append('/assemblyresource:%s' % child.get_string(for_signature))
return listCmd
def add_version(target, source, env):
if env.has_key('VERSION'):
if SCons.Util.is_String(target[0]):
versionfile = target[0] + '_VersionInfo.cs'
else:
versionfile = target[0].name + '_VersionInfo.cs'
source.append(env.Command(versionfile, [Value(getVersionAsmDirective(*parseVersion(env)))], generateVersionId))
return (target, source)
# this check is needed because .NET assemblies like to have '.' in the name.
# scons interprets that as an extension and doesn't append the suffix as a result
def lib_emitter(target, source, env):
newtargets = []
for tnode in target:
t = tnode.name
if not t.endswith(env['SHLIBSUFFIX']):
t += env['SHLIBSUFFIX']
newtargets.append(t)
return (newtargets, source)
def add_depends(target, source, env):
"""Add dependency information before the build order is established"""
if (env.has_key('NETMODULES')):
mods = SCons.Util.flatten(env['NETMODULES'])
for mod in mods:
# add as dependency
for t in target:
env.Depends(t, mod)
if (env.has_key('ASSEMBLYREFS')):
refs = SCons.Util.flatten(env['ASSEMBLYREFS'])
for ref in refs:
# add as dependency
for t in target:
env.Depends(t, ref)
return (target, source)
csc_action = SCons.Action.Action('$CSCCOM', '$CSCCOMSTR')
MsCliBuilder = SCons.Builder.Builder(action = '$CSCCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
emitter = add_version,
suffix = '.exe')
csclib_action = SCons.Action.Action('$CSCLIBCOM', '$CSCLIBCOMSTR')
MsCliLibBuilder = SCons.Builder.Builder(action = '$CSCLIBCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
emitter = [lib_emitter, add_version, add_depends],
suffix = '$SHLIBSUFFIX')
cscmod_action = SCons.Action.Action('$CSCMODCOM', '$CSCMODCOMSTR')
MsCliModBuilder = SCons.Builder.Builder(action = '$CSCMODCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
emitter = [add_version, add_depends],
suffix = '$CLIMODSUFFIX')
def module_deps(target, source, env):
for s in source:
dir = s.dir.srcdir
if (dir is not None and dir is not type(None)):
for t in target:
env.Depends(t,s)
return (target, source)
clilink_action = SCons.Action.Action('$CLILINKCOM', '$CLILINKCOMSTR')
MsCliLinkBuilder = SCons.Builder.Builder(action = '$CLILINKCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
emitter = [lib_emitter, add_version, module_deps], # don't know the best way yet to get module dependencies added
suffix = '.dll') #'$SHLIBSUFFIX')
# TODO : This probably needs some more work... it hasn't been used since
# finding the abilities of the VS 2005 C++ linker for .NET.
MsCliAsmLinkBuilder = SCons.Builder.Builder(action = '$CLIASMLINKCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
suffix = '.dll')
typelib_prefix = 'Interop.'
def typelib_emitter(target, source, env):
newtargets = []
for tnode in target:
t = tnode.name
if not t.startswith(typelib_prefix):
t = typelib_prefix + t
newtargets.append(t)
return (newtargets, source)
def tlbimpFlags(target, source, env, for_signature):
listCmd = []
basename = os.path.splitext(target[0].name)[0]
# strip off typelib_prefix (such as 'Interop.') so it isn't in the namespace
if basename.startswith(typelib_prefix):
basename = basename[len(typelib_prefix):]
listCmd.append('-namespace:%s' % basename)
listCmd.append('-out:%s' % target[0].tpath)
for s in source:
if (str(s).endswith('.snk')):
listCmd.append('-keyfile:%s' % s.get_string(for_signature))
return listCmd
typelibimp_action = SCons.Action.Action('$TYPELIBIMPCOM', '$TYPELIBIMPCOMSTR')
MsCliTypeLibBuilder = SCons.Builder.Builder(action = '$TYPELIBIMPCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
emitter = [typelib_emitter, add_depends],
suffix = '.dll')
res_action = SCons.Action.Action('$CLIRCCOM', '$CLIRCCOMSTR')
def res_emitter(target, source, env):
# prepend NAMESPACE if provided
if (env.has_key('NAMESPACE')):
newtargets = []
for t in target:
tname = t.name
# this is a cheesy way to get rid of '.aspx' in .resx file names
idx = tname.find('.aspx.')
if idx >= 0:
tname = tname[:idx] + tname[idx+5:]
newtargets.append('%s.%s' % (env['NAMESPACE'], tname))
return (newtargets, source)
else:
return (targets, source)
MsCliResBuilder = SCons.Builder.Builder(action=res_action,
emitter=res_emitter,
src_suffix='.resx',
suffix='.resources',
src_builder=[],
source_scanner=SCons.Tool.SourceFileScanner)
SCons.Tool.SourceFileScanner.add_scanner('.resx', SCons.Defaults.CScan)
def generate(env):
envpaths = env['ENV']['PATH']
env['CLIREFPATHS'] = envpaths.split(os.pathsep)
env['CLIMODPATHS'] = []
env['ASSEMBLYREFS'] = []
env['NETMODULES'] = []
env['BUILDERS']['CLIProgram'] = MsCliBuilder
env['BUILDERS']['CLIAssembly'] = MsCliLibBuilder
env['BUILDERS']['CLILibrary'] = MsCliLibBuilder
env['BUILDERS']['CLIModule'] = MsCliModBuilder
env['BUILDERS']['CLILink'] = MsCliLinkBuilder
env['BUILDERS']['CLIAsmLink'] = MsCliAsmLinkBuilder
env['BUILDERS']['CLIRes'] = MsCliResBuilder
env['BUILDERS']['CLITypeLib'] = MsCliTypeLibBuilder
env['CSC'] = env.Detect('gmcs') or 'csc'
env['_CSCLIBS'] = "${_stripixes('-r:', CILLIBS, '', '-r', '', __env__)}"
env['_CSCLIBPATH'] = "${_stripixes('-lib:', CILLIBPATH, '', '-r', '', __env__)}"
env['CSCFLAGS'] = SCons.Util.CLVar('-nologo -noconfig')
env['_CSCFLAGS'] = cscFlags
env['_CSC_SOURCES'] = cscSources
env['_CSC_SOURCES_NO_RESOURCES'] = cscSourcesNoResources
env['_CSC_REFS'] = cscRefs
env['_CSC_MODS'] = cscMods
env['CSCCOM'] = '$CSC $CSCFLAGS $_CSCFLAGS -out:${TARGET.abspath} $_CSC_REFS $_CSC_MODS $_CSC_SOURCES'
env['CSCLIBCOM'] = '$CSC -t:library $CSCFLAGS $_CSCFLAGS $_CSCLIBPATH $_CSCLIBS -out:${TARGET.abspath} $_CSC_REFS $_CSC_MODS $_CSC_SOURCES'
env['CSCMODCOM'] = '$CSC -t:module $CSCFLAGS $_CSCFLAGS -out:${TARGET.abspath} $_CSC_REFS $_CSC_MODS $_CSC_SOURCES_NO_RESOURCES'
env['CLIMODPREFIX'] = ''
env['CLIMODSUFFIX'] = '.netmodule'
env['CSSUFFIX'] = '.cs'
# this lets us link .netmodules together into a single assembly
env['CLILINK'] = 'link'
env['CLILINKFLAGS'] = SCons.Util.CLVar('-nologo -ltcg -dll -noentry')
env['_CLILINK_SOURCES'] = cliLinkSources
env['CLILINKCOM'] = '$CLILINK $CLILINKFLAGS -out:${TARGET.abspath} $_CLILINK_SOURCES' # $SOURCES'
env['CLIASMLINK'] = 'al'
env['CLIASMLINKFLAGS'] = SCons.Util.CLVar('')
env['_ASMLINK_SOURCES'] = alLinkSources
env['CLIASMLINKCOM'] = '$CLIASMLINK $CLIASMLINKFLAGS -out:${TARGET.abspath} $_ASMLINK_SOURCES'
env['CLIRC'] = 'resgen'
env['CLIRCFLAGS'] = ''
env['CLIRCCOM'] = '$CLIRC $CLIRCFLAGS $SOURCES $TARGETS'
env['TYPELIBIMP'] = 'tlbimp'
env['TYPELIBIMPFLAGS'] = SCons.Util.CLVar('-sysarray')
env['_TYPELIBIMPFLAGS'] = tlbimpFlags
env['TYPELIBIMPCOM'] = '$TYPELIBIMP $SOURCES $TYPELIBIMPFLAGS $_TYPELIBIMPFLAGS'
SConsEnvironment.CLIRefs = CLIRefs
SConsEnvironment.CLIMods = CLIMods
SConsEnvironment.AddToRefPaths = AddToRefPaths
SConsEnvironment.AddToModPaths = AddToModPaths
SConsEnvironment.PublisherPolicy = PublisherPolicy
def exists(env):
return env.Detect('csc') or env.Detect('gmcs')

156
tools/csharp/csharp.xml Normal file
View file

@ -0,0 +1,156 @@
<!--
__COPYRIGHT__
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
-->
<tool name="mscs">
<summary>
Sets construction variables for the Microsoft CSharp Compiler
</summary>
</tool>
<builder name="CLILibrary">
<summary>
Builds a .NET assembly (dynamically linkable binary) from a list of sources.
<example>
env.CLILibrary('MyAsm', 'MyAsm.cs')
</example>
</summary>
</builder>
<builder name="CLILink">
<summary>
Uses Microsoft C++ linker to link netmodules into a single .NET assembly:
<example>
env.CLILink('Common', ['mod1.netmodule', 'mod2.netmodule'])
</example>
</summary>
</builder>
<builder name="CLIModule">
<summary>
Builds a .NET netmodule (statically linkable binary) from a list of sources:
<example>
env.CLIModule('MyMod', 'MyMod.cs')
</example>
</summary>
</builder>
<builder name="CLIProgram">
<summary>
Builds a .NET executable from a list of sources.
If the $WINEXE value is set, the sources will be compiled as a windows app, rather than a console app:
<example>
env.Program('MyApp', 'MyApp.cs', WINEXE=1)
</example>
</summary>
</builder>
<builder name="CLIRes">
<summary>
Builds a Microsoft binary resource file (extension of .resources) from XML source files.
If the $NAMESPACE value is set, its value is prepended to the name of the target file:
<example>
env.CLIRes('app.resx', NAMESPACE='MyCompany.ProductX')
</example>
</summary>
</builder>
<builder name="CLITypeLib">
<summary>
Builds a .NET interop assembly that contains converted type definitions found within a COM type library DLL. Prepends the .NET assembly with 'Interop.':
<example>
env.CLITypeLib('MyLib', ['MyLib.dll', 'keyfile.snk'])
</example>
</summary>
</builder>
<cvar name="CLILINK">
<summary>
The Microsoft C++ linker.
</summary>
</cvar>
<cvar name="CLILINKFLAGS">
<summary>
General options passed to the Microsoft C++ linker.
</summary>
</cvar>
<cvar name="CLILINKCOM">
<summary>
The command line used to link .NET netmodules into an assembly. Any options specified in the $CLILINKFLAGS construction variable is included on this command line.
</summary>
</cvar>
<cvar name="CSC">
<summary>
The CSharp Compiler.
</summary>
</cvar>
<cvar name="CSCFLAGS">
<summary>
General options that are passed to the CSharp Compiler.
</summary>
</cvar>
<cvar name="CSCCOM">
<summary>
The command line used to compile a CSharp source file to a console or windows executable. Any options specified in the $CSCFLAGS is included on this command line.
</summary>
</cvar>
<cvar name="CSCLIBCOM">
<summary>
The command line used to compile a CSharp source file to a .NET assembly. Any options specified in the $CSCFLAGS is included on this command line.
</summary>
</cvar>
<cvar name="CSCMODCOM">
<summary>
The command line used to compile a CSharp source file to a .NET netmodule. Any options specified in the $CSCFLAGS is included on this command line.
</summary>
</cvar>
<cvar name="CLIRC">
<summary>
The Microsoft .NET resource compiler.
</summary>
</cvar>
<cvar name="CLIRCFLAGS">
<summary>
General options passed to the Microsoft .NET resource compiler.
</summary>
</cvar>
<cvar name="CLIRCCOM">
<summary>
The command line used to compile XML resource files to a .NET resource binary. Any options specified in the $CLIRCFLAGS construction variable is included on this command line.
</summary>
</cvar>
<cvar name="TYPELIBIMP">
<summary>
The Microsoft Type Library Importer.
</summary>
</cvar>
<cvar name="TYPELIBIMPFLAGS">
<summary>
General options passed to the Microsoft Type Library Importer.
</summary>
</cvar>
<cvar name="TYPELIBIMPCOM">
<summary>
The command line used to convert type definitions found within a COM type library into equivalent definitions in a .NET assembly. Any options specified in the $TYPELIBIMPFLAGS construction variable is included on this command line.
</summary>
</cvar>

56
tools/csharp/mono.py Normal file
View file

@ -0,0 +1,56 @@
# -*- mode:python; coding:utf-8; -*-
# Copyright (c) 2009 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# A tool for processing C# code.
# This C# Tool for Mono taken from http://www.scons.org/wiki/CsharpBuilder.
import os.path
import SCons.Builder
import SCons.Node.FS
import SCons.Util
csccom = "$CSC $CSCFLAGS -out:${TARGET.abspath} $SOURCES"
csclibcom = "$CSC -t:library $CSCLIBFLAGS $_CSCLIBPATH $_CSCLIBS -out:${TARGET.abspath} $SOURCES"
McsBuilder = SCons.Builder.Builder(action = '$CSCCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
suffix = '.exe')
McsLibBuilder = SCons.Builder.Builder(action = '$CSCLIBCOM',
source_factory = SCons.Node.FS.default_fs.Entry,
suffix = '.dll')
def generate(env):
env['BUILDERS']['CLIProgram'] = McsBuilder
env['BUILDERS']['CLILibrary'] = McsLibBuilder
env['CSC'] = 'mcs'
env['_CSCLIBS'] = "${_stripixes('-r:', CILLIBS, '', '-r', '', __env__)}"
env['_CSCLIBPATH'] = "${_stripixes('-lib:', CILLIBPATH, '', '-r', '', __env__)}"
env['CSCFLAGS'] = SCons.Util.CLVar('')
env['CSCCOM'] = SCons.Action.Action(csccom)
env['CSCLIBCOM'] = SCons.Action.Action(csclibcom)
def exists(env):
return internal_zip or env.Detect('mcs')