From 0439857774dae56f0d5580207c159bba6e3f520e Mon Sep 17 00:00:00 2001
From: Andrea Del Signore <sejerpz@gmail.com>
Date: Wed, 8 Aug 2012 12:21:17 +0200
Subject: [PATCH] Added support for plugable C code generators

---
 compiler/Makefile.am       |    5 +++-
 compiler/valacompiler.vala |   60 +++++++++++++++++++++++++++++++++++++-------
 vala/valaprofile.vala      |    3 ++-
 vapi/config.vapi           |    1 +
 4 files changed, 58 insertions(+), 11 deletions(-)

diff --git a/compiler/Makefile.am b/compiler/Makefile.am
index d6e7f81..fc25be4 100644
--- a/compiler/Makefile.am
+++ b/compiler/Makefile.am
@@ -7,7 +7,9 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/vala \
 	-I$(top_srcdir)/codegen \
 	$(GLIB_CFLAGS) \
+	$(GMODULE_CFLAGS) \
 	-DPACKAGE_DATADIR=\"$(pkgdatadir)\" \
+	-DPACKAGE_LIBEXECDIR=\"$(pkglibexecdir)\" \
 	$(NULL)
 
 BUILT_SOURCES = valac.vala.stamp
@@ -26,12 +28,13 @@ valac_SOURCES = \
 	$(NULL)
 
 valac.vala.stamp: $(valac_VALASOURCES)
-	$(VALA_V)$(VALAC) $(COVERAGE_VALAFLAGS) $(VALAFLAGS) -C --vapidir $(srcdir)/../vapi --pkg gobject-2.0 --vapidir $(srcdir)/../gee --pkg gee --vapidir $(srcdir)/../ccode --pkg ccode --vapidir $(srcdir)/../vala --pkg vala --vapidir $(srcdir)/../codegen --pkg codegen --pkg config $^
+	$(VALA_V)$(VALAC) $(COVERAGE_VALAFLAGS) $(VALAFLAGS) -C --vapidir $(srcdir)/../vapi --pkg gobject-2.0 --pkg gmodule-2.0 --vapidir $(srcdir)/../gee --pkg gee --vapidir $(srcdir)/../ccode --pkg ccode --vapidir $(srcdir)/../vala --pkg vala --vapidir $(srcdir)/../codegen --pkg codegen --pkg config $^
 	@touch $@
 
 valac_LDADD = \
 	$(COVERAGE_LIBS) \
 	$(GLIB_LIBS) \
+	$(GMODULE_LIBS) \
 	../codegen/libvala@PACKAGE_SUFFIX@.la \
 	$(NULL)
 
