1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using Microsoft.Build.Framework;
5 using Microsoft.Build.Utilities;
6 
7 namespace ILLink.Tasks
8 {
9 	public class ILLink : Task
10 	{
11 		/// <summary>
12 		///   Paths to the assembly files that should be considered as
13 		///   input to the linker. Currently the linker will
14 		///   additionally be able to resolve any assemblies in the
15 		///   same directory as an assembly in AssemblyPaths, but this
16 		///   behavior should not be relied upon. Instead, work under
17 		///   the assumption that only the AssemblyPaths given will be
18 		///   resolved.
19 		/// </summary>
20 		[Required]
21 		public ITaskItem [] AssemblyPaths { get; set; }
22 
23 		/// <summary>
24 		///   The names of the assemblies to root. This should contain
25 		///   assembly names without an extension, not file names or
26 		///   paths. Exactly which parts of the assemblies get rooted
27 		///   is subject to change. Currently these get passed to
28 		///   illink with "-a", which roots the entry point for
29 		///   executables, and everything for libraries. To control
30 		///   the linker more explicitly, either pass descriptor
31 		///   files, or pass extra arguments for illink.
32 		/// </summary>
33 		[Required]
34 		public ITaskItem [] RootAssemblyNames { get; set; }
35 
36 		/// <summary>
37 		///   The directory in which to place linked assemblies.
38 		/// </summary>
39 		[Required]
40 		public ITaskItem OutputDirectory { get; set; }
41 
42 		/// <summary>
43 		///   A list of XML root descriptor files specifying linker
44 		///   roots at a granular level. See the mono/linker
45 		///   documentation for details about the format.
46 		/// </summary>
47 		public ITaskItem [] RootDescriptorFiles { get; set; }
48 
49 		/// <summary>
50 		///   Extra arguments to pass to illink, delimited by spaces.
51 		/// </summary>
52 		public string ExtraArgs { get; set; }
53 
54 		/// <summary>
55 		///   Make illink dump dependencies file for linker analyzer tool.
56 		/// </summary>
57 		public bool DumpDependencies { get; set; }
58 
Execute()59 		public override bool Execute ()
60 		{
61 			string [] args = GenerateCommandLineCommands ();
62 			var argsString = String.Join (" ", args);
63 			Log.LogMessageFromText ($"illink {argsString}", MessageImportance.Normal);
64 			int ret = Mono.Linker.Driver.Main (args);
65 			return ret == 0;
66 		}
67 
GenerateCommandLineCommands()68 		string [] GenerateCommandLineCommands ()
69 		{
70 			var args = new List<string> ();
71 
72 			if (RootDescriptorFiles != null) {
73 				foreach (var rootFile in RootDescriptorFiles) {
74 					args.Add ("-x");
75 					args.Add (rootFile.ItemSpec);
76 				}
77 			}
78 
79 			foreach (var assemblyItem in RootAssemblyNames) {
80 				args.Add ("-a");
81 				args.Add (assemblyItem.ItemSpec);
82 			}
83 
84 			HashSet<string> directories = new HashSet<string> ();
85 			foreach (var assembly in AssemblyPaths) {
86 				var assemblyPath = assembly.ItemSpec;
87 				var dir = Path.GetDirectoryName (assemblyPath);
88 				if (!directories.Contains (dir)) {
89 					directories.Add (dir);
90 					args.Add ("-d");
91 					args.Add (dir);
92 				}
93 
94 				string action = assembly.GetMetadata ("action");
95 				if ((action != null) && (action.Length > 0)) {
96 					args.Add ("-p");
97 					args.Add (action);
98 					args.Add (Path.GetFileNameWithoutExtension (assemblyPath));
99 				}
100 			}
101 
102 			if (OutputDirectory != null) {
103 				args.Add ("-out");
104 				args.Add (OutputDirectory.ItemSpec);
105 			}
106 
107 			if (ExtraArgs != null) {
108 				args.AddRange (ExtraArgs.Split (' '));
109 			}
110 
111 			if (DumpDependencies)
112 				args.Add ("--dump-dependencies");
113 
114 			return args.ToArray ();
115 		}
116 
117 	}
118 }
119