2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / tools / mkbundle / mkbundle.cs
1 //
2 // mkbundle: tool to create bundles.
3 //
4 // Based on the `make-bundle' Perl script written by Paolo Molaro (lupus@debian.org)
5 //
6 // Author:
7 //   Miguel de Icaza
8 //
9 // (C) Novell, Inc 2004
10 //
11 using System;
12 using System.Xml;
13 using System.Collections;
14 using System.Reflection;
15 using System.IO;
16 using System.Runtime.InteropServices;
17 using Mono.Posix;
18
19 class MakeBundle {
20         static string output = "a.out";
21         static string object_out = null;
22         static ArrayList link_paths = new ArrayList ();
23         static bool autodeps = false;
24         static bool keeptemp = false;
25         static bool compile_only = false;
26         
27         static int Main (string [] args)
28         {
29                 ArrayList sources = new ArrayList ();
30                 int top = args.Length;
31                 link_paths.Add (".");
32                 
33                 for (int i = 0; i < top; i++){
34                         switch (args [i]){
35                         case "-h":
36                                 Help ();
37                                 return 1;
38
39                         case "-c":
40                                 compile_only = true;
41                                 break;
42                                 
43                         case "-o": 
44                                 if (i+1 == top){
45                                         Help (); 
46                                         return 1;
47                                 }
48                                 output = args [++i];
49                                 break;
50
51                         case "-oo":
52                                 if (i+1 == top){
53                                         Help (); 
54                                         return 1;
55                                 }
56                                 object_out = args [++i];
57                                 break;
58
59                         case "-L":
60                                 if (i+1 == top){
61                                         Help (); 
62                                         return 1;
63                                 }
64                                 link_paths.Add (args [i]);
65                                 break;
66
67                         case "--nodeps":
68                                 autodeps = false;
69                                 break;
70
71                         case "--deps":
72                                 autodeps = true;
73                                 break;
74
75                         case "--keeptemp":
76                                 keeptemp = true;
77                                 break;
78                                 
79                         default:
80                                 sources.Add (args [i]);
81                                 break;
82                         }
83                 }
84
85                 Console.WriteLine ("Sources: {0} Auto-dependencies: {1}", sources.Count, autodeps);
86                 if (sources.Count == 0 || output == null) {
87                         Help ();
88                         Environment.Exit (1);
89                 }
90
91                 ArrayList assemblies = LoadAssemblies (sources);
92                 ArrayList files = new ArrayList ();
93                 foreach (Assembly a in assemblies)
94                         QueueAssembly (files, a.CodeBase);
95
96                 GenerateBundles (files);
97                 //GenerateJitWrapper ();
98                 
99                 return 0;
100         }
101
102         static void GenerateBundles (ArrayList files)
103         {
104                 string temp_s = "temp.s"; // Path.GetTempFileName ();
105                 string temp_c = "temp.c";
106                 string temp_o = Path.GetTempFileName () + ".o";
107
108                 if (compile_only)
109                         temp_c = output;
110                 if (object_out != null)
111                         temp_o = object_out;
112                 
113                 try {
114                         ArrayList c_bundle_names = new ArrayList ();
115                         byte [] buffer = new byte [8192];
116
117                         StreamWriter ts = new StreamWriter (File.Create (temp_s));
118                         StreamWriter tc = new StreamWriter (File.Create (temp_c));
119                         string prog = null;
120
121                         tc.WriteLine ("/* This source code was produced by mkbundle, do not edit */");
122                         tc.WriteLine ("#include <mono/metadata/assembly.h>\n");
123                         foreach (string url in files){
124                                 string fname = url.Substring (7);
125                                 string aname = fname.Substring (fname.LastIndexOf ("/") + 1);
126                                 string encoded = aname.Replace ("-", "_").Replace (".", "_");
127
128                                 if (prog == null)
129                                         prog = aname;
130                                 
131                                 Console.WriteLine ("   embedding: " + fname);
132                                 
133                                 FileInfo fi = new FileInfo (fname);
134                                 FileStream fs = File.OpenRead (fname);
135
136                                 ts.WriteLine (
137                                         ".globl assembly_data_{0}\n" +
138                                         "\t.section .rodata\n" +
139                                         "\t.align 32\n" +
140                                         "\t.type assembly_data_{0}, @object\n" +
141                                         "\t.size assembly_data_{0}, {1}\n" +
142                                         "assembly_data_{0}:\n",
143                                         encoded, fi.Length);
144
145
146                                 int n;
147                                 while ((n = fs.Read (buffer, 0, 8192)) != 0){
148                                         for (int i = 0; i < n; i++){
149                                                 ts.Write ("\t.byte {0}\n", buffer [i]);
150                                         }
151                                 }
152                                 ts.WriteLine ();
153
154                                 tc.WriteLine ("extern const unsigned char assembly_data_{0} [];", encoded);
155                                 tc.WriteLine ("static const MonoBundledAssembly assembly_bundle_{0} = {{\"{1}\", assembly_data_{0}, {2}}};",
156                                               encoded, aname, fi.Length);
157
158                                 c_bundle_names.Add ("assembly_bundle_" + encoded);
159                         }
160                         ts.Close ();
161                         Console.WriteLine ("Compiling:");
162                         string cmd = String.Format ("as -o {0} {1} ", temp_o, temp_s);
163                         Console.WriteLine (cmd);
164                         int ret = system (cmd);
165                         if (ret != 0){
166                                 Error ("[Fail]");
167                                 return;
168                         }
169
170                         tc.WriteLine ("\nstatic const MonoBundledAssembly *bundled [] = {");
171                         foreach (string c in c_bundle_names){
172                                 tc.WriteLine ("\t&{0},", c);
173                         }
174                         tc.WriteLine ("\tNULL\n};\n");
175                         tc.WriteLine ("static char *image_name = \"{0}\";", prog);
176                                       
177                         StreamReader s = new StreamReader (Assembly.GetAssembly (typeof(MakeBundle)).GetManifestResourceStream ("template.c"));
178                         tc.Write (s.ReadToEnd ());
179                         tc.Close ();
180
181                         if (compile_only)
182                                 return;
183                         
184                         cmd = String.Format ("cc -o {2} -Wall {0} `pkg-config --cflags --libs mono` {1}",
185                                              temp_c, temp_o, output);
186                         Console.WriteLine (cmd);
187                         ret = system (cmd);
188                         if (ret != 0){
189                                 Error ("[Fail]");
190                                 return;
191                         }
192                         Console.WriteLine ("Done");
193                 } finally {
194                         if (!keeptemp){
195                                 if (object_out == null){
196                                         File.Delete (temp_o);
197                                 }
198                                 if (!compile_only){
199                                         File.Delete (temp_c);
200                                 }
201                                 File.Delete (temp_s);
202                         }
203                 }
204         }
205         
206         static ArrayList LoadAssemblies (ArrayList sources)
207         {
208                 ArrayList assemblies = new ArrayList ();
209                 bool error = false;
210                 
211                 foreach (string name in sources){
212                         Assembly a = LoadAssembly (name);
213
214                         if (a == null){
215                                 error = true;
216                                 continue;
217                         }
218                         
219                         assemblies.Add (a);
220                 }
221
222                 if (error)
223                         Environment.Exit (1);
224
225                 return assemblies;
226         }
227         
228         static void QueueAssembly (ArrayList files, string codebase)
229         {
230                 if (files.Contains (codebase))
231                         return;
232
233                 files.Add (codebase);
234                 Assembly a = Assembly.LoadFrom (codebase);
235
236                 if (!autodeps)
237                         return;
238                 
239                 foreach (AssemblyName an in a.GetReferencedAssemblies ())
240                         QueueAssembly (files, an.CodeBase);
241         }
242
243         static Assembly LoadAssembly (string assembly)
244         {
245                 Assembly a;
246                 
247                 try {
248                         char[] path_chars = { '/', '\\' };
249                         
250                         if (assembly.IndexOfAny (path_chars) != -1) {
251                                 a = Assembly.LoadFrom (assembly);
252                         } else {
253                                 string ass = assembly;
254                                 if (ass.EndsWith (".dll"))
255                                         ass = assembly.Substring (0, assembly.Length - 4);
256                                 a = Assembly.Load (ass);
257                         }
258                         return a;
259                 } catch (FileNotFoundException){
260                         string total_log = "";
261                         
262                         foreach (string dir in link_paths){
263                                 string full_path = Path.Combine (dir, assembly);
264                                 if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
265                                         full_path += ".dll";
266                                 
267                                 try {
268                                         a = Assembly.LoadFrom (full_path);
269                                         return a;
270                                 } catch (FileNotFoundException ff) {
271                                         total_log += ff.FusionLog;
272                                         continue;
273                                 }
274                         }
275                         Error ("Cannot find assembly `" + assembly + "'" );
276                         Console.WriteLine ("Log: \n" + total_log);
277                 } catch (BadImageFormatException f) {
278                         Error ("Cannot load assembly (bad file format)" + f.FusionLog);
279                 } catch (FileLoadException f){
280                         Error ("Cannot load assembly " + f.FusionLog);
281                 } catch (ArgumentNullException){
282                         Error("Cannot load assembly (null argument)");
283                 }
284                 return null;
285         }
286
287         static void Error (string msg)
288         {
289                 Console.Error.WriteLine (msg);
290                 Environment.Exit (1);
291         }
292
293         static void Help ()
294         {
295                 Console.WriteLine ("Usage is: mkbundle [options] assembly1 [assembly2...]\n\n" +
296                                    "Options:\n" +
297                                    "    -c          Produce stub only, do not compile\n" +
298                                    "    -o out      Specifies output filename\n" +
299                                    "    -oo obj     Specifies output filename for helper object file" +
300                                    "    -L path     Adds `path' to the search path for assemblies\n" +
301                                    "    --nodeps    Turns off automatic dependency embedding (default)\n" +
302                                    "    --deps      Turns on automatic dependency embedding\n" +
303                                    "    --keeptemp  Keeps the temporary files\n");
304         }
305
306         [DllImport ("libc")]
307         static extern int system (string s);
308 }
309