2 // CommonCompilerOptions.cs
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
6 // (C) 2005 Rafael Teixeira
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
32 using System.Diagnostics;
33 using System.Reflection;
34 using System.Reflection.Emit;
37 namespace Mono.GetOptions.Useful
40 public enum TargetType {
41 Library, Exe, Module, WinExe
44 public struct FileToCompile {
45 public string Filename;
46 public Encoding Encoding;
48 public FileToCompile(string filename, Encoding encoding)
50 this.Filename = filename;
51 this.Encoding = encoding;
55 public enum InternalCompilerErrorReportAction {
59 public delegate void ModuleAdder (System.Reflection.Module module);
60 public delegate void AssemblyAdder (Assembly loadedAssembly);
62 public class CommonCompilerOptions : Options {
64 public CommonCompilerOptions() : this(null, null) { }
66 public CommonCompilerOptions(string[] args) : this(args, null) {}
68 public CommonCompilerOptions(string[] args, ErrorReporter reportError) : base(args, OptionsParsingMode.Both, false, true, true, reportError)
70 PathsToSearchForLibraries.Add (Directory.GetCurrentDirectory ());
73 [Option(-1, "References packages listed. {packagelist}=package,...", "pkg")]
74 public WhatToDoNext ReferenceSomePackage(string packageName)
76 return ReferencePackage(packageName)?WhatToDoNext.GoAhead:WhatToDoNext.AbandonProgram;
79 private Encoding currentEncoding = null;
81 [Option(-1, "Select codepage by {ID} (number, 'utf8' or 'reset') to process following source files", "codepage")]
82 public string CurrentCodepage {
84 switch (value.ToLower()) {
86 currentEncoding = null;
88 case "utf8": case "utf-8":
89 currentEncoding = Encoding.UTF8;
93 currentEncoding = Encoding.GetEncoding(int.Parse(value));
94 } catch (NotSupportedException) {
95 ReportError (0, string.Format("Ignoring unsupported codepage number {0}.", value));
97 ReportError (0, string.Format("Ignoring unsupported codepage ID {0}.", value));
104 private ArrayList warningsToIgnore = new ArrayList();
105 public int[] WarningsToIgnore { get { return (int[])warningsToIgnore.ToArray(typeof(int)); } }
107 [Option(-1, "Ignores warning number {XXXX}", "ignorewarn", SecondLevelHelp = true)]
108 public WhatToDoNext SetIgnoreWarning(int warningNumber)
110 warningsToIgnore.Add(warningNumber);
111 return WhatToDoNext.GoAhead;
114 [Option("Sets warning {level} (the highest is 4, the default)", "wlevel", SecondLevelHelp = true)]
115 public int WarningLevel = 4;
117 // Output file options
118 //------------------------------------------------------------------
119 public TargetType TargetFileType = TargetType.Exe;
121 string outputFileName = null;
122 string firstSourceFile = null;
123 string targetFileExtension = ".exe";
125 [Option("Specifies the output {file} name", 'o', "out")]
126 public string OutputFileName
128 set { outputFileName = value; }
131 if (outputFileName == null) {
132 int pos = firstSourceFile.LastIndexOf(".");
135 outputFileName = firstSourceFile.Substring(0, pos);
137 outputFileName = firstSourceFile;
138 // TODO: what Codegen does here to get hid of this dependency
139 // string bname = CodeGen.Basename(outputFileName);
140 // if (bname.IndexOf(".") == -1)
141 outputFileName += targetFileExtension;
143 return outputFileName;
148 [Option("Specifies the target {type} for the output file (exe [default], winexe, library, module)", 't', "target")]
149 public WhatToDoNext SetTarget(string type)
151 switch (type.ToLower()) {
153 TargetFileType = TargetType.Library;
154 targetFileExtension = ".dll";
158 TargetFileType = TargetType.Exe;
159 targetFileExtension = ".exe";
163 TargetFileType = TargetType.WinExe;
164 targetFileExtension = ".exe";
168 TargetFileType = TargetType.Module;
169 targetFileExtension = ".netmodule";
172 return WhatToDoNext.GoAhead;
175 [Option("Specifies the {name} of the Class or Module that contains Sub Main \tor inherits from System.Windows.Forms.Form.\tNeeded to select among many entry-points for a program (target=exe|winexe)",
177 public string MainClassName = null;
179 // TODO: force option to accept number in hex format
180 // [Option("[NOT IMPLEMENTED YET]The base {address} for a library or module (hex)", SecondLevelHelp = true)]
181 public int baseaddress;
183 // input file options
184 //------------------------------------------------------------------
185 [Option(-1, "Imports all type information from files in the module-list. {module-list}:module,...", "addmodule")]
186 public string AddedModule { set { foreach(string module in value.Split(',')) NetModulesToAdd.Add(module); } }
188 // [Option("[NOT IMPLEMENTED YET]Include all files in the current directory and subdirectories according to the {wildcard}", "recurse")]
189 public WhatToDoNext Recurse(string wildcard)
191 //AddFiles (DirName, true); // TODO wrong semantics
192 return WhatToDoNext.GoAhead;
195 [Option(-1, "References metadata from the specified assembly-list. {assembly-list}:assembly,...", 'r', "reference")]
196 public string AddedReference { set { foreach (string assembly in value.Split(',')) AssembliesToReference.Add(assembly); } }
198 [Option("List of directories to search for referenced assemblies. \t{path-list}:path,...", "libpath", "lib")]
199 public string AddedLibPath { set { foreach(string path in value.Split(',')) PathsToSearchForLibraries.Add(path); } }
201 // support for the Compact Framework
202 //------------------------------------------------------------------
203 // [Option("[NOT IMPLEMENTED YET]Sets the compiler to TargetFileType the Compact Framework", "netcf")]
204 public bool CompileForCompactFramework = false;
206 // [Option("[NOT IMPLEMENTED YET]Specifies the {path} to the location of mscorlib.dll and microsoft.visualbasic.dll", "sdkpath")]
207 public string SDKPath = null;
210 //------------------------------------------------------------------
211 public ArrayList EmbeddedResources = new ArrayList();
213 //TODO: support -res:file[,id[,public|private]] what depends on changes at Mono.GetOptions
214 [Option(-1, "Adds the specified file as an embedded assembly resource. \t{details}:file[,id[,public|private]]", "resource", "res")]
215 public string AddedResource { set { EmbeddedResources.Add(value); } }
217 public ArrayList LinkedResources = new ArrayList();
219 [Option(-1, "Adds the specified file as a linked assembly resource. \t{details}:file[,id[,public|private]]", "linkresource", "linkres")]
220 public string AddedLinkresource { set { LinkedResources.Add(value); } }
222 public ArrayList Win32Resources = new ArrayList();
224 // [Option(-1, "[NOT IMPLEMENTED YET]Specifies a Win32 resource {file} (.res)", "win32resource")]
225 public string AddedWin32resource { set { Win32Resources.Add(value); } }
227 public ArrayList Win32Icons = new ArrayList();
229 // [Option(-1, "[NOT IMPLEMENTED YET]Specifies a Win32 icon {file} (.ico) for the default Win32 resources", "win32icon")]
230 public string AddedWin32icon { set { Win32Icons.Add(value); } }
232 // code generation options
233 //------------------------------------------------------------------
235 // [Option("[NOT IMPLEMENTED YET]Enable optimizations", "optimize", VBCStyleBoolean = true)]
236 public bool Optimize = false;
238 public bool CheckedContext = true;
240 [Option("Remove integer checks. Default off.", SecondLevelHelp = true, VBCStyleBoolean = true)]
241 public virtual bool removeintchecks { set { CheckedContext = !value; } }
243 [Option("Emit full debugging information", 'g', "debug", VBCStyleBoolean = true)]
244 public bool WantDebuggingSupport = false;
246 [Option("Emit full debugging information (default)", "debug:full", SecondLevelHelp = true)]
247 public bool debugfull {
249 WantDebuggingSupport = value;
250 FullDebugging = value;
255 [Option("Emit MDB file only", "debug:pdbonly", SecondLevelHelp = true)]
256 public bool debugpdbonly {
258 WantDebuggingSupport = value;
259 FullDebugging = !value;
264 public bool MdbOnly = false;
265 public bool FullDebugging = true;
268 // errors and warnings options
269 //------------------------------------------------------------------
271 [Option("Treat warnings as errors", "warnaserror", SecondLevelHelp = true)]
272 public bool WarningsAreErrors = false;
274 [Option("Disable warnings", "nowarn", SecondLevelHelp = true)]
275 public bool NoWarnings { set { if (value) WarningLevel = 0; } }
279 //------------------------------------------------------------------
280 public Hashtable Defines = new Hashtable();
282 [Option(-1, "Declares global conditional compilation symbol(s). {symbol-list}:name=value,...", 'd', "define")]
283 public string DefineSymbol {
285 foreach(string item in value.Split(',')) {
286 string[] dados = item.Split('=');
287 if (dados.Length > 1)
288 Defines.Add(dados[0], dados[1]);
290 Defines.Add(dados[0], "true");
295 [Option("Don\'t assume the standard library", "nostdlib", SecondLevelHelp = true)]
296 public bool NoStandardLibraries = false;
298 [Option("Disables implicit references to assemblies", "noconfig", SecondLevelHelp = true)]
299 public bool NoConfig = false;
301 [Option("Allows unsafe code", "unsafe", SecondLevelHelp = true)]
302 public bool AllowUnsafeCode = false;
304 [Option("Debugger {arguments}", "debug-args", SecondLevelHelp = true)]
305 public WhatToDoNext SetDebugArgs(string args)
307 DebugListOfArguments.AddRange(args.Split(','));
308 return WhatToDoNext.GoAhead;
311 public ArrayList Imports = new ArrayList();
313 [Option(-1, "Declare global Imports for listed namespaces. {import-list}:namespace,...", "imports")]
314 public string ImportNamespaces
317 foreach(string importedNamespace in value.Split(','))
318 Imports.Add(importedNamespace);
322 [Option("Specifies the root {namespace} for all type declarations", "rootnamespace", SecondLevelHelp = true)]
323 public string RootNamespace = null;
326 //------------------------------------------------------------------
327 // [Option("[NOT IMPLEMENTED YET]Delay-sign the assembly using only the public portion of the strong name key", VBCStyleBoolean = true)]
328 public bool delaysign;
330 // [Option("[NOT IMPLEMENTED YET]Specifies a strong name key {container}")]
331 public string keycontainer;
333 // [Option("[NOT IMPLEMENTED YET]Specifies a strong name key {file}")]
334 public string keyfile;
336 // Compiler output options
337 //------------------------------------------------------------------
339 [Option("Do not display compiler copyright banner", "nologo")]
340 public bool DontShowBanner = false;
342 //TODO: Correct semantics
343 [Option("Commands the compiler to show only error messages for syntax-related errors and warnings", 'q', "quiet", SecondLevelHelp = true)]
344 public bool SuccintErrorDisplay = false;
346 [Option("Display verbose messages", 'v', "verbose", SecondLevelHelp = true)]
347 public bool Verbose = false;
349 [Option("[IGNORED] Emit compiler output in UTF8 character encoding", "utf8output", SecondLevelHelp = true, VBCStyleBoolean = true)]
350 public bool OutputInUTF8;
352 // [Option("[NOT IMPLEMENTED YET]Create bug report {file}", "bugreport")]
353 public string CreateBugReport;
355 Hashtable sourceFiles = new Hashtable ();
356 public override void DefaultArgumentProcessor(string fileName)
358 if (firstSourceFile == null)
359 firstSourceFile = fileName;
361 if (!sourceFiles.Contains(fileName)) {
362 SourceFilesToCompile.Add(new FileToCompile(fileName, currentEncoding));
363 sourceFiles.Add(fileName, fileName);
365 base.DefaultArgumentProcessor(fileName);
368 public ArrayList AssembliesToReference = new ArrayList();
369 public ArrayList NetModulesToAdd = new ArrayList();
370 public ArrayList PathsToSearchForLibraries = new ArrayList();
371 public ArrayList DebugListOfArguments = new ArrayList ();
372 public ArrayList SourceFilesToCompile = new ArrayList();
374 public bool ReferencePackage(string packageName)
376 if (packageName == ""){
381 ProcessStartInfo pi = new ProcessStartInfo ();
382 pi.FileName = "pkg-config";
383 pi.RedirectStandardOutput = true;
384 pi.UseShellExecute = false;
385 pi.Arguments = "--libs " + packageName;
388 p = Process.Start (pi);
389 } catch (Exception e) {
390 ReportError (0, "Couldn't run pkg-config: " + e.Message);
394 if (p.StandardOutput == null){
395 ReportError (0, "Specified package did not return any information");
397 string pkgout = p.StandardOutput.ReadToEnd ();
399 if (p.ExitCode != 0) {
400 ReportError (0, "Error running pkg-config. Check the above output.");
405 if (pkgout != null) {
406 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
407 Split (new Char [] { ' ', '\t'});
408 foreach(string arg in xargs) {
409 string[] zargs = arg.Split(':', '=');
411 if (zargs.Length > 1)
412 AddedReference = zargs[1];
414 AddedReference = arg;
415 } catch (Exception e) {
416 ReportError (0, "Something wrong with argument (" + arg + ") in 'pkg-config --libs' output: " + e.Message);
425 private bool printTimeStamps = false;
427 // Last time we took the time
430 public void StartTime (string msg)
432 if (!printTimeStamps)
435 last_time = DateTime.Now;
437 Console.WriteLine("[*] {0}", msg);
440 public void ShowTime (string msg)
442 if (!printTimeStamps)
445 DateTime now = DateTime.Now;
446 TimeSpan span = now - last_time;
450 "[{0:00}:{1:000}] {2}",
451 (int) span.TotalSeconds, span.Milliseconds, msg);
454 [Option("Displays time stamps of various compiler events", "timestamp", SecondLevelHelp = true)]
455 public virtual bool PrintTimeStamps {
458 printTimeStamps = true;
459 last_time = DateTime.Now;
460 DebugListOfArguments.Add("timestamp");
464 public bool BeQuiet { get { return DontShowBanner || SuccintErrorDisplay; } }
466 private void LoadAssembly (AssemblyAdder adder, string assemblyName, ref int errors, bool soft)
469 string total_log = "";
472 char[] path_chars = { '/', '\\' };
474 if (assemblyName.IndexOfAny (path_chars) != -1)
475 a = Assembly.LoadFrom(assemblyName);
477 string ass = assemblyName;
478 if (ass.EndsWith (".dll"))
479 ass = assemblyName.Substring (0, assemblyName.Length - 4);
480 a = Assembly.Load (ass);
485 catch (FileNotFoundException) {
486 if (PathsToSearchForLibraries != null) {
487 foreach (string dir in PathsToSearchForLibraries) {
488 string full_path = Path.Combine(dir, assemblyName + ".dll");
491 a = Assembly.LoadFrom (full_path);
495 catch (FileNotFoundException ff) {
496 total_log += ff.FusionLog;
504 ReportError (6, "Can not find assembly '" + assemblyName + "'\nLog: " + total_log);
506 catch (BadImageFormatException f) {
507 ReportError (6, "Bad file format while loading assembly\nLog: " + f.FusionLog);
508 } catch (FileLoadException f){
509 ReportError (6, "File Load Exception: " + assemblyName + "\nLog: " + f.FusionLog);
510 } catch (ArgumentNullException){
511 ReportError (6, "Argument Null exception");
517 public virtual string [] AssembliesToReferenceSoftly {
519 // For now the "default config" is hardcoded we can move this outside later
520 return new string [] { "System", "System.Data", "System.Xml" };
525 /// Loads all assemblies referenced on the command line
527 public bool LoadReferencedAssemblies (AssemblyAdder adder)
529 StartTime("Loading referenced assemblies");
534 // Load Core Library for default compilation
535 if (!NoStandardLibraries)
536 LoadAssembly(adder, "mscorlib", ref errors, false);
538 foreach (string r in AssembliesToReference)
539 LoadAssembly(adder, r, ref errors, false);
542 foreach (string r in AssembliesToReferenceSoftly)
543 if (!(AssembliesToReference.Contains(r) || AssembliesToReference.Contains (r + ".dll")))
544 LoadAssembly(adder, r, ref soft_errors, true);
546 ShowTime("References loaded");
550 private void LoadModule (MethodInfo adder_method, AssemblyBuilder assemblyBuilder, ModuleAdder adder, string module, ref int errors)
552 System.Reflection.Module m;
553 string total_log = "";
557 m = (System.Reflection.Module)adder_method.Invoke (assemblyBuilder, new object [] { module });
559 catch (TargetInvocationException ex) {
560 throw ex.InnerException;
564 catch (FileNotFoundException) {
565 foreach (string dir in PathsToSearchForLibraries) {
566 string full_path = Path.Combine (dir, module);
567 if (!module.EndsWith (".netmodule"))
568 full_path += ".netmodule";
572 m = (System.Reflection.Module) adder_method.Invoke (assemblyBuilder, new object [] { full_path });
574 catch (TargetInvocationException ex) {
575 throw ex.InnerException;
580 catch (FileNotFoundException ff) {
581 total_log += ff.FusionLog;
585 ReportError (6, "Cannot find module `" + module + "'" );
586 Console.WriteLine ("Log: \n" + total_log);
588 catch (BadImageFormatException f) {
589 ReportError (6, "Cannot load module (bad file format)" + f.FusionLog);
591 catch (FileLoadException f) {
592 ReportError (6, "Cannot load module " + f.FusionLog);
594 catch (ArgumentNullException) {
595 ReportError (6, "Cannot load module (null argument)");
600 public void UnsupportedFeatureOnthisRuntime(string feature)
602 ReportError (0, string.Format("Cannot use {0} on this runtime: Try the Mono runtime instead.", feature));
603 Environment.Exit (1);
606 public bool LoadAddedNetModules(AssemblyBuilder assemblyBuilder, ModuleAdder adder)
610 if (NetModulesToAdd.Count > 0) {
611 StartTime("Loading added netmodules");
613 MethodInfo adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic);
614 if (adder_method == null)
615 UnsupportedFeatureOnthisRuntime("/addmodule");
617 foreach (string module in NetModulesToAdd)
618 LoadModule (adder_method, assemblyBuilder, adder, module, ref errors);
626 public void AdjustCodegenWhenTargetIsNetModule(AssemblyBuilder assemblyBuilder)
628 if (TargetFileType == TargetType.Module) {
629 StartTime("Adjusting AssemblyBuilder for NetModule target");
630 PropertyInfo module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
631 if (module_only == null)
632 UnsupportedFeatureOnthisRuntime("/target:module");
634 MethodInfo set_method = module_only.GetSetMethod (true);
635 set_method.Invoke (assemblyBuilder, BindingFlags.Default, null, new object[]{true}, null);
642 // Given a path specification, splits the path from the file/pattern
644 void SplitPathAndPattern (string spec, out string path, out string pattern)
646 int p = spec.LastIndexOf ("/");
649 // Windows does not like /file.cs, switch that to:
654 pattern = spec.Substring (1);
656 path = spec.Substring (0, p);
657 pattern = spec.Substring (p + 1);
662 p = spec.LastIndexOf ("\\");
664 path = spec.Substring (0, p);
665 pattern = spec.Substring (p + 1);
673 bool AddFiles (string spec, bool recurse)
675 string path, pattern;
677 SplitPathAndPattern(spec, out path, out pattern);
678 if (pattern.IndexOf("*") == -1) {
679 DefaultArgumentProcessor(spec);
683 string [] files = null;
685 files = Directory.GetFiles(path, pattern);
686 } catch (System.IO.DirectoryNotFoundException) {
687 ReportError (2001, "Source file '" + spec + "' could not be found");
689 } catch (System.IO.IOException){
690 ReportError (2001, "Source file '" + spec + "' could not be found");
693 foreach (string f in files)
694 DefaultArgumentProcessor (f);
699 string [] dirs = null;
702 dirs = Directory.GetDirectories(path);
706 foreach (string d in dirs) {
708 // Don't include path in this string, as each
709 // directory entry already does
710 AddFiles (d + "/" + pattern, true);
716 public void EmbedResources(AssemblyBuilder builder)
718 if (EmbeddedResources != null)
719 foreach (string file in EmbeddedResources)
720 builder.AddResourceFile (file, file); // TODO: deal with resource IDs
723 public virtual bool NothingToCompile {
725 if (SourceFilesToCompile.Count == 0) {
738 public class CommonCompilerOptions2 : CommonCompilerOptions
740 [Option("Specify target CPU platform {ID}. ID can be x86, Itanium, x64 (AMD 64bit) or anycpu (the default).", "platform", SecondLevelHelp = true)]
741 public string TargetPlatform;
743 [Option("What {action} (prompt | send | none) should be done when an internal compiler error occurs.\tThe default is none what just prints the error data in the compiler output", "errorreport", SecondLevelHelp = true)]
744 public InternalCompilerErrorReportAction HowToReportErrors = InternalCompilerErrorReportAction.none;
746 [Option("Filealign internal blocks to the {blocksize} in bytes. Valid values are 512, 1024, 2048, 4096, and 8192.", "filealign", SecondLevelHelp = true)]
747 public int FileAlignBlockSize = 0; // 0 means use appropriate (not fixed) default
749 [Option("Generate documentation from xml commments.", "doc", SecondLevelHelp = true, VBCStyleBoolean = true)]
750 public bool GenerateXmlDocumentation = false;
752 [Option("Generate documentation from xml commments to an specific {file}.", "docto", SecondLevelHelp = true)]
753 public string GenerateXmlDocumentationToFileName = null;