7d1963cc3db22e5225e5404117e7a9f0dc4cb03d
[mono.git] / mcs / tools / linker / Mono.Linker / Driver.cs
1 //
2 // Driver.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2006 Jb Evain
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.IO;
32 using SR = System.Reflection;
33 using System.Xml.XPath;
34
35 using Mono.Linker.Steps;
36
37 namespace Mono.Linker {
38
39         public class Driver {
40
41                 static readonly string _linker = "Mono CIL Linker";
42
43                 public static int Main (string [] args)
44                 {
45                         if (args.Length == 0)
46                                 Usage ("No parameters specified");
47
48                         try {
49
50                                 Driver driver = new Driver (args);
51                                 driver.Run ();
52
53                         } catch (Exception e) {
54                                 Console.WriteLine ("Fatal error in {0}", _linker);
55                                 Console.WriteLine (e);
56                                 return 1;
57                         }
58
59                         return 0;
60                 }
61
62                 Queue _queue;
63
64                 public Driver (string [] args)
65                 {
66                         _queue = new Queue (args);
67                 }
68
69                 bool HaveMoreTokens ()
70                 {
71                         return _queue.Count > 0;
72                 }
73
74                 void Run ()
75                 {
76                         Pipeline p = GetStandardPipeline ();
77                         LinkContext context = GetDefaultContext (p);
78                         I18nAssemblies assemblies = I18nAssemblies.All;
79                         ArrayList custom_steps = new ArrayList ();
80
81                         bool resolver = false;
82                         while (HaveMoreTokens ()) {
83                                 string token = GetParam ();
84                                 if (token.Length < 2)
85                                         Usage ("Option is too short");
86
87                                 if (! (token [0] == '-' || token [1] == '/'))
88                                         Usage ("Expecting an option, got instead: " + token);
89
90                                 if (token [0] == '-' && token [1] == '-') {
91
92                                         if (token.Length < 3)
93                                                 Usage ("Option is too short");
94
95                                         switch (token [2]) {
96                                         case 'v':
97                                                 Version ();
98                                                 break;
99                                         case 'a':
100                                                 About ();
101                                                 break;
102                                         default:
103                                                 Usage (null);
104                                                 break;
105                                         }
106                                 }
107
108                                 switch (token [1]) {
109                                 case 'd': {
110                                         DirectoryInfo info = new DirectoryInfo (GetParam ());
111                                         context.Resolver.AddSearchDirectory (info.FullName);
112                                         break;
113                                 }
114                                 case 'o':
115                                         context.OutputDirectory = GetParam ();
116                                         break;
117                                 case 'c':
118                                         context.CoreAction = ParseAssemblyAction (GetParam ());
119                                         break;
120                                 case 'p':
121                                         AssemblyAction action = ParseAssemblyAction (GetParam ());
122                                         context.Actions [GetParam ()] = action;
123                                         break;
124                                 case 's':
125                                         custom_steps.Add (GetParam ());
126                                         break;
127                                 case 'x':
128                                         foreach (string file in GetFiles (GetParam ()))
129                                                 p.PrependStep (new ResolveFromXmlStep (new XPathDocument (file)));
130                                         resolver = true;
131                                         break;
132                                 case 'a':
133                                         foreach (string file in GetFiles (GetParam ()))
134                                                 p.PrependStep (new ResolveFromAssemblyStep (file));
135                                         resolver = true;
136                                         break;
137                                 case 'i':
138                                         foreach (string file in GetFiles (GetParam ()))
139                                                 p.PrependStep (new ResolveFromXApiStep (new XPathDocument (file)));
140                                         resolver = true;
141                                         break;
142                                 case 'l':
143                                         assemblies = ParseI18n (GetParam ());
144                                         break;
145                                 case 'm':
146                                         context.SetParameter (GetParam (), GetParam ());
147                                         break;
148                                 case 'b':
149                                         context.LinkSymbols = bool.Parse (GetParam ());
150                                         break;
151                                 case 'g':
152                                         if (!bool.Parse (GetParam ()))
153                                                 p.RemoveStep (typeof (RegenerateGuidStep));
154                                         break;
155                                 default:
156                                         Usage ("Unknown option: `" + token [1] + "'");
157                                         break;
158                                 }
159                         }
160
161                         if (!resolver)
162                                 Usage ("No resolver was created (use -x, -a or -i)");
163
164                         foreach (string custom_step in custom_steps)
165                                 AddCustomStep (p, custom_step);
166
167                         p.AddStepAfter (typeof (LoadReferencesStep), new LoadI18nAssemblies (assemblies));
168
169                         p.Process (context);
170                 }
171
172                 static void AddCustomStep (Pipeline pipeline, string arg)
173                 {
174                         int pos = arg.IndexOf (":");
175                         if (pos == -1) {
176                                 pipeline.AppendStep (ResolveStep (arg));
177                                 return;
178                         }
179
180                         string [] parts = arg.Split (':');
181                         if (parts.Length != 2)
182                                 Usage ("Step is specified as TYPE:STEP");
183
184                         if (parts [0].IndexOf (",") > -1)
185                                 pipeline.AddStepBefore (FindStep (pipeline, parts [1]), ResolveStep (parts [0]));
186                         else if (parts [1].IndexOf (",") > -1)
187                                 pipeline.AddStepAfter (FindStep (pipeline, parts [0]), ResolveStep (parts [1]));
188                         else
189                                 Usage ("No comma separator in TYPE or STEP");
190                 }
191
192                 static Type FindStep (Pipeline pipeline, string name)
193                 {
194                         foreach (IStep step in pipeline.GetSteps ()) {
195                                 Type t = step.GetType ();
196                                 if (t.Name == name)
197                                         return t;
198                         }
199
200                         return null;
201                 }
202
203                 static IStep ResolveStep (string type)
204                 {
205                         Type step = Type.GetType (type, false);
206                         if (step == null)
207                                 Usage (String.Format ("Step type '{0}' not found.", type));
208                         if (!typeof (IStep).IsAssignableFrom (step))
209                                 Usage (String.Format ("Step type '{0}' does not implement IStep interface.", type));
210                         return (IStep) Activator.CreateInstance (step);
211                 }
212
213                 static string [] GetFiles (string param)
214                 {
215                         if (param.Length < 1 || param [0] != '@')
216                                 return new string [] {param};
217
218                         string file = param.Substring (1);
219                         return ReadLines (file);
220                 }
221
222                 static string [] ReadLines (string file)
223                 {
224                         ArrayList lines = new ArrayList ();
225                         using (StreamReader reader = new StreamReader (file)) {
226                                 string line;
227                                 while ((line = reader.ReadLine ()) != null)
228                                         lines.Add (line);
229                         }
230                         return (string []) lines.ToArray (typeof (string));
231                 }
232
233                 static I18nAssemblies ParseI18n (string str)
234                 {
235                         I18nAssemblies assemblies = I18nAssemblies.None;
236                         string [] parts = str.Split (',');
237                         foreach (string part in parts)
238                                 assemblies |= (I18nAssemblies) Enum.Parse (typeof (I18nAssemblies), part.Trim (), true);
239
240                         return assemblies;
241                 }
242
243                 static AssemblyAction ParseAssemblyAction (string s)
244                 {
245                         return (AssemblyAction) Enum.Parse (typeof (AssemblyAction), s, true);
246                 }
247
248                 string GetParam ()
249                 {
250                         if (_queue.Count == 0)
251                                 Usage ("Expecting a parameter");
252
253                         return (string) _queue.Dequeue ();
254                 }
255
256                 static LinkContext GetDefaultContext (Pipeline pipeline)
257                 {
258                         LinkContext context = new LinkContext (pipeline);
259                         context.CoreAction = AssemblyAction.Skip;
260                         context.OutputDirectory = "output";
261                         return context;
262                 }
263
264                 static void Usage (string msg)
265                 {
266                         Console.WriteLine (_linker);
267                         if (msg != null)
268                                 Console.WriteLine ("Error: " + msg);
269                         Console.WriteLine ("monolinker [options] -x|-a|-i file");
270
271                         Console.WriteLine ("   --about     About the {0}", _linker);
272                         Console.WriteLine ("   --version   Print the version number of the {0}", _linker);
273                         Console.WriteLine ("   -out        Specify the output directory, default to `output'");
274                         Console.WriteLine ("   -c          Action on the core assemblies, skip, copy or link, default to skip");
275                         Console.WriteLine ("   -p          Action per assembly");
276                         Console.WriteLine ("   -s          Add a new step to the pipeline.");
277                         Console.WriteLine ("   -d          Add a directory where the linker will look for assemblies");
278                         Console.WriteLine ("   -b          Generate debug symbols for each linked module (true or false)");
279                         Console.WriteLine ("   -g          Generate a new unique guid for each linked module (true or false)");
280                         Console.WriteLine ("   -l          List of i18n assemblies to copy to the output directory");
281                         Console.WriteLine ("                 separated with a comma: none,all,cjk,mideast,other,rare,west");
282                         Console.WriteLine ("                 default is all");
283                         Console.WriteLine ("   -x          Link from an XML descriptor");
284                         Console.WriteLine ("   -a          Link from a list of assemblies");
285                         Console.WriteLine ("   -i          Link from an mono-api-info descriptor");
286                         Console.WriteLine ("");
287
288                         Environment.Exit (1);
289                 }
290
291                 static void Version ()
292                 {
293                         Console.WriteLine ("{0} Version {1}",
294                                 _linker,
295                             System.Reflection.Assembly.GetExecutingAssembly ().GetName ().Version);
296
297                         Environment.Exit(1);
298                 }
299
300                 static void About ()
301                 {
302                         Console.WriteLine ("For more information, visit the project Web site");
303                         Console.WriteLine ("   http://www.mono-project.com/");
304
305                         Environment.Exit(1);
306                 }
307
308                 static Pipeline GetStandardPipeline ()
309                 {
310                         Pipeline p = new Pipeline ();
311                         p.AppendStep (new LoadReferencesStep ());
312                         p.AppendStep (new BlacklistStep ());
313                         p.AppendStep (new TypeMapStep ());
314                         p.AppendStep (new MarkStep ());
315                         p.AppendStep (new SweepStep ());
316                         p.AppendStep (new CleanStep ());
317                         p.AppendStep (new RegenerateGuidStep ());
318                         p.AppendStep (new OutputStep ());
319                         return p;
320                 }
321         }
322 }