Permalink
Fetching contributors…
Cannot retrieve contributors at this time
333 lines (291 sloc) 11.4 KB
//
// Authors:
// Miguel de Icaza
//
// Copyright 2011 Xamarin Inc.
// Copyright 2009-2010 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Options;
#if !MONOMAC
using MonoTouch.ObjCRuntime;
#endif
class BindingTouch {
#if MONOMAC
static string baselibdll = "MonoMac.dll";
static string RootNS = "MonoMac";
static Type CoreObject = typeof (MonoMac.Foundation.NSObject);
static string tool_name = "bmac";
static string compiler = "mcs";
static string net_sdk = "4";
#else
static string baselibdll = "/Developer/MonoTouch/usr/lib/mono/2.1/monotouch.dll";
static string RootNS = "MonoTouch";
static Type CoreObject = typeof (MonoTouch.Foundation.NSObject);
static string tool_name = "btouch";
static string compiler = "/Developer/MonoTouch/usr/bin/smcs";
static string net_sdk = null;
#endif
public static string ToolName {
get { return tool_name; }
}
static void ShowHelp (OptionSet os)
{
Console.WriteLine ("{0} - Mono Objective-C API binder", tool_name);
Console.WriteLine ("Usage is:\n {0} [options] apifile1.cs [apifileN] [-s=core1.cs [-s=core2.cs]] [-x=extra1.cs [-x=extra2.cs]]", tool_name);
os.WriteOptionDescriptions (Console.Out);
}
static int Main (string [] args)
{
try {
return Main2 (args);
} catch (Exception ex) {
ErrorHelper.Show (ex);
return 1;
}
}
static int Main2 (string [] args)
{
bool show_help = false;
bool zero_copy = false;
bool alpha = false;
string basedir = null;
string tmpdir = null;
string ns = null;
string outfile = null;
bool delete_temp = true, debug = false;
bool verbose = false;
bool unsafef = true;
bool external = false;
bool pmode = true;
bool nostdlib = false;
bool clean_mono_path = false;
bool native_exception_marshalling = false;
bool inline_selectors = false;
List<string> sources;
var resources = new List<string> ();
#if !MONOMAC
var linkwith = new List<string> ();
#endif
var references = new List<string> ();
var libs = new List<string> ();
var core_sources = new List<string> ();
var extra_sources = new List<string> ();
var defines = new List<string> ();
bool binding_third_party = true;
string generate_file_list = null;
var os = new OptionSet () {
{ "h|?|help", "Displays the help", v => show_help = true },
{ "a", "Include alpha bindings", v => alpha = true },
{ "outdir=", "Sets the output directory for the temporary binding files", v => { basedir = v; }},
{ "o|out=", "Sets the name of the output library", v => outfile = v },
{ "tmpdir=", "Sets the working directory for temp files", v => { tmpdir = v; delete_temp = false; }},
{ "debug", "Generates a debugging build of the binding", v => debug = true },
{ "sourceonly=", "Only generates the source", v => generate_file_list = v },
{ "ns=", "Sets the namespace for storing helper classes", v => ns = v },
{ "unsafe", "Sets the unsafe flag for the build", v=> unsafef = true },
#if MONOMAC
{ "core", "Use this to build monomac.dll", v => binding_third_party = false },
#else
{ "core", "Use this to build monotouch.dll", v => binding_third_party = false },
#endif
{ "r=", "Adds a reference", v => references.Add (v) },
{ "lib=", "Adds the directory to the search path for the compiler", v => libs.Add (v) },
{ "compiler=", "Sets the compiler to use", v => compiler = v },
{ "sdk=", "Sets the .NET SDK to use", v => net_sdk = v },
{ "d=", "Defines a symbol", v => defines.Add (v) },
{ "s=", "Adds a source file required to build the API", v => core_sources.Add (v) },
{ "v", "Sets verbose mode", v => verbose = true },
{ "x=", "Adds the specified file to the build, used after the core files are compiled", v => extra_sources.Add (v) },
{ "e", "Generates smaller classes that can not be subclassed (previously called 'external mode')", v => external = true },
{ "p", "Sets private mode", v => pmode = false },
{ "baselib=", "Sets the base library", v => baselibdll = v },
{ "use-zero-copy", v=> zero_copy = true },
{ "nostdlib", "Does not reference mscorlib.dll library", l => nostdlib = true },
{ "no-mono-path", "Launches compiler with empty MONO_PATH", l => clean_mono_path = true },
{ "native-exception-marshalling", "Enable the marshalling support for Objective-C exceptions", l => native_exception_marshalling = true },
{ "inline-selectors:", "If Selector.GetHandle is inlined and does not need to be cached (default: false)", v => inline_selectors = string.Equals ("true", v, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty (v) },
#if !MONOMAC
{ "link-with=,", "Link with a native library {0:FILE} to the binding, embedded as a resource named {1:ID}",
(path, id) => {
if (path == null || path.Length == 0)
throw new Exception ("-link-with=FILE,ID requires a filename.");
if (id == null || id.Length == 0)
id = Path.GetFileName (path);
if (linkwith.Contains (id))
throw new Exception ("-link-with=FILE,ID cannot assign the same resource id to multiple libraries.");
resources.Add (string.Format ("-res:{0},{1}", path, id));
linkwith.Add (id);
}
},
#endif
};
try {
sources = os.Parse (args);
} catch (Exception e){
Console.Error.WriteLine ("{0}: {1}", tool_name, e.Message);
Console.Error.WriteLine ("see {0} --help for more information", tool_name);
return 1;
}
if (show_help || sources.Count == 0){
Console.WriteLine ("Error: no api file provided");
ShowHelp (os);
return 0;
}
if (alpha)
defines.Add ("ALPHA");
if (tmpdir == null)
tmpdir = GetWorkDir ();
if (outfile == null)
outfile = Path.GetFileNameWithoutExtension (sources [0]) + ".dll";
string refs = (references.Count > 0 ? "-r:" + String.Join (" -r:", references.ToArray ()) : "");
string paths = (libs.Count > 0 ? "-lib:" + String.Join (" -lib:", libs.ToArray ()) : "");
try {
var api_file = sources [0];
var tmpass = Path.Combine (tmpdir, "temp.dll");
// -nowarn:436 is to avoid conflicts in definitions between core.dll and the sources
var cargs = String.Format ("{10} -debug -unsafe -target:library {0} -nowarn:436 -out:{1} -r:{2} {3} {4} {5} -r:{6} {7} {8} {9}",
string.Join (" ", sources.ToArray ()),
tmpass, Environment.GetCommandLineArgs ()[0],
string.Join (" ", core_sources.ToArray ()), refs, unsafef ? "-unsafe" : "",
baselibdll, string.Join (" ", defines.Select (x=> "-define:" + x).ToArray ()), paths,
nostdlib ? "-nostdlib" : null,
!String.IsNullOrEmpty (net_sdk) ? "-sdk:" + net_sdk : null);
var si = new ProcessStartInfo (compiler, cargs) {
UseShellExecute = false,
};
if (clean_mono_path) {
// HACK: We are calling btouch with forced 2.1 path but we need working mono for compiler
si.EnvironmentVariables.Remove ("MONO_PATH");
}
if (verbose)
Console.WriteLine ("{0} {1}", si.FileName, si.Arguments);
var p = Process.Start (si);
p.WaitForExit ();
if (p.ExitCode != 0){
Console.WriteLine ("{0}: API binding contains errors.", tool_name);
return 1;
}
Assembly api;
try {
api = Assembly.LoadFrom (tmpass);
} catch (Exception e) {
if (verbose)
Console.WriteLine (e);
Console.Error.WriteLine ("Error loading API definition from {0}", tmpass);
return 1;
}
Assembly baselib;
try {
baselib = Assembly.LoadFrom (baselibdll);
} catch (Exception e){
if (verbose)
Console.WriteLine (e);
Console.Error.WriteLine ("Error loading base library {0}", baselibdll);
return 1;
}
#if !MONOMAC
foreach (object attr in api.GetCustomAttributes (typeof (LinkWithAttribute), true)) {
LinkWithAttribute linkWith = (LinkWithAttribute) attr;
if (!linkwith.Contains (linkWith.LibraryName)) {
Console.Error.WriteLine ("Missing native library {0}, please use `--link-with' to specify the path to this library.", linkWith.LibraryName);
return 1;
}
}
#endif
var types = new List<Type> ();
foreach (var t in api.GetTypes ()){
if (t.GetCustomAttributes (typeof (BaseTypeAttribute), true).Length > 0 ||
t.GetCustomAttributes (typeof (StaticAttribute), true).Length > 0)
types.Add (t);
}
var g = new Generator (pmode, external, debug, types.ToArray ()){
MessagingNS = ns == null ? Path.GetFileNameWithoutExtension (api_file) : ns,
CoreMessagingNS = RootNS + ".ObjCRuntime",
BindThirdPartyLibrary = binding_third_party,
CoreNSObject = CoreObject,
BaseDir = basedir != null ? basedir : tmpdir,
ZeroCopyStrings = zero_copy,
NativeExceptionMarshalling = native_exception_marshalling,
#if MONOMAC
OnlyX86 = true,
#endif
Alpha = alpha,
InlineSelectors = inline_selectors,
};
foreach (var mi in baselib.GetType (RootNS + ".ObjCRuntime.Messaging").GetMethods ()){
if (mi.Name.IndexOf ("_objc_msgSend") != -1)
g.RegisterMethodName (mi.Name);
}
g.Go ();
if (generate_file_list != null){
using (var f = File.CreateText (generate_file_list)){
g.GeneratedFiles.ForEach (x => f.WriteLine (x));
}
return 0;
}
cargs = String.Format ("{0} -target:library -out:{1} {2} {3} {4} {5} {6} {7} -r:{8} {9} {10}",
unsafef ? "-unsafe" : "", /* 0 */
outfile, /* 1 */
string.Join (" ", defines.Select (x=> "-define:" + x).ToArray ()), /* 2 */
String.Join (" ", g.GeneratedFiles.ToArray ()), /* 3 */
String.Join (" ", core_sources.ToArray ()), /* 4 */
String.Join (" ", sources.Skip (1).ToArray ()), /* 5 */
String.Join (" ", extra_sources.ToArray ()), /* 6 */
refs, /* 7 */
baselibdll, /* 8 */
String.Join (" ", resources.ToArray ()), /* 9 */
nostdlib ? "-nostdlib" : null
);
si = new ProcessStartInfo (compiler, cargs) {
UseShellExecute = false,
};
if (verbose)
Console.WriteLine ("{0} {1}", si.FileName, si.Arguments);
p = Process.Start (si);
p.WaitForExit ();
if (p.ExitCode != 0){
Console.WriteLine ("{0}: API binding contains errors.", tool_name);
return 1;
}
} finally {
if (delete_temp)
Directory.Delete (tmpdir, true);
}
return 0;
}
static string GetWorkDir ()
{
while (true){
string p = Path.Combine (Path.GetTempPath(), Path.GetRandomFileName());
if (Directory.Exists (p))
continue;
var di = Directory.CreateDirectory (p);
return di.FullName;
}
}
}