// // Driver.cs // // Author: // Jb Evain (jbevain@gmail.com) // // (C) 2006 Jb Evain // // 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. // using System; using System.Collections; using System.IO; using SR = System.Reflection; using System.Xml.XPath; using Mono.Linker.Steps; namespace Mono.Linker { public class Driver { static readonly string _linker = "Mono CIL Linker"; public static int Main (string [] args) { if (args.Length == 0) Usage ("No parameters specified"); try { Driver driver = new Driver (args); driver.Run (); } catch (Exception e) { Console.WriteLine ("Fatal error in {0}", _linker); Console.WriteLine (e); return 1; } return 0; } Queue _queue; public Driver (string [] args) { _queue = new Queue (args); } bool HaveMoreTokens () { return _queue.Count > 0; } void Run () { Pipeline p = GetStandardPipeline (); LinkContext context = GetDefaultContext (p); I18nAssemblies assemblies = I18nAssemblies.All; ArrayList custom_steps = new ArrayList (); bool resolver = false; while (HaveMoreTokens ()) { string token = GetParam (); if (token.Length < 2) Usage ("Option is too short"); if (! (token [0] == '-' || token [1] == '/')) Usage ("Expecting an option, got instead: " + token); if (token [0] == '-' && token [1] == '-') { if (token.Length < 3) Usage ("Option is too short"); switch (token [2]) { case 'v': Version (); break; case 'a': About (); break; default: Usage (null); break; } } switch (token [1]) { case 'd': { DirectoryInfo info = new DirectoryInfo (GetParam ()); context.Resolver.AddSearchDirectory (info.FullName); break; } case 'o': context.OutputDirectory = GetParam (); break; case 'c': context.CoreAction = ParseAssemblyAction (GetParam ()); break; case 'p': AssemblyAction action = ParseAssemblyAction (GetParam ()); context.Actions [GetParam ()] = action; break; case 's': custom_steps.Add (GetParam ()); break; case 'x': foreach (string file in GetFiles (GetParam ())) p.PrependStep (new ResolveFromXmlStep (new XPathDocument (file))); resolver = true; break; case 'a': foreach (string file in GetFiles (GetParam ())) p.PrependStep (new ResolveFromAssemblyStep (file)); resolver = true; break; case 'i': foreach (string file in GetFiles (GetParam ())) p.PrependStep (new ResolveFromXApiStep (new XPathDocument (file))); resolver = true; break; case 'l': assemblies = ParseI18n (GetParam ()); break; case 'm': context.SetParameter (GetParam (), GetParam ()); break; case 'b': context.LinkSymbols = bool.Parse (GetParam ()); break; case 'g': if (!bool.Parse (GetParam ())) p.RemoveStep (typeof (RegenerateGuidStep)); break; default: Usage ("Unknown option: `" + token [1] + "'"); break; } } if (!resolver) Usage ("No resolver was created (use -x, -a or -i)"); foreach (string custom_step in custom_steps) AddCustomStep (p, custom_step); p.AddStepAfter (typeof (LoadReferencesStep), new LoadI18nAssemblies (assemblies)); p.Process (context); } static void AddCustomStep (Pipeline pipeline, string arg) { int pos = arg.IndexOf (":"); if (pos == -1) { pipeline.AppendStep (ResolveStep (arg)); return; } string [] parts = arg.Split (':'); if (parts.Length != 2) Usage ("Step is specified as TYPE:STEP"); if (parts [0].IndexOf (",") > -1) pipeline.AddStepBefore (FindStep (pipeline, parts [1]), ResolveStep (parts [0])); else if (parts [1].IndexOf (",") > -1) pipeline.AddStepAfter (FindStep (pipeline, parts [0]), ResolveStep (parts [1])); else Usage ("No comma separator in TYPE or STEP"); } static Type FindStep (Pipeline pipeline, string name) { foreach (IStep step in pipeline.GetSteps ()) { Type t = step.GetType (); if (t.Name == name) return t; } return null; } static IStep ResolveStep (string type) { Type step = Type.GetType (type, false); if (step == null) Usage (String.Format ("Step type '{0}' not found.", type)); if (!typeof (IStep).IsAssignableFrom (step)) Usage (String.Format ("Step type '{0}' does not implement IStep interface.", type)); return (IStep) Activator.CreateInstance (step); } static string [] GetFiles (string param) { if (param.Length < 1 || param [0] != '@') return new string [] {param}; string file = param.Substring (1); return ReadLines (file); } static string [] ReadLines (string file) { ArrayList lines = new ArrayList (); using (StreamReader reader = new StreamReader (file)) { string line; while ((line = reader.ReadLine ()) != null) lines.Add (line); } return (string []) lines.ToArray (typeof (string)); } static I18nAssemblies ParseI18n (string str) { I18nAssemblies assemblies = I18nAssemblies.None; string [] parts = str.Split (','); foreach (string part in parts) assemblies |= (I18nAssemblies) Enum.Parse (typeof (I18nAssemblies), part.Trim (), true); return assemblies; } static AssemblyAction ParseAssemblyAction (string s) { return (AssemblyAction) Enum.Parse (typeof (AssemblyAction), s, true); } string GetParam () { if (_queue.Count == 0) Usage ("Expecting a parameter"); return (string) _queue.Dequeue (); } static LinkContext GetDefaultContext (Pipeline pipeline) { LinkContext context = new LinkContext (pipeline); context.CoreAction = AssemblyAction.Skip; context.OutputDirectory = "output"; return context; } static void Usage (string msg) { Console.WriteLine (_linker); if (msg != null) Console.WriteLine ("Error: " + msg); Console.WriteLine ("monolinker [options] -x|-a|-i file"); Console.WriteLine (" --about About the {0}", _linker); Console.WriteLine (" --version Print the version number of the {0}", _linker); Console.WriteLine (" -out Specify the output directory, default to `output'"); Console.WriteLine (" -c Action on the core assemblies, skip, copy or link, default to skip"); Console.WriteLine (" -p Action per assembly"); Console.WriteLine (" -s Add a new step to the pipeline."); Console.WriteLine (" -d Add a directory where the linker will look for assemblies"); Console.WriteLine (" -b Generate debug symbols for each linked module (true or false)"); Console.WriteLine (" -g Generate a new unique guid for each linked module (true or false)"); Console.WriteLine (" -l List of i18n assemblies to copy to the output directory"); Console.WriteLine (" separated with a comma: none,all,cjk,mideast,other,rare,west"); Console.WriteLine (" default is all"); Console.WriteLine (" -x Link from an XML descriptor"); Console.WriteLine (" -a Link from a list of assemblies"); Console.WriteLine (" -i Link from an mono-api-info descriptor"); Console.WriteLine (""); Environment.Exit (1); } static void Version () { Console.WriteLine ("{0} Version {1}", _linker, System.Reflection.Assembly.GetExecutingAssembly ().GetName ().Version); Environment.Exit(1); } static void About () { Console.WriteLine ("For more information, visit the project Web site"); Console.WriteLine (" http://www.mono-project.com/"); Environment.Exit(1); } static Pipeline GetStandardPipeline () { Pipeline p = new Pipeline (); p.AppendStep (new LoadReferencesStep ()); p.AppendStep (new BlacklistStep ()); p.AppendStep (new TypeMapStep ()); p.AppendStep (new MarkStep ()); p.AppendStep (new SweepStep ()); p.AppendStep (new CleanStep ()); p.AppendStep (new RegenerateGuidStep ()); p.AppendStep (new OutputStep ()); return p; } } }