2010-05-25 Jb Evain <jbevain@novell.com>
[mcs.git] / tools / macpack / MacPack.cs
blob405fac4d7c267de25d01045310eff036a0451fbe
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.
27 using System;
28 using System.Collections;
29 using System.IO;
30 using System.Text;
31 using System.Reflection;
32 using System.Runtime.InteropServices;
34 namespace Mac {
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;
44 public class Pack {
46 private PackOptions opts;
48 public Pack () {}
50 public Pack (PackOptions opts) {
51 this.opts = opts;
54 public bool Generate () {
55 if (opts.output == null){
56 opts.output = ".";
59 if (opts.assembly == null){
60 Console.Error.WriteLine ("Error: No assembly to macpack was specified");
61 Usage ();
62 return false;
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);
71 opts.appname = t;
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;
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))));
90 } catch (Exception e){
91 Console.Error.WriteLine ("Error while processing {0} (Details: {1})", res, e.GetType ());
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)));
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;
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);
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 ();
156 return true;
159 public static void CopyDirectory (string src, string dest) {
160 string [] files;
162 if (dest [dest.Length-1] != Path.DirectorySeparatorChar) {
163 dest += Path.DirectorySeparatorChar;
166 if (!Directory.Exists (dest)) {
167 Directory.CreateDirectory (dest);
170 files = Directory.GetFileSystemEntries (src);
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);
181 static void Usage ()
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");
194 static int Main (string [] args) {
195 PackOptions options = new PackOptions ();
196 ArrayList resources = new ArrayList ();
198 for (int i = 0; i < args.Length; i++){
199 string s = args [i];
200 string key, value;
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;
211 } else {
212 key = s;
213 if (i+1 < args.Length)
214 value = args [i+1];
215 else
216 value = null;
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;
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);
260 break;
262 break;
264 case "-h": case "-help":
265 Usage ();
266 break;
268 default:
269 options.assembly = key;
270 break;
274 options.resource = (string [])resources.ToArray (typeof (string));
275 Pack pack = new Pack (options);
276 if (pack.Generate ())
277 return 0;
278 return -1;
281 [DllImport ("libc")]
282 static extern int chmod (string path, uint mode);