* Makefile: Build the make-map.exe in Mono.Unix.Native; add /nowarn:0618 to
[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, 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.IO;
34 using System.Text;
35 using Microsoft.Vsa;
36 using Microsoft.JScript;
37 using System.Reflection;
38 using System.Collections;
39 using Microsoft.JScript.Vsa;
40 using System.Reflection.Emit;
41 using Mono.CSharp;
42
43 namespace Mono.JScript {
44         
45         class Driver {
46                 //
47                 // Assemblies references to be linked. Initialized with
48                 // mscorlib.dll
49                 //
50                 private static ArrayList references;
51
52                 // Lookup paths
53                 private static ArrayList link_paths;
54
55                 // jscript files
56                 private static ArrayList files;
57                         
58                 private static string first_source;
59
60                 private static bool want_debugging_support = false;
61
62                 private static string output_file;
63
64                 private static Assembly [] assemblies = new Assembly [0];
65
66                 private static bool StdLib;
67
68                 private static void Usage ()
69                 {
70                         Console.WriteLine ("Mono JScript compiler\n" +
71                                            "(C) 2003 - 2004 Cesar Lopez Nataren\n" +
72                                            "(C) 2004 - 2005 Novell Inc (http://novell.com)\n\n" +
73                                            "mjs [options] source-file\n" +
74                                            "   /about              About the Mono JScript compiler\n" +
75                                            "   /lib:PATH1,PATH2    Adds the paths to the assembly link path\n" +
76                                            "   /r[eference]:ASS    Reference the specified assembly\n");
77                 }
78
79                 private static void About ()
80                 {
81                         Console.WriteLine (
82                                            "The Mono JScript compiler is:\n" +
83                                            "(C) 2003 - 2004 Cesar Lopez Nataren\n" +
84                                            "(C) 2004 - 2005 Novell Inc.\n\n" +
85                                            "The compiler source code is released under the terms of both the MIT X11 and MPL\n" +
86                                            "The compiler was written by Cesar Lopez Nataren");
87                         Environment.Exit (0);
88                 }
89
90                 /// <summary>
91                 ///   Loads all assemblies referenced on the command line
92                 /// </summary>
93                 private static void LoadReferences ()
94                 {
95                         foreach (string r in references)
96                                 LoadAssembly (r, false);
97                         return;
98                 }
99
100                 private static void LoadAssembly (string assembly, bool soft)
101                 {
102                         Assembly a;
103                         string total_log = "";
104
105                         try {
106                                 char [] path_chars = { '/', '\\' };
107
108                                 if (assembly.IndexOfAny (path_chars) != -1) {
109                                         a = Assembly.LoadFrom (assembly);
110                                 } else {
111                                         string ass = assembly;
112                                         if (ass.EndsWith (".dll") || ass.EndsWith (".exe"))
113                                                 ass = assembly.Substring (0, assembly.Length - 4);
114                                         a = Assembly.Load (ass);
115                                 }
116                                 AddAssembly (a);
117
118                         } catch (FileNotFoundException){
119                                 foreach (string dir in link_paths){
120                                         string full_path = Path.Combine (dir, assembly);
121                                         if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
122                                                 full_path += ".dll";
123
124                                         try {
125                                                 a = Assembly.LoadFrom (full_path);
126                                                 AddAssembly (a);
127                                                 return;
128                                         } catch (FileNotFoundException ff) {
129                                                 total_log += ff.FusionLog;
130                                                 continue;
131                                         }
132                                 }
133                                 if (!soft)
134                                         Console.WriteLine ("Cannot find assembly `" + assembly + "'" );
135                         } catch (BadImageFormatException f) {
136                                 Console.WriteLine ("Cannot load assembly (bad file format)" + f.FusionLog);
137                         } catch (FileLoadException f){
138                                 Console.WriteLine ("Cannot load assembly " + f.FusionLog);
139                         } catch (ArgumentNullException){
140                                 Console.WriteLine ("Cannot load assembly (null argument)");
141                         }
142                 }       
143
144                 /// <summary>
145                 ///   Registers an assembly to load types from.
146                 /// </summary>
147                 private static void AddAssembly (Assembly a)
148                 {
149                         foreach (Assembly assembly in assemblies) {
150                                 if (a == assembly)
151                                         return;
152                         }
153
154                         int top = assemblies.Length;
155                         Assembly [] n = new Assembly [top + 1];
156
157                         assemblies.CopyTo (n, 0);
158                 
159                         n [top] = a;
160                         assemblies = n;
161                 }
162
163                 static string [] LoadArgs (string file)
164                 {
165                         StreamReader f;
166                         ArrayList args = new ArrayList ();
167                         string line;
168                         try {
169                                 f = new StreamReader (file);
170                         } catch {
171                                 return null;
172                         }
173
174                         StringBuilder sb = new StringBuilder ();
175                         
176                         while ((line = f.ReadLine ()) != null){
177                                 int t = line.Length;
178
179                                 for (int i = 0; i < t; i++){
180                                         char c = line [i];
181                                         
182                                         if (c == '"' || c == '\''){
183                                                 char end = c;
184                                                 
185                                                 for (i++; i < t; i++){
186                                                         c = line [i];
187
188                                                         if (c == end)
189                                                                 break;
190                                                         sb.Append (c);
191                                                 }
192                                         } else if (c == ' '){
193                                                 if (sb.Length > 0){
194                                                         args.Add (sb.ToString ());
195                                                         sb.Length = 0;
196                                                 }
197                                         } else
198                                                 sb.Append (c);
199                                 }
200                                 if (sb.Length > 0){
201                                         args.Add (sb.ToString ());
202                                         sb.Length = 0;
203                                 }
204                         }
205
206                         string [] ret_value = new string [args.Count];
207                         args.CopyTo (ret_value, 0);
208
209                         return ret_value;
210                 }       
211
212                 //
213                 // Returns the directory where the system assemblies are installed
214                 //
215                 static string GetSystemDir ()
216                 {
217                         return Path.GetDirectoryName (typeof (object).Assembly.Location);
218
219                 }
220
221                 static void SetOutputFile (string name)
222                 {
223                         output_file = name;
224                 }
225
226                 static void Version ()
227                 {
228                         string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
229                         Console.WriteLine ("Mono JScript compiler version {0}", version);
230                         Environment.Exit (0);
231                 }
232         
233                 static bool UnixParseOption (string arg, ref string [] args, ref int i)
234                 {
235                         switch (arg){
236                         case "--version":
237                                 Version ();
238                                 return true;
239                                 
240                         case "/?": case "/h": case "/help":
241                         case "--help":
242                                 Usage ();
243                                 Environment.Exit (0);
244                                 return true;
245                         case "-o": 
246                         case "--output":
247                                 if ((i + 1) >= args.Length){
248                                         Usage ();
249                                         Environment.Exit (1);
250                                 }
251                                 SetOutputFile (args [++i]);
252                                 return true;
253                         case "-r":
254                                 if ((i + 1) >= args.Length){
255                                         Usage ();
256                                         Environment.Exit (1);
257                                 }
258                                 
259                                 references.Add (args [++i]);
260                                 return true;
261                                 
262                         case "-L":
263                                 if ((i + 1) >= args.Length){
264                                         Usage ();       
265                                         Environment.Exit (1);
266                                 }
267                                 link_paths.Add (args [++i]);
268                                 return true;
269                                 
270                         case "--about":
271                                 About ();
272                                 return true;
273                         }
274                         return false;
275                 }
276
277                 //
278                 // This parses the -arg and /arg options to the compiler, even if the strings
279                 // in the following text use "/arg" on the strings.
280                 //
281                 static bool CSCParseOption (string option, ref string [] args, ref int i)
282                 {
283                         int idx = option.IndexOf (':');
284                         string arg, value;
285                         if (idx == -1){
286                                 arg = option;
287                                 value = "";
288                         } else {
289                                 arg = option.Substring (0, idx);
290
291                                 value = option.Substring (idx + 1);
292                         }
293
294                         switch (arg){
295                         case "/nologo":
296                                 return true;
297
298                         case "/out":
299                                 if (value == ""){
300                                         Usage ();
301                                         Environment.Exit (1);
302                                 }
303                                 SetOutputFile (value);
304                                 return true;
305
306                         case "/r":
307                         case "/reference": {
308                                 if (value == ""){
309                                         Console.WriteLine ("/reference requires an argument");
310                                         Environment.Exit (1);
311                                 }
312
313                                 string [] refs = value.Split (new char [] { ';', ',' });
314                                 foreach (string r in refs){
315                                         references.Add (r);
316                                 }
317                                 return true;
318                         }
319
320                         case "/lib": {
321                                 string [] libdirs;
322                                 
323                                 if (value == ""){
324                                         Console.WriteLine ("/lib requires an argument");
325                                         Environment.Exit (1);
326                                 }
327
328                                 libdirs = value.Split (new Char [] { ',' });
329                                 foreach (string dir in libdirs)
330                                         link_paths.Add (dir);
331                                 return true;
332                         }
333                         }
334                         return false;
335                 }
336         
337                 static string [] AddArgs (string [] args, string [] extra_args)
338                 {
339                         string [] new_args;
340                         new_args = new string [extra_args.Length + args.Length];
341
342                         // if args contains '--' we have to take that into account
343                         // split args into first half and second half based on '--'
344                         // and add the extra_args before --
345                         int split_position = Array.IndexOf (args, "--");
346                         if (split_position != -1)
347                                 {
348                                         Array.Copy (args, new_args, split_position);
349                                         extra_args.CopyTo (new_args, split_position);
350                                         Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
351                                 }
352                         else
353                                 {
354                                         args.CopyTo (new_args, 0);
355                                         extra_args.CopyTo (new_args, args.Length);
356                                 }
357
358                         return new_args;
359                 }
360
361                 //
362                 // Given a path specification, splits the path from the file/pattern
363                 //
364                 static void SplitPathAndPattern (string spec, out string path, out string pattern)
365                 {
366                         int p = spec.LastIndexOf ('/');
367                         if (p != -1){
368                                 //
369                                 // Windows does not like /file.cs, switch that to:
370                                 // "\", "file.cs"
371                                 //
372                                 if (p == 0){
373                                         path = "\\";
374                                         pattern = spec.Substring (1);
375                                 } else {
376                                         path = spec.Substring (0, p);
377                                         pattern = spec.Substring (p + 1);
378                                 }
379                                 return;
380                         }
381
382                         p = spec.LastIndexOf ('\\');
383                         if (p != -1){
384                                 path = spec.Substring (0, p);
385                                 pattern = spec.Substring (p + 1);
386                                 return;
387                         }
388
389                         path = ".";
390                         pattern = spec;
391                 }
392
393                 static void ProcessFile (string f)
394                 {
395                         if (first_source == null)
396                                 first_source = f;
397
398                         files.Add (f);
399                 }
400
401                 static void CompileFiles (string spec, bool recurse)
402                 {
403                         string path, pattern;
404
405                         SplitPathAndPattern (spec, out path, out pattern);
406                         if (pattern.IndexOf ('*') == -1){
407                                 ProcessFile (spec);
408                                 return;
409                         }
410
411                         string [] files = null;
412                         try {
413                                 files = Directory.GetFiles (path, pattern);
414                         } catch (System.IO.DirectoryNotFoundException) {
415                                 Console.WriteLine ("Source file `" + spec + "' could not be found");
416                                 return;
417                         } catch (System.IO.IOException){
418                                 Console.WriteLine ("Source file `" + spec + "' could not be found");
419                                 return;
420                         }
421                         foreach (string f in files)
422                                 ProcessFile (f);
423
424                         if (!recurse)
425                                 return;
426                         
427                         string [] dirs = null;
428
429                         try {
430                                 dirs = Directory.GetDirectories (path);
431                         } catch {
432                         }
433                         
434                         foreach (string d in dirs) {
435                                         
436                                 // Don't include path in this string, as each
437                                 // directory entry already does
438                                 CompileFiles (d + "/" + pattern, true);
439                         }
440                 }
441
442                 internal static bool MainDriver (string [] args)
443                 {
444                         int i;
445                         bool parsing_options = true;
446
447                         references = new ArrayList ();
448                         link_paths = new ArrayList ();
449                         files = new ArrayList ();
450
451                         Hashtable response_file_list = null;
452
453                         for (i = 0; i < args.Length; i++){
454                                 string arg = args [i];
455                                 if (arg == "")
456                                         continue;
457                                 
458                                 if (arg.StartsWith ("@")){
459                                         string [] extra_args;
460                                         string response_file = arg.Substring (1);
461
462                                         if (response_file_list == null)
463                                                 response_file_list = new Hashtable ();
464                                         
465                                         if (response_file_list.Contains (response_file)){
466                                                 Console.WriteLine ("Response file `" + response_file + "' specified multiple times");
467                                                 Environment.Exit (1);
468                                         }
469                                         
470                                         response_file_list.Add (response_file, response_file);
471                                                     
472                                         extra_args = LoadArgs (response_file);
473                                         if (extra_args == null){
474                                                 Console.WriteLine ("Unable to open response file: " + response_file);
475                                                 return false;
476                                         }
477
478                                         args = AddArgs (args, extra_args);
479                                         continue;
480                                 }
481
482                                 if (parsing_options){
483                                         if (arg == "--"){
484                                                 parsing_options = false;
485                                                 continue;
486                                         }
487                                         
488                                         if (arg.StartsWith ("-")){
489                                                 if (UnixParseOption (arg, ref args, ref i))
490                                                         continue;
491
492                                                 // Try a -CSCOPTION
493                                                 string csc_opt = "/" + arg.Substring (1);
494                                                 if (CSCParseOption (csc_opt, ref args, ref i))
495                                                         continue;
496                                         } else {
497                                                 if (arg.StartsWith ("/")){
498                                                         if (CSCParseOption (arg, ref args, ref i))
499                                                                 continue;
500                                                 }
501                                         }
502                                 }
503                                 CompileFiles (arg, false); 
504                         }
505
506                         //
507                         // If there is nothing to put in the assembly, and we are not a library
508                         //
509                         if (first_source == null) /* && embedded_resources == null && resources == null) */ {
510                                 Console.WriteLine ("fatal error JS2026: No input sources specified");
511                                 return false;
512                         }
513
514                         //
515                         // Load Core Library for default compilation
516                         //
517                         if (StdLib)
518                                 references.Insert (0, "mscorlib");
519
520
521                         //
522                         // Load assemblies required
523                         //
524                         link_paths.Add (GetSystemDir ());
525                         link_paths.Add (Directory.GetCurrentDirectory ());
526                         LoadReferences ();
527                         return true;
528                 }
529         
530                 //
531                 // Entry point
532                 //
533                 private static void Main (string [] args) {
534         
535                         if (args.Length < 1) {
536                                 Usage ();
537                                 Environment.Exit (0);
538                         }                       
539                         MainDriver (args);
540                         VsaEngine engine = new VsaEngine ();
541                         engine.InitVsaEngine ("mjs:com.mono-project", new MonoEngineSite ());
542                         
543                         foreach (string asm in references) {
544                                 IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm, VsaItemType.Reference, VsaItemFlag.None);
545                                 item.AssemblyName = asm;
546                         }
547
548                         string asm_name = String.Empty;
549                         
550                         foreach (Assembly assembly in assemblies) {
551                                 asm_name = assembly.GetName ().FullName;
552                                 IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm_name, VsaItemType.Reference, VsaItemFlag.None);
553                                 item.AssemblyName = asm_name;
554                         }
555
556                         foreach (string file in files) {
557                                 IVsaCodeItem item = (IVsaCodeItem) engine.Items.CreateItem (file, VsaItemType.Code, VsaItemFlag.None);
558                                 item.SourceText = GetCodeFromFile (file);
559                         }
560                         engine.SetOption ("debug", want_debugging_support);
561                         engine.SetOption ("link_path", link_paths);
562                         engine.SetOption ("first_source", first_source);
563                         engine.SetOption ("assemblies", assemblies);
564                         engine.Compile ();
565                 }
566
567                 static string GetCodeFromFile (string fn)
568                 {
569                         try {
570                                 StreamReader reader = new StreamReader (fn);
571                                 return reader.ReadToEnd ();
572                         } catch (FileNotFoundException) {
573                                 throw new JScriptException (JSError.FileNotFound);
574                         } catch (ArgumentNullException) {
575                                 throw new JScriptException (JSError.FileNotFound);
576                         } catch (ArgumentException) {
577                                 throw new JScriptException (JSError.FileNotFound);
578                         } catch (IOException) {
579                                 throw new JScriptException (JSError.NoError);
580                         } catch (OutOfMemoryException) {
581                                 throw new JScriptException (JSError.OutOfMemory);
582                         }
583                 }
584         }
585
586         class MonoEngineSite : IVsaSite {
587                 public void GetCompiledState (out byte [] pe, out byte [] debugInfo)
588                 {
589                         throw new NotImplementedException ();
590                 }
591
592                 public object GetEventSourceInstance (string itemName, string eventSourceName)
593                 {
594                         throw new NotImplementedException ();
595                 }
596
597                 public object GetGlobalInstance (string name)
598                 {
599                         throw new NotImplementedException ();
600                 }
601
602                 public void Notify (string notify, object info)
603                 {
604                         throw new NotImplementedException ();
605                 }
606
607                 public bool OnCompilerError (IVsaError error)
608                 {
609                         throw new NotImplementedException ();
610                 }
611         }
612 }