//
// driver.cs: Guides the compilation process through the different phases.
//
// Author:
// Cesar Lopez Nataren (cesar@ciencias.unam.mx)
//
// (C) 2003, Cesar Lopez Nataren
// (C) Copyright 2005, Novell Inc. (http://www.novell.com)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Text;
using Microsoft.Vsa;
using Microsoft.JScript;
using System.Reflection;
using System.Collections;
using Microsoft.JScript.Vsa;
using System.Reflection.Emit;
using Mono.CSharp;
namespace Mono.JScript {
class Driver {
//
// Assemblies references to be linked. Initialized with
// mscorlib.dll
//
private static ArrayList references;
// Lookup paths
private static ArrayList link_paths;
// jscript files
private static ArrayList files;
private static string first_source;
private static bool want_debugging_support = false;
private static string output_file;
private static Assembly [] assemblies = new Assembly [0];
private static bool StdLib;
private static void Usage ()
{
Console.WriteLine ("Mono JScript compiler\n" +
"(C) 2003 - 2004 Cesar Lopez Nataren\n" +
"(C) 2004 - 2005 Novell Inc (http://novell.com)\n\n" +
"mjs [options] source-file\n" +
" /about About the Mono JScript compiler\n" +
" /lib:PATH1,PATH2 Adds the paths to the assembly link path\n" +
" /r[eference]:ASS Reference the specified assembly\n");
}
private static void About ()
{
Console.WriteLine (
"The Mono JScript compiler is:\n" +
"(C) 2003 - 2004 Cesar Lopez Nataren\n" +
"(C) 2004 - 2005 Novell Inc.\n\n" +
"The compiler source code is released under the terms of both the MIT X11 and MPL\n" +
"The compiler was written by Cesar Lopez Nataren");
Environment.Exit (0);
}
///
/// Loads all assemblies referenced on the command line
///
private static void LoadReferences ()
{
foreach (string r in references)
LoadAssembly (r, false);
return;
}
private static void LoadAssembly (string assembly, bool soft)
{
Assembly a;
string total_log = "";
try {
char [] path_chars = { '/', '\\' };
if (assembly.IndexOfAny (path_chars) != -1) {
a = Assembly.LoadFrom (assembly);
} else {
string ass = assembly;
if (ass.EndsWith (".dll") || ass.EndsWith (".exe"))
ass = assembly.Substring (0, assembly.Length - 4);
a = Assembly.Load (ass);
}
AddAssembly (a);
} catch (FileNotFoundException){
foreach (string dir in link_paths){
string full_path = Path.Combine (dir, assembly);
if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
full_path += ".dll";
try {
a = Assembly.LoadFrom (full_path);
AddAssembly (a);
return;
} catch (FileNotFoundException ff) {
total_log += ff.FusionLog;
continue;
}
}
if (!soft)
Console.WriteLine ("Cannot find assembly `" + assembly + "'" );
} catch (BadImageFormatException f) {
Console.WriteLine ("Cannot load assembly (bad file format)" + f.FusionLog);
} catch (FileLoadException f){
Console.WriteLine ("Cannot load assembly " + f.FusionLog);
} catch (ArgumentNullException){
Console.WriteLine ("Cannot load assembly (null argument)");
}
}
///
/// Registers an assembly to load types from.
///
private static void AddAssembly (Assembly a)
{
foreach (Assembly assembly in assemblies) {
if (a == assembly)
return;
}
int top = assemblies.Length;
Assembly [] n = new Assembly [top + 1];
assemblies.CopyTo (n, 0);
n [top] = a;
assemblies = n;
}
static string [] LoadArgs (string file)
{
StreamReader f;
ArrayList args = new ArrayList ();
string line;
try {
f = new StreamReader (file);
} catch {
return null;
}
StringBuilder sb = new StringBuilder ();
while ((line = f.ReadLine ()) != null){
int t = line.Length;
for (int i = 0; i < t; i++){
char c = line [i];
if (c == '"' || c == '\''){
char end = c;
for (i++; i < t; i++){
c = line [i];
if (c == end)
break;
sb.Append (c);
}
} else if (c == ' '){
if (sb.Length > 0){
args.Add (sb.ToString ());
sb.Length = 0;
}
} else
sb.Append (c);
}
if (sb.Length > 0){
args.Add (sb.ToString ());
sb.Length = 0;
}
}
string [] ret_value = new string [args.Count];
args.CopyTo (ret_value, 0);
return ret_value;
}
//
// Returns the directory where the system assemblies are installed
//
static string GetSystemDir ()
{
return Path.GetDirectoryName (typeof (object).Assembly.Location);
}
static void SetOutputFile (string name)
{
output_file = name;
}
static void Version ()
{
string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
Console.WriteLine ("Mono JScript compiler version {0}", version);
Environment.Exit (0);
}
static bool UnixParseOption (string arg, ref string [] args, ref int i)
{
switch (arg){
case "--version":
Version ();
return true;
case "/?": case "/h": case "/help":
case "--help":
Usage ();
Environment.Exit (0);
return true;
case "-o":
case "--output":
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
SetOutputFile (args [++i]);
return true;
case "-r":
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
references.Add (args [++i]);
return true;
case "-L":
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
link_paths.Add (args [++i]);
return true;
case "--about":
About ();
return true;
}
return false;
}
//
// This parses the -arg and /arg options to the compiler, even if the strings
// in the following text use "/arg" on the strings.
//
static bool CSCParseOption (string option, ref string [] args, ref int i)
{
int idx = option.IndexOf (':');
string arg, value;
if (idx == -1){
arg = option;
value = "";
} else {
arg = option.Substring (0, idx);
value = option.Substring (idx + 1);
}
switch (arg){
case "/nologo":
return true;
case "/out":
if (value == ""){
Usage ();
Environment.Exit (1);
}
SetOutputFile (value);
return true;
case "/r":
case "/reference": {
if (value == ""){
Console.WriteLine ("/reference requires an argument");
Environment.Exit (1);
}
string [] refs = value.Split (new char [] { ';', ',' });
foreach (string r in refs){
references.Add (r);
}
return true;
}
case "/lib": {
string [] libdirs;
if (value == ""){
Console.WriteLine ("/lib requires an argument");
Environment.Exit (1);
}
libdirs = value.Split (new Char [] { ',' });
foreach (string dir in libdirs)
link_paths.Add (dir);
return true;
}
case "/about":
About ();
return true;
}
return false;
}
static string [] AddArgs (string [] args, string [] extra_args)
{
string [] new_args;
new_args = new string [extra_args.Length + args.Length];
// if args contains '--' we have to take that into account
// split args into first half and second half based on '--'
// and add the extra_args before --
int split_position = Array.IndexOf (args, "--");
if (split_position != -1)
{
Array.Copy (args, new_args, split_position);
extra_args.CopyTo (new_args, split_position);
Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
}
else
{
args.CopyTo (new_args, 0);
extra_args.CopyTo (new_args, args.Length);
}
return new_args;
}
//
// Given a path specification, splits the path from the file/pattern
//
static void SplitPathAndPattern (string spec, out string path, out string pattern)
{
int p = spec.LastIndexOf ('/');
if (p != -1){
//
// Windows does not like /file.cs, switch that to:
// "\", "file.cs"
//
if (p == 0){
path = "\\";
pattern = spec.Substring (1);
} else {
path = spec.Substring (0, p);
pattern = spec.Substring (p + 1);
}
return;
}
p = spec.LastIndexOf ('\\');
if (p != -1){
path = spec.Substring (0, p);
pattern = spec.Substring (p + 1);
return;
}
path = ".";
pattern = spec;
}
static void ProcessFile (string f)
{
if (first_source == null)
first_source = f;
files.Add (f);
}
static void CompileFiles (string spec, bool recurse)
{
string path, pattern;
SplitPathAndPattern (spec, out path, out pattern);
if (pattern.IndexOf ('*') == -1){
ProcessFile (spec);
return;
}
string [] files = null;
try {
files = Directory.GetFiles (path, pattern);
} catch (System.IO.DirectoryNotFoundException) {
Console.WriteLine ("Source file `" + spec + "' could not be found");
return;
} catch (System.IO.IOException){
Console.WriteLine ("Source file `" + spec + "' could not be found");
return;
}
foreach (string f in files)
ProcessFile (f);
if (!recurse)
return;
string [] dirs = null;
try {
dirs = Directory.GetDirectories (path);
} catch {
}
foreach (string d in dirs) {
// Don't include path in this string, as each
// directory entry already does
CompileFiles (d + "/" + pattern, true);
}
}
internal static bool MainDriver (string [] args)
{
int i;
bool parsing_options = true;
references = new ArrayList ();
link_paths = new ArrayList ();
files = new ArrayList ();
Hashtable response_file_list = null;
for (i = 0; i < args.Length; i++){
string arg = args [i];
if (arg == "")
continue;
if (arg.StartsWith ("@")){
string [] extra_args;
string response_file = arg.Substring (1);
if (response_file_list == null)
response_file_list = new Hashtable ();
if (response_file_list.Contains (response_file)){
Console.WriteLine ("Response file `" + response_file + "' specified multiple times");
Environment.Exit (1);
}
response_file_list.Add (response_file, response_file);
extra_args = LoadArgs (response_file);
if (extra_args == null){
Console.WriteLine ("Unable to open response file: " + response_file);
return false;
}
args = AddArgs (args, extra_args);
continue;
}
if (parsing_options){
if (arg == "--"){
parsing_options = false;
continue;
}
if (arg.StartsWith ("-")){
if (UnixParseOption (arg, ref args, ref i))
continue;
// Try a -CSCOPTION
string csc_opt = "/" + arg.Substring (1);
if (CSCParseOption (csc_opt, ref args, ref i))
continue;
} else {
if (arg.StartsWith ("/")){
if (CSCParseOption (arg, ref args, ref i))
continue;
}
}
}
CompileFiles (arg, false);
}
//
// If there is nothing to put in the assembly, and we are not a library
//
if (first_source == null) /* && embedded_resources == null && resources == null) */ {
Console.WriteLine ("fatal error JS2026: No input sources specified");
return false;
}
//
// Load Core Library for default compilation
//
if (StdLib)
references.Insert (0, "mscorlib");
//
// Load assemblies required
//
link_paths.Add (GetSystemDir ());
link_paths.Add (Directory.GetCurrentDirectory ());
LoadReferences ();
return true;
}
//
// Entry point
//
private static void Main (string [] args) {
if (args.Length < 1) {
Usage ();
Environment.Exit (0);
}
MainDriver (args);
VsaEngine engine = new VsaEngine ();
engine.InitVsaEngine ("mjs:com.mono-project", new MonoEngineSite ());
foreach (string asm in references) {
IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm, VsaItemType.Reference, VsaItemFlag.None);
item.AssemblyName = asm;
}
string asm_name = String.Empty;
foreach (Assembly assembly in assemblies) {
asm_name = assembly.GetName ().FullName;
IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm_name, VsaItemType.Reference, VsaItemFlag.None);
item.AssemblyName = asm_name;
}
foreach (string file in files) {
IVsaCodeItem item = (IVsaCodeItem) engine.Items.CreateItem (file, VsaItemType.Code, VsaItemFlag.None);
item.SourceText = GetCodeFromFile (file);
}
engine.SetOption ("debug", want_debugging_support);
engine.SetOption ("link_path", link_paths);
engine.SetOption ("first_source", first_source);
engine.SetOption ("assemblies", assemblies);
engine.Compile ();
}
static string GetCodeFromFile (string fn)
{
try {
StreamReader reader = new StreamReader (fn);
return reader.ReadToEnd ();
} catch (FileNotFoundException) {
throw new JScriptException (JSError.FileNotFound);
} catch (ArgumentNullException) {
throw new JScriptException (JSError.FileNotFound);
} catch (ArgumentException) {
throw new JScriptException (JSError.FileNotFound);
} catch (IOException) {
throw new JScriptException (JSError.NoError);
} catch (OutOfMemoryException) {
throw new JScriptException (JSError.OutOfMemory);
}
}
}
class MonoEngineSite : IVsaSite {
public void GetCompiledState (out byte [] pe, out byte [] debugInfo)
{
throw new NotImplementedException ();
}
public object GetEventSourceInstance (string itemName, string eventSourceName)
{
throw new NotImplementedException ();
}
public object GetGlobalInstance (string name)
{
throw new NotImplementedException ();
}
public void Notify (string notify, object info)
{
throw new NotImplementedException ();
}
public bool OnCompilerError (IVsaError error)
{
throw new NotImplementedException ();
}
}
}