Merge pull request #2778 from xmcclure/checked-build-updates-2
[mono.git] / mcs / tools / resgen / monoresgen.cs
index ee97b3b9852304e3a6618613a888469874a396d9..d37ec8604daecd6e2deaff901e3084d940c5bdd8 100644 (file)
@@ -13,14 +13,17 @@ using System.Globalization;
 using System.Text;
 using System.IO;
 using System.Collections;
+using System.Collections.Generic;
 using System.Resources;
 using System.Reflection;
+using System.Xml;
 
 class ResGen {
 
        static Assembly swf;
        static Type resxr;
        static Type resxw;
+       static HashSet<string> symbols = new HashSet<string> ();
 
        /*
         * We load the ResX format stuff on demand, since the classes are in 
@@ -39,23 +42,18 @@ class ResGen {
        }
 
        static void Usage () {
-#if NET_2_0
-               string Usage = @"Mono Resource Generator version 1.2 for the 2.0 profile
-Usage:
-               resgen2 source.ext [dest.ext]
-               resgen2 [options] /compile source.ext[,dest.resources] [...]";
-#else
-               string Usage = @"Mono Resource Generator version 1.2 for the 1.0 profile
+
+               string Usage = @"Mono Resource Generator version " + Consts.MonoVersion +
+                   @"
 Usage:
                resgen source.ext [dest.ext]
-               resgen /compile source.ext[,dest.resources] [...]";
-#endif
+               resgen [options] /compile source.ext[,dest.resources] [...]";
                Usage += @"
 
 Convert a resource file from one format to another.
 The currently supported formats are: '.txt' '.resources' '.resx' '.po'.
 If the destination file is not specified, source.resources will be used.";
-#if NET_2_0
+
                Usage += @"
 
 Options:
@@ -65,13 +63,18 @@ Options:
        output file name (if not set).
 -usesourcepath, /useSourcePath
        to resolve relative file paths, use the directory of the resource 
-       file as current directory.";
-#else
-               Usage += @"
-The /compile option takes a list of .resX or .txt files to convert to
-.resources files in one bulk operation, replacing .ext with .resources for
-the output file name (if not set).";
-#endif
+       file as current directory.
+-define, /define:SYMBOL1,SYMBOL2
+       takes a comma-separated list of symbols that control conditional
+       inclusion of resources file. The source file needs to be in 
+       the '.txt' format.
+
+       Resources enclosed with #ifdef SYMBOL1 ... #endif directives
+       will be included in the destination file when SYMBOL1 has
+       been specified using /define option.
+
+       Resources enclosed with #if ! SYMBOL2 ... #endif directives
+       will be included only if SYMBOL2 has NOT been specified.";
                Usage += @"
 ";
                Console.WriteLine( Usage );
@@ -84,7 +87,7 @@ the output file name (if not set).";
                        return new PoResourceReader (stream);
                case ".txt":
                case ".text":
-                       return new TxtResourceReader (stream);
+                       return new TxtResourceReader (stream, symbols);
                case ".resources":
                        return new ResourceReader (stream);
                case ".resx":
@@ -152,6 +155,16 @@ the output file name (if not set).";
                } catch (Exception e) {
                        Console.WriteLine ("Error: {0}", e.Message);
                        Exception inner = e.InnerException;
+
+                       // under 2.0 ResXResourceReader can wrap an exception into an XmlException
+                       // and this hides some helpful message from the original exception
+                       XmlException xex = (inner as XmlException);
+                       if (xex != null) {
+                               // message is identical to the inner exception (from MWF ResXResourceReader)
+                               Console.WriteLine ("Position: Line {0}, Column {1}.", xex.LineNumber, xex.LinePosition);
+                               inner = inner.InnerException;
+                       }
+
                        if (inner is TargetInvocationException && inner.InnerException != null)
                                inner = inner.InnerException;
                        if (inner != null)
@@ -200,7 +213,7 @@ the output file name (if not set).";
                                }
                                compileMultiple = true;
                                break;
-#if NET_2_0
+
                        case "/usesourcepath":
                        case "-usesourcepath":
                                if (compileMultiple) {
@@ -213,8 +226,24 @@ the output file name (if not set).";
                                }
                                useSourcePath = true;
                                break;
-#endif
+
                        default:
+                               if (args [i].StartsWith ("/d:")       ||
+                                   args [i].StartsWith ("-d:")       ||
+                                   args [i].StartsWith ("/define:")  ||
+                                   args [i].StartsWith ("-define:")  ||
+                                   args [i].StartsWith ("/D:")       ||
+                                   args [i].StartsWith ("-D:")       ||
+                                   args [i].StartsWith ("/DEFINE:")  ||
+                                   args [i].StartsWith ("-DEFINE:")) {
+
+                                       string defines = args [i].Substring (args [i].IndexOf (':') + 1);
+                                       foreach (string s in defines.Split (',') ) {
+                                               symbols.Add(s);
+                                       }
+                                       break;
+                               }
+
                                if (!IsFileArgument (args [i])) {
                                        Usage ();
                                        return 1;
@@ -279,7 +308,7 @@ the output file name (if not set).";
                        // check for Unix platforms - see FAQ for more details
                        // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
                        int platform = (int) Environment.OSVersion.Platform;
-                       return ((platform == 4) || (platform == 128));
+                       return ((platform == 4) || (platform == 128) || (platform == 6));
                }
        }
 
@@ -358,10 +387,13 @@ class TxtResourceWriter : IResourceWriter {
 class TxtResourceReader : IResourceReader {
        Hashtable data;
        Stream s;
+       HashSet <String> defines;
        
-       public TxtResourceReader (Stream stream) {
+       public TxtResourceReader (Stream stream, IEnumerable<string> symbols) {
                data = new Hashtable ();
                s = stream;
+
+               defines = new HashSet<String> (symbols);
                Load ();
        }
        
@@ -371,17 +403,84 @@ class TxtResourceReader : IResourceReader {
        public IDictionaryEnumerator GetEnumerator() {
                return data.GetEnumerator ();
        }
-       
+
+       static string NextWord(ref string line) {
+               int i, j;
+               string keywd;
+               line = line.TrimStart ();
+               for (i = 0; i < line.Length && !Char.IsWhiteSpace (line [i]) && line [i] != ';'; i++ );
+
+               if (i < line.Length) {
+                       for (j = i; j < line.Length && Char.IsWhiteSpace (line [j]) && line [j] != ';'; j++ );
+
+                       keywd = line.Substring (0, i);
+                       line = line.Substring (j).TrimStart ();
+               } else {
+                       keywd = line;
+                       line = "";
+               }
+               return keywd;
+       }
+
        void Load () {
                StreamReader reader = new StreamReader (s);
                string line, key, val;
+               Stack<bool> conditional = new Stack<bool> (5);
                int epos, line_num = 0;
+
+               conditional.Push(true);
                while ((line = reader.ReadLine ()) != null) {
                        line_num++;
                        line = line.Trim ();
-                       if (line.Length == 0 || line [0] == '#' ||
-                           line [0] == ';')
+
+                       if (line.Length == 0 || line [0] == ';')
                                continue;
+
+                       if (line [0] == '#') {
+                               bool stat;
+                               bool neg = false;
+                               string keywd, symbol;
+
+                               line = line.Substring (1);
+                               keywd = NextWord (ref line).ToLower ();
+                               symbol = "";
+
+                               if (line.Length > 0) {
+                                       if (line[0] == '!') {
+                                               line = line.Substring (1);
+                                               neg = true;
+                                       }
+                                       symbol = NextWord (ref line);
+                               }
+
+                               switch (keywd) {
+                               case "endif":
+                               case "else":
+                                       stat = conditional.Pop ();
+                                       if (conditional.Count < 1)
+                                               throw new Exception (String.Format ("Found an #{0} without matching #ifdef", keywd));
+
+                                       if (keywd == "else")
+                                               conditional.Push (conditional.Peek () && !stat);
+                                       break;
+
+                               case "ifdef":
+                               case "if":
+                                       if (symbol.Length == 0)
+                                               throw new Exception (String.Format ("Missing symbol after {0}", keywd));
+                                       stat = defines.Contains (symbol);
+                                       if (neg)
+                                               stat = !stat;
+
+                                       conditional.Push (conditional.Peek () && stat);
+                                       break;
+
+                }
+                               continue;
+                       }
+                       if (conditional.Peek () == false)
+                               continue;
+
                        epos = line.IndexOf ('=');
                        if (epos < 0) 
                                throw new Exception ("Invalid format at line " + line_num);
@@ -399,6 +498,8 @@ class TxtResourceReader : IResourceReader {
 
                        data.Add (key, val);
                }
+               if (conditional.Count > 1)
+                       throw new Exception ("Found an #ifdef but not a matching #endif before reaching the end of the file.");
        }
 
        // \\n -> \n ...
@@ -422,13 +523,11 @@ class TxtResourceReader : IResourceReader {
                                case 't':
                                        b.Append ('\t');
                                        break;
-#if NET_2_0
                                case 'u':
                                        int ch = int.Parse (value.Substring (++i, 4), NumberStyles.HexNumber);
                                        b.Append (char.ConvertFromUtf32 (ch));
                                        i += 3;
                                        break;
-#endif
                                case '\\':
                                        b.Append ('\\');
                                        break;