// // IKVMCompilerManager.cs // // Author: // Mike Krüger // // Copyright (C) 2007 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.CodeDom.Compiler; using System.Diagnostics; using System.IO; using System.Text; using MonoDevelop.Core; using MonoDevelop.Core.Execution; using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.Projects; using Mono.Addins; namespace JavaBinding { public static class IKVMCompilerManager { static string GenerateOptionString (DotNetProjectConfiguration configuration) { JavaCompilerParameters parameters = (configuration.CompilationParameters as JavaCompilerParameters) ?? new JavaCompilerParameters (); StringBuilder result = new StringBuilder (); result.Append (configuration.DebugMode ? " -g " : " -g:none "); if (parameters.Optimize) result.Append (" -O "); if (parameters.Deprecation) result.Append (" -deprecation "); if (parameters.GenWarnings) result.Append (" -nowarn "); result.Append (" -encoding utf8 "); return result.ToString (); } public static BuildResult Compile (ProjectItemCollection projectItems, DotNetProjectConfiguration configuration, ConfigurationSelector configurationSelector, IProgressMonitor monitor) { JavaCompilerParameters parameters = (configuration.CompilationParameters as JavaCompilerParameters) ?? new JavaCompilerParameters (); string outdir = configuration.OutputDirectory; string options = GenerateOptionString (configuration); string compiler = parameters.CompilerPath; if (String.IsNullOrEmpty (compiler)) compiler = "javac"; StringBuilder files = new StringBuilder (); foreach (ProjectFile finfo in projectItems.GetAll ()) { if (finfo.Subtype != Subtype.Directory && finfo.BuildAction == BuildAction.Compile) { files.Append (" \""); files.Append (finfo.Name); files.Append ("\""); } } StringBuilder classpath = new StringBuilder (parameters.ClassPath); AppendClasspath (classpath, GenerateReferenceStubs (monitor, configurationSelector, configuration, parameters, projectItems)); AppendClasspath (classpath, GenerateReferenceStub (monitor, configurationSelector, configuration, new ProjectReference(ReferenceType.Gac, "mscorlib"))); StringBuilder args = new StringBuilder (); args.Append (options.ToString ()); if (parameters.Compiler == JavaCompiler.Gcj) args.Append ("-C "); if (classpath.Length != 0) { args.Append (" -classpath "); args.Append (classpath.ToString ()); } args.Append (files.ToString ()); args.Append (" -d "); args.Append (outdir); CompilerResults results = new CompilerResults (new TempFileCollection ()); StringWriter output = new StringWriter (); StringWriter error = new StringWriter (); bool success; try { success = Compile (monitor, compiler, args.ToString (), configuration, parameters, output, error); ParseJavaOutput (parameters.Compiler, error.ToString(), results); } catch (Exception ex) { string errorMsg = AddinManager.CurrentLocalizer.GetString ("Execution on '{0}' failed. Make sure the Java compiler is properly installed", compiler); monitor.ReportError (errorMsg, ex); success = false; results.Errors.Add (new CompilerError ("", 0, 0, "", errorMsg)); } if (success) { output = new StringWriter (); error = new StringWriter (); CompileToAssembly (monitor, configuration, parameters, projectItems, output, error); ParseIkvmOutput (parameters.Compiler, error.ToString(), results); } return new BuildResult (results, ""); } static void AppendClasspath (StringBuilder path, string jar) { if (path.Length > 0) path.Append (":"); path.Append (jar); } static string GenerateReferenceStubs (IProgressMonitor monitor, ConfigurationSelector configurationSelector, DotNetProjectConfiguration configuration, JavaCompilerParameters compilerparameters, ProjectItemCollection projectItems) { StringBuilder result = new StringBuilder (); foreach (ProjectReference reference in projectItems.GetAll ()) { AppendClasspath (result, GenerateReferenceStub (monitor, configurationSelector, configuration, reference)); } return result.ToString (); } static string GenerateReferenceStub (IProgressMonitor monitor, ConfigurationSelector configurationSelector, DotNetProjectConfiguration configuration, ProjectReference reference) { StringBuilder result = new StringBuilder (); foreach (string fileName in reference.GetReferencedFileNames (configurationSelector)) { string name = Path.GetFileNameWithoutExtension (Path.GetFileName (fileName)); string outputName = Path.Combine (configuration.OutputDirectory, name + ".jar"); if (!System.IO.File.Exists (outputName)) { monitor.Log.WriteLine (String.Format (GettextCatalog.GetString ("Generating {0} reference stub ..."), name)); monitor.Log.WriteLine ("ikvmstub \"" + fileName + "\""); ProcessWrapper p = Runtime.ProcessService.StartProcess ("ikvmstub", "\"" + fileName + "\"", configuration.OutputDirectory, monitor.Log, monitor.Log, null); p.WaitForExit (); if (p.ExitCode != 0) { monitor.ReportError ("Stub generation failed.", null); if (File.Exists (outputName)) { try { File.Delete (outputName); } catch { // Ignore } } } } AppendClasspath (result, outputName); } return result.ToString (); } static string TargetToString (CompileTarget target) { switch (target) { case CompileTarget.WinExe: return "winexe"; case CompileTarget.Library: return "library"; } return "exe"; } static void CompileToAssembly (IProgressMonitor monitor, DotNetProjectConfiguration configuration, JavaCompilerParameters compilerparameters, ProjectItemCollection projectItems, TextWriter output, TextWriter error) { monitor.Log.WriteLine (GettextCatalog.GetString ("Generating assembly ...")); LogTextWriter chainedError = new LogTextWriter (); chainedError.ChainWriter (monitor.Log); chainedError.ChainWriter (error); LogTextWriter chainedOutput = new LogTextWriter (); chainedOutput.ChainWriter (monitor.Log); chainedOutput.ChainWriter (output); string outdir = configuration.OutputDirectory; string outclass = Path.Combine (outdir, configuration.OutputAssembly + ".class"); string asm = Path.GetFileNameWithoutExtension (outclass); StringBuilder args = new StringBuilder ("-recurse:*.class "); args.Append ("-assembly:"); args.Append (asm); args.Append (" -target:"); args.Append (TargetToString (configuration.CompileTarget)); if (configuration.DebugMode) args.Append (" -debug"); args.Append (" -srcpath:"); args.Append (configuration.ParentItem.BaseDirectory); foreach (ProjectReference lib in projectItems.GetAll ()) { foreach (string fileName in lib.GetReferencedFileNames (configuration.Selector)) { args.Append (" -r:"); args.Append (fileName); } } foreach (string fileName in new ProjectReference(ReferenceType.Gac, "mscorlib").GetReferencedFileNames (configuration.Selector)) { args.Append (" -r:"); args.Append (fileName); } monitor.Log.WriteLine ("ikvmc " + args); Process process = Runtime.ProcessService.StartProcess ("ikvmc", args.ToString (), configuration.OutputDirectory, chainedOutput, chainedError, null); process.WaitForExit (); } static bool Compile (IProgressMonitor monitor, string compiler, string args, DotNetProjectConfiguration configuration, JavaCompilerParameters compilerparameters, TextWriter output, TextWriter error) { LogTextWriter chainedError = new LogTextWriter (); chainedError.ChainWriter (monitor.Log); chainedError.ChainWriter (error); LogTextWriter chainedOutput = new LogTextWriter (); chainedOutput.ChainWriter (monitor.Log); chainedOutput.ChainWriter (output); monitor.Log.WriteLine (GettextCatalog.GetString ("Compiling Java source code ...")); monitor.Log.WriteLine (compiler + " " + args); Process process = Runtime.ProcessService.StartProcess (compiler, args, null, chainedOutput, chainedError, null); process.WaitForExit (); return process.ExitCode == 0; } static void ParseJavaOutput (JavaCompiler compiler, string errorStr, CompilerResults cr) { TextReader reader = new StringReader (errorStr); string line; while ((line = reader.ReadLine ()) != null) { CompilerError error = CreateJavaErrorFromString (compiler, line); if (error != null) cr.Errors.Add (error); } reader.Close (); } private static CompilerError CreateJavaErrorFromString (JavaCompiler compiler, string next) { CompilerError result = new CompilerError (); int errorCol = 0; string col = next.Trim (); if (col.Length == 1 && col == "^") errorCol = next.IndexOf ("^"); int index1 = next.IndexOf (".java:"); if (index1 < 0) return null; string s2 = next.Substring (index1 + 6); int index2 = s2.IndexOf (":"); int line = Int32.Parse (next.Substring (index1 + 6, index2)); string msg = next.Substring (index1 + index2 + 7).Trim (); if (msg.StartsWith ("warning:")) { result.IsWarning = true; msg = msg.Substring (8).Trim (); } result.Column = errorCol; result.Line = line; result.ErrorText = msg; result.FileName = Path.GetFullPath (next.Substring (0, index1) + ".java"); return result; } static void ParseIkvmOutput (JavaCompiler compiler, string errorStr, CompilerResults cr) { TextReader reader = new StringReader (errorStr); string line; while ((line = reader.ReadLine ()) != null) { CompilerError error = CreateIkvmErrorFromString (line); if (error != null) cr.Errors.Add (error); } reader.Close (); } static CompilerError CreateIkvmErrorFromString (string error) { if (error.StartsWith ("Note") || error.StartsWith ("Warning")) return null; string trimmed = error.Trim (); if (trimmed.StartsWith ("(to avoid this warning add")) return null; CompilerError cerror = new CompilerError (); cerror.ErrorText = error; return cerror; } } }