[sgen] Evacuate from emptier blocks to fuller ones
[mono.git] / mcs / tools / al / Al.cs
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 //
10
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;
20
21 using Mono.Security.Cryptography;
22
23 namespace Mono.AssemblyLinker
24 {
25         class ModuleInfo {
26                 public string fileName;
27                 public string target;
28         }
29
30         class ResourceInfo {
31                 public string name;
32                 public string fileName;
33                 public string target;
34                 public bool isEmbedded;
35                 public bool isPrivate;
36         }
37
38         enum Target {
39                 Dll,
40                 Exe,
41                 Win
42         }
43
44         enum DelaySign {
45                 NotSet,
46                 Yes,
47                 No
48         }
49
50         public class AssemblyLinker {
51
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;
67
68                 public static int Main (String[] args) {
69                         return new AssemblyLinker ().DynMain (args);
70                 }
71
72                 private int DynMain (String[] args) {
73                         ParseArgs (args);
74
75                         DoIt ();
76
77                         return 0;
78                 }
79
80                 private void ParseArgs (string[] args) 
81                 {
82                         ArrayList flat_args = new ArrayList ();
83
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;
90                                 }
91
92                                 if (str.Length == 1)
93                                         ReportMissingFileSpec ("@");
94
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);
100                         }
101
102                         if (flat_args.Count == 0)
103                                 Usage ();
104
105                         foreach (string str in flat_args) {
106                                 if ((str [0] != '-') && (str [0] != '/')) {
107                                         inputFiles.Add (GetModuleInfo (str));
108                                         continue;
109                                 }
110
111                                 if (!ParseOption(str)) {
112                                         if (RunningOnUnix) {
113                                                 // cope with absolute filenames for modules on unix, as
114                                                 // they also match the option pattern
115                                                 //
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;
121                                                 }
122                                         }
123
124                                         Report (1013, String.Format ("Unrecognized command line option: '{0}'", str));
125                                         break;
126                                 }
127                         }
128
129                         if ((inputFiles.Count == 0) && (resources.Count == 0))
130                                 Report (1016, "No valid input files were specified");
131
132                         if (outFile == null)
133                                 Report (1017, "No target filename was specified");
134
135                         if (target == Target.Dll && (entryPoint != null))
136                                 Report (1035, "Libraries cannot have an entry point");
137
138                         if (target == Target.Exe && (entryPoint == null))
139                                 Report (1036, "Entry point required for executable applications");                                              
140                 }
141
142                 private bool ParseOption (string str)
143                 {
144                         string arg;
145                         string opt = GetCommand (str, out arg);
146
147                         switch (opt) {
148                         case "help":
149                         case "?":
150                                 Usage ();
151                                 return true;
152
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;
172                                                 }
173                                         }
174                                         resources.Add (res);
175                                         return true;
176                                 }
177
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;
198                                                 }
199                                         }
200                                         resources.Add (res);
201                                         return true;
202                                 }
203
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);
215                                 }
216                                 return true;
217
218                         case "base":
219                                 ReportNotImplemented (opt);
220                                 return true;
221
222                         case "baseaddress":
223                                 ReportNotImplemented (opt);
224                                 return true;
225
226                         case "bugreport":
227                                 ReportNotImplemented (opt);
228                                 return true;
229
230                         case "comp":
231                         case "company":
232                                 if (arg == null)
233                                         ReportMissingText (opt);
234                                 AddCattr (typeof (AssemblyCompanyAttribute), arg);
235                                 return true;
236
237                         case "config":
238                         case "configuration":
239                                 if (arg == null)
240                                         ReportMissingText (opt);
241                                 AddCattr (typeof (AssemblyConfigurationAttribute), arg);
242                                 return true;
243
244                         case "copy":
245                         case "copyright":
246                                 if (arg == null)
247                                         ReportMissingText (opt);
248                                 AddCattr (typeof (AssemblyCopyrightAttribute), arg);
249                                 return true;
250
251                         case "c":
252                         case "culture":
253                                 if (arg == null)
254                                         ReportMissingText (opt);
255                                 culture = arg;
256                                 return true;
257
258                         case "delay":
259                         case "delaysign":
260                         case "delay+":
261                         case "delaysign+":
262                                 delaysign = DelaySign.Yes;
263                                 return true;
264
265                         case "delay-":
266                         case "delaysign-":
267                                 delaysign = DelaySign.No;
268                                 return true;
269
270                         case "descr":
271                         case "description":
272                                 if (arg == null)
273                                         ReportMissingText (opt);
274                                 AddCattr (typeof (AssemblyDescriptionAttribute), arg);
275                                 return true;
276
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;
288                         }
289                         case "fileversion":
290                                 if (arg == null)
291                                         ReportMissingText (opt);
292
293                                 AddCattr (typeof (AssemblyFileVersionAttribute), arg);
294                                 return true;
295
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);
307                                 }
308                                 return true;
309
310                         case "fullpaths":
311                                 fullPaths = true;
312                                 return true;
313
314                         case "keyf":
315                         case "keyfile":
316                                 if (arg == null)
317                                         ReportMissingText (opt);
318                                 keyfile = arg;
319                                 return true;
320
321                         case "keyn":
322                         case "keyname":
323                                 if (arg == null)
324                                         ReportMissingText (opt);
325                                 keyname = arg;
326                                 return true;
327
328                         case "main":
329                                 if (arg == null)
330                                         ReportMissingText (opt);
331                                 entryPoint = arg;
332                                 return true;
333
334                         case "nologo":
335                                 return true;
336
337                         case "out":
338                                 if (arg == null)
339                                         ReportMissingFileSpec (opt);
340                                 outFile = arg;
341                                 return true;
342
343                         case "prod":
344                         case "product":
345                                 if (arg == null)
346                                         ReportMissingText (opt);
347                                 AddCattr (typeof (AssemblyProductAttribute), arg);
348                                 return true;
349
350                         case "productv":
351                         case "productversion":
352                                 if (arg == null)
353                                         ReportMissingText (opt);
354                                 AddCattr (typeof (AssemblyInformationalVersionAttribute), arg);
355                                 return true;
356
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;
376                                 }
377                                 return true;
378
379                         case "template":
380                                 if (arg == null)
381                                         ReportMissingFileSpec (opt);
382                                 isTemplateFile = true;
383                                 templateFile = Path.Combine (Directory.GetCurrentDirectory (), arg);
384                                 return true;
385
386                         case "title":
387                                 if (arg == null)
388                                         ReportMissingText (opt);
389                                 AddCattr (typeof (AssemblyTitleAttribute), arg);
390                                 return true;
391
392                         case "trade":
393                         case "trademark":
394                                 if (arg == null)
395                                         ReportMissingText (opt);
396                                 AddCattr (typeof (AssemblyTrademarkAttribute), arg);
397                                 return true;
398
399                         case "v":
400                         case "version":
401                                 // This option conflicts with the standard UNIX meaning
402                                 if (arg == null) {
403                                         Version ();
404                                         break;
405                                 }
406                                 AddCattr (typeof (AssemblyVersionAttribute), arg);
407                                 return true;
408
409                         case "win32icon":
410                                 if (arg == null)
411                                         ReportMissingFileSpec (opt);
412                                 win32IconFile = arg;
413                                 return true;
414
415                         case "win32res":
416                                 if (arg == null)
417                                         ReportMissingFileSpec (opt);
418                                 win32ResFile = arg;
419                                 return true;
420                         }
421                         return false;
422                 }
423
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));
430                         }
431                 }
432
433                 private ModuleInfo GetModuleInfo (string str)
434                 {
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;
441                 }
442
443                 private string GetCommand (string str, out string command_arg) {
444                         if ((str [0] == '-') && (str.Length > 1) && (str [1] == '-'))
445                                 str = str.Substring (1);
446
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);
450                         
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;
457                         }
458                                 
459                         return command.ToLower ();
460                 }
461
462                 private void AddCattr (Type attrType, Type arg, object value) {
463                         cattrs.Add (new CustomAttributeBuilder (attrType.GetConstructor (new Type [] { arg }), new object [] { value }));
464                 }
465
466                 private void AddCattr (Type attrType, object value) {
467                         AddCattr (attrType, typeof (string), value);
468                 }
469
470                 private void PrintVersion () {
471                         Console.WriteLine ("Mono Assembly Linker (al.exe) version " + Consts.MonoVersion);
472                 }
473
474                 private void Version () {
475                         PrintVersion ();
476                         Environment.Exit (0);
477                 }
478
479                 private void Usage () {
480                         PrintVersion ();
481                         
482                         foreach (string s in usage)
483                                 Console.WriteLine (s);
484                         Environment.Exit (0);
485                 }
486
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);
490                 }
491
492                 private void ReportWarning (int errorNum, string msg) {
493                         Console.WriteLine (String.Format ("ALINK: warning A{0:0000}: {1}", errorNum, msg));
494                 }
495
496                 private void ReportInvalidArgument (string option, string value) {
497                         Report (1012, String.Format ("'{0}' is not a valid setting for option '{1}'", value, option));
498                 }
499
500                 private void ReportMissingArgument (string option) {
501                         Report (1003, String.Format ("Compiler option '{0}' must be followed by an argument", option));
502                 }
503
504                 private void ReportNotImplemented (string option) {
505                         Report (0, String.Format ("Compiler option '{0}' is not implemented", option));
506                 }
507
508                 private void ReportMissingFileSpec (string option) {
509                         Report (1008, String.Format ("Missing file specification for '{0}' command-line option", option));
510                 }
511
512                 private void ReportMissingText (string option) {
513                         Report (1010, String.Format ("Missing ':<text>' for '{0}' option", option));
514                 }
515
516                 // copied from /mcs/mcs/codegen.cs
517                 private void SetPublicKey (AssemblyName an, byte[] strongNameBlob) 
518                 {
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);
528                                         
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 };
531
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);
536                         }
537                 }
538
539                 private void SetKeyPair (AssemblyName aname)
540                 {
541                         if (keyfile != null) {
542                                 if (!File.Exists (keyfile)) {
543                                         Report (1044, String.Format ("Couldn't open '{0}' key file.", keyfile));
544                                 }
545
546                                 using (FileStream fs = File.OpenRead (keyfile)) {
547                                         byte[] data = new byte [fs.Length];
548                                         try {
549                                                 fs.Read (data, 0, data.Length);
550
551                                                 if (delaysign == DelaySign.Yes) {
552                                                         SetPublicKey (aname, data);
553                                                 } else {
554                                                         CryptoConvert.FromCapiPrivateKeyBlob (data);
555                                                         aname.KeyPair = new StrongNameKeyPair (data);
556                                                 }
557                                         }
558                                         catch (CryptographicException) {
559                                                 if (delaysign != DelaySign.Yes) {
560                                                         if (data.Length == 16) {
561                                                                 // error # is different for ECMA key
562                                                                 Report (1019, "Could not strongname the assembly. " + 
563                                                                         "ECMA key can only be used to delay-sign assemblies");
564                                                         } else {
565                                                                 Report (1028, String.Format ("Key file {0}' is missing it's private key " +
566                                                                         "or couldn't be decoded.", keyfile));
567                                                         }
568                                                 } else {
569                                                         Report (1044, String.Format ("Couldn't decode '{0}' key file.", keyfile));
570                                                 }
571                                         }
572                                         fs.Close ();
573                                 }
574                         } else if (keyname != null) {
575                                 // delay-sign doesn't apply to key containers
576                                 aname.KeyPair = new StrongNameKeyPair (keyname);
577                         }
578                 }
579                 
580                 private void DoIt () {
581                         AssemblyName aname = new AssemblyName ();
582                         aname.Name = Path.GetFileNameWithoutExtension (outFile);
583                         if (culture != null)
584                                 aname.CultureInfo = new CultureInfo (culture);
585
586                         string fileName = Path.GetFileName (outFile);
587
588                         AssemblyBuilder ab;
589                         
590                         /*
591                          * Emit Manifest
592                          * */
593
594                         if (isTemplateFile) {
595                                 // LAMESPEC: according to MSDN, the template assembly must have a
596                                 // strong name but this is not enforced
597                                 Assembly assembly = Assembly.LoadFrom (templateFile);
598
599                                 // inherit signing related settings from template, but do not
600                                 // override command-line options
601                                 object [] attrs = assembly.GetCustomAttributes (true);
602                                 foreach (object o in attrs) {
603                                         if (o is AssemblyKeyFileAttribute) {
604                                                 if (keyfile != null)
605                                                         // ignore if specified on command line
606                                                         continue;
607                                                 AssemblyKeyFileAttribute keyFileAttr = (AssemblyKeyFileAttribute) o;
608                                                 // ignore null or zero-length keyfile
609                                                 if (keyFileAttr.KeyFile == null || keyFileAttr.KeyFile.Length == 0)
610                                                         continue;
611                                                 keyfile = Path.Combine (Path.GetDirectoryName(templateFile),
612                                                         keyFileAttr.KeyFile);
613                                         } else if (o is AssemblyDelaySignAttribute) {
614                                                 if (delaysign != DelaySign.NotSet)
615                                                         // ignore if specified on command line
616                                                         continue;
617                                                 AssemblyDelaySignAttribute delaySignAttr = (AssemblyDelaySignAttribute) o;
618                                                 delaysign = delaySignAttr.DelaySign ? DelaySign.Yes :
619                                                         DelaySign.No;
620                                         } else if (o is AssemblyKeyNameAttribute) {
621                                                 if (keyname != null)
622                                                         // ignore if specified on command line
623                                                         continue;
624                                                 AssemblyKeyNameAttribute keynameAttr = (AssemblyKeyNameAttribute) o;
625                                                 // ignore null or zero-length keyname
626                                                 if (keynameAttr.KeyName == null || keynameAttr.KeyName.Length == 0)
627                                                         continue;
628                                                 keyname = keynameAttr.KeyName;
629                                         }
630                                 }
631                                 aname.Version = assembly.GetName().Version;
632                                 aname.HashAlgorithm = assembly.GetName().HashAlgorithm;
633                         }
634
635                         SetKeyPair (aname);
636
637                         if (fileName != outFile)
638                                 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save, Path.GetDirectoryName (outFile));
639                         else
640                                 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save);
641
642                         foreach (CustomAttributeBuilder cb in cattrs)
643                                 ab.SetCustomAttribute (cb);
644
645                         /*
646                          * Emit modules
647                          */
648
649                         foreach (ModuleInfo mod in inputFiles) {
650                                 MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
651                                 if (mi == null)
652                                         Report (0, "Cannot add modules on this runtime: try the Mono runtime instead.");
653
654                                 if (mod.target != null) {
655                                         File.Copy (mod.fileName, mod.target, true);
656                                         mod.fileName = mod.target;
657                                 }
658
659                                 bool isAssembly = false;
660                                 try {
661                                         AssemblyName.GetAssemblyName (mod.fileName);
662                                         isAssembly = true;
663                                 }
664                                 catch (Exception) {
665                                 }
666
667                                 if (isAssembly)
668                                         ReportWarning (1020, "Ignoring included assembly '" + mod.fileName + "'");
669                                 else
670                                         mi.Invoke (ab, new object [] { mod.fileName });
671                         }
672
673                         /*
674                          * Set entry point
675                          */
676
677                         if (entryPoint != null) {
678                                 string mainClass = entryPoint.Substring (0, entryPoint.LastIndexOf ('.'));
679                                 string mainMethod = entryPoint.Substring (entryPoint.LastIndexOf ('.') + 1);
680
681                                 MethodInfo mainMethodInfo = null;
682
683                                 try {
684                                         Type mainType = ab.GetType (mainClass);
685                                         if (mainType != null)
686                                                 mainMethodInfo = mainType.GetMethod (mainMethod);
687                                 }
688                                 catch (Exception ex) {
689                                         Console.WriteLine (ex);
690                                 }
691                                 if (mainMethodInfo != null)
692                                         ab.SetEntryPoint (mainMethodInfo);
693                                 else
694                                         Report (1037, "Unable to find the entry point method '" + entryPoint + "'");
695                         }
696
697                         /*
698                          * Emit resources
699                          */
700
701                         ab.DefineVersionInfoResource ();
702
703                         if (win32IconFile != null) {
704                                 try {
705                                         MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
706                                         if (mi == null)
707                                                 Report (0, "Cannot embed win32 icons on this runtime: try the Mono runtime instead.");
708                                         mi.Invoke (ab, new object [] {  win32IconFile });
709                                 }
710                                 catch (Exception ex) {
711                                         Report (1031, "Error reading icon '" + win32IconFile + "' --" + ex);
712                                 }
713                         }
714
715                         if (win32ResFile != null) {
716                                 try {
717                                         ab.DefineUnmanagedResource (win32ResFile);
718                                 }
719                                 catch (Exception ex) {
720                                         Report (1019, "Metadata failure creating assembly -- " + ex);
721                                 }
722                         }
723
724                         foreach (ResourceInfo res in resources) {
725                                 if (res.name == null)
726                                         res.name = Path.GetFileName (res.fileName);
727
728                                 foreach (ResourceInfo res2 in resources)
729                                         if ((res != res2) && (res.name == res2.name))
730                                                 Report (1046, String.Format ("Resource identifier '{0}' has already been used in this assembly", res.name));
731
732                                 if (res.isEmbedded) {
733                                         MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
734                                                 null, CallingConventions.Any, new Type [] { typeof (string), typeof (string) }, null);
735                                         if (mi == null)
736                                                 Report (0, "Cannot embed resources on this runtime: try the Mono runtime instead.");
737                                         mi.Invoke (ab, new object [] { res.name, res.fileName });
738                                 }
739                                 else {
740                                         if (res.target != null) {
741                                                 File.Copy (res.fileName, res.target, true);
742                                                 res.fileName = res.target;
743                                         }
744                                         
745                                         // AddResourceFile must receive a file name and not a path.
746                                         // Drop directory and give warning if we have a path.
747                                         var resourceFileName = Path.GetFileName(res.fileName);
748                                         if (Path.GetDirectoryName (res.fileName) != null || Path.IsPathRooted(res.fileName)) {
749                                                 ReportWarning (99999, 
750                                                         String.Format ("Path '{0}' in the resource name is not supported. Using just file name '{1}'",
751                                                                 res.fileName,
752                                                                 resourceFileName));
753                                         }
754
755                                         ab.AddResourceFile (res.name, resourceFileName,
756                                                         res.isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public);
757                                 }
758                         }
759
760                         try {
761                                 ab.Save (fileName);
762                         }
763                         catch (Exception ex) {
764                                 Report (1019, "Metadata failure creating assembly -- " + ex);
765                         }
766                 }
767
768                 private void LoadArgs (string file, ArrayList args) {
769                         StreamReader f = null;
770                         string line;
771                         try {
772                                 f = new StreamReader (file);
773
774                                 StringBuilder sb = new StringBuilder ();
775                         
776                                 while ((line = f.ReadLine ()) != null){
777                                         int t = line.Length;
778
779                                         for (int i = 0; i < t; i++){
780                                                 char c = line [i];
781                                                 
782                                                 if (c == '"' || c == '\''){
783                                                         char end = c;
784                                                         
785                                                         for (i++; i < t; i++){
786                                                                 c = line [i];
787
788                                                                 if (c == end)
789                                                                         break;
790                                                                 sb.Append (c);
791                                                         }
792                                                 } else if (c == ' '){
793                                                         if (sb.Length > 0){
794                                                                 args.Add (sb.ToString ());
795                                                                 sb.Length = 0;
796                                                         }
797                                                 } else
798                                                         sb.Append (c);
799                                         }
800                                         if (sb.Length > 0){
801                                                 args.Add (sb.ToString ());
802                                                 sb.Length = 0;
803                                         }
804                                 }
805                         } catch (Exception ex) {
806                                 Report (1007, "Error opening response file '" + file + "' -- '" + ex.Message + "'");
807                         } finally {
808                                 if (f != null)
809                                         f.Close ();
810                         }
811                 }
812
813                 string[] usage = {
814                         "Usage: al [options] [sources]",
815                         "Options: ('/out' must be specified)",
816                         "",
817                         "  /? or /help               Display this usage message",
818                         "  @<filename>               Read response file for more options",
819                         "  /algid:<id>               Algorithm used to hash files (in hexadecimal)",
820                         "  /base[address]:<addr>     Base address for the library",
821                         "  /bugreport:<filename>     Create a 'Bug Report' file",
822                         "  /comp[any]:<text>         Company name",
823                         "  /config[uration]:<text>   Configuration string",
824                         "  /copy[right]:<text>       Copyright message",
825                         "  /c[ulture]:<text>         Supported culture",
826                         "  /delay[sign][+|-]         Delay sign this assembly",
827                         "  /descr[iption]:<text>     Description",
828                         "  /e[vidence]:<filename>    Security evidence file to embed",
829                         "  /fileversion:<version>    Optional Win32 version (overrides assembly version)",
830                         "  /flags:<flags>            Assembly flags  (in hexadecimal)",
831                         "  /fullpaths                Display files using fully-qualified filenames",
832                         "  /keyf[ile]:<filename>     File containing key to sign the assembly",
833                         "  /keyn[ame]:<text>         Key container name of key to sign assembly",
834                         "  /main:<method>            Specifies the method name of the entry point",
835                         "  /nologo                   Suppress the startup banner and copyright message",
836                         "  /out:<filename>           Output file name for the assembly manifest",
837                         "  /prod[uct]:<text>         Product name",
838                         "  /productv[ersion]:<text>  Product version",
839                         "  /t[arget]:lib[rary]       Create a library",
840                         "  /t[arget]:exe             Create a console executable",
841                         "  /t[arget]:win[exe]        Create a Windows executable",
842                         "  /template:<filename>      Specifies an assembly to get default options from",
843                         "  /title:<text>             Title",
844                         "  /trade[mark]:<text>       Trademark message",
845                         "  /v[ersion]:<version>      Version (use * to auto-generate remaining numbers)",
846                         "  /win32icon:<filename>     Use this icon for the output",
847                         "  /win32res:<filename>      Specifies the Win32 resource file",
848                         "",
849                         "Sources: (at least one source input is required)",
850                         "  <filename>[,<targetfile>] add file to assembly",
851                         "  /embed[resource]:<filename>[,<name>[,Private]]",
852                         "                            embed the file as a resource in the assembly",
853                         "  /link[resource]:<filename>[,<name>[,<targetfile>[,Private]]]",
854                         "                            link the file as a resource to the assembly",
855                 };
856         }
857 }