Merge pull request #2542 from akoeplinger/remove-changelog
[mono.git] / mcs / tools / macpack / MacPack.cs
1 //
2 // Author: Geoff Norton
3 // de-MonoOptionification: miguel.
4 //
5 // Copyright (C) 2004-2005 Geoff Norton.
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26
27 using System;
28 using System.Collections;
29 using System.IO;
30 using System.Text;
31 using System.Reflection;
32 using System.Runtime.InteropServices;
33
34 namespace Mac {
35
36         public class PackOptions {
37                 public string appname;
38                 public string output;
39                 public string assembly;
40                 public string icon;
41                 public string[] resource;
42                 public int mode;
43         }
44         public class Pack {
45
46                 private PackOptions opts;
47
48                 public Pack () {}
49
50                 public Pack (PackOptions opts) {
51                         this.opts = opts;
52                 }
53
54                 public bool Generate () {
55                         if (opts.output == null){
56                                 opts.output = ".";
57                         }
58                         
59                         if (opts.assembly == null){
60                                 Console.Error.WriteLine ("Error: No assembly to macpack was specified");
61                                 Usage ();
62                                 return false;
63                         }
64
65                         if (opts.appname == null){
66                                 string t = Path.ChangeExtension (opts.assembly, null);
67                                 int p = t.IndexOf (Path.DirectorySeparatorChar);
68                                 if (p != -1)
69                                         t = t.Substring (p+1);
70                                 
71                                 opts.appname = t;
72                         }
73
74                         if (Directory.Exists (Path.Combine (opts.output, String.Format ("{0}.app", opts.appname)))) {
75                                 Console.WriteLine ("ERROR: That application already exists.  Please delete it first");
76                                 return false;
77                         }
78                         Directory.CreateDirectory (Path.Combine (opts.output, String.Format ("{0}.app", opts.appname)));
79                         Directory.CreateDirectory (Path.Combine (opts.output, String.Format ("{0}.app/Contents", opts.appname)));
80                         Directory.CreateDirectory (Path.Combine (opts.output, String.Format ("{0}.app/Contents/MacOS", opts.appname)));
81                         Directory.CreateDirectory (Path.Combine (opts.output, String.Format ("{0}.app/Contents/Resources", opts.appname)));
82                         if (opts.resource != null) {
83                                 foreach (string res in opts.resource) {
84                                         try {
85                                                 if (Directory.Exists (res)) {
86                                                         CopyDirectory (res, Path.Combine (opts.output, String.Format ("{0}.app/Contents/Resources/{1}", opts.appname, Path.GetFileName (res))));
87                                                 } else {
88                                                         File.Copy (res, Path.Combine (opts.output, String.Format ("{0}.app/Contents/Resources/{1}", opts.appname, Path.GetFileName (res))));
89                                                 }
90                                         } catch  (Exception e){
91                                                 Console.Error.WriteLine ("Error while processing {0} (Details: {1})", res, e.GetType ());
92                                         }
93                                 }
94                         }
95                         if (opts.icon != null)
96                                 File.Copy (opts.icon, Path.Combine (opts.output, String.Format ("{0}.app/Contents/Resources/{1}", opts.appname, Path.GetFileName (opts.icon))));
97                         if (opts.mode <= 2) {
98                                 File.Copy (opts.assembly, Path.Combine (opts.output, String.Format ("{0}.app/Contents/Resources/{0}.exe", opts.appname))); 
99                         } else {
100                                 File.Copy (opts.assembly, Path.Combine (opts.output, String.Format ("{0}.app/Contents/Resources/{0}", opts.appname))); 
101                         }
102
103                         Stream s = Assembly.GetEntryAssembly ().GetManifestResourceStream ("LOADER");
104                         BinaryReader reader = new BinaryReader (s);
105                         byte[] data = reader.ReadBytes ((int)s.Length);
106                         reader.Close ();
107                         BinaryWriter writer = new BinaryWriter (File.Create (Path.Combine (opts.output, String.Format ("{0}.app/Contents/MacOS/{0}", opts.appname))));
108                         string script = Encoding.ASCII.GetString (data);
109                         switch (opts.mode) {
110                                 default:
111                                 case 0:
112                                         script = script.Replace ("%MWF_MODE%", "0");
113                                         script = script.Replace ("%COCOASHARP_MODE%", "0");
114                                         script = script.Replace ("%X11_MODE%", "0");
115                                         break;
116                                 case 1:
117                                         script = script.Replace ("%MWF_MODE%", "1");
118                                         script = script.Replace ("%COCOASHARP_MODE%", "0");
119                                         script = script.Replace ("%X11_MODE%", "0");
120                                         break;
121                                 case 2:
122                                         script = script.Replace ("%MWF_MODE%", "0");
123                                         script = script.Replace ("%COCOASHARP_MODE%", "1");
124                                         script = script.Replace ("%X11_MODE%", "0");
125                                         break;
126                                 case 3:
127                                         script = script.Replace ("%MWF_MODE%", "0");
128                                         script = script.Replace ("%COCOASHARP_MODE%", "0");
129                                         script = script.Replace ("%X11_MODE%", "1");
130                                         break;
131                         }
132                         data = Encoding.ASCII.GetBytes (script);
133                         writer.Write (data, 0, data.Length);
134                         writer.Close ();
135                         try {
136                                 chmod (Path.Combine (opts.output,
137                                                      String.Format ("{0}.app/Contents/MacOS/{0}", opts.appname)),
138                                        Convert.ToUInt32 ("755", 8));
139                         } catch {
140                                 Console.WriteLine ("WARNING: It was not possible to set the executable permissions on\n" +
141                                                    "the file {0}.app/Contents/MacOS/{0}, the bundle might not work", opts.appname);
142                         }
143
144                         s = Assembly.GetEntryAssembly ().GetManifestResourceStream ("PLIST");
145                         reader = new BinaryReader (s);
146                         data = reader.ReadBytes ((int)s.Length);
147                         reader.Close ();
148                         writer = new BinaryWriter (File.Create (Path.Combine (opts.output, String.Format ("{0}.app/Contents/Info.plist", opts.appname))));
149                         string plist = Encoding.UTF8.GetString (data);
150                         plist = plist.Replace ("%APPNAME%", opts.appname);
151                         plist = plist.Replace ("%ICONFILE%", Path.GetFileName (opts.icon));
152                         data = Encoding.UTF8.GetBytes (plist);
153                         writer.Write (data, 0, data.Length);
154                         writer.Close ();
155
156                         return true;
157                 }
158
159                 public static void CopyDirectory (string src, string dest) {
160                         string [] files;
161
162                         if (dest [dest.Length-1] != Path.DirectorySeparatorChar) {
163                                 dest += Path.DirectorySeparatorChar;
164                         }
165
166                         if (!Directory.Exists (dest)) {
167                                 Directory.CreateDirectory (dest);
168                         }
169
170                         files = Directory.GetFileSystemEntries (src);
171                         
172                         foreach (string file in files) {
173                                 if (Directory.Exists (file)) {
174                                         CopyDirectory (file, dest + Path.GetFileName (file));
175                                 } else {
176                                         File.Copy (file, dest + Path.GetFileName (file), true);
177                                 }
178                         }
179                 }
180
181                 static void Usage ()
182                 {
183                         Console.WriteLine ("\n" + 
184                                            "Usage is:\n" +
185                                            "macpack [options] assembly\n" +
186                                            "   -n appname  -appname:appname    Application Name\n" +
187                                            "   -o output   -output:OUTPUT      Output directory\n" +
188                                            "   -a assembly                     Assembly to pack\n" +
189                                            "   -i file     -icon file          Icon filename\n" +
190                                            "   -r resource1,resource2          Additional files to bundle\n" +
191                                            "   -m [winforms|cocoa|x11|console] The mode for the application");
192                 }
193                 
194                 static int Main (string [] args) {
195                         PackOptions options = new PackOptions ();
196                         ArrayList resources = new ArrayList ();
197                         
198                         for (int i = 0; i < args.Length; i++){
199                                 string s = args [i];
200                                 string key, value;
201                                 
202                                 if (s.Length > 2){
203                                         int p = s.IndexOf (':');
204                                         if (p != -1){
205                                                 key = s.Substring (0, p);
206                                                 value = s.Substring (p + 1);
207                                         } else {
208                                                 key = s;
209                                                 value = null;
210                                         }
211                                 } else {
212                                         key = s;
213                                         if (i+1 < args.Length)
214                                                 value = args [i+1];
215                                         else
216                                                 value = null;
217                                 }
218
219                                 switch (key){
220                                 case "-n": case "-appname":
221                                         options.appname = value;
222                                         break;
223                                 case "-o": case "-output":
224                                         options.output = value;
225                                         break;
226                                 case "-a": case "-assembly":
227                                         options.assembly = value;
228                                         break;
229                                 case "-i": case "-icon":
230                                         options.icon = value;
231                                         break;
232                                 case "-r": case "-resource":
233                                         foreach (string ss in value.Split (new char [] {','}))
234                                                 resources.Add (ss);
235                                         break;
236                                 case "-about":
237                                         Console.WriteLine ("MacPack 1.0 by Geoff Norton\n");
238                                         break;
239                                         
240                                 case "-m": case "-mode":
241                                         switch (value){
242                                         case "winforms":
243                                                 options.mode = 1;
244                                                 break;
245                                         case "x11":
246                                                 options.mode = 3;
247                                                 break;
248                                         case "console":
249                                                 options.mode = 0;
250                                                 break;
251                                         case "cocoa":
252                                                 options.mode = 2;
253                                                 break;
254                                         default:
255                                                 try {
256                                                         options.mode = Int32.Parse (value);
257                                                 } catch {
258                                                         Console.Error.WriteLine ("Could not recognize option {0} as the mode", value);
259                                                 }
260                                                 break;
261                                         }
262                                         break;
263
264                                 case "-h": case "-help":
265                                         Usage ();
266                                         break;
267                                         
268                                 default:
269                                         options.assembly = key;
270                                         break;
271                                 }
272                         }
273                         
274                         options.resource = (string [])resources.ToArray (typeof (string));
275                         Pack pack = new Pack (options);
276                         if (pack.Generate ())
277                                 return 0;
278                         return -1;
279                 }
280         
281                 [DllImport ("libc")]
282                 static extern int chmod (string path, uint mode);
283         }
284 }