AL: Use IKVM.Reflection to get custom attributes from the template assembly,
[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.Collections.Generic;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Security.Cryptography;
19 using System.Text;
20 using System.Configuration.Assemblies;
21
22 using Mono.Security.Cryptography;
23 using IKR = IKVM.Reflection;
24
25 namespace Mono.AssemblyLinker
26 {
27         class ModuleInfo {
28                 public string fileName;
29                 public string target;
30         }
31
32         class ResourceInfo {
33                 public string name;
34                 public string fileName;
35                 public string target;
36                 public bool isEmbedded;
37                 public bool isPrivate;
38         }
39
40         enum Target {
41                 Dll,
42                 Exe,
43                 Win
44         }
45
46         enum DelaySign {
47                 NotSet,
48                 Yes,
49                 No
50         }
51
52         public class AssemblyLinker {
53
54                 ArrayList inputFiles = new ArrayList ();
55                 ArrayList resources = new ArrayList ();
56                 ArrayList cattrs = new ArrayList ();
57                 bool fullPaths;
58                 string outFile;
59                 string entryPoint;
60                 string win32IconFile;
61                 string win32ResFile;
62                 string templateFile;
63                 bool isTemplateFile = false;
64                 Target target = Target.Dll;
65                 DelaySign delaysign = DelaySign.NotSet;
66                 string keyfile;
67                 string keyname;
68                 string culture;
69
70                 public static int Main (String[] args) {
71                         return new AssemblyLinker ().DynMain (args);
72                 }
73
74                 private int DynMain (String[] args) {
75                         ParseArgs (args);
76
77                         DoIt ();
78
79                         return 0;
80                 }
81
82                 private void ParseArgs (string[] args) 
83                 {
84                         ArrayList flat_args = new ArrayList ();
85
86                         // Process response files
87                         Hashtable response_files = new Hashtable ();
88                         foreach (string str in args) {
89                                 if (str [0] != '@') {
90                                         flat_args.Add (str);
91                                         continue;
92                                 }
93
94                                 if (str.Length == 1)
95                                         ReportMissingFileSpec ("@");
96
97                                 string resfile_name = Path.GetFullPath (str.Substring (1));
98                                 if (response_files.ContainsKey (resfile_name))
99                                         Report (1006, "Response file '" + resfile_name + "' was already included");
100                                 response_files [resfile_name] = resfile_name;
101                                 LoadArgs (resfile_name, flat_args);
102                         }
103
104                         if (flat_args.Count == 0)
105                                 Usage ();
106
107                         foreach (string str in flat_args) {
108                                 if ((str [0] != '-') && (str [0] != '/')) {
109                                         inputFiles.Add (GetModuleInfo (str));
110                                         continue;
111                                 }
112
113                                 if (!ParseOption(str)) {
114                                         if (RunningOnUnix) {
115                                                 // cope with absolute filenames for modules on unix, as
116                                                 // they also match the option pattern
117                                                 //
118                                                 // `/home/test.cs' is considered as a module, however
119                                                 // '/test.cs' is considered as error
120                                                 if (str.Length > 2 && str.IndexOf ('/', 2) != -1) {
121                                                         inputFiles.Add (GetModuleInfo (str));
122                                                         continue;
123                                                 }
124                                         }
125
126                                         Report (1013, String.Format ("Unrecognized command line option: '{0}'", str));
127                                         break;
128                                 }
129                         }
130
131                         if ((inputFiles.Count == 0) && (resources.Count == 0))
132                                 Report (1016, "No valid input files were specified");
133
134                         if (outFile == null)
135                                 Report (1017, "No target filename was specified");
136
137                         if (target == Target.Dll && (entryPoint != null))
138                                 Report (1035, "Libraries cannot have an entry point");
139
140                         if (target == Target.Exe && (entryPoint == null))
141                                 Report (1036, "Entry point required for executable applications");                                              
142                 }
143
144                 private bool ParseOption (string str)
145                 {
146                         string arg;
147                         string opt = GetCommand (str, out arg);
148
149                         switch (opt) {
150                         case "help":
151                         case "?":
152                                 Usage ();
153                                 return true;
154
155                         case "embed": {
156                                         if (arg == null)
157                                                 ReportMissingFileSpec (opt);
158                                         ResourceInfo res = new ResourceInfo ();
159                                         res.isEmbedded = true;
160                                         String [] parts = arg.Split (',');
161                                         res.fileName = parts [0];
162                                         if (parts.Length > 1)
163                                                 res.name = parts [1];
164                                         if (parts.Length > 2) {
165                                                 switch (parts [2]) {
166                                                 case "public":
167                                                         break;
168                                                 case "private":
169                                                         res.isPrivate = true;
170                                                         break;
171                                                 default:
172                                                         ReportInvalidArgument (opt, parts [2]);
173                                                         break;
174                                                 }
175                                         }
176                                         resources.Add (res);
177                                         return true;
178                                 }
179
180                         case "link": {
181                                         if (arg == null)
182                                                 ReportMissingFileSpec (opt);
183                                         ResourceInfo res = new ResourceInfo ();
184                                         String [] parts = arg.Split (',');
185                                         res.fileName = parts [0];
186                                         if (parts.Length > 1)
187                                                 res.name = parts [1];
188                                         if (parts.Length > 2)
189                                                 res.target = parts [2];
190                                         if (parts.Length > 3) {
191                                                 switch (parts [3]) {
192                                                 case "public":
193                                                         break;
194                                                 case "private":
195                                                         res.isPrivate = true;
196                                                         break;
197                                                 default:
198                                                         ReportInvalidArgument (opt, parts [3]);
199                                                         break;
200                                                 }
201                                         }
202                                         resources.Add (res);
203                                         return true;
204                                 }
205
206                         case "algid":
207                                 if (arg == null)
208                                         ReportMissingArgument (opt);
209                                 try {
210                                         string realArg = arg;
211                                         if (realArg.StartsWith ("0x"))
212                                                 realArg = realArg.Substring (2);
213                                         uint val = Convert.ToUInt32 (realArg, 16);
214                                         AddCattr (typeof (AssemblyAlgorithmIdAttribute), typeof (uint), val);
215                                 } catch (Exception) {
216                                         ReportInvalidArgument (opt, arg);
217                                 }
218                                 return true;
219
220                         case "base":
221                                 ReportNotImplemented (opt);
222                                 return true;
223
224                         case "baseaddress":
225                                 ReportNotImplemented (opt);
226                                 return true;
227
228                         case "bugreport":
229                                 ReportNotImplemented (opt);
230                                 return true;
231
232                         case "comp":
233                         case "company":
234                                 if (arg == null)
235                                         ReportMissingText (opt);
236                                 AddCattr (typeof (AssemblyCompanyAttribute), arg);
237                                 return true;
238
239                         case "config":
240                         case "configuration":
241                                 if (arg == null)
242                                         ReportMissingText (opt);
243                                 AddCattr (typeof (AssemblyConfigurationAttribute), arg);
244                                 return true;
245
246                         case "copy":
247                         case "copyright":
248                                 if (arg == null)
249                                         ReportMissingText (opt);
250                                 AddCattr (typeof (AssemblyCopyrightAttribute), arg);
251                                 return true;
252
253                         case "c":
254                         case "culture":
255                                 if (arg == null)
256                                         ReportMissingText (opt);
257                                 culture = arg;
258                                 return true;
259
260                         case "delay":
261                         case "delaysign":
262                         case "delay+":
263                         case "delaysign+":
264                                 delaysign = DelaySign.Yes;
265                                 return true;
266
267                         case "delay-":
268                         case "delaysign-":
269                                 delaysign = DelaySign.No;
270                                 return true;
271
272                         case "descr":
273                         case "description":
274                                 if (arg == null)
275                                         ReportMissingText (opt);
276                                 AddCattr (typeof (AssemblyDescriptionAttribute), arg);
277                                 return true;
278
279                         case "e":
280                         case "evidence": {
281                                 if (arg == null)
282                                         ReportMissingFileSpec (opt);
283                                 ResourceInfo res = new ResourceInfo ();
284                                 res.name = "Security.Evidence";
285                                 res.fileName = arg;
286                                 res.isEmbedded = true;
287                                 res.isPrivate = true;
288                                 resources.Add (res);
289                                 return true;
290                         }
291                         case "fileversion":
292                                 if (arg == null)
293                                         ReportMissingText (opt);
294
295                                 AddCattr (typeof (AssemblyFileVersionAttribute), arg);
296                                 return true;
297
298                         case "flags":
299                                 if (arg == null)
300                                         ReportMissingArgument (opt);
301                                 try {
302                                         string realArg = arg;
303                                         if (realArg.StartsWith ("0x"))
304                                                 realArg = realArg.Substring (2);
305                                         uint val = Convert.ToUInt32 (realArg, 16);
306                                         AddCattr (typeof (AssemblyFlagsAttribute), typeof (uint), val);
307                                 } catch (Exception) {
308                                         ReportInvalidArgument (opt, arg);
309                                 }
310                                 return true;
311
312                         case "fullpaths":
313                                 fullPaths = true;
314                                 return true;
315
316                         case "keyf":
317                         case "keyfile":
318                                 if (arg == null)
319                                         ReportMissingText (opt);
320                                 keyfile = arg;
321                                 return true;
322
323                         case "keyn":
324                         case "keyname":
325                                 if (arg == null)
326                                         ReportMissingText (opt);
327                                 keyname = arg;
328                                 return true;
329
330                         case "main":
331                                 if (arg == null)
332                                         ReportMissingText (opt);
333                                 entryPoint = arg;
334                                 return true;
335
336                         case "nologo":
337                                 return true;
338
339                         case "out":
340                                 if (arg == null)
341                                         ReportMissingFileSpec (opt);
342                                 outFile = arg;
343                                 return true;
344
345                         case "prod":
346                         case "product":
347                                 if (arg == null)
348                                         ReportMissingText (opt);
349                                 AddCattr (typeof (AssemblyProductAttribute), arg);
350                                 return true;
351
352                         case "productv":
353                         case "productversion":
354                                 if (arg == null)
355                                         ReportMissingText (opt);
356                                 AddCattr (typeof (AssemblyInformationalVersionAttribute), arg);
357                                 return true;
358
359                         case "t":
360                         case "target":
361                                 if (arg == null)
362                                         ReportMissingText (opt);
363                                 switch (arg) {
364                                 case "lib":
365                                 case "library":
366                                         target = Target.Dll;
367                                         break;
368                                 case "exe":
369                                         target = Target.Exe;
370                                         break;
371                                 case "win":
372                                 case "winexe":
373                                         Report (0, "target:win is not implemented");
374                                         break;
375                                 default:
376                                         ReportInvalidArgument (opt, arg);
377                                         break;
378                                 }
379                                 return true;
380
381                         case "template":
382                                 if (arg == null)
383                                         ReportMissingFileSpec (opt);
384                                 isTemplateFile = true;
385                                 templateFile = Path.Combine (Directory.GetCurrentDirectory (), arg);
386                                 return true;
387
388                         case "title":
389                                 if (arg == null)
390                                         ReportMissingText (opt);
391                                 AddCattr (typeof (AssemblyTitleAttribute), arg);
392                                 return true;
393
394                         case "trade":
395                         case "trademark":
396                                 if (arg == null)
397                                         ReportMissingText (opt);
398                                 AddCattr (typeof (AssemblyTrademarkAttribute), arg);
399                                 return true;
400
401                         case "v":
402                         case "version":
403                                 // This option conflicts with the standard UNIX meaning
404                                 if (arg == null) {
405                                         Version ();
406                                         break;
407                                 }
408                                 AddCattr (typeof (AssemblyVersionAttribute), arg);
409                                 return true;
410
411                         case "win32icon":
412                                 if (arg == null)
413                                         ReportMissingFileSpec (opt);
414                                 win32IconFile = arg;
415                                 return true;
416
417                         case "win32res":
418                                 if (arg == null)
419                                         ReportMissingFileSpec (opt);
420                                 win32ResFile = arg;
421                                 return true;
422                         }
423                         return false;
424                 }
425
426                 private bool RunningOnUnix {
427                         get {
428                                 // check for Unix platforms - see FAQ for more details
429                                 // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
430                                 int platform = (int) Environment.OSVersion.Platform;
431                                 return ((platform == 4) || (platform == 128) || (platform == 6));
432                         }
433                 }
434
435                 private ModuleInfo GetModuleInfo (string str)
436                 {
437                         string [] parts = str.Split (',');
438                         ModuleInfo mod = new ModuleInfo ();
439                         mod.fileName = parts [0];
440                         if (parts.Length > 1)
441                                 mod.target = parts [1];
442                         return mod;
443                 }
444
445                 private string GetCommand (string str, out string command_arg) {
446                         if ((str [0] == '-') && (str.Length > 1) && (str [1] == '-'))
447                                 str = str.Substring (1);
448
449                         int end_index = str.IndexOfAny (new char[] {':', '='}, 1);
450                         string command = str.Substring (1,
451                                                                                         end_index == -1 ? str.Length - 1 : end_index - 1);
452                         
453                         if (end_index != -1) {
454                                 command_arg = str.Substring (end_index+1);
455                                 if (command_arg == String.Empty)
456                                         command_arg = null;
457                         } else {
458                                 command_arg = null;
459                         }
460                                 
461                         return command.ToLower ();
462                 }
463
464                 private void AddCattr (Type attrType, Type arg, object value) {
465                         cattrs.Add (new CustomAttributeBuilder (attrType.GetConstructor (new Type [] { arg }), new object [] { value }));
466                 }
467
468                 private void AddCattr (Type attrType, object value) {
469                         AddCattr (attrType, typeof (string), value);
470                 }
471
472                 private void PrintVersion () {
473                         Console.WriteLine ("Mono Assembly Linker (al.exe) version " + Consts.MonoVersion);
474                 }
475
476                 private void Version () {
477                         PrintVersion ();
478                         Environment.Exit (0);
479                 }
480
481                 private void Usage () {
482                         PrintVersion ();
483                         
484                         foreach (string s in usage)
485                                 Console.WriteLine (s);
486                         Environment.Exit (0);
487                 }
488
489                 private void Report (int errorNum, string msg) {
490                         Console.WriteLine (String.Format ("ALINK: error A{0:0000}: {1}", errorNum, msg));
491                         Environment.Exit (1);
492                 }
493
494                 private void ReportWarning (int errorNum, string msg) {
495                         Console.WriteLine (String.Format ("ALINK: warning A{0:0000}: {1}", errorNum, msg));
496                 }
497
498                 private void ReportInvalidArgument (string option, string value) {
499                         Report (1012, String.Format ("'{0}' is not a valid setting for option '{1}'", value, option));
500                 }
501
502                 private void ReportMissingArgument (string option) {
503                         Report (1003, String.Format ("Compiler option '{0}' must be followed by an argument", option));
504                 }
505
506                 private void ReportNotImplemented (string option) {
507                         Report (0, String.Format ("Compiler option '{0}' is not implemented", option));
508                 }
509
510                 private void ReportMissingFileSpec (string option) {
511                         Report (1008, String.Format ("Missing file specification for '{0}' command-line option", option));
512                 }
513
514                 private void ReportMissingText (string option) {
515                         Report (1010, String.Format ("Missing ':<text>' for '{0}' option", option));
516                 }
517
518                 // copied from /mcs/mcs/codegen.cs
519                 private void SetPublicKey (AssemblyName an, byte[] strongNameBlob) 
520                 {
521                         // check for possible ECMA key
522                         if (strongNameBlob.Length == 16) {
523                                 // will be rejected if not "the" ECMA key
524                                 an.SetPublicKey (strongNameBlob);
525                         } else {
526                                 // take it, with or without, a private key
527                                 RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob);
528                                 // and make sure we only feed the public part to Sys.Ref
529                                 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
530                                         
531                                 // AssemblyName.SetPublicKey requires an additional header
532                                 byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 };
533
534                                 byte[] encodedPublicKey = new byte [12 + publickey.Length];
535                                 Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12);
536                                 Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length);
537                                 an.SetPublicKey (encodedPublicKey);
538                         }
539                 }
540
541                 private void SetKeyPair (AssemblyName aname)
542                 {
543                         if (keyfile != null) {
544                                 if (!File.Exists (keyfile)) {
545                                         Report (1044, String.Format ("Couldn't open '{0}' key file.", keyfile));
546                                 }
547
548                                 using (FileStream fs = File.OpenRead (keyfile)) {
549                                         byte[] data = new byte [fs.Length];
550                                         try {
551                                                 fs.Read (data, 0, data.Length);
552
553                                                 if (delaysign == DelaySign.Yes) {
554                                                         SetPublicKey (aname, data);
555                                                 } else {
556                                                         CryptoConvert.FromCapiPrivateKeyBlob (data);
557                                                         aname.KeyPair = new StrongNameKeyPair (data);
558                                                 }
559                                         }
560                                         catch (CryptographicException) {
561                                                 if (delaysign != DelaySign.Yes) {
562                                                         if (data.Length == 16) {
563                                                                 // error # is different for ECMA key
564                                                                 Report (1019, "Could not strongname the assembly. " + 
565                                                                         "ECMA key can only be used to delay-sign assemblies");
566                                                         } else {
567                                                                 Report (1028, String.Format ("Key file {0}' is missing it's private key " +
568                                                                         "or couldn't be decoded.", keyfile));
569                                                         }
570                                                 } else {
571                                                         Report (1044, String.Format ("Couldn't decode '{0}' key file.", keyfile));
572                                                 }
573                                         }
574                                         fs.Close ();
575                                 }
576                         } else if (keyname != null) {
577                                 // delay-sign doesn't apply to key containers
578                                 aname.KeyPair = new StrongNameKeyPair (keyname);
579                         }
580                 }
581                 
582                 private void DoIt () {
583                         AssemblyName aname = new AssemblyName ();
584                         aname.Name = Path.GetFileNameWithoutExtension (outFile);
585                         if (culture != null)
586                                 aname.CultureInfo = new CultureInfo (culture);
587
588                         string fileName = Path.GetFileName (outFile);
589
590                         AssemblyBuilder ab;
591                         
592                         /*
593                          * Emit Manifest
594                          * */
595
596                         if (isTemplateFile)
597                                 aname = ReadCustomAttributesFromTemplateFile (templateFile, aname);
598
599                         SetKeyPair (aname);
600
601                         if (fileName != outFile)
602                                 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save, Path.GetDirectoryName (outFile));
603                         else
604                                 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save);
605
606                         foreach (CustomAttributeBuilder cb in cattrs)
607                                 ab.SetCustomAttribute (cb);
608
609                         /*
610                          * Emit modules
611                          */
612
613                         foreach (ModuleInfo mod in inputFiles) {
614                                 MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
615                                 if (mi == null)
616                                         Report (0, "Cannot add modules on this runtime: try the Mono runtime instead.");
617
618                                 if (mod.target != null) {
619                                         File.Copy (mod.fileName, mod.target, true);
620                                         mod.fileName = mod.target;
621                                 }
622
623                                 bool isAssembly = false;
624                                 try {
625                                         AssemblyName.GetAssemblyName (mod.fileName);
626                                         isAssembly = true;
627                                 }
628                                 catch (Exception) {
629                                 }
630
631                                 if (isAssembly)
632                                         ReportWarning (1020, "Ignoring included assembly '" + mod.fileName + "'");
633                                 else
634                                         mi.Invoke (ab, new object [] { mod.fileName });
635                         }
636
637                         /*
638                          * Set entry point
639                          */
640
641                         if (entryPoint != null) {
642                                 string mainClass = entryPoint.Substring (0, entryPoint.LastIndexOf ('.'));
643                                 string mainMethod = entryPoint.Substring (entryPoint.LastIndexOf ('.') + 1);
644
645                                 MethodInfo mainMethodInfo = null;
646
647                                 try {
648                                         Type mainType = ab.GetType (mainClass);
649                                         if (mainType != null)
650                                                 mainMethodInfo = mainType.GetMethod (mainMethod);
651                                 }
652                                 catch (Exception ex) {
653                                         Console.WriteLine (ex);
654                                 }
655                                 if (mainMethodInfo != null)
656                                         ab.SetEntryPoint (mainMethodInfo);
657                                 else
658                                         Report (1037, "Unable to find the entry point method '" + entryPoint + "'");
659                         }
660
661                         /*
662                          * Emit resources
663                          */
664
665                         ab.DefineVersionInfoResource ();
666
667                         if (win32IconFile != null) {
668                                 try {
669                                         MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
670                                         if (mi == null)
671                                                 Report (0, "Cannot embed win32 icons on this runtime: try the Mono runtime instead.");
672                                         mi.Invoke (ab, new object [] {  win32IconFile });
673                                 }
674                                 catch (Exception ex) {
675                                         Report (1031, "Error reading icon '" + win32IconFile + "' --" + ex);
676                                 }
677                         }
678
679                         if (win32ResFile != null) {
680                                 try {
681                                         ab.DefineUnmanagedResource (win32ResFile);
682                                 }
683                                 catch (Exception ex) {
684                                         Report (1019, "Metadata failure creating assembly -- " + ex);
685                                 }
686                         }
687
688                         foreach (ResourceInfo res in resources) {
689                                 if (res.name == null)
690                                         res.name = Path.GetFileName (res.fileName);
691
692                                 foreach (ResourceInfo res2 in resources)
693                                         if ((res != res2) && (res.name == res2.name))
694                                                 Report (1046, String.Format ("Resource identifier '{0}' has already been used in this assembly", res.name));
695
696                                 if (res.isEmbedded) {
697                                         MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
698                                                 null, CallingConventions.Any, new Type [] { typeof (string), typeof (string) }, null);
699                                         if (mi == null)
700                                                 Report (0, "Cannot embed resources on this runtime: try the Mono runtime instead.");
701                                         mi.Invoke (ab, new object [] { res.name, res.fileName });
702                                 }
703                                 else {
704                                         if (res.target != null) {
705                                                 File.Copy (res.fileName, res.target, true);
706                                                 res.fileName = res.target;
707                                         }
708                                         
709                                         // AddResourceFile must receive a file name and not a path.
710                                         // Drop directory and give warning if we have a path.
711                                         var resourceFileName = Path.GetFileName(res.fileName);
712                                         if (Path.GetDirectoryName (res.fileName) != null || Path.IsPathRooted(res.fileName)) {
713                                                 ReportWarning (99999, 
714                                                         String.Format ("Path '{0}' in the resource name is not supported. Using just file name '{1}'",
715                                                                 res.fileName,
716                                                                 resourceFileName));
717                                         }
718
719                                         ab.AddResourceFile (res.name, resourceFileName,
720                                                         res.isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public);
721                                 }
722                         }
723
724                         try {
725                                 ab.Save (fileName);
726                         }
727                         catch (Exception ex) {
728                                 Report (1019, "Metadata failure creating assembly -- " + ex);
729                         }
730                 }
731
732                 private AssemblyName ReadCustomAttributesFromTemplateFile (string templateFile, AssemblyName aname)
733                 {
734                         // LAMESPEC: according to MSDN, the template assembly must have a
735                         // strong name but this is not enforced
736                         const IKR.UniverseOptions options = IKR.UniverseOptions.MetadataOnly;
737
738                         var universe = new IKR.Universe (options);
739                         var asm = universe.LoadFile (templateFile);
740
741                         // Create missing assemblies, we don't want to load them!
742                         // Code taken from ikdasm
743                         var names = new HashSet<string> ();
744                         IKR.AssemblyName[] assembly_refs = asm.ManifestModule.__GetReferencedAssemblies ();
745
746                         var resolved_assemblies = new IKR.Assembly [assembly_refs.Length];
747                         for (int i = 0; i < resolved_assemblies.Length; i++) {
748                                 string name = assembly_refs [i].Name;
749
750                                 while (names.Contains (name)) {
751                                         name = name + "_" + i;
752                                 }
753                                 names.Add (name);
754                                 resolved_assemblies [i] = universe.CreateMissingAssembly (assembly_refs [i].FullName);
755                         }
756                         asm.ManifestModule.__ResolveReferencedAssemblies (resolved_assemblies);
757
758                         foreach (var attr_data in asm.__GetCustomAttributes (null, false)) {
759                                 string asm_name = attr_data.AttributeType.Assembly.GetName ().Name;
760                                 if (asm_name != "mscorlib")
761                                         continue;
762
763                                 switch (attr_data.AttributeType.FullName) {
764                                         case "System.Reflection.AssemblyKeyFileAttribute": {
765                                                 if (keyfile != null)
766                                                         // ignore if specified on command line
767                                                         continue;
768
769                                                 // / AssemblyKeyFileAttribute .ctor(string keyFile)
770                                                 string key_file_value = (string) attr_data.ConstructorArguments [0].Value;
771
772                                                 if (!String.IsNullOrEmpty (key_file_value))
773                                                         keyfile = Path.Combine (Path.GetDirectoryName (templateFile), key_file_value);
774                                         }
775                                         break;
776
777                                         case "System.Reflection.AssemblyDelaySignAttribute": {
778                                                 if (delaysign != DelaySign.NotSet)
779                                                         // ignore if specified on command line
780                                                         continue;
781
782                                                 // AssemblyDelaySignAttribute .ctor(bool delaySign)
783                                                 bool delay_sign_value = (bool) attr_data.ConstructorArguments [0].Value;
784                                                 delaysign = delay_sign_value ? DelaySign.Yes : DelaySign.No;
785                                         }
786                                         break;
787
788                                         case "System.Reflection.AssemblyKeyNameAttribute": {
789                                                 if (keyname != null)
790                                                         // ignore if specified on command line
791                                                         continue;
792
793                                                 // AssemblyKeyNameAttribute .ctor(string keyName)
794                                                 string key_name_value = (string) attr_data.ConstructorArguments [0].Value;
795
796                                                 // ignore null or zero-length keyname
797                                                 if (!String.IsNullOrEmpty (key_name_value))
798                                                         keyname = key_name_value;
799                                         }
800                                         break;
801                                 }
802                         }
803
804                         var asm_name_for_template_file = asm.GetName ();
805                         aname.Version = asm_name_for_template_file.Version;
806                         aname.HashAlgorithm = asm_name_for_template_file.HashAlgorithm;
807
808                         return aname;
809                 }
810
811                 private void LoadArgs (string file, ArrayList args) {
812                         StreamReader f = null;
813                         string line;
814                         try {
815                                 f = new StreamReader (file);
816
817                                 StringBuilder sb = new StringBuilder ();
818                         
819                                 while ((line = f.ReadLine ()) != null){
820                                         int t = line.Length;
821
822                                         for (int i = 0; i < t; i++){
823                                                 char c = line [i];
824                                                 
825                                                 if (c == '"' || c == '\''){
826                                                         char end = c;
827                                                         
828                                                         for (i++; i < t; i++){
829                                                                 c = line [i];
830
831                                                                 if (c == end)
832                                                                         break;
833                                                                 sb.Append (c);
834                                                         }
835                                                 } else if (c == ' '){
836                                                         if (sb.Length > 0){
837                                                                 args.Add (sb.ToString ());
838                                                                 sb.Length = 0;
839                                                         }
840                                                 } else
841                                                         sb.Append (c);
842                                         }
843                                         if (sb.Length > 0){
844                                                 args.Add (sb.ToString ());
845                                                 sb.Length = 0;
846                                         }
847                                 }
848                         } catch (Exception ex) {
849                                 Report (1007, "Error opening response file '" + file + "' -- '" + ex.Message + "'");
850                         } finally {
851                                 if (f != null)
852                                         f.Close ();
853                         }
854                 }
855
856                 string[] usage = {
857                         "Usage: al [options] [sources]",
858                         "Options: ('/out' must be specified)",
859                         "",
860                         "  /? or /help               Display this usage message",
861                         "  @<filename>               Read response file for more options",
862                         "  /algid:<id>               Algorithm used to hash files (in hexadecimal)",
863                         "  /base[address]:<addr>     Base address for the library",
864                         "  /bugreport:<filename>     Create a 'Bug Report' file",
865                         "  /comp[any]:<text>         Company name",
866                         "  /config[uration]:<text>   Configuration string",
867                         "  /copy[right]:<text>       Copyright message",
868                         "  /c[ulture]:<text>         Supported culture",
869                         "  /delay[sign][+|-]         Delay sign this assembly",
870                         "  /descr[iption]:<text>     Description",
871                         "  /e[vidence]:<filename>    Security evidence file to embed",
872                         "  /fileversion:<version>    Optional Win32 version (overrides assembly version)",
873                         "  /flags:<flags>            Assembly flags  (in hexadecimal)",
874                         "  /fullpaths                Display files using fully-qualified filenames",
875                         "  /keyf[ile]:<filename>     File containing key to sign the assembly",
876                         "  /keyn[ame]:<text>         Key container name of key to sign assembly",
877                         "  /main:<method>            Specifies the method name of the entry point",
878                         "  /nologo                   Suppress the startup banner and copyright message",
879                         "  /out:<filename>           Output file name for the assembly manifest",
880                         "  /prod[uct]:<text>         Product name",
881                         "  /productv[ersion]:<text>  Product version",
882                         "  /t[arget]:lib[rary]       Create a library",
883                         "  /t[arget]:exe             Create a console executable",
884                         "  /t[arget]:win[exe]        Create a Windows executable",
885                         "  /template:<filename>      Specifies an assembly to get default options from",
886                         "  /title:<text>             Title",
887                         "  /trade[mark]:<text>       Trademark message",
888                         "  /v[ersion]:<version>      Version (use * to auto-generate remaining numbers)",
889                         "  /win32icon:<filename>     Use this icon for the output",
890                         "  /win32res:<filename>      Specifies the Win32 resource file",
891                         "",
892                         "Sources: (at least one source input is required)",
893                         "  <filename>[,<targetfile>] add file to assembly",
894                         "  /embed[resource]:<filename>[,<name>[,Private]]",
895                         "                            embed the file as a resource in the assembly",
896                         "  /link[resource]:<filename>[,<name>[,<targetfile>[,Private]]]",
897                         "                            link the file as a resource to the assembly",
898                 };
899         }
900 }