2 // driver.cs: Guides the compilation process through the different phases.
5 // Cesar Lopez Nataren (cesar@ciencias.unam.mx)
7 // (C) 2003, Cesar Lopez Nataren
8 // (C) Copyright 2005, 2006, Novell Inc. (http://www.novell.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Globalization;
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;
45 namespace Mono.JScript {
49 // Assemblies references to be linked. Initialized with
52 private static ArrayList references;
55 private static ArrayList link_paths;
58 private static ArrayList files;
60 private static string first_source;
62 private static bool want_debugging_support = false;
64 private static string output_file;
65 private static int warning_level = -1;
67 private static Assembly [] assemblies = new Assembly [0];
69 private static bool StdLib = true;
71 private static void Usage ()
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" +
86 " @file Read response file for more options\n\n" +
87 "Options can be of the form -option or /option");
90 private static void About ()
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");
102 /// Loads all assemblies referenced on the command line
104 private static void LoadReferences ()
106 foreach (string r in references)
107 LoadAssembly (r, false);
110 private static void LoadAssembly (string assembly, bool soft)
113 string total_log = "";
116 char [] path_chars = { '/', '\\' };
118 if (assembly.IndexOfAny (path_chars) != -1) {
119 a = Assembly.LoadFrom (assembly);
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);
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"))
135 a = Assembly.LoadFrom (full_path);
138 } catch (FileNotFoundException ff) {
139 total_log += ff.FusionLog;
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)");
155 /// Registers an assembly to load types from.
157 private static void AddAssembly (Assembly a)
159 foreach (Assembly assembly in assemblies) {
164 int top = assemblies.Length;
165 Assembly [] n = new Assembly [top + 1];
167 assemblies.CopyTo (n, 0);
173 static string [] LoadArgs (string file)
176 ArrayList args = new ArrayList ();
179 f = new StreamReader (file);
184 StringBuilder sb = new StringBuilder ();
186 while ((line = f.ReadLine ()) != null){
189 for (int i = 0; i < t; i++){
192 if (c == '"' || c == '\''){
195 for (i++; i < t; i++){
202 } else if (c == ' '){
204 args.Add (sb.ToString ());
211 args.Add (sb.ToString ());
216 string [] ret_value = new string [args.Count];
217 args.CopyTo (ret_value, 0);
223 // Returns the directory where the system assemblies are installed
225 static string GetSystemDir ()
227 return Path.GetDirectoryName (typeof (object).Assembly.Location);
231 static void SetOutputFile (string name)
236 static void Version ()
238 string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
239 Console.WriteLine ("Mono JScript compiler version {0}", version);
240 Environment.Exit (0);
243 static bool UnixParseOption (string arg, ref string [] args, ref int i)
250 case "/?": case "/h": case "/help":
253 Environment.Exit (0);
257 if ((i + 1) >= args.Length){
259 Environment.Exit (1);
261 SetOutputFile (args [++i]);
264 if ((i + 1) >= args.Length){
266 Environment.Exit (1);
269 references.Add (args [++i]);
273 if ((i + 1) >= args.Length){
275 Environment.Exit (1);
277 link_paths.Add (args [++i]);
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.
291 static bool CSCParseOption (string option, ref string [] args, ref int i)
293 int idx = option.IndexOf (':');
299 arg = option.Substring (0, idx);
301 value = option.Substring (idx + 1);
311 Environment.Exit (1);
313 SetOutputFile (value);
319 if (value == String.Empty) {
321 Environment.Exit (1);
323 packages = String.Join (" ", value.Split (new Char [] { ';', ',', '\n', '\r'}));
325 ProcessStartInfo pi = new ProcessStartInfo ();
326 pi.FileName = "pkg-config";
327 pi.RedirectStandardOutput = true;
328 pi.UseShellExecute = false;
329 pi.Arguments = "--libs " + packages;
332 p = Process.Start (pi);
333 } catch (Exception e) {
334 Console.Error.WriteLine ("Couldn't run pkg-config: " + e.Message);
335 Environment.Exit (1);
338 if (p.StandardOutput == null){
339 Console.Error.WriteLine ("Specified package did not return any information");
342 string pkgout = p.StandardOutput.ReadToEnd ();
344 if (p.ExitCode != 0) {
345 Console.Error.WriteLine ("Error running pkg-config. Check the above output.");
346 Environment.Exit (1);
350 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
351 Split (new Char [] { ' ', '\t'});
352 args = AddArgs (args, xargs);
360 Console.WriteLine ("/reference requires an argument");
361 Environment.Exit (1);
364 string [] refs = value.Split (new char [] { ';', ',' });
365 foreach (string r in refs)
374 Console.WriteLine ("/lib requires an argument");
375 Environment.Exit (1);
378 libdirs = value.Split (new Char [] { ',' });
379 foreach (string dir in libdirs)
380 link_paths.Add (dir);
398 if (value.Length == 0) {
399 Console.WriteLine ("fatal error JS2013: Target type is invalid");
400 Environment.Exit (1);
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);
409 Console.WriteLine ("fatal error JS2013: Target '{0}' is invalid."
410 + " Specify 'exe' or 'library'", value);
411 Environment.Exit (1);
415 if (value.Length == 0) {
416 Console.WriteLine ("fatal error JS2028: No warning level specified"
417 + " with option '{0}'", option);
418 Environment.Exit (1);
422 warning_level = int.Parse (value, CultureInfo.InvariantCulture);
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);
436 static string [] AddArgs (string [] args, string [] extra_args)
439 new_args = new string [extra_args.Length + args.Length];
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);
450 args.CopyTo (new_args, 0);
451 extra_args.CopyTo (new_args, args.Length);
457 // Given a path specification, splits the path from the file/pattern
459 static void SplitPathAndPattern (string spec, out string path, out string pattern)
461 int p = spec.LastIndexOf ('/');
464 // Windows does not like /file.cs, switch that to:
469 pattern = spec.Substring (1);
471 path = spec.Substring (0, p);
472 pattern = spec.Substring (p + 1);
477 p = spec.LastIndexOf ('\\');
479 path = spec.Substring (0, p);
480 pattern = spec.Substring (p + 1);
488 static void ProcessFile (string f)
490 if (first_source == null)
496 static void CompileFiles (string spec, bool recurse)
498 string path, pattern;
500 SplitPathAndPattern (spec, out path, out pattern);
501 if (pattern.IndexOf ('*') == -1){
506 string [] files = null;
508 files = Directory.GetFiles (path, pattern);
509 } catch (System.IO.DirectoryNotFoundException) {
510 Console.WriteLine ("Source file `" + spec + "' could not be found");
512 } catch (System.IO.IOException){
513 Console.WriteLine ("Source file `" + spec + "' could not be found");
516 foreach (string f in files)
522 string [] dirs = null;
525 dirs = Directory.GetDirectories (path);
529 foreach (string d in dirs) {
531 // Don't include path in this string, as each
532 // directory entry already does
533 CompileFiles (d + "/" + pattern, true);
537 internal static bool MainDriver (string [] args)
540 bool parsing_options = true;
542 references = new ArrayList ();
543 link_paths = new ArrayList ();
544 files = new ArrayList ();
546 Hashtable response_file_list = null;
548 for (i = 0; i < args.Length; i++){
549 string arg = args [i];
553 if (arg.StartsWith ("@")){
554 string [] extra_args;
555 string response_file = arg.Substring (1);
557 if (response_file_list == null)
558 response_file_list = new Hashtable ();
560 if (response_file_list.Contains (response_file)){
561 Console.WriteLine ("Response file `" + response_file + "' specified multiple times");
562 Environment.Exit (1);
565 response_file_list.Add (response_file, response_file);
567 extra_args = LoadArgs (response_file);
568 if (extra_args == null){
569 Console.WriteLine ("Unable to open response file: " + response_file);
573 args = AddArgs (args, extra_args);
577 if (parsing_options){
579 parsing_options = false;
583 if (arg.StartsWith ("-")){
584 if (UnixParseOption (arg, ref args, ref i))
588 string csc_opt = "/" + arg.Substring (1);
589 if (CSCParseOption (csc_opt, ref args, ref i))
592 if (arg.StartsWith ("/")){
593 if (CSCParseOption (arg, ref args, ref i))
598 CompileFiles (arg, false);
602 // If there is nothing to put in the assembly, and we are not a library
604 if (first_source == null) /* && embedded_resources == null && resources == null) */ {
605 Console.WriteLine ("fatal error JS2026: No input sources specified");
610 // Load Core Library for default compilation
613 references.Insert (0, "mscorlib");
617 // Load assemblies required
619 link_paths.Add (GetSystemDir ());
620 link_paths.Add (Directory.GetCurrentDirectory ());
628 private static void Main (string [] args) {
629 if (args.Length < 1) {
631 Environment.Exit (0);
634 VsaEngine engine = new VsaEngine ();
635 engine.InitVsaEngine ("mjs:com.mono-project", new MonoEngineSite ());
637 foreach (string asm in references) {
638 IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm, VsaItemType.Reference, VsaItemFlag.None);
639 item.AssemblyName = asm;
642 string asm_name = String.Empty;
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;
650 foreach (string file in files) {
651 IVsaCodeItem item = (IVsaCodeItem) engine.Items.CreateItem (file, VsaItemType.Code, VsaItemFlag.None);
652 item.SourceText = GetCodeFromFile (file);
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);
664 static string GetCodeFromFile (string fn)
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);
683 class MonoEngineSite : IVsaSite {
684 public void GetCompiledState (out byte [] pe, out byte [] debugInfo)
686 throw new NotImplementedException ();
689 public object GetEventSourceInstance (string itemName, string eventSourceName)
691 throw new NotImplementedException ();
694 public object GetGlobalInstance (string name)
696 throw new NotImplementedException ();
699 public void Notify (string notify, object info)
701 throw new NotImplementedException ();
704 public bool OnCompilerError (IVsaError error)
706 throw new NotImplementedException ();