retag
[mcs.git] / tools / al / Al.cs
blob38f4331d2b02892ad1d3e704e769d4bde8cc2595
1 //
2 // Mono.AssemblyLinker.AssemblyLinker
3 //
4 // Author(s):
5 // Zoltan Varga (vargaz@freemail.hu)
6 //
7 // (C) Ximian, Inc. http://www.ximian.com
8 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
9 //
11 using System;
12 using System.Globalization;
13 using System.IO;
14 using System.Collections;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Security.Cryptography;
18 using System.Text;
19 using System.Configuration.Assemblies;
21 using Mono.Security.Cryptography;
23 namespace Mono.AssemblyLinker
25 class ModuleInfo {
26 public string fileName;
27 public string target;
30 class ResourceInfo {
31 public string name;
32 public string fileName;
33 public string target;
34 public bool isEmbedded;
35 public bool isPrivate;
38 enum Target {
39 Dll,
40 Exe,
41 Win
44 enum DelaySign {
45 NotSet,
46 Yes,
50 public class AssemblyLinker {
52 ArrayList inputFiles = new ArrayList ();
53 ArrayList resources = new ArrayList ();
54 ArrayList cattrs = new ArrayList ();
55 bool fullPaths;
56 string outFile;
57 string entryPoint;
58 string win32IconFile;
59 string win32ResFile;
60 string templateFile;
61 bool isTemplateFile = false;
62 Target target = Target.Dll;
63 DelaySign delaysign = DelaySign.NotSet;
64 string keyfile;
65 string keyname;
66 string culture;
68 public static int Main (String[] args) {
69 return new AssemblyLinker ().DynMain (args);
72 private int DynMain (String[] args) {
73 ParseArgs (args);
75 DoIt ();
77 return 0;
80 private void ParseArgs (string[] args)
82 ArrayList flat_args = new ArrayList ();
84 // Process response files
85 Hashtable response_files = new Hashtable ();
86 foreach (string str in args) {
87 if (str [0] != '@') {
88 flat_args.Add (str);
89 continue;
92 if (str.Length == 1)
93 ReportMissingFileSpec ("@");
95 string resfile_name = Path.GetFullPath (str.Substring (1));
96 if (response_files.ContainsKey (resfile_name))
97 Report (1006, "Response file '" + resfile_name + "' was already included");
98 response_files [resfile_name] = resfile_name;
99 LoadArgs (resfile_name, flat_args);
102 if (flat_args.Count == 0)
103 Usage ();
105 foreach (string str in flat_args) {
106 if ((str [0] != '-') && (str [0] != '/')) {
107 inputFiles.Add (GetModuleInfo (str));
108 continue;
111 if (!ParseOption(str)) {
112 if (RunningOnUnix) {
113 // cope with absolute filenames for modules on unix, as
114 // they also match the option pattern
116 // `/home/test.cs' is considered as a module, however
117 // '/test.cs' is considered as error
118 if (str.Length > 2 && str.IndexOf ('/', 2) != -1) {
119 inputFiles.Add (GetModuleInfo (str));
120 continue;
124 Report (1013, String.Format ("Unrecognized command line option: '{0}'", str));
125 break;
129 if ((inputFiles.Count == 0) && (resources.Count == 0))
130 Report (1016, "No valid input files were specified");
132 if (outFile == null)
133 Report (1017, "No target filename was specified");
135 if (target == Target.Dll && (entryPoint != null))
136 Report (1035, "Libraries cannot have an entry point");
138 if (target == Target.Exe && (entryPoint == null))
139 Report (1036, "Entry point required for executable applications");
142 private bool ParseOption (string str)
144 string arg;
145 string opt = GetCommand (str, out arg);
147 switch (opt) {
148 case "help":
149 case "?":
150 Usage ();
151 return true;
153 case "embed": {
154 if (arg == null)
155 ReportMissingFileSpec (opt);
156 ResourceInfo res = new ResourceInfo ();
157 res.isEmbedded = true;
158 String [] parts = arg.Split (',');
159 res.fileName = parts [0];
160 if (parts.Length > 1)
161 res.name = parts [1];
162 if (parts.Length > 2) {
163 switch (parts [2]) {
164 case "public":
165 break;
166 case "private":
167 res.isPrivate = true;
168 break;
169 default:
170 ReportInvalidArgument (opt, parts [2]);
171 break;
174 resources.Add (res);
175 return true;
178 case "link": {
179 if (arg == null)
180 ReportMissingFileSpec (opt);
181 ResourceInfo res = new ResourceInfo ();
182 String [] parts = arg.Split (',');
183 res.fileName = parts [0];
184 if (parts.Length > 1)
185 res.name = parts [1];
186 if (parts.Length > 2)
187 res.target = parts [2];
188 if (parts.Length > 3) {
189 switch (parts [3]) {
190 case "public":
191 break;
192 case "private":
193 res.isPrivate = true;
194 break;
195 default:
196 ReportInvalidArgument (opt, parts [3]);
197 break;
200 resources.Add (res);
201 return true;
204 case "algid":
205 if (arg == null)
206 ReportMissingArgument (opt);
207 try {
208 string realArg = arg;
209 if (realArg.StartsWith ("0x"))
210 realArg = realArg.Substring (2);
211 uint val = Convert.ToUInt32 (realArg, 16);
212 AddCattr (typeof (AssemblyAlgorithmIdAttribute), typeof (uint), val);
213 } catch (Exception) {
214 ReportInvalidArgument (opt, arg);
216 return true;
218 case "base":
219 ReportNotImplemented (opt);
220 return true;
222 case "baseaddress":
223 ReportNotImplemented (opt);
224 return true;
226 case "bugreport":
227 ReportNotImplemented (opt);
228 return true;
230 case "comp":
231 case "company":
232 if (arg == null)
233 ReportMissingText (opt);
234 AddCattr (typeof (AssemblyCompanyAttribute), arg);
235 return true;
237 case "config":
238 case "configuration":
239 if (arg == null)
240 ReportMissingText (opt);
241 AddCattr (typeof (AssemblyConfigurationAttribute), arg);
242 return true;
244 case "copy":
245 case "copyright":
246 if (arg == null)
247 ReportMissingText (opt);
248 AddCattr (typeof (AssemblyCopyrightAttribute), arg);
249 return true;
251 case "c":
252 case "culture":
253 if (arg == null)
254 ReportMissingText (opt);
255 culture = arg;
256 return true;
258 case "delay":
259 case "delaysign":
260 case "delay+":
261 case "delaysign+":
262 delaysign = DelaySign.Yes;
263 return true;
265 case "delay-":
266 case "delaysign-":
267 delaysign = DelaySign.No;
268 return true;
270 case "descr":
271 case "description":
272 if (arg == null)
273 ReportMissingText (opt);
274 AddCattr (typeof (AssemblyDescriptionAttribute), arg);
275 return true;
277 case "e":
278 case "evidence":
279 if (arg == null)
280 ReportMissingFileSpec (opt);
281 ResourceInfo res = new ResourceInfo ();
282 res.name = "Security.Evidence";
283 res.fileName = arg;
284 res.isEmbedded = true;
285 res.isPrivate = true;
286 resources.Add (res);
287 return true;
289 case "fileversion":
290 if (arg == null)
291 ReportMissingText (opt);
293 AddCattr (typeof (AssemblyFileVersionAttribute), arg);
294 return true;
296 case "flags":
297 if (arg == null)
298 ReportMissingArgument (opt);
299 try {
300 string realArg = arg;
301 if (realArg.StartsWith ("0x"))
302 realArg = realArg.Substring (2);
303 uint val = Convert.ToUInt32 (realArg, 16);
304 AddCattr (typeof (AssemblyFlagsAttribute), typeof (uint), val);
305 } catch (Exception) {
306 ReportInvalidArgument (opt, arg);
308 return true;
310 case "fullpaths":
311 fullPaths = true;
312 return true;
314 case "keyf":
315 case "keyfile":
316 if (arg == null)
317 ReportMissingText (opt);
318 keyfile = arg;
319 return true;
321 case "keyn":
322 case "keyname":
323 if (arg == null)
324 ReportMissingText (opt);
325 keyname = arg;
326 return true;
328 case "main":
329 if (arg == null)
330 ReportMissingText (opt);
331 entryPoint = arg;
332 return true;
334 case "nologo":
335 return true;
337 case "out":
338 if (arg == null)
339 ReportMissingFileSpec (opt);
340 outFile = arg;
341 return true;
343 case "prod":
344 case "product":
345 if (arg == null)
346 ReportMissingText (opt);
347 AddCattr (typeof (AssemblyProductAttribute), arg);
348 return true;
350 case "productv":
351 case "productversion":
352 if (arg == null)
353 ReportMissingText (opt);
354 AddCattr (typeof (AssemblyInformationalVersionAttribute), arg);
355 return true;
357 case "t":
358 case "target":
359 if (arg == null)
360 ReportMissingText (opt);
361 switch (arg) {
362 case "lib":
363 case "library":
364 target = Target.Dll;
365 break;
366 case "exe":
367 target = Target.Exe;
368 break;
369 case "win":
370 case "winexe":
371 Report (0, "target:win is not implemented");
372 break;
373 default:
374 ReportInvalidArgument (opt, arg);
375 break;
377 return true;
379 case "template":
380 if (arg == null)
381 ReportMissingFileSpec (opt);
382 isTemplateFile = true;
383 templateFile = Path.Combine (Directory.GetCurrentDirectory (), arg);
384 return true;
386 case "title":
387 if (arg == null)
388 ReportMissingText (opt);
389 AddCattr (typeof (AssemblyTitleAttribute), arg);
390 return true;
392 case "trade":
393 case "trademark":
394 if (arg == null)
395 ReportMissingText (opt);
396 AddCattr (typeof (AssemblyTrademarkAttribute), arg);
397 return true;
399 case "v":
400 case "version":
401 // This option conflicts with the standard UNIX meaning
402 if (arg == null) {
403 Version ();
404 break;
406 AddCattr (typeof (AssemblyVersionAttribute), arg);
407 return true;
409 case "win32icon":
410 if (arg == null)
411 ReportMissingFileSpec (opt);
412 win32IconFile = arg;
413 return true;
415 case "win32res":
416 if (arg == null)
417 ReportMissingFileSpec (opt);
418 win32ResFile = arg;
419 return true;
421 return false;
424 private bool RunningOnUnix {
425 get {
426 // check for Unix platforms - see FAQ for more details
427 // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
428 int platform = (int) Environment.OSVersion.Platform;
429 return ((platform == 4) || (platform == 128) || (platform == 6));
433 private ModuleInfo GetModuleInfo (string str)
435 string [] parts = str.Split (',');
436 ModuleInfo mod = new ModuleInfo ();
437 mod.fileName = parts [0];
438 if (parts.Length > 1)
439 mod.target = parts [1];
440 return mod;
443 private string GetCommand (string str, out string command_arg) {
444 if ((str [0] == '-') && (str.Length > 1) && (str [1] == '-'))
445 str = str.Substring (1);
447 int end_index = str.IndexOfAny (new char[] {':', '='}, 1);
448 string command = str.Substring (1,
449 end_index == -1 ? str.Length - 1 : end_index - 1);
451 if (end_index != -1) {
452 command_arg = str.Substring (end_index+1);
453 if (command_arg == String.Empty)
454 command_arg = null;
455 } else {
456 command_arg = null;
459 return command.ToLower ();
462 private void AddCattr (Type attrType, Type arg, object value) {
463 cattrs.Add (new CustomAttributeBuilder (attrType.GetConstructor (new Type [] { arg }), new object [] { value }));
466 private void AddCattr (Type attrType, object value) {
467 AddCattr (attrType, typeof (string), value);
470 private void PrintVersion () {
471 Console.WriteLine ("Mono Assembly Linker (al.exe) version " + Consts.MonoVersion);
474 private void Version () {
475 PrintVersion ();
476 Environment.Exit (0);
479 private void Usage () {
480 PrintVersion ();
482 foreach (string s in usage)
483 Console.WriteLine (s);
484 Environment.Exit (0);
487 private void Report (int errorNum, string msg) {
488 Console.WriteLine (String.Format ("ALINK: error A{0:0000}: {1}", errorNum, msg));
489 Environment.Exit (1);
492 private void ReportWarning (int errorNum, string msg) {
493 Console.WriteLine (String.Format ("ALINK: warning A{0:0000}: {1}", errorNum, msg));
496 private void ReportInvalidArgument (string option, string value) {
497 Report (1012, String.Format ("'{0}' is not a valid setting for option '{1}'", value, option));
500 private void ReportMissingArgument (string option) {
501 Report (1003, String.Format ("Compiler option '{0}' must be followed by an argument", option));
504 private void ReportNotImplemented (string option) {
505 Report (0, String.Format ("Compiler option '{0}' is not implemented", option));
508 private void ReportMissingFileSpec (string option) {
509 Report (1008, String.Format ("Missing file specification for '{0}' command-line option", option));
512 private void ReportMissingText (string option) {
513 Report (1010, String.Format ("Missing ':<text>' for '{0}' option", option));
516 // copied from /mcs/mcs/codegen.cs
517 private void SetPublicKey (AssemblyName an, byte[] strongNameBlob)
519 // check for possible ECMA key
520 if (strongNameBlob.Length == 16) {
521 // will be rejected if not "the" ECMA key
522 an.SetPublicKey (strongNameBlob);
523 } else {
524 // take it, with or without, a private key
525 RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob);
526 // and make sure we only feed the public part to Sys.Ref
527 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
529 // AssemblyName.SetPublicKey requires an additional header
530 byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 };
532 byte[] encodedPublicKey = new byte [12 + publickey.Length];
533 Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12);
534 Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length);
535 an.SetPublicKey (encodedPublicKey);
539 private void SetKeyPair (AssemblyName aname)
541 #if ONLY_1_1
542 switch (delaysign) {
543 case DelaySign.Yes:
544 AddCattr (typeof (AssemblyDelaySignAttribute),
545 typeof (bool), true);
546 break;
547 case DelaySign.No:
548 AddCattr (typeof (AssemblyDelaySignAttribute),
549 typeof (bool), false);
550 break;
552 #endif
554 if (keyfile != null) {
555 if (!File.Exists (keyfile)) {
556 Report (1044, String.Format ("Couldn't open '{0}' key file.", keyfile));
559 #if ONLY_1_1
560 AddCattr (typeof (AssemblyKeyFileAttribute),
561 keyfile);
562 #endif
564 using (FileStream fs = File.OpenRead (keyfile)) {
565 byte[] data = new byte [fs.Length];
566 try {
567 fs.Read (data, 0, data.Length);
569 if (delaysign == DelaySign.Yes) {
570 SetPublicKey (aname, data);
571 } else {
572 CryptoConvert.FromCapiPrivateKeyBlob (data);
573 aname.KeyPair = new StrongNameKeyPair (data);
576 catch (CryptographicException) {
577 if (delaysign != DelaySign.Yes) {
578 if (data.Length == 16) {
579 // error # is different for ECMA key
580 Report (1019, "Could not strongname the assembly. " +
581 "ECMA key can only be used to delay-sign assemblies");
582 } else {
583 Report (1028, String.Format ("Key file {0}' is missing it's private key " +
584 "or couldn't be decoded.", keyfile));
586 } else {
587 Report (1044, String.Format ("Couldn't decode '{0}' key file.", keyfile));
590 fs.Close ();
592 } else if (keyname != null) {
593 #if ONLY_1_1
594 AddCattr (typeof (AssemblyKeyNameAttribute),
595 keyname);
596 #endif
597 // delay-sign doesn't apply to key containers
598 aname.KeyPair = new StrongNameKeyPair (keyname);
602 private void DoIt () {
603 AssemblyName aname = new AssemblyName ();
604 aname.Name = Path.GetFileNameWithoutExtension (outFile);
605 if (culture != null)
606 aname.CultureInfo = new CultureInfo (culture);
608 string fileName = Path.GetFileName (outFile);
610 AssemblyBuilder ab;
613 * Emit Manifest
614 * */
616 if (isTemplateFile) {
617 // LAMESPEC: according to MSDN, the template assembly must have a
618 // strong name but this is not enforced
619 Assembly assembly = Assembly.LoadFrom (templateFile);
621 // inherit signing related settings from template, but do not
622 // override command-line options
623 object [] attrs = assembly.GetCustomAttributes (true);
624 foreach (object o in attrs) {
625 if (o is AssemblyKeyFileAttribute) {
626 if (keyfile != null)
627 // ignore if specified on command line
628 continue;
629 AssemblyKeyFileAttribute keyFileAttr = (AssemblyKeyFileAttribute) o;
630 // ignore null or zero-length keyfile
631 if (keyFileAttr.KeyFile == null || keyFileAttr.KeyFile.Length == 0)
632 continue;
633 keyfile = Path.Combine (Path.GetDirectoryName(templateFile),
634 keyFileAttr.KeyFile);
635 } else if (o is AssemblyDelaySignAttribute) {
636 if (delaysign != DelaySign.NotSet)
637 // ignore if specified on command line
638 continue;
639 AssemblyDelaySignAttribute delaySignAttr = (AssemblyDelaySignAttribute) o;
640 delaysign = delaySignAttr.DelaySign ? DelaySign.Yes :
641 DelaySign.No;
642 } else if (o is AssemblyKeyNameAttribute) {
643 if (keyname != null)
644 // ignore if specified on command line
645 continue;
646 AssemblyKeyNameAttribute keynameAttr = (AssemblyKeyNameAttribute) o;
647 // ignore null or zero-length keyname
648 if (keynameAttr.KeyName == null || keynameAttr.KeyName.Length == 0)
649 continue;
650 keyname = keynameAttr.KeyName;
653 aname.Version = assembly.GetName().Version;
654 aname.HashAlgorithm = assembly.GetName().HashAlgorithm;
657 SetKeyPair (aname);
659 if (fileName != outFile)
660 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save, Path.GetDirectoryName (outFile));
661 else
662 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save);
664 foreach (CustomAttributeBuilder cb in cattrs)
665 ab.SetCustomAttribute (cb);
668 * Emit modules
671 foreach (ModuleInfo mod in inputFiles) {
672 MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
673 if (mi == null)
674 Report (0, "Cannot add modules on this runtime: try the Mono runtime instead.");
676 if (mod.target != null) {
677 File.Copy (mod.fileName, mod.target, true);
678 mod.fileName = mod.target;
681 bool isAssembly = false;
682 try {
683 AssemblyName.GetAssemblyName (mod.fileName);
684 isAssembly = true;
686 catch (Exception) {
689 if (isAssembly)
690 ReportWarning (1020, "Ignoring included assembly '" + mod.fileName + "'");
691 else
692 mi.Invoke (ab, new object [] { mod.fileName });
696 * Set entry point
699 if (entryPoint != null) {
700 string mainClass = entryPoint.Substring (0, entryPoint.LastIndexOf ('.'));
701 string mainMethod = entryPoint.Substring (entryPoint.LastIndexOf ('.') + 1);
703 MethodInfo mainMethodInfo = null;
705 try {
706 Type mainType = ab.GetType (mainClass);
707 if (mainType != null)
708 mainMethodInfo = mainType.GetMethod (mainMethod);
710 catch (Exception ex) {
711 Console.WriteLine (ex);
713 if (mainMethodInfo != null)
714 ab.SetEntryPoint (mainMethodInfo);
715 else
716 Report (1037, "Unable to find the entry point method '" + entryPoint + "'");
720 * Emit resources
723 ab.DefineVersionInfoResource ();
725 if (win32IconFile != null) {
726 try {
727 MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
728 if (mi == null)
729 Report (0, "Cannot embed win32 icons on this runtime: try the Mono runtime instead.");
730 mi.Invoke (ab, new object [] { win32IconFile });
732 catch (Exception ex) {
733 Report (1031, "Error reading icon '" + win32IconFile + "' --" + ex);
737 if (win32ResFile != null) {
738 try {
739 ab.DefineUnmanagedResource (win32ResFile);
741 catch (Exception ex) {
742 Report (1019, "Metadata failure creating assembly -- " + ex);
746 foreach (ResourceInfo res in resources) {
747 if (res.name == null)
748 res.name = Path.GetFileName (res.fileName);
750 foreach (ResourceInfo res2 in resources)
751 if ((res != res2) && (res.name == res2.name))
752 Report (1046, String.Format ("Resource identifier '{0}' has already been used in this assembly", res.name));
754 if (res.isEmbedded) {
755 MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
756 null, CallingConventions.Any, new Type [] { typeof (string), typeof (string) }, null);
757 if (mi == null)
758 Report (0, "Cannot embed resources on this runtime: try the Mono runtime instead.");
759 mi.Invoke (ab, new object [] { res.name, res.fileName });
761 else {
762 if (res.target != null) {
763 File.Copy (res.fileName, res.target, true);
764 res.fileName = res.target;
767 ab.AddResourceFile (res.name, res.fileName,
768 res.isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public);
772 try {
773 ab.Save (fileName);
775 catch (Exception ex) {
776 Report (1019, "Metadata failure creating assembly -- " + ex);
780 private void LoadArgs (string file, ArrayList args) {
781 StreamReader f = null;
782 string line;
783 try {
784 f = new StreamReader (file);
786 StringBuilder sb = new StringBuilder ();
788 while ((line = f.ReadLine ()) != null){
789 int t = line.Length;
791 for (int i = 0; i < t; i++){
792 char c = line [i];
794 if (c == '"' || c == '\''){
795 char end = c;
797 for (i++; i < t; i++){
798 c = line [i];
800 if (c == end)
801 break;
802 sb.Append (c);
804 } else if (c == ' '){
805 if (sb.Length > 0){
806 args.Add (sb.ToString ());
807 sb.Length = 0;
809 } else
810 sb.Append (c);
812 if (sb.Length > 0){
813 args.Add (sb.ToString ());
814 sb.Length = 0;
817 } catch (Exception ex) {
818 Report (1007, "Error opening response file '" + file + "' -- '" + ex.Message + "'");
819 } finally {
820 if (f != null)
821 f.Close ();
825 string[] usage = {
826 "Usage: al [options] [sources]",
827 "Options: ('/out' must be specified)",
829 " /? or /help Display this usage message",
830 " @<filename> Read response file for more options",
831 " /algid:<id> Algorithm used to hash files (in hexadecimal)",
832 " /base[address]:<addr> Base address for the library",
833 " /bugreport:<filename> Create a 'Bug Report' file",
834 " /comp[any]:<text> Company name",
835 " /config[uration]:<text> Configuration string",
836 " /copy[right]:<text> Copyright message",
837 " /c[ulture]:<text> Supported culture",
838 " /delay[sign][+|-] Delay sign this assembly",
839 " /descr[iption]:<text> Description",
840 " /e[vidence]:<filename> Security evidence file to embed",
841 " /fileversion:<version> Optional Win32 version (overrides assembly version)",
842 " /flags:<flags> Assembly flags (in hexadecimal)",
843 " /fullpaths Display files using fully-qualified filenames",
844 " /keyf[ile]:<filename> File containing key to sign the assembly",
845 " /keyn[ame]:<text> Key container name of key to sign assembly",
846 " /main:<method> Specifies the method name of the entry point",
847 " /nologo Suppress the startup banner and copyright message",
848 " /out:<filename> Output file name for the assembly manifest",
849 " /prod[uct]:<text> Product name",
850 " /productv[ersion]:<text> Product version",
851 " /t[arget]:lib[rary] Create a library",
852 " /t[arget]:exe Create a console executable",
853 " /t[arget]:win[exe] Create a Windows executable",
854 " /template:<filename> Specifies an assembly to get default options from",
855 " /title:<text> Title",
856 " /trade[mark]:<text> Trademark message",
857 " /v[ersion]:<version> Version (use * to auto-generate remaining numbers)",
858 " /win32icon:<filename> Use this icon for the output",
859 " /win32res:<filename> Specifies the Win32 resource file",
861 "Sources: (at least one source input is required)",
862 " <filename>[,<targetfile>] add file to assembly",
863 " /embed[resource]:<filename>[,<name>[,Private]]",
864 " embed the file as a resource in the assembly",
865 " /link[resource]:<filename>[,<name>[,<targetfile>[,Private]]]",
866 " link the file as a resource to the assembly",