#region License // // The Open Toolkit Library License // // Copyright (c) 2006 - 2010 the Open Toolkit library. // // 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. // #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using Bind.Structures; namespace Bind { using Delegate = Bind.Structures.Delegate; using Enum = Bind.Structures.Enum; using Type = Bind.Structures.Type; sealed class CppSpecWriter : ISpecWriter { readonly char[] numbers = "0123456789".ToCharArray(); const string AllowDeprecated = "ALLOW_DEPRECATED_GL"; const string DigitPrefix = "T"; // Prefix for identifiers that start with a digit const string OutputFileCpp = "glcore++.cpp"; const string OutputFileHeader = "glcore++.h"; const string OutputFileHeaderCompat = "glcompat++.h"; const string OutputFileHeaderEnums = "glenums++.h"; BindStreamWriter sw_h = new BindStreamWriter(Path.GetTempFileName()); BindStreamWriter sw_cpp = new BindStreamWriter(Path.GetTempFileName()); BindStreamWriter sw_h_compat = new BindStreamWriter(Path.GetTempFileName()); BindStreamWriter sw_cpp_compat = new BindStreamWriter(Path.GetTempFileName()); BindStreamWriter sw_h_enums = new BindStreamWriter(Path.GetTempFileName()); #region WriteBindings public void WriteBindings(IBind generator) { WriteBindings(generator.Delegates, generator.Wrappers, generator.Enums); } void WriteBindings(DelegateCollection delegates, FunctionCollection wrappers, EnumCollection enums) { Console.WriteLine("Writing bindings to {0}", Settings.OutputPath); if (!Directory.Exists(Settings.OutputPath)) Directory.CreateDirectory(Settings.OutputPath); // Hack: Fix 3dfx extension category so it doesn't start with a digit if (wrappers.ContainsKey("3dfx")) { var three_dee_fx = wrappers["3dfx"]; wrappers.Remove("3dfx"); wrappers.Add(DigitPrefix + "3dfx", three_dee_fx); } Settings.DefaultOutputNamespace = "OpenTK"; // Enums using (var sw = sw_h_enums) { WriteEnums(sw, enums); sw.Flush(); sw.Close(); } // Core definitions using (var sw = sw_h) { WriteDefinitions(sw, enums, wrappers, Type.CSTypes, false); sw.Flush(); sw.Close(); } // Compatibility definitions using (var sw = sw_h_compat) { WriteDefinitions(sw, enums, wrappers, Type.CSTypes, true); sw.Flush(); sw.Close(); } // Core & compatibility declarations using (var sw = sw_cpp) { WriteDeclarations(sw, wrappers, Type.CSTypes); sw.Flush(); sw.Close(); } string output_header = Path.Combine(Settings.OutputPath, OutputFileHeader); string output_cpp = Path.Combine(Settings.OutputPath, OutputFileCpp); string output_header_compat = Path.Combine(Settings.OutputPath, OutputFileHeaderCompat); string output_header_enums = Path.Combine(Settings.OutputPath, OutputFileHeaderEnums); Move(sw_h.File, output_header); Move(sw_cpp.File, output_cpp); Move(sw_h_compat.File, output_header_compat); Move(sw_h_enums.File, output_header_enums); } void Move(string file, string dest) { if (File.Exists(dest)) File.Delete(dest); File.Move(file, dest); } #endregion #region WriteDefinitions void WriteDefinitions(BindStreamWriter sw, EnumCollection enums, FunctionCollection wrappers, Dictionary CSTypes, bool deprecated_only) { sw.WriteLine("#pragma once"); if (!deprecated_only) { sw.WriteLine("#ifndef GLCOREPP_H"); sw.WriteLine("#define GLCOREPP_H"); } else { sw.WriteLine("#ifndef GLCOMPATPP_H"); sw.WriteLine("#define GLCOMPATPP_H"); } sw.WriteLine(); WriteLicense(sw); sw.WriteLine("namespace {0}", Settings.OutputNamespace); sw.WriteLine("{"); sw.Indent(); sw.WriteLine("namespace {0}", Settings.GLClass); sw.WriteLine("{"); sw.Indent(); foreach (string extension in wrappers.Keys) { if (extension != "Core") { sw.WriteLine("namespace {0}", extension); sw.WriteLine("{"); sw.Indent(); } // Avoid multiple definitions of the same function Delegate last_delegate = null; // Write delegates sw.WriteLine("namespace Delegates"); sw.WriteLine("{"); sw.Indent(); var functions = wrappers[extension].Where(f => f.Deprecated == deprecated_only); last_delegate = null; foreach (var f in functions) { WriteDelegate(sw, f.WrappedDelegate, ref last_delegate); } sw.Unindent(); sw.WriteLine("};"); // Write wrappers sw.WriteLine("void Init();"); last_delegate = null; foreach (var f in functions) { if (last_delegate == f.WrappedDelegate) continue; last_delegate = f.WrappedDelegate; var parameters = f.WrappedDelegate.Parameters.ToString() .Replace("String[]", "String*") .Replace("[OutAttribute]", String.Empty); sw.WriteLine("inline {0} {1}{2}", f.WrappedDelegate.ReturnType, f.TrimmedName, parameters); sw.WriteLine("{"); sw.Indent(); WriteMethodBody(sw, f); sw.Unindent(); sw.WriteLine("}"); } if (extension != "Core") { sw.Unindent(); sw.WriteLine("};"); } } sw.Unindent(); sw.WriteLine("};"); sw.Unindent(); sw.WriteLine("}"); sw.WriteLine("#endif"); } #endregion #region WriteDeclarations void WriteDeclarations(BindStreamWriter sw, FunctionCollection wrappers, Dictionary CSTypes) { sw.WriteLine("#ifdef GLPP_INTERNAL_COMPILE_DECLARATIONS"); WriteLicense(sw); sw.WriteLine("namespace {0}", Settings.OutputNamespace); sw.WriteLine("{"); sw.Indent(); sw.WriteLine("using namespace Internals;"); // Used to avoid multiple declarations of the same function Delegate last_delegate = null; // Declare all functions (deprecated and core). // This is necessary for projects that wish to move from // deprecated APIs to core piece-by-piece. foreach (var ext in wrappers.Keys) { last_delegate = null; var functions = wrappers[ext]; foreach (var function in functions) { if (function.WrappedDelegate == last_delegate) continue; last_delegate = function.WrappedDelegate; string path = GetNamespace(ext); sw.WriteLine("{0}::Delegates::p{1} {0}::Delegates::{1} = 0;", path, function.Name); } } sw.WriteLine(); // Add Init() methods foreach (var ext in wrappers.Keys) { string path = GetNamespace(ext); sw.WriteLine("void {0}::Init()", path); sw.WriteLine("{"); sw.Indent(); last_delegate = null; var functions = wrappers[ext]; foreach (var function in functions) { if (function.WrappedDelegate == last_delegate) continue; last_delegate = function.WrappedDelegate; sw.WriteLine("{0}::Delegates::{1} = ({0}::Delegates::p{1})GetAddress(\"gl{1}\");", path, function.WrappedDelegate.Name); } sw.Unindent(); sw.WriteLine("}"); } sw.Unindent(); sw.WriteLine("}"); sw.WriteLine("#endif"); } static string GetNamespace(string ext) { if (ext == "Core") return Settings.GLClass; else return String.Format("{0}::{1}", Settings.GLClass, Char.IsDigit(ext[0]) ? DigitPrefix + ext : ext); } #endregion #region ISpecWriter Members #region WriteEnums public void WriteEnums(BindStreamWriter sw, EnumCollection enums) { sw.WriteLine("#pragma once"); sw.WriteLine("#ifndef GLENUMSPP_H"); sw.WriteLine("#define GLENUMSPP_H"); sw.WriteLine(); WriteLicense(sw); sw.WriteLine("namespace {0}", Settings.OutputNamespace); sw.WriteLine("{"); sw.Indent(); foreach (Enum @enum in enums.Values) { sw.WriteLine("struct {0} : Enumeration<{0}>", @enum.Name); sw.WriteLine("{"); sw.Indent(); sw.WriteLine("inline {0}(int value) : Enumeration<{0}>(value) {{ }}", @enum.Name); sw.WriteLine("enum"); sw.WriteLine("{"); sw.Indent(); foreach (var c in @enum.ConstantCollection.Values) { // C++ doesn't have the concept of "unchecked", so remove this. if (!c.Unchecked) sw.WriteLine("{0},", c); else sw.WriteLine("{0},", c.ToString().Replace("unchecked", String.Empty)); } sw.Unindent(); sw.WriteLine("};"); sw.Unindent(); sw.WriteLine("};"); sw.WriteLine(); } sw.Unindent(); sw.WriteLine("}"); sw.WriteLine("#endif"); } #endregion #region WriteTypes public void WriteTypes(BindStreamWriter sw, Dictionary CSTypes) { sw.WriteLine(); foreach (string s in CSTypes.Keys) { sw.WriteLine("typedef {0} {1};", s, CSTypes[s]); } } #endregion #region WriteDelegates public void WriteDelegates(BindStreamWriter sw, DelegateCollection delegates) { throw new NotSupportedException(); } static void WriteDelegate(BindStreamWriter sw, Delegate d, ref Delegate last_delegate) { // Avoid multiple definitions of the same function if (d != last_delegate) { last_delegate = d; var parameters = d.Parameters.ToString() .Replace("String[]", "String*") .Replace("[OutAttribute]", String.Empty); sw.WriteLine("typedef {0} (APIENTRY *p{1}){2};", d.ReturnType, d.Name, parameters); sw.WriteLine("extern p{0} {0};", d.Name); } } #endregion #region WriteImports public void WriteImports(BindStreamWriter sw, DelegateCollection delegates) { throw new NotSupportedException(); } #endregion #region WriteWrappers public void WriteWrappers(BindStreamWriter sw, FunctionCollection wrappers, Dictionary CSTypes) { throw new NotSupportedException(); } static void WriteMethodBody(BindStreamWriter sw, Function f) { var callstring = f.Parameters.CallString() .Replace("String[]", "String*"); if (f.ReturnType != null && !f.ReturnType.ToString().ToLower().Contains("void")) sw.WriteLine("return Delegates::{0}{1};", f.WrappedDelegate.Name, callstring); else sw.WriteLine("Delegates::{0}{1};", f.WrappedDelegate.Name, callstring); } static DocProcessor processor = new DocProcessor(Path.Combine(Settings.DocPath, Settings.DocFile)); static Dictionary docfiles; void WriteDocumentation(BindStreamWriter sw, Function f) { if (docfiles == null) { docfiles = new Dictionary(); foreach (string file in Directory.GetFiles(Settings.DocPath)) { docfiles.Add(Path.GetFileName(file), file); } } string docfile = null; try { docfile = Settings.FunctionPrefix + f.WrappedDelegate.Name + ".xml"; if (!docfiles.ContainsKey(docfile)) docfile = Settings.FunctionPrefix + f.TrimmedName + ".xml"; if (!docfiles.ContainsKey(docfile)) docfile = Settings.FunctionPrefix + f.TrimmedName.TrimEnd(numbers) + ".xml"; string doc = null; if (docfiles.ContainsKey(docfile)) { doc = processor.ProcessFile(docfiles[docfile]); } if (doc == null) { doc = "/// "; } int summary_start = doc.IndexOf("") + "".Length; string warning = "[deprecated: v{0}]"; string category = "[requires: {0}]"; if (f.Deprecated) { warning = String.Format(warning, f.DeprecatedVersion); doc = doc.Insert(summary_start, warning); } if (f.Extension != "Core" && !String.IsNullOrEmpty(f.Category)) { category = String.Format(category, f.Category); doc = doc.Insert(summary_start, category); } else if (!String.IsNullOrEmpty(f.Version)) { if (f.Category.StartsWith("VERSION")) category = String.Format(category, "v" + f.Version); else category = String.Format(category, "v" + f.Version + " and " + f.Category); doc = doc.Insert(summary_start, category); } sw.WriteLine(doc); } catch (Exception e) { Console.WriteLine("[Warning] Error processing file {0}: {1}", docfile, e.ToString()); } } #endregion #endregion #region WriteLicense public void WriteLicense(BindStreamWriter sw) { sw.WriteLine(File.ReadAllText(Path.Combine(Settings.InputPath, Settings.LicenseFile))); sw.WriteLine(); } #endregion } }