diff --git a/compiler/valacompiler.vala b/compiler/valacompiler.vala
index c1c9f6f..22fbe2a 100644
--- a/compiler/valacompiler.vala
+++ b/compiler/valacompiler.vala
@@ -74,6 +74,7 @@ class Vala.Compiler {
 	static bool quiet_mode;
 	static bool verbose_mode;
 	static string profile;
+	static string profile_plugindir;
 	static bool nostdpkg;
 	static bool enable_version_header;
 	static bool disable_version_header;
@@ -86,6 +87,12 @@ class Vala.Compiler {
 
 	private CodeContext context;
 
+	private Module? profile_module;
+	private CustomProfileFactoryDelegate custom_profile_factory;
+
+	[CCode(has_target=false)]
+	private delegate Vala.CodeGenerator CustomProfileFactoryDelegate(Vala.CodeContext context);
+
 	const OptionEntry[] options = {
 		{ "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
 		{ "girdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gir_directories, "Look for .gir files in DIRECTORY", "DIRECTORY..." },
@@ -128,6 +135,7 @@ class Vala.Compiler {
 		{ "dump-tree", 0, 0, OptionArg.FILENAME, ref dump_tree, "Write code tree to FILE", "FILE" },
 		{ "save-temps", 0, 0, OptionArg.NONE, ref save_temps, "Keep temporary files", null },
 		{ "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the default", "PROFILE" },
+		{ "profile-plugindir", 0, 0, OptionArg.STRING, ref profile_plugindir, "Directory used to search for profile plugins", "DIRECTORY" },
 		{ "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", null },
 		{ "verbose", 'v', 0, OptionArg.NONE, ref verbose_mode, "Print additional messages to the console", null },
 		{ "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code generation", "MAJOR.MINOR" },
@@ -207,12 +215,35 @@ class Vala.Compiler {
 		context.thread = thread;
 		context.mem_profiler = mem_profiler;
 		context.save_temps = save_temps;
+
+		if (profile_plugindir == null) {
+			profile_plugindir = Config.PACKAGE_LIBEXECDIR;
+		}
 		if (profile == "gobject-2.0" || profile == "gobject" || profile == null) {
 			// default profile
 			context.profile = Profile.GOBJECT;
 			context.add_define ("GOBJECT");
 		} else {
-			Report.error (null, "Unknown profile %s".printf (profile));
+			if (Module.supported()) {
+				string path = Module.build_path (profile_plugindir, "%s%s".printf(profile, Config.PACKAGE_SUFFIX));
+
+				profile_module = Module.open (path, ModuleFlags.BIND_LOCAL);
+				if (profile_module != null) {
+					void* codegen_factory_func;
+
+					if (profile_module.symbol("vala_codegenerator_factory", out codegen_factory_func)) {
+						custom_profile_factory = (CustomProfileFactoryDelegate) codegen_factory_func;
+						context.profile = Profile.CUSTOM;
+						context.add_define (profile.up());
+					} else {
+						Report.error (null, "A valid factory function was not found. Can't use module '%s' for profile %s".printf (path, profile));
+					}
+				} else {
+					Report.error (null, "Can't load module '%s'. Unknown profile %s".printf (path, profile));
+				}
+			} else {
+				Report.error (null, "Unknown profile %s".printf (profile));
+			}
 		}
 		nostdpkg |= fast_vapi_filename != null;
 		context.nostdpkg = nostdpkg;
@@ -243,14 +274,16 @@ class Vala.Compiler {
 			Report.error (null, "This version of valac only supports GLib 2");
 		}
 
-		for (int i = 16; i <= glib_minor; i += 2) {
-			context.add_define ("GLIB_2_%d".printf (i));
-		}
+		if (context.profile != Profile.CUSTOM) {
+			for (int i = 16; i <= glib_minor; i += 2) {
+				context.add_define ("GLIB_2_%d".printf (i));
+			}
 
-		if (!nostdpkg) {
-			/* default packages */
-			context.add_external_package ("glib-2.0");
-			context.add_external_package ("gobject-2.0");
+			if (!nostdpkg) {
+				/* default packages */
+				context.add_external_package ("glib-2.0");
+				context.add_external_package ("gobject-2.0");
+			}
 		}
 
 		if (packages != null) {
@@ -273,7 +306,16 @@ class Vala.Compiler {
 			return quit ();
 		}
 
-		context.codegen = new GDBusServerModule ();
+		if (context.profile == Profile.CUSTOM) {
+			var codegen = custom_profile_factory(context) as Vala.CodeGenerator;
+			if (codegen != null) {
+				context.codegen = codegen;
+			} else {
+				Report.error (null, "Invalid code generator for profile %s".printf (profile));
+			}
+		} else {
+			context.codegen = new GDBusServerModule ();
+		}
 
 		bool has_c_files = false;
 
diff --git a/vala/valaprofile.vala b/vala/valaprofile.vala
index 8024965..a5aebdb 100644
--- a/vala/valaprofile.vala
+++ b/vala/valaprofile.vala
@@ -21,5 +21,6 @@
  */
 
 public enum Vala.Profile {
-	GOBJECT
+	GOBJECT,
+	CUSTOM
 }
diff --git a/vapi/config.vapi b/vapi/config.vapi
index 2c6df9e..aada11b 100644
--- a/vapi/config.vapi
+++ b/vapi/config.vapi
@@ -26,4 +26,5 @@ namespace Config {
 	public const string BUILD_VERSION;
 	public const string PACKAGE_DATADIR;
 	public const string PACKAGE_SUFFIX;
+	public const string PACKAGE_LIBEXECDIR;
 }
-- 
1.7.10.msysgit.1

