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