2 // mkbundle: tool to create bundles.
4 // Based on the `make-bundle' Perl script written by Paolo Molaro (lupus@debian.org)
9 // (C) Novell, Inc 2004
12 using System
.Diagnostics
;
14 using System
.Collections
;
15 using System
.Reflection
;
17 using System
.Runtime
.InteropServices
;
20 using ICSharpCode
.SharpZipLib
.Zip
.Compression
.Streams
;
23 static string output
= "a.out";
24 static string object_out
= null;
25 static ArrayList link_paths
= new ArrayList ();
26 static bool autodeps
= false;
27 static bool keeptemp
= false;
28 static bool compile_only
= false;
29 static bool static_link
= false;
30 static string config_file
= null;
31 static string machine_config_file
= null;
32 static string config_dir
= null;
33 static string style
= "linux";
37 static int Main (string [] args
)
39 ArrayList sources
= new ArrayList ();
40 int top
= args
.Length
;
45 for (int i
= 0; i
< top
; i
++){
47 case "--help": case "-h": case "-?":
68 object_out
= args
[++i
];
76 link_paths
.Add (args
[++i
]);
91 if (style
== "windows") {
92 Console
.Error
.WriteLine ("The option `{0}' is not supported on this platform.", args
[i
]);
96 Console
.WriteLine ("Note that statically linking the LGPL Mono runtime has more licensing restrictions than dynamically linking.");
97 Console
.WriteLine ("See http://www.mono-project.com/Licensing for details on licensing.");
105 config_file
= args
[++i
];
107 case "--machine-config":
113 machine_config_file
= args
[++i
];
115 Console
.WriteLine ("WARNING:\n Check that the machine.config file you are bundling\n doesn't contain sensitive information specific to this machine.");
123 config_dir
= args
[++i
];
132 sources
.Add (args
[i
]);
137 Console
.WriteLine ("Sources: {0} Auto-dependencies: {1}", sources
.Count
, autodeps
);
138 if (sources
.Count
== 0 || output
== null) {
140 Environment
.Exit (1);
143 ArrayList assemblies
= LoadAssemblies (sources
);
144 ArrayList files
= new ArrayList ();
145 foreach (Assembly a
in assemblies
)
146 QueueAssembly (files
, a
.CodeBase
);
148 GenerateBundles (files
);
149 //GenerateJitWrapper ();
154 static void WriteSymbol (StreamWriter sw
, string name
, long size
)
160 "\t.section .rodata\n" +
162 "\t.type {0}, @object\n" +
163 "\t.size {0}, {1}\n" +
169 "\t.section __TEXT,__text,regular,pure_instructions\n" +
170 "\t.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32\n" +
180 "\t.section .rdata,\"dr\"\n" +
188 static void GenerateBundles (ArrayList files
)
190 string temp_s
= "temp.s"; // Path.GetTempFileName ();
191 string temp_c
= "temp.c";
192 string temp_o
= "temp.o";
196 if (object_out
!= null)
200 ArrayList c_bundle_names
= new ArrayList ();
201 ArrayList config_names
= new ArrayList ();
202 byte [] buffer
= new byte [8192];
204 using (StreamWriter ts
= new StreamWriter (File
.Create (temp_s
))) {
205 using (StreamWriter tc
= new StreamWriter (File
.Create (temp_c
))) {
208 tc
.WriteLine ("/* This source code was produced by mkbundle, do not edit */");
209 tc
.WriteLine ("#include <mono/metadata/assembly.h>\n");
212 tc
.WriteLine ("typedef struct _compressed_data {");
213 tc
.WriteLine ("\tMonoBundledAssembly assembly;");
214 tc
.WriteLine ("\tint compressed_size;");
215 tc
.WriteLine ("} CompressedAssembly;\n");
218 foreach (string url
in files
){
219 string fname
= new Uri (url
).LocalPath
;
220 string aname
= Path
.GetFileName (fname
);
221 string encoded
= aname
.Replace ("-", "_").Replace (".", "_");
226 Console
.WriteLine (" embedding: " + fname
);
228 Stream stream
= File
.OpenRead (fname
);
230 long real_size
= stream
.Length
;
233 MemoryStream ms
= new MemoryStream ();
234 DeflaterOutputStream deflate
= new DeflaterOutputStream (ms
);
235 while ((n
= stream
.Read (buffer
, 0, 8192)) != 0){
236 deflate
.Write (buffer
, 0, n
);
240 byte [] bytes
= ms
.GetBuffer ();
241 stream
= new MemoryStream (bytes
, 0, (int) ms
.Length
, false, false);
244 WriteSymbol (ts
, "assembly_data_" + encoded
, stream
.Length
);
246 while ((n
= stream
.Read (buffer
, 0, 8192)) != 0){
247 for (int i
= 0; i
< n
; i
++){
248 ts
.Write ("\t.byte {0}\n", buffer
[i
]);
254 tc
.WriteLine ("extern const unsigned char assembly_data_{0} [];", encoded
);
255 tc
.WriteLine ("static CompressedAssembly assembly_bundle_{0} = {{{{\"{1}\"," +
256 " assembly_data_{0}, {2}}}, {3}}};",
257 encoded
, aname
, real_size
, stream
.Length
);
258 double ratio
= ((double) stream
.Length
* 100) / real_size
;
259 Console
.WriteLine (" compression ratio: {0:.00}%", ratio
);
261 tc
.WriteLine ("extern const unsigned char assembly_data_{0} [];", encoded
);
262 tc
.WriteLine ("static const MonoBundledAssembly assembly_bundle_{0} = {{\"{1}\", assembly_data_{0}, {2}}};",
263 encoded
, aname
, real_size
);
267 c_bundle_names
.Add ("assembly_bundle_" + encoded
);
270 FileStream cf
= File
.OpenRead (fname
+ ".config");
271 Console
.WriteLine (" config from: " + fname
+ ".config");
272 tc
.WriteLine ("extern const unsigned char assembly_config_{0} [];", encoded
);
273 WriteSymbol (ts
, "assembly_config_" + encoded
, cf
.Length
);
274 while ((n
= cf
.Read (buffer
, 0, 8192)) != 0){
275 for (int i
= 0; i
< n
; i
++){
276 ts
.Write ("\t.byte {0}\n", buffer
[i
]);
280 config_names
.Add (new string[] {aname, encoded}
);
281 } catch (FileNotFoundException
) {
282 /* we ignore if the config file doesn't exist */
286 if (config_file
!= null){
289 conf
= File
.OpenRead (config_file
);
291 Error (String
.Format ("Failure to open {0}", config_file
));
294 Console
.WriteLine ("System config from: " + config_file
);
295 tc
.WriteLine ("extern const unsigned char system_config;");
296 WriteSymbol (ts
, "system_config", config_file
.Length
);
299 while ((n
= conf
.Read (buffer
, 0, 8192)) != 0){
300 for (int i
= 0; i
< n
; i
++){
301 ts
.Write ("\t.byte {0}\n", buffer
[i
]);
305 ts
.Write ("\t.byte 0\n");
309 if (machine_config_file
!= null){
312 conf
= File
.OpenRead (machine_config_file
);
314 Error (String
.Format ("Failure to open {0}", machine_config_file
));
317 Console
.WriteLine ("Machine config from: " + machine_config_file
);
318 tc
.WriteLine ("extern const unsigned char machine_config;");
319 WriteSymbol (ts
, "machine_config", machine_config_file
.Length
);
322 while ((n
= conf
.Read (buffer
, 0, 8192)) != 0){
323 for (int i
= 0; i
< n
; i
++){
324 ts
.Write ("\t.byte {0}\n", buffer
[i
]);
328 ts
.Write ("\t.byte 0\n");
333 Console
.WriteLine ("Compiling:");
334 string cmd
= String
.Format ("{0} -o {1} {2} ", GetEnv ("AS", "as"), temp_o
, temp_s
);
335 int ret
= Execute (cmd
);
342 tc
.WriteLine ("\nstatic const CompressedAssembly *compressed [] = {");
344 tc
.WriteLine ("\nstatic const MonoBundledAssembly *bundled [] = {");
346 foreach (string c
in c_bundle_names
){
347 tc
.WriteLine ("\t&{0},", c
);
349 tc
.WriteLine ("\tNULL\n};\n");
350 tc
.WriteLine ("static char *image_name = \"{0}\";", prog
);
352 tc
.WriteLine ("\nstatic void install_dll_config_files (void) {\n");
353 foreach (string[] ass
in config_names
){
354 tc
.WriteLine ("\tmono_register_config_for_assembly (\"{0}\", assembly_config_{1});\n", ass
[0], ass
[1]);
356 if (config_file
!= null)
357 tc
.WriteLine ("\tmono_config_parse_memory (&system_config);\n");
358 if (machine_config_file
!= null)
359 tc
.WriteLine ("\tmono_register_machine_config (&machine_config);\n");
360 tc
.WriteLine ("}\n");
362 if (config_dir
!= null)
363 tc
.WriteLine ("static const char *config_dir = \"{0}\";", config_dir
);
365 tc
.WriteLine ("static const char *config_dir = NULL;");
367 Stream template_stream
;
369 template_stream
= Assembly
.GetAssembly (typeof(MakeBundle
)).GetManifestResourceStream ("template_z.c");
371 template_stream
= Assembly
.GetAssembly (typeof(MakeBundle
)).GetManifestResourceStream ("template.c");
374 StreamReader s
= new StreamReader (template_stream
);
375 string template
= s
.ReadToEnd ();
379 Stream template_main_stream
= Assembly
.GetAssembly (typeof(MakeBundle
)).GetManifestResourceStream ("template_main.c");
380 StreamReader st
= new StreamReader (template_main_stream
);
381 string maintemplate
= st
.ReadToEnd ();
382 tc
.Write (maintemplate
);
390 string zlib
= (compress
? "-lz" : "");
391 string debugging
= "-g";
392 string cc
= GetEnv ("CC", IsUnix
? "cc" : "gcc -mno-cygwin");
394 if (style
== "linux")
399 smonolib
= "`pkg-config --variable=libdir mono-2`/libmono-2.0.a ";
401 smonolib
= "-Wl,-Bstatic -lmono-2.0 -Wl,-Bdynamic ";
402 cmd
= String
.Format ("{4} -o {2} -Wall `pkg-config --cflags mono-2` {0} {3} " +
403 "`pkg-config --libs-only-L mono-2` " + smonolib
+
404 "`pkg-config --libs-only-l mono-2 | sed -e \"s/\\-lmono-2.0 //\"` {1}",
405 temp_c
, temp_o
, output
, zlib
, cc
);
408 cmd
= String
.Format ("{4} " + debugging
+ " -o {2} -Wall {0} `pkg-config --cflags --libs mono-2` {3} {1}",
409 temp_c
, temp_o
, output
, zlib
, cc
);
417 Console
.WriteLine ("Done");
422 if (object_out
== null){
423 File
.Delete (temp_o
);
426 File
.Delete (temp_c
);
428 File
.Delete (temp_s
);
433 static ArrayList
LoadAssemblies (ArrayList sources
)
435 ArrayList assemblies
= new ArrayList ();
438 foreach (string name
in sources
){
439 Assembly a
= LoadAssembly (name
);
450 Environment
.Exit (1);
455 static void QueueAssembly (ArrayList files
, string codebase
)
457 if (files
.Contains (codebase
))
460 files
.Add (codebase
);
461 Assembly a
= Assembly
.LoadFrom (new Uri(codebase
).LocalPath
);
466 foreach (AssemblyName an
in a
.GetReferencedAssemblies ()) {
467 a
= Assembly
.Load (an
);
468 QueueAssembly (files
, a
.CodeBase
);
472 static Assembly
LoadAssembly (string assembly
)
477 char[] path_chars
= { '/', '\\' }
;
479 if (assembly
.IndexOfAny (path_chars
) != -1) {
480 a
= Assembly
.LoadFrom (assembly
);
482 string ass
= assembly
;
483 if (ass
.EndsWith (".dll"))
484 ass
= assembly
.Substring (0, assembly
.Length
- 4);
485 a
= Assembly
.Load (ass
);
488 } catch (FileNotFoundException
){
489 string total_log
= "";
491 foreach (string dir
in link_paths
){
492 string full_path
= Path
.Combine (dir
, assembly
);
493 if (!assembly
.EndsWith (".dll") && !assembly
.EndsWith (".exe"))
497 a
= Assembly
.LoadFrom (full_path
);
499 } catch (FileNotFoundException ff
) {
500 total_log
+= ff
.FusionLog
;
504 Error ("Cannot find assembly `" + assembly
+ "'" );
505 Console
.WriteLine ("Log: \n" + total_log
);
506 } catch (BadImageFormatException f
) {
507 Error ("Cannot load assembly (bad file format)" + f
.FusionLog
);
508 } catch (FileLoadException f
){
509 Error ("Cannot load assembly " + f
.FusionLog
);
510 } catch (ArgumentNullException
){
511 Error("Cannot load assembly (null argument)");
516 static void Error (string msg
)
518 Console
.Error
.WriteLine (msg
);
519 Environment
.Exit (1);
524 Console
.WriteLine ("Usage is: mkbundle [options] assembly1 [assembly2...]\n\n" +
526 " -c Produce stub only, do not compile\n" +
527 " -o out Specifies output filename\n" +
528 " -oo obj Specifies output filename for helper object file\n" +
529 " -L path Adds `path' to the search path for assemblies\n" +
530 " --nodeps Turns off automatic dependency embedding (default)\n" +
531 " --deps Turns on automatic dependency embedding\n" +
532 " --keeptemp Keeps the temporary files\n" +
533 " --config F Bundle system config file `F'\n" +
534 " --config-dir D Set MONO_CFG_DIR to `D'\n" +
535 " --machine-config F Use the given file as the machine.config for the application.\n" +
536 " --static Statically link to mono libs\n" +
537 " --nomain Don't include a main() function, for libraries\n" +
538 " -z Compress the assemblies before embedding.\n");
542 static extern int system (string s
);
544 static extern int uname (IntPtr buf
);
546 static void DetectOS ()
549 Console
.WriteLine ("OS is: Windows");
554 IntPtr buf
= UnixMarshal
.AllocHeap(8192);
555 if (uname (buf
) != 0){
556 Console
.WriteLine ("Warning: Unable to detect OS");
559 string os
= Marshal
.PtrToStringAnsi (buf
);
560 Console
.WriteLine ("OS is: " + os
);
564 UnixMarshal
.FreeHeap(buf
);
569 int p
= (int) Environment
.OSVersion
.Platform
;
570 return ((p
== 4) || (p
== 128) || (p
== 6));
574 static int Execute (string cmdLine
)
577 Console
.WriteLine (cmdLine
);
578 return system (cmdLine
);
581 // on Windows, we have to pipe the output of a
582 // `cmd` interpolation to dos2unix, because the shell does not
583 // strip the CRLFs generated by the native pkg-config distributed
585 StringBuilder b
= new StringBuilder ();
587 for (int i
= 0; i
< cmdLine
.Length
; i
++) {
588 if (cmdLine
[i
] == '`') {
589 if (count
% 2 != 0) {
590 b
.Append ("|dos2unix");
594 b
.Append (cmdLine
[i
]);
596 cmdLine
= b
.ToString ();
597 Console
.WriteLine (cmdLine
);
599 ProcessStartInfo psi
= new ProcessStartInfo ();
600 psi
.UseShellExecute
= false;
602 psi
.Arguments
= String
.Format ("-c \"{0}\"", cmdLine
);
604 using (Process p
= Process
.Start (psi
)) {
610 static string GetEnv (string name
, string defaultValue
)
612 string s
= Environment
.GetEnvironmentVariable (name
);
613 return s
!= null ? s
: defaultValue
;