Merge pull request #2707 from lambdageek/dev/monoerror-mono_image_load_module_dynamic
[mono.git] / mcs / tools / mjs / mjs.cs
1 //
2 // driver.cs: Guides the compilation process through the different phases.
3 //
4 // Author: 
5 //      Cesar Lopez Nataren (cesar@ciencias.unam.mx)
6 //
7 // (C) 2003, Cesar Lopez Nataren
8 // (C) Copyright 2005, 2006, Novell Inc. (http://www.novell.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Globalization;
34 using System.IO;
35 using System.Text;
36 using Microsoft.Vsa;
37 using Microsoft.JScript;
38 using System.Reflection;
39 using System.Collections;
40 using System.Diagnostics;
41 using Microsoft.JScript.Vsa;
42 using System.Reflection.Emit;
43 using Mono.CSharp;
44
45 namespace Mono.JScript {
46         
47         class Driver {
48                 //
49                 // Assemblies references to be linked. Initialized with
50                 // mscorlib.dll
51                 //
52                 private static ArrayList references;
53
54                 // Lookup paths
55                 private static ArrayList link_paths;
56
57                 // jscript files
58                 private static ArrayList files;
59                         
60                 private static string first_source;
61
62                 private static bool want_debugging_support = false;
63
64                 private static string output_file;
65                 private static int warning_level = -1;
66
67                 private static Assembly [] assemblies = new Assembly [0];
68
69                 private static bool StdLib = true;
70
71                 private static void Usage ()
72                 {
73                         Console.WriteLine ("Mono JScript compiler\n" +
74                                            "Copyright (C) 2003 - 2004 Cesar Lopez Nataren\n" +
75                                            "Copyright (C) 2004 - 2006 Novell Inc (http://novell.com)\n\n" +
76                                            "mjs [options] source-file\n" +
77                                            "   /about              About the Mono JScript compiler\n" +
78                                            "   /lib:PATH1,PATH2    Adds the paths to the assembly link path\n" +
79                                            "   /nostdlib[+|-]      Does not load core libraries\n" +
80                                            "   /out:<file>         Specify name of binary output file\n" +
81                                            "   /pkg:P1[,Pn]        References packages P1..Pn\n" +
82                                            "   /r[eference]:ASS    Reference the specified assembly\n" +
83
84                                            "\n" +
85                                            "Resources:\n" +
86                                            "   @file               Read response file for more options\n\n" +
87                                            "Options can be of the form -option or /option");
88                 }
89
90                 private static void About ()
91                 {
92                         Console.WriteLine (
93                                            "The Mono JScript compiler is:\n" +
94                                            "Copyright (C) 2003 - 2004 Cesar Lopez Nataren\n" +
95                                            "Copyright (C) 2004 - 2006 Novell Inc.\n\n" +
96                                            "The compiler source code is released under the terms of both the MIT X11 and MPL\n" +
97                                            "The compiler was written by Cesar Lopez Nataren");
98                         Environment.Exit (0);
99                 }
100
101                 /// <summary>
102                 ///   Loads all assemblies referenced on the command line
103                 /// </summary>
104                 private static void LoadReferences ()
105                 {
106                         foreach (string r in references)
107                                 LoadAssembly (r, false);
108                 }
109
110                 private static void LoadAssembly (string assembly, bool soft)
111                 {
112                         Assembly a;
113                         string total_log = "";
114
115                         try {
116                                 char [] path_chars = { '/', '\\' };
117
118                                 if (assembly.IndexOfAny (path_chars) != -1) {
119                                         a = Assembly.LoadFrom (assembly);
120                                 } else {
121                                         string ass = assembly;
122                                         if (ass.EndsWith (".dll") || ass.EndsWith (".exe"))
123                                                 ass = assembly.Substring (0, assembly.Length - 4);
124                                         a = Assembly.Load (ass);
125                                 }
126                                 AddAssembly (a);
127
128                         } catch (FileNotFoundException){
129                                 foreach (string dir in link_paths){
130                                         string full_path = Path.Combine (dir, assembly);
131                                         if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
132                                                 full_path += ".dll";
133
134                                         try {
135                                                 a = Assembly.LoadFrom (full_path);
136                                                 AddAssembly (a);
137                                                 return;
138                                         } catch (FileNotFoundException ff) {
139                                                 total_log += ff.FusionLog;
140                                                 continue;
141                                         }
142                                 }
143                                 if (!soft)
144                                         Console.WriteLine ("Cannot find assembly `" + assembly + "'" );
145                         } catch (BadImageFormatException f) {
146                                 Console.WriteLine ("Cannot load assembly (bad file format)" + f.FusionLog);
147                         } catch (FileLoadException f){
148                                 Console.WriteLine ("Cannot load assembly " + f.FusionLog);
149                         } catch (ArgumentNullException){
150                                 Console.WriteLine ("Cannot load assembly (null argument)");
151                         }
152                 }       
153
154                 /// <summary>
155                 ///   Registers an assembly to load types from.
156                 /// </summary>
157                 private static void AddAssembly (Assembly a)
158                 {
159                         foreach (Assembly assembly in assemblies) {
160                                 if (a == assembly)
161                                         return;
162                         }
163
164                         int top = assemblies.Length;
165                         Assembly [] n = new Assembly [top + 1];
166
167                         assemblies.CopyTo (n, 0);
168
169                         n [top] = a;
170                         assemblies = n;
171                 }
172
173                 static string [] LoadArgs (string file)
174                 {
175                         StreamReader f;
176                         ArrayList args = new ArrayList ();
177                         string line;
178                         try {
179                                 f = new StreamReader (file);
180                         } catch {
181                                 return null;
182                         }
183
184                         StringBuilder sb = new StringBuilder ();
185                         
186                         while ((line = f.ReadLine ()) != null){
187                                 int t = line.Length;
188
189                                 for (int i = 0; i < t; i++){
190                                         char c = line [i];
191                                         
192                                         if (c == '"' || c == '\''){
193                                                 char end = c;
194                                                 
195                                                 for (i++; i < t; i++){
196                                                         c = line [i];
197
198                                                         if (c == end)
199                                                                 break;
200                                                         sb.Append (c);
201                                                 }
202                                         } else if (c == ' '){
203                                                 if (sb.Length > 0){
204                                                         args.Add (sb.ToString ());
205                                                         sb.Length = 0;
206                                                 }
207                                         } else
208                                                 sb.Append (c);
209                                 }
210                                 if (sb.Length > 0){
211                                         args.Add (sb.ToString ());
212                                         sb.Length = 0;
213                                 }
214                         }
215
216                         string [] ret_value = new string [args.Count];
217                         args.CopyTo (ret_value, 0);
218
219                         return ret_value;
220                 }       
221
222                 //
223                 // Returns the directory where the system assemblies are installed
224                 //
225                 static string GetSystemDir ()
226                 {
227                         return Path.GetDirectoryName (typeof (object).Assembly.Location);
228
229                 }
230
231                 static void SetOutputFile (string name)
232                 {
233                         output_file = name;
234                 }
235
236                 static void Version ()
237                 {
238                         string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
239                         Console.WriteLine ("Mono JScript compiler version {0}", version);
240                         Environment.Exit (0);
241                 }
242         
243                 static bool UnixParseOption (string arg, ref string [] args, ref int i)
244                 {
245                         switch (arg){
246                         case "--version":
247                                 Version ();
248                                 return true;
249                                 
250                         case "/?": case "/h": case "/help":
251                         case "--help":
252                                 Usage ();
253                                 Environment.Exit (0);
254                                 return true;
255                         case "-o": 
256                         case "--output":
257                                 if ((i + 1) >= args.Length){
258                                         Usage ();
259                                         Environment.Exit (1);
260                                 }
261                                 SetOutputFile (args [++i]);
262                                 return true;
263                         case "-r":
264                                 if ((i + 1) >= args.Length){
265                                         Usage ();
266                                         Environment.Exit (1);
267                                 }
268
269                                 references.Add (args [++i]);
270                                 return true;
271                                 
272                         case "-L":
273                                 if ((i + 1) >= args.Length){
274                                         Usage ();       
275                                         Environment.Exit (1);
276                                 }
277                                 link_paths.Add (args [++i]);
278                                 return true;
279                                 
280                         case "--about":
281                                 About ();
282                                 return true;
283                         }
284                         return false;
285                 }
286
287                 //
288                 // This parses the -arg and /arg options to the compiler, even if the strings
289                 // in the following text use "/arg" on the strings.
290                 //
291                 static bool CSCParseOption (string option, ref string [] args, ref int i)
292                 {
293                         int idx = option.IndexOf (':');
294                         string arg, value;
295                         if (idx == -1){
296                                 arg = option;
297                                 value = "";
298                         } else {
299                                 arg = option.Substring (0, idx);
300
301                                 value = option.Substring (idx + 1);
302                         }
303
304                         switch (arg){
305                         case "/nologo":
306                                 return true;
307
308                         case "/out":
309                                 if (value == ""){
310                                         Usage ();
311                                         Environment.Exit (1);
312                                 }
313                                 SetOutputFile (value);
314                                 return true;
315
316                         case "/pkg":
317                                 string packages;
318
319                                 if (value == String.Empty) {
320                                         Usage ();
321                                         Environment.Exit (1);
322                                 }
323                                 packages = String.Join (" ", value.Split (new Char [] { ';', ',', '\n', '\r'}));
324
325                                 ProcessStartInfo pi = new ProcessStartInfo ();
326                                 pi.FileName = "pkg-config";
327                                 pi.RedirectStandardOutput = true;
328                                 pi.UseShellExecute = false;
329                                 pi.Arguments = "--libs " + packages;
330                                 Process p = null;
331                                 try {
332                                         p = Process.Start (pi);
333                                 } catch (Exception e) {
334                                         Console.Error.WriteLine ("Couldn't run pkg-config: " + e.Message);
335                                         Environment.Exit (1);
336                                 }
337
338                                 if (p.StandardOutput == null){
339                                         Console.Error.WriteLine ("Specified package did not return any information");
340                                         return true;
341                                 }
342                                 string pkgout = p.StandardOutput.ReadToEnd ();
343                                 p.WaitForExit ();
344                                 if (p.ExitCode != 0) {
345                                         Console.Error.WriteLine ("Error running pkg-config. Check the above output.");
346                                         Environment.Exit (1);
347                                 }
348
349                                 if (pkgout != null){
350                                         string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
351                                                 Split (new Char [] { ' ', '\t'});
352                                         args = AddArgs (args, xargs);
353                                 }
354                                 p.Close ();
355                                 return true;
356
357                         case "/r":
358                         case "/reference": {
359                                 if (value == ""){
360                                         Console.WriteLine ("/reference requires an argument");
361                                         Environment.Exit (1);
362                                 }
363
364                                 string [] refs = value.Split (new char [] { ';', ',' });
365                                 foreach (string r in refs)
366                                         references.Add (r);
367                                 return true;
368                         }
369
370                         case "/lib": {
371                                 string [] libdirs;
372                                 
373                                 if (value == ""){
374                                         Console.WriteLine ("/lib requires an argument");
375                                         Environment.Exit (1);
376                                 }
377
378                                 libdirs = value.Split (new Char [] { ',' });
379                                 foreach (string dir in libdirs)
380                                         link_paths.Add (dir);
381                                 return true;
382                         }
383
384                         case "/about":
385                                 About ();
386                                 return true;
387
388
389                         case "/nostdlib":
390                         case "/nostdlib+":
391                                 StdLib = false;
392                                 return true;
393
394                         case "/nostdlib-":
395                                 StdLib = true;
396                                 return true;
397                         case "/target":
398                                 if (value.Length == 0) {
399                                         Console.WriteLine ("fatal error JS2013: Target type is invalid");
400                                         Environment.Exit (1);
401                                 }
402
403                                 if (string.Compare ("exe", value, true) == 0) {
404                                         // this is the default (and only) supported target
405                                 } else if (string.Compare ("library", value, true) != 0) {
406                                         Console.WriteLine ("mjs currently does not support creating libraries");
407                                         Environment.Exit (1);
408                                 } else {
409                                         Console.WriteLine ("fatal error JS2013: Target '{0}' is invalid."
410                                                 + " Specify 'exe' or 'library'", value);
411                                         Environment.Exit (1);
412                                 }
413                                 return true;
414                         case "/warn":
415                                 if (value.Length == 0) {
416                                         Console.WriteLine ("fatal error JS2028: No warning level specified"
417                                                 + " with option '{0}'", option);
418                                         Environment.Exit (1);
419                                 }
420
421                                 try {
422                                         warning_level = int.Parse (value, CultureInfo.InvariantCulture);
423                                 } catch {
424                                 }
425
426                                 if (warning_level < 0 || warning_level > 4) {
427                                         Console.WriteLine ("fatal error JS2015: Invalid warning level specified"
428                                                 + " with option '{0}'", option);
429                                         Environment.Exit (1);
430                                 }
431                                 return true;
432                         }
433                         return false;
434                 }
435         
436                 static string [] AddArgs (string [] args, string [] extra_args)
437                 {
438                         string [] new_args;
439                         new_args = new string [extra_args.Length + args.Length];
440
441                         // if args contains '--' we have to take that into account
442                         // split args into first half and second half based on '--'
443                         // and add the extra_args before --
444                         int split_position = Array.IndexOf (args, "--");
445                         if (split_position != -1) {
446                                 Array.Copy (args, new_args, split_position);
447                                 extra_args.CopyTo (new_args, split_position);
448                                 Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
449                         } else {
450                                 args.CopyTo (new_args, 0);
451                                 extra_args.CopyTo (new_args, args.Length);
452                         }
453                         return new_args;
454                 }
455
456                 //
457                 // Given a path specification, splits the path from the file/pattern
458                 //
459                 static void SplitPathAndPattern (string spec, out string path, out string pattern)
460                 {
461                         int p = spec.LastIndexOf ('/');
462                         if (p != -1){
463                                 //
464                                 // Windows does not like /file.cs, switch that to:
465                                 // "\", "file.cs"
466                                 //
467                                 if (p == 0){
468                                         path = "\\";
469                                         pattern = spec.Substring (1);
470                                 } else {
471                                         path = spec.Substring (0, p);
472                                         pattern = spec.Substring (p + 1);
473                                 }
474                                 return;
475                         }
476
477                         p = spec.LastIndexOf ('\\');
478                         if (p != -1){
479                                 path = spec.Substring (0, p);
480                                 pattern = spec.Substring (p + 1);
481                                 return;
482                         }
483
484                         path = ".";
485                         pattern = spec;
486                 }
487
488                 static void ProcessFile (string f)
489                 {
490                         if (first_source == null)
491                                 first_source = f;
492
493                         files.Add (f);
494                 }
495
496                 static void CompileFiles (string spec, bool recurse)
497                 {
498                         string path, pattern;
499
500                         SplitPathAndPattern (spec, out path, out pattern);
501                         if (pattern.IndexOf ('*') == -1){
502                                 ProcessFile (spec);
503                                 return;
504                         }
505
506                         string [] files = null;
507                         try {
508                                 files = Directory.GetFiles (path, pattern);
509                         } catch (System.IO.DirectoryNotFoundException) {
510                                 Console.WriteLine ("Source file `" + spec + "' could not be found");
511                                 return;
512                         } catch (System.IO.IOException){
513                                 Console.WriteLine ("Source file `" + spec + "' could not be found");
514                                 return;
515                         }
516                         foreach (string f in files)
517                                 ProcessFile (f);
518
519                         if (!recurse)
520                                 return;
521                         
522                         string [] dirs = null;
523
524                         try {
525                                 dirs = Directory.GetDirectories (path);
526                         } catch {
527                         }
528                         
529                         foreach (string d in dirs) {
530                                         
531                                 // Don't include path in this string, as each
532                                 // directory entry already does
533                                 CompileFiles (d + "/" + pattern, true);
534                         }
535                 }
536
537                 internal static bool MainDriver (string [] args)
538                 {
539                         int i;
540                         bool parsing_options = true;
541
542                         references = new ArrayList ();
543                         link_paths = new ArrayList ();
544                         files = new ArrayList ();
545
546                         Hashtable response_file_list = null;
547
548                         for (i = 0; i < args.Length; i++){
549                                 string arg = args [i];
550                                 if (arg == "")
551                                         continue;
552                                 
553                                 if (arg.StartsWith ("@")){
554                                         string [] extra_args;
555                                         string response_file = arg.Substring (1);
556
557                                         if (response_file_list == null)
558                                                 response_file_list = new Hashtable ();
559                                         
560                                         if (response_file_list.Contains (response_file)){
561                                                 Console.WriteLine ("Response file `" + response_file + "' specified multiple times");
562                                                 Environment.Exit (1);
563                                         }
564                                         
565                                         response_file_list.Add (response_file, response_file);
566                                                     
567                                         extra_args = LoadArgs (response_file);
568                                         if (extra_args == null){
569                                                 Console.WriteLine ("Unable to open response file: " + response_file);
570                                                 return false;
571                                         }
572
573                                         args = AddArgs (args, extra_args);
574                                         continue;
575                                 }
576
577                                 if (parsing_options){
578                                         if (arg == "--"){
579                                                 parsing_options = false;
580                                                 continue;
581                                         }
582                                         
583                                         if (arg.StartsWith ("-")){
584                                                 if (UnixParseOption (arg, ref args, ref i))
585                                                         continue;
586
587                                                 // Try a -CSCOPTION
588                                                 string csc_opt = "/" + arg.Substring (1);
589                                                 if (CSCParseOption (csc_opt, ref args, ref i))
590                                                         continue;
591                                         } else {
592                                                 if (arg.StartsWith ("/")){
593                                                         if (CSCParseOption (arg, ref args, ref i))
594                                                                 continue;
595                                                 }
596                                         }
597                                 }
598                                 CompileFiles (arg, false); 
599                         }
600
601                         //
602                         // If there is nothing to put in the assembly, and we are not a library
603                         //
604                         if (first_source == null) /* && embedded_resources == null && resources == null) */ {
605                                 Console.WriteLine ("fatal error JS2026: No input sources specified");
606                                 return false;
607                         }
608
609                         //
610                         // Load Core Library for default compilation
611                         //
612                         if (StdLib)
613                                 references.Insert (0, "mscorlib");
614
615
616                         //
617                         // Load assemblies required
618                         //
619                         link_paths.Add (GetSystemDir ());
620                         link_paths.Add (Directory.GetCurrentDirectory ());
621                         LoadReferences ();
622                         return true;
623                 }
624         
625                 //
626                 // Entry point
627                 //
628                 private static void Main (string [] args) {
629                         if (args.Length < 1) {
630                                 Usage ();
631                                 Environment.Exit (0);
632                         }                       
633                         MainDriver (args);
634                         VsaEngine engine = new VsaEngine ();
635                         engine.InitVsaEngine ("mjs:com.mono-project", new MonoEngineSite ());
636                         
637                         foreach (string asm in references) {
638                                 IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm, VsaItemType.Reference, VsaItemFlag.None);
639                                 item.AssemblyName = asm;
640                         }
641
642                         string asm_name = String.Empty;
643                         
644                         foreach (Assembly assembly in assemblies) {
645                                 asm_name = assembly.GetName ().FullName;
646                                 IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm_name, VsaItemType.Reference, VsaItemFlag.None);
647                                 item.AssemblyName = asm_name;
648                         }
649
650                         foreach (string file in files) {
651                                 IVsaCodeItem item = (IVsaCodeItem) engine.Items.CreateItem (file, VsaItemType.Code, VsaItemFlag.None);
652                                 item.SourceText = GetCodeFromFile (file);
653                         }
654                         engine.SetOption ("debug", want_debugging_support);
655                         engine.SetOption ("link_path", link_paths);
656                         engine.SetOption ("first_source", first_source);
657                         engine.SetOption ("assemblies", assemblies);
658                         engine.SetOption ("out", output_file);
659                         if (warning_level != -1)
660                                 engine.SetOption ("WarningLevel", warning_level);
661                         engine.Compile ();
662                 }
663
664                 static string GetCodeFromFile (string fn)
665                 {
666                         try {
667                                 StreamReader reader = new StreamReader (fn);
668                                 return reader.ReadToEnd ();
669                         } catch (FileNotFoundException) {
670                                 throw new JScriptException (JSError.FileNotFound);
671                         } catch (ArgumentNullException) {
672                                 throw new JScriptException (JSError.FileNotFound);
673                         } catch (ArgumentException) {
674                                 throw new JScriptException (JSError.FileNotFound);
675                         } catch (IOException) {
676                                 throw new JScriptException (JSError.NoError);
677                         } catch (OutOfMemoryException) {
678                                 throw new JScriptException (JSError.OutOfMemory);
679                         }
680                 }
681         }
682
683         class MonoEngineSite : IVsaSite {
684                 public void GetCompiledState (out byte [] pe, out byte [] debugInfo)
685                 {
686                         throw new NotImplementedException ();
687                 }
688
689                 public object GetEventSourceInstance (string itemName, string eventSourceName)
690                 {
691                         throw new NotImplementedException ();
692                 }
693
694                 public object GetGlobalInstance (string name)
695                 {
696                         throw new NotImplementedException ();
697                 }
698
699                 public void Notify (string notify, object info)
700                 {
701                         throw new NotImplementedException ();
702                 }
703
704                 public bool OnCompilerError (IVsaError error)
705                 {
706                         throw new NotImplementedException ();
707                 }
708         }
709 }