[msitools] preprocessor: add condition evaluator



commit 7d50947ca464d4cbad135919385cde86fd2c4d69
Author: Marc-André Lureau <marcandre lureau gmail com>
Date:   Fri Apr 12 03:24:37 2013 +0200

    preprocessor: add condition evaluator

 tests/data/wixl/arch-condition.wxs |   13 ++
 tests/wixl.at                      |    5 +-
 tools/wixl/msi.vala                |    2 +-
 tools/wixl/preprocessor.vala       |  237 ++++++++++++++++++++++++++++++++++++
 tools/wixl/util.vala               |    4 +
 5 files changed, 259 insertions(+), 2 deletions(-)
---
diff --git a/tests/data/wixl/arch-condition.wxs b/tests/data/wixl/arch-condition.wxs
new file mode 100644
index 0000000..c870003
--- /dev/null
+++ b/tests/data/wixl/arch-condition.wxs
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='windows-1252'?>
+<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
+  <?if $(var.Arch) = x64 ?>
+    <?define ArchString = "(64 bit)" ?>
+    <?define Win64 = "yes" ?>
+    <?define ProgramFilesFolder = "ProgramFiles64Folder" ?>
+  <?else ?>
+    <?define ArchString = "" ?>
+    <?define Win64 = "no" ?>
+    <?define ProgramFilesFolder = "ProgramFilesFolder" ?>
+  <?endif ?>
+  <?warning $(var.Win64)?>
+</Wix>
diff --git a/tests/wixl.at b/tests/wixl.at
index c11304c..d846b84 100644
--- a/tests/wixl.at
+++ b/tests/wixl.at
@@ -108,7 +108,10 @@ AT_CHECK_WIXL([-D Bar -o out.msi IncludeTest.wxs], [0], [ignore],
 AT_CHECK_WIXL([-D Foo -o out.msi IncludeTest.wxs], [0], [ignore],
 [IncludeTest.wxs:4: warning: Foo
 ])
-# FIXME: add tons of tests on out.msi
+AT_WIXLDATA([arch-condition.wxs])
+AT_CHECK_WIXL([-o out.msi arch-condition.wxs], [0], [ignore],
+[arch-condition.wxs:12: warning: no
+])
 AT_CHECK([test -f out.msi], [0])
 AT_CLEANUP
 
diff --git a/tools/wixl/msi.vala b/tools/wixl/msi.vala
index 855f711..ed3c949 100644
--- a/tools/wixl/msi.vala
+++ b/tools/wixl/msi.vala
@@ -647,7 +647,7 @@ namespace Wixl {
                 critical ("Unhandled property type");
         }
 
-            public void save (Libmsi.Database db) throws GLib.Error {
+        public void save (Libmsi.Database db) throws GLib.Error {
             properties.save (db);
         }
 
diff --git a/tools/wixl/preprocessor.vala b/tools/wixl/preprocessor.vala
index 16f30a0..b5b7f63 100644
--- a/tools/wixl/preprocessor.vala
+++ b/tools/wixl/preprocessor.vala
@@ -84,6 +84,238 @@ namespace Wixl {
             return result + str[end:str.length];
         }
 
+        class EvalCondition: Object {
+            File? file;
+            Preprocessor preprocessor;
+            Scanner scanner;
+
+            enum Op {
+                NONE,
+                EQUAL,
+
+                OPEN_VAR,
+                EQ,
+                EC,
+                NE,
+                LT,
+                LTE,
+                GT,
+                GTE,
+                AND,
+                OR,
+                NOT,
+            }
+
+            construct {
+                scanner = new Scanner (null);
+                scanner.config.scan_identifier_1char = true;
+                scanner.config.cset_identifier_nth =
+                    (string)scanner.config.cset_identifier_nth + ".";
+                scanner.scope_add_symbol (0, "and", (void*)Op.AND);
+                scanner.scope_add_symbol (0, "or", (void*)Op.OR);
+                scanner.scope_add_symbol (0, "not", (void*)Op.NOT);
+            }
+
+            public EvalCondition (Preprocessor pp, File? file = null) {
+                this.file = file;
+                this.preprocessor = pp;
+            }
+
+            bool to_bool (Value val) {
+                Value result = false;
+                val.transform (ref result);
+                return (bool) result;
+            }
+
+            bool accept (int type) {
+                var accepted = (int)scanner.peek_next_token () == type;
+                if (accepted)
+                    scanner.get_next_token ();
+                return accepted;
+            }
+
+            bool expect (int type) throws GLib.Error {
+                if (!accept (type))
+                    throw new Wixl.Error.FAILED ("Expected token %d", type);
+
+                return true;
+            }
+
+            bool accept_symbol (Op symbol) {
+                bool accepted = false;
+                if (scanner.peek_next_token () == TokenType.SYMBOL) {
+                    accepted = (int)scanner.next_value.symbol == symbol;
+                }
+
+                if (accepted)
+                    scanner.get_next_token ();
+                return accepted;
+            }
+
+            Value term () throws GLib.Error {
+                if (accept (TokenType.LEFT_PAREN)) {
+                    Value val = expr ();
+                    expect (TokenType.RIGHT_PAREN);
+                    return val;
+                } else if (accept (TokenType.INT)) {
+                    return scanner.cur_value ().int;
+                } else if (accept (TokenType.STRING)) {
+                    return scanner.cur_value ().string;
+                } else if (accept (TokenType.IDENTIFIER)) {
+                    return scanner.cur_value ().identifier;
+                } else if (accept ('$')) {
+                    if (accept (TokenType.LEFT_PAREN)) {
+                        expect (TokenType.IDENTIFIER);
+                        var val = preprocessor.eval_variable (scanner.cur_value ().identifier, file);
+                        expect (TokenType.RIGHT_PAREN);
+                        return val;
+                    }
+                }
+
+#if 0
+                debug ("term %d", scanner.peek_next_token ());
+                if (accept (TokenType.SYMBOL))
+                    message ("symbol: %d", (int)scanner.cur_value ().symbol);
+#endif
+
+                throw new Wixl.Error.FAILED ("Invalid term");
+            }
+
+            int to_int (Value val) {
+                Value result = 0;
+                val.transform (ref result);
+                return result.get_int ();
+            }
+
+            string to_string (Value val) {
+                Value result = "";
+                val.transform (ref result);
+                return result.get_string ();
+            }
+
+            bool cmp_term () throws GLib.Error {
+                var op = Op.NONE;
+                Value left = term ();
+
+                if (accept ('<'))
+                    op = Op.LT;
+                else if (accept ('>'))
+                    op = Op.GT;
+                else if (accept (TokenType.EQUAL_SIGN))
+                    return to_string (left) == to_string (term ());
+                else if (accept ('!') && expect ('='))
+                    return to_string (left) != to_string (term ());
+                else if (accept ('~') && expect ('=')) {
+                    return to_string (left).ascii_casecmp (to_string (term ())) == 0;
+                }
+
+                if (op != Op.NONE) {
+                    bool eq = false;
+                    if (accept ('='))
+                        eq = true;
+
+                    var a = to_int (left);
+                    var b = to_int (term ());
+
+                    if (eq && a == b)
+                        return true;
+
+                    if (op == Op.GT)
+                        return a > b;
+                    else if (op == Op.LT)
+                        return a < b;
+                }
+
+                return to_bool (left);
+            }
+
+            bool not_term () throws GLib.Error {
+                if (accept_symbol (Op.NOT))
+                    return !not_term ();
+                else
+                    return cmp_term ();
+            }
+
+            bool expr () throws GLib.Error {
+                bool handled = false;
+
+                var result = not_term ();
+                do {
+                    // TODO: I think wixl condition are left-evaled
+                    handled = true;
+                    if (accept_symbol (Op.AND))
+                        result = not_term () && result;
+                    else if (accept_symbol (Op.OR))
+                        result = not_term () || result;
+                    else
+                        handled = false;
+                } while (handled);
+
+                return result;
+            }
+
+            public bool eval (string str) throws GLib.Error {
+                scanner.input_text (str, str.length);
+                var result = expr ();
+
+                if (!accept (TokenType.EOF))
+                    warning ("condition invaluation incomplete, next token %d",
+                             scanner.peek_next_token ());
+
+                return result;
+            }
+        }
+
+#if 0
+       void test (string v, bool expect = true) {
+            var evaluator = new EvalCondition (this);
+
+            message (v);
+            if (evaluator.eval (v) != expect) {
+                error ("expected %s", yesno (expect));
+            }
+       }
+
+       void tests () {
+           Environment.set_variable ("FOO", "bar", true);
+           test ("1");
+           test ("0", false);
+           test ("not 1", false);
+           test ("not not 1");
+           test ("((not (not (1))))");
+           test ("\"not1\"", false);
+           test ("\"foo\"", false);
+           test ("\"\"", false);
+           test ("\"foo\" = \"foo\"");
+           test ("1 = 1");
+           test ("1 = 2", false);
+           test ("1 != 2");
+           test ("1 < 2");
+           test ("1 <= 2");
+           test ("2 <= 2");
+           test ("2 >= 2");
+           test ("2 >= 1");
+           test ("2 > 1");
+           test ("\"foo\" ~= \"fOo\"");
+           test ("\"foo\" = \"bar\"", false);
+           test ("not (1 != 2)", false);
+           test ("0 or 0", false);
+           test ("0 or 1", true);
+           test ("0 and 1", false);
+           test ("1 and 1", true);
+           test ("not (1 != 2) or (1 != 2)", true);
+           test ("$(env.FOO) = \"bar\"", true);
+           test ("$(env.FOO) = bar", true);
+           Environment.set_variable ("FOO", "2", true);
+           test ("$(env.FOO) < 3", true);
+       }
+#endif
+
+        public bool eval_condition (string str, File? file) throws GLib.Error {
+            var evaluator = new EvalCondition (this, file);
+            return evaluator.eval (str);
+        }
+
         class Location: Object {
             public File file;
             public int line;
@@ -173,6 +405,11 @@ namespace Wixl {
                             throw new Wixl.Error.FAILED ("Unmatched endif");
                         context = ifstack.pop_head ();
                         break;
+                    case "if":
+                        ifstack.push_head (context);
+                        var value = reader.const_value ().strip ();
+                        context = new IfContext (context.enabled && context.is_true, eval_condition (value, 
file), IfContext.State.IF);
+                        break;
                     default:
                         handled = false;
                         break;
diff --git a/tools/wixl/util.vala b/tools/wixl/util.vala
index 1b201ec..a3033cb 100644
--- a/tools/wixl/util.vala
+++ b/tools/wixl/util.vala
@@ -104,6 +104,10 @@ namespace Wixl {
         return prefix + hash[0:32].up ();
     }
 
+    public string yesno (bool yes) {
+        return yes ? "yes" : "no";
+    }
+
     public bool parse_yesno (string? str, bool default = false) {
         if (str == null)
             return default;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]