Add check to see if a registered type IsExternal
[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 //
9
10
11 using System;
12 using System.IO;
13 using System.Collections;
14 using System.Reflection;
15 using System.Reflection.Emit;
16
17 namespace Mono.AssemblyLinker
18 {
19         class ModuleInfo {
20                 public string fileName;
21                 public string target;
22         }
23
24         class ResourceInfo {
25                 public string name;
26                 public string fileName;
27                 public string target;
28                 public bool isEmbedded;
29                 public bool isPrivate;
30         }
31
32         enum Target {
33                 Dll,
34                 Exe,
35                 Win
36         }
37
38         public class AssemblyLinker {
39
40                 ArrayList inputFiles = new ArrayList ();
41                 ArrayList resources = new ArrayList ();
42                 ArrayList cattrs = new ArrayList ();
43                 string outputFile;
44                 bool fullPaths;
45                 string outFile;
46                 string entryPoint;
47                 string win32IconFile;
48                 string win32ResFile;
49                 Target target;
50
51                 public static int Main (String[] args) {
52                         return new AssemblyLinker ().DynMain (args);
53                 }
54
55                 private int DynMain (String[] args) {
56                         ParseArgs (args);
57
58                         DoIt ();
59
60                         return 0;
61                 }
62
63                 private void ParseArgs (string[] args) {
64
65                         if (args.Length == 0)
66                                 Usage ();
67
68                         foreach (string str in args) {
69                                 if ((str [0] != '-') && (str [0] != '/')) {
70                                         string[] parts = str.Split (',');
71                                         ModuleInfo mod = new ModuleInfo ();
72                                         mod.fileName = parts [0];
73                                         if (parts.Length > 1)
74                                                 mod.target = parts [1];
75                                         inputFiles.Add (mod);
76                                         continue;
77                                 }
78
79                                 string arg;
80                                 string opt = GetCommand (str, out arg);
81
82                                 ResourceInfo res;
83                                 switch (opt) {
84                                 case "help":
85                                 case "?":
86                                         Usage ();
87                                         break;
88
89                                 case "embed": {
90                                         if (arg == null)
91                                                 ReportMissingFileSpec (opt);
92                                         res = new ResourceInfo ();
93                                         res.isEmbedded = true;
94                                         String[] parts = arg.Split (',');
95                                         res.fileName = parts [0];
96                                         if (parts.Length > 1)
97                                                 res.name = parts [1];
98                                         if (parts.Length > 2) {
99                                                 switch (parts [2]) {
100                                                 case "public":
101                                                         break;
102                                                 case "private":
103                                                         res.isPrivate = true;
104                                                         break;
105                                                 default:
106                                                         ReportInvalidArgument (opt, parts [2]);
107                                                         break;
108                                                 }
109                                         }
110                                         resources.Add (res);
111                                         break;
112                                 }
113
114                                 case "link": {
115                                         if (arg == null)
116                                                 ReportMissingFileSpec (opt);
117                                         res = new ResourceInfo ();
118                                         String[] parts = arg.Split (',');
119                                         res.fileName = parts [0];
120                                         if (parts.Length > 1)
121                                                 res.name = parts [1];
122                                         if (parts.Length > 2)
123                                                 res.target = parts [2];
124                                         if (parts.Length > 3) {
125                                                 switch (parts [3]) {
126                                                 case "public":
127                                                         break;
128                                                 case "private":
129                                                         res.isPrivate = true;
130                                                         break;
131                                                 default:
132                                                         ReportInvalidArgument (opt, parts [3]);
133                                                         break;
134                                                 }
135                                         }
136                                         resources.Add (res);
137                                         break;
138                                 }
139
140                                 case "algid":
141                                         if (arg == null)
142                                                 ReportMissingArgument (opt);
143                                         try {
144                                                 int val = Int32.Parse (arg);
145                                                 AddCattr (typeof (AssemblyAlgorithmIdAttribute), typeof (uint), val);
146                                         }
147                                         catch (Exception) {
148                                                 ReportInvalidArgument (opt, arg);
149                                         }
150                                         break;
151
152                                 case "base":
153                                         ReportNotImplemented (opt);
154                                         break;
155
156                                 case "baseaddress":
157                                         ReportNotImplemented (opt);
158                                         break;
159
160                                 case "bugreport":
161                                         ReportNotImplemented (opt);
162                                         break;
163
164                                 case "comp":
165                                 case "company":
166                                         if (arg == null)
167                                                 ReportMissingText (opt);
168                                         AddCattr (typeof (AssemblyCompanyAttribute), arg);
169                                         break;
170
171                                 case "config":
172                                 case "configuration":
173                                         if (arg == null)
174                                                 ReportMissingText (opt);
175                                         AddCattr (typeof (AssemblyConfigurationAttribute), arg);
176                                         break;
177
178                                 case "copy":
179                                 case "copyright":
180                                         if (arg == null)
181                                                 ReportMissingText (opt);
182                                         AddCattr (typeof (AssemblyCopyrightAttribute), arg);
183                                         break;
184
185                                 case "c":
186                                 case "culture":
187                                         if (arg == null)
188                                                 ReportMissingText (opt);
189                                         AddCattr (typeof (AssemblyCultureAttribute), arg);
190                                         break;
191
192                                 case "delay":
193                                 case "delaysign":
194                                         ReportNotImplemented (opt);
195                                         break;
196
197                                 case "descr":
198                                 case "description":
199                                         if (arg == null)
200                                                 ReportMissingText (opt);
201                                         AddCattr (typeof (AssemblyDescriptionAttribute), arg);
202                                         break;
203
204                                 case "e":
205                                 case "evidence":
206                                         if (arg == null)
207                                                 ReportMissingFileSpec (opt);
208                                         res = new ResourceInfo ();
209                                         res.name = "Security.Evidence";
210                                         res.fileName = arg;
211                                         res.isEmbedded = true;
212                                         res.isPrivate = true;
213                                         resources.Add (res);
214                                         break;
215
216                                 case "fileversion":
217                                         if (arg == null)
218                                                 ReportMissingText (opt);
219
220                                         AddCattr (typeof (AssemblyFileVersionAttribute), arg);
221                                         break;
222
223                                 case "flags":
224                                         if (arg == null)
225                                                 ReportMissingArgument (opt);
226                                         try {
227                                                 int val = Int32.Parse (arg);
228                                                 AddCattr (typeof (AssemblyFlagsAttribute), typeof (uint), val);
229                                         }
230                                         catch (Exception) {
231                                                 ReportInvalidArgument (opt, arg);
232                                         }
233                                         break;
234
235                                 case "fullpaths":
236                                         fullPaths = true;
237                                         break;
238
239                                 case "keyf":
240                                 case "keyfile":
241                                         if (arg == null)
242                                                 ReportMissingText (opt);
243                                         AddCattr (typeof (AssemblyKeyFileAttribute), arg);
244                                         break;
245                                         
246                                 case "keyn":
247                                 case "keyname":
248                                         if (arg == null)
249                                                 ReportMissingText (opt);
250                                         AddCattr (typeof (AssemblyKeyNameAttribute), arg);
251                                         break;
252
253                                 case "main":
254                                         if (arg == null)
255                                                 ReportMissingText (opt);
256                                         entryPoint = arg;
257                                         break;
258
259                                 case "nologo":
260                                         break;
261
262                                 case "out":
263                                         if (arg == null)
264                                                 ReportMissingFileSpec (opt);
265                                         outFile = arg;
266                                         break;
267
268                                 case "prod":
269                                 case "product":
270                                         if (arg == null)
271                                                 ReportMissingText (opt);
272                                         AddCattr (typeof (AssemblyProductAttribute), arg);
273                                         break;
274
275                                 case "productv":
276                                 case "productversion":
277                                         if (arg == null)
278                                                 ReportMissingText (opt);
279                                         AddCattr (typeof (AssemblyInformationalVersionAttribute), arg);
280                                         break;
281
282                                 case "t":
283                                 case "target":
284                                         if (arg == null)
285                                                 ReportMissingText (opt);
286                                         switch (arg) {
287                                         case "lib":
288                                         case "library":
289                                                 target = Target.Dll;
290                                                 break;
291                                         case "exe":
292                                                 target = Target.Exe;
293                                                 break;
294                                         case "win":
295                                         case "winexe":
296                                                 Report (0, "target:win is not implemented");
297                                                 break;
298                                         default:
299                                                 ReportInvalidArgument (opt, arg);
300                                                 break;
301                                         }
302                                         break;
303
304                                 case "template":
305                                         if (arg == null)
306                                                 ReportMissingFileSpec (opt);
307                                         ReportNotImplemented (opt);
308                                         break;
309
310                                 case "title":
311                                         if (arg == null)
312                                                 ReportMissingText (opt);
313                                         AddCattr (typeof (AssemblyTitleAttribute), arg);
314                                         break;
315
316                                 case "trade":
317                                 case "trademark":
318                                         if (arg == null)
319                                                 ReportMissingText (opt);
320                                         AddCattr (typeof (AssemblyTrademarkAttribute), arg);
321                                         break;
322
323                                 case "v":
324                                 case "version":
325                                         // This option conflicts with the standard UNIX meaning
326                                         if (arg == null) {
327                                                 Version ();
328                                                 break;
329                                         }
330                                         AddCattr (typeof (AssemblyVersionAttribute), arg);
331                                         break;
332
333                                 case "win32icon":
334                                         if (arg == null)
335                                                 ReportMissingFileSpec (opt);
336                                         win32IconFile = arg;
337                                         break;
338
339                                 case "win32res":
340                                         if (arg == null)
341                                                 ReportMissingFileSpec (opt);
342                                         win32ResFile = arg;
343                                         break;
344
345                                 default:
346                                         Report (1013, String.Format ("Unrecognized command line option: '{0}'", opt));
347                                         break;
348                                 }
349                         }
350
351                         if ((inputFiles.Count == 0) && (resources.Count == 0))
352                                 Report (1016, "No valid input files were specified");
353
354                         if (outFile == null)
355                                 Report (1017, "No target filename was specified");
356
357                         if (target == Target.Dll && (entryPoint != null))
358                                 Report (1035, "Libraries cannot have an entry point");
359
360                         if (target == Target.Exe && (entryPoint == null))
361                                 Report (1036, "Entry point required for executable applications");
362                 }
363
364                 private string GetCommand (string str, out string command_arg) {
365                         if ((str [0] == '-') && (str.Length > 1) && (str [1] == '-'))
366                                 str = str.Substring (1);
367
368                         int end_index = str.IndexOfAny (new char[] {':', '='}, 1);
369                         string command = str.Substring (1,
370                                                                                         end_index == -1 ? str.Length - 1 : end_index - 1);
371                         
372                         if (end_index != -1) {
373                                 command_arg = str.Substring (end_index+1);
374                                 if (command_arg == String.Empty)
375                                         command_arg = null;
376                         } else {
377                                 command_arg = null;
378                         }
379                                 
380                         return command.ToLower ();
381                 }
382
383                 private void AddCattr (Type attrType, Type arg, object value) {
384                         cattrs.Add (new CustomAttributeBuilder (attrType.GetConstructor (new Type [] { arg }), new object [] { value }));
385                 }
386
387                 private void AddCattr (Type attrType, object value) {
388                         AddCattr (attrType, typeof (string), value);
389                 }
390
391                 private void AddResource (ResourceInfo res) {
392                         foreach (ResourceInfo res2 in resources) {
393                                 if (res.name == res2.name) {
394
395                                 }
396                         }
397                         resources.Add (res);
398                 }
399
400                 private void PrintVersion () {
401                         Console.WriteLine ("Mono Assembly Linker (al.exe) version " + Assembly.GetExecutingAssembly ().GetName ().Version.ToString ());
402                 }
403
404                 private void Version () {
405                         PrintVersion ();
406                         Environment.Exit (0);
407                 }
408
409                 private void Usage () {
410                         PrintVersion ();
411                         
412                         foreach (string s in usage)
413                                 Console.WriteLine (s);
414                         Environment.Exit (0);
415                 }
416
417                 private void Report (int errorNum, string msg) {
418                         Console.WriteLine (String.Format ("ALINK: error A{0:0000}: {1}", errorNum, msg));
419                         Environment.Exit (1);
420                 }
421
422                 private void ReportWarning (int errorNum, string msg) {
423                         Console.WriteLine (String.Format ("ALINK: warning A{0:0000}: {1}", errorNum, msg));
424                 }
425
426                 private void ReportInvalidArgument (string option, string value) {
427                         Report (1012, String.Format ("'{0}' is not a valid setting for option '{1}'", value, option));
428                 }
429
430                 private void ReportMissingArgument (string option) {
431                         Report (1003, String.Format ("Compiler option '{0}' must be followed by an argument", option));
432                 }
433
434                 private void ReportNotImplemented (string option) {
435                         Report (0, String.Format ("Compiler option '{0}' is not implemented", option));
436                 }
437
438                 private void ReportMissingFileSpec (string option) {
439                         Report (1008, String.Format ("Missing file specification for '{0}' command-line option", option));
440                 }
441
442                 private void ReportMissingText (string option) {
443                         Report (1010, String.Format ("Missing ':<text>' for '{0}' option", option));
444                 }
445
446                 private void DoIt () {
447                         AssemblyName aname = new AssemblyName ();
448                         aname.Name = Path.GetFileNameWithoutExtension (outFile);
449
450                         string fileName = Path.GetFileName (outFile);
451
452                         AssemblyBuilder ab;
453
454                         if (fileName != outFile)
455                                 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save, Path.GetDirectoryName (outFile));
456                         else
457                                 ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save);
458
459                         foreach (CustomAttributeBuilder cb in cattrs)
460                                 ab.SetCustomAttribute (cb);
461
462                         /*
463                          * Emit modules
464                          */
465
466                         foreach (ModuleInfo mod in inputFiles) {
467                                 MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
468                                 if (mi == null)
469                                         Report (0, "Cannot add modules on this runtime: try the Mono runtime instead.");
470
471                                 if (mod.target != null) {
472                                         File.Copy (mod.fileName, mod.target, true);
473                                         mod.fileName = mod.target;
474                                 }
475
476                                 bool isAssembly = false;
477                                 try {
478                                         AssemblyName.GetAssemblyName (mod.fileName);
479                                         isAssembly = true;
480                                 }
481                                 catch (Exception) {
482                                 }
483
484                                 if (isAssembly)
485                                         ReportWarning (1020, "Ignoring included assembly '" + mod.fileName + "'");
486                                 else
487                                         mi.Invoke (ab, new object [] { mod.fileName });
488                         }
489
490                         /*
491                          * Set entry point
492                          */
493
494                         if (entryPoint != null) {
495                                 string mainClass = entryPoint.Substring (0, entryPoint.LastIndexOf ('.'));
496                                 string mainMethod = entryPoint.Substring (entryPoint.LastIndexOf ('.') + 1);
497
498                                 MethodInfo mainMethodInfo = null;
499
500                                 try {
501                                         Type mainType = ab.GetType (mainClass);
502                                         if (mainType != null)
503                                                 mainMethodInfo = mainType.GetMethod (mainMethod);
504                                 }
505                                 catch (Exception ex) {
506                                         Console.WriteLine (ex);
507                                 }
508                                 if (mainMethodInfo != null)
509                                         ab.SetEntryPoint (mainMethodInfo);
510                                 else
511                                         Report (1037, "Unable to find the entry point method '" + entryPoint + "'");
512                         }
513
514                         /*
515                          * Emit resources
516                          */
517
518                         ab.DefineVersionInfoResource ();
519
520                         if (win32IconFile != null) {
521                                 try {
522                                         MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
523                                         if (mi == null)
524                                                 Report (0, "Cannot embed win32 icons on this runtime: try the Mono runtime instead.");
525                                         mi.Invoke (ab, new object [] {  win32IconFile });
526                                 }
527                                 catch (Exception ex) {
528                                         Report (1031, "Error reading icon '" + win32IconFile + "' --" + ex);
529                                 }
530                         }
531
532                         if (win32ResFile != null) {
533                                 try {
534                                         ab.DefineUnmanagedResource (win32ResFile);
535                                 }
536                                 catch (Exception ex) {
537                                         Report (1019, "Metadata failure creating assembly -- " + ex);
538                                 }
539                         }
540
541                         foreach (ResourceInfo res in resources) {
542                                 if (res.name == null)
543                                         res.name = Path.GetFileName (res.fileName);
544
545                                 foreach (ResourceInfo res2 in resources)
546                                         if ((res != res2) && (res.name == res2.name))
547                                                 Report (1046, String.Format ("Resource identifier '{0}' has already been used in this assembly", res.name));
548
549                                 if (res.isEmbedded) {
550                                         MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
551                                                 null, CallingConventions.Any, new Type [] { typeof (string), typeof (string) }, null);
552                                         if (mi == null)
553                                                 Report (0, "Cannot embed resources on this runtime: try the Mono runtime instead.");
554                                         mi.Invoke (ab, new object [] { res.name, res.fileName });
555                                 }
556                                 else {
557                                         if (res.target != null) {
558                                                 File.Copy (res.fileName, res.target, true);
559                                                 res.fileName = res.target;
560                                         }
561
562                                         ab.DefineResource (res.name, "", res.fileName, 
563                                                                            res.isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public);
564                                 }
565                         }
566
567                         try {
568                                 ab.Save (fileName);
569                         }
570                         catch (Exception ex) {
571                                 Report (1019, "Metadata failure creating assembly -- " + ex);
572                         }
573                 }
574
575                 string[] usage = {
576                         "Usage: al [options] [sources]",
577                         "Options: ('/out' must be specified)",
578                         "",
579                         "  /? or /help               Display this usage message",
580                         "  @<filename>               Read response file for more options",
581                         "  /algid:<id>               Algorithm used to hash files (in hexadecimal)",
582                         "  /base[address]:<addr>     Base address for the library",
583                         "  /bugreport:<filename>     Create a 'Bug Report' file",
584                         "  /comp[any]:<text>         Company name",
585                         "  /config[uration]:<text>   Configuration string",
586                         "  /copy[right]:<text>       Copyright message",
587                         "  /c[ulture]:<text>         Supported culture",
588                         "  /delay[sign][+|-]         Delay sign this assembly",
589                         "  /descr[iption]:<text>     Description",
590                         "  /e[vidence]:<filename>    Security evidence file to embed",
591                         "  /fileversion:<version>    Optional Win32 version (overrides assembly version)",
592                         "  /flags:<flags>            Assembly flags  (in hexadecimal)",
593                         "  /fullpaths                Display files using fully-qualified filenames",
594                         "  /keyf[ile]:<filename>     File containing key to sign the assembly",
595                         "  /keyn[ame]:<text>         Key container name of key to sign assembly",
596                         "  /main:<method>            Specifies the method name of the entry point",
597                         "  /nologo                   Suppress the startup banner and copyright message",
598                         "  /out:<filename>           Output file name for the assembly manifest",
599                         "  /prod[uct]:<text>         Product name",
600                         "  /productv[ersion]:<text>  Product version",
601                         "  /t[arget]:lib[rary]       Create a library",
602                         "  /t[arget]:exe             Create a console executable",
603                         "  /t[arget]:win[exe]        Create a Windows executable",
604                         "  /template:<filename>      Specifies an assembly to get default options from",
605                         "  /title:<text>             Title",
606                         "  /trade[mark]:<text>       Trademark message",
607                         "  /v[ersion]:<version>      Version (use * to auto-generate remaining numbers)",
608                         "  /win32icon:<filename>     Use this icon for the output",
609                         "  /win32res:<filename>      Specifies the Win32 resource file",
610                         "",
611                         "Sources: (at least one source input is required)",
612                         "  <filename>[,<targetfile>] add file to assembly",
613                         "  /embed[resource]:<filename>[,<name>[,Private]]",
614                         "                            embed the file as a resource in the assembly",
615                         "  /link[resource]:<filename>[,<name>[,<targetfile>[,Private]]]",
616                         "                            link the file as a resource to the assembly",
617                 };
618
619         }
620 }