summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
authorMax-Gerd Retzlaff <m.retzlaff@gmx.net>2022-08-23 17:38:34 +0200
committerJens Geyer <jensg@apache.org>2022-08-30 23:58:57 +0200
commit04057ac28a72ad4001def05a7bc8e13cc640b5ca (patch)
tree44435ef2391b86828f97736cfdecc4e374782404 /compiler
parent39fa1854a75e96235e2f50a45546b874da29e756 (diff)
downloadthrift-04057ac28a72ad4001def05a7bc8e13cc640b5ca.tar.gz
Revert "THRIFT-5501 Remove Common Lisp support"
This reverts commit d88d4f93b3390989bd47a43f3941ca7d576750f6.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/cpp/CMakeLists.txt1
-rw-r--r--compiler/cpp/Makefile.am1
-rw-r--r--compiler/cpp/compiler.vcxproj1
-rw-r--r--compiler/cpp/src/thrift/generate/t_cl_generator.cc558
-rw-r--r--compiler/cpp/src/thrift/generate/t_generator.cc2
-rw-r--r--compiler/cpp/tests/CMakeLists.txt1
6 files changed, 563 insertions, 1 deletions
diff --git a/compiler/cpp/CMakeLists.txt b/compiler/cpp/CMakeLists.txt
index 717b645ba..a23004110 100644
--- a/compiler/cpp/CMakeLists.txt
+++ b/compiler/cpp/CMakeLists.txt
@@ -73,6 +73,7 @@ endmacro()
# The following compiler can be enabled or disabled
THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" ON)
+THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" ON)
THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" ON)
THRIFT_ADD_COMPILER(d "Enable compiler for D" ON)
THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" ON)
diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am
index cc776ef5e..55c82943f 100644
--- a/compiler/cpp/Makefile.am
+++ b/compiler/cpp/Makefile.am
@@ -70,6 +70,7 @@ thrift_SOURCES = src/thrift/audit/t_audit.cpp \
# Specific client generator source
thrift_SOURCES += src/thrift/generate/t_c_glib_generator.cc \
+ src/thrift/generate/t_cl_generator.cc \
src/thrift/generate/t_cpp_generator.cc \
src/thrift/generate/t_d_generator.cc \
src/thrift/generate/t_dart_generator.cc \
diff --git a/compiler/cpp/compiler.vcxproj b/compiler/cpp/compiler.vcxproj
index ae77f3d0c..9fbf903a4 100644
--- a/compiler/cpp/compiler.vcxproj
+++ b/compiler/cpp/compiler.vcxproj
@@ -54,6 +54,7 @@
<ClCompile Include="src\thrift\audit\t_audit.cpp" />
<ClCompile Include="src\thrift\common.cc" />
<ClCompile Include="src\thrift\generate\t_c_glib_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_cl_generator.cc" />
<ClCompile Include="src\thrift\generate\t_cpp_generator.cc" />
<ClCompile Include="src\thrift\generate\t_d_generator.cc" />
<ClCompile Include="src\thrift\generate\t_dart_generator.cc" />
diff --git a/compiler/cpp/src/thrift/generate/t_cl_generator.cc b/compiler/cpp/src/thrift/generate/t_cl_generator.cc
new file mode 100644
index 000000000..06b6b6563
--- /dev/null
+++ b/compiler/cpp/src/thrift/generate/t_cl_generator.cc
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2008- Patrick Collison <patrick@collison.ie>
+ * Copyright (c) 2006- Facebook
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include <string>
+#include <algorithm>
+
+#include "thrift/platform.h"
+#include "t_oop_generator.h"
+
+using namespace std;
+
+
+/**
+ * Common Lisp code generator.
+ *
+ * @author Patrick Collison <patrick@collison.ie>
+ */
+class t_cl_generator : public t_oop_generator {
+ public:
+ t_cl_generator(
+ t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program)
+ {
+ no_asd = false;
+ system_prefix = "thrift-gen-";
+
+ std::map<std::string, std::string>::const_iterator iter;
+
+ for(iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if(iter->first.compare("no_asd") == 0) {
+ no_asd = true;
+ } else if (iter->first.compare("sys_pref") == 0) {
+ system_prefix = iter->second;
+ } else {
+ throw "unknown option cl:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-cl";
+ copy_options_ = option_string;
+ }
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_typedef (t_typedef* ttypedef) override;
+ void generate_enum (t_enum* tenum) override;
+ void generate_const (t_const* tconst) override;
+ void generate_struct (t_struct* tstruct) override;
+ void generate_xception (t_struct* txception) override;
+ void generate_service (t_service* tservice) override;
+ void generate_cl_struct (std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_cl_struct_internal (std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_exception_sig(std::ostream& out, t_function* f);
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ std::string cl_autogen_comment();
+ void asdf_def(std::ostream &out);
+ void package_def(std::ostream &out);
+ void package_in(std::ostream &out);
+ std::string generated_package();
+ std::string prefix(std::string name);
+ std::string package_of(t_program* program);
+ std::string package();
+ std::string render_includes();
+
+ std::string type_name(t_type* ttype);
+ std::string typespec (t_type *t);
+ std::string function_signature(t_function* tfunction);
+ std::string argument_list(t_struct* tstruct);
+
+ std::string cl_docstring(std::string raw);
+
+ private:
+
+ int temporary_var;
+ /**
+ * Isolate the variable definitions, as they can require structure definitions
+ */
+ ofstream_with_content_based_conditional_update f_asd_;
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_vars_;
+
+ std::string copy_options_;
+
+ bool no_asd;
+ std::string system_prefix;
+};
+
+
+void t_cl_generator::init_generator() {
+ MKDIR(get_out_dir().c_str());
+ string program_dir = get_out_dir() + "/" + program_name_;
+ MKDIR(program_dir.c_str());
+
+ temporary_var = 0;
+
+ string f_types_name = program_dir + "/" + program_name_ + "-types.lisp";
+ string f_vars_name = program_dir + "/" + program_name_ + "-vars.lisp";
+
+ f_types_.open(f_types_name);
+ f_types_ << cl_autogen_comment() << endl;
+ f_vars_.open(f_vars_name);
+ f_vars_ << cl_autogen_comment() << endl;
+
+ package_def(f_types_);
+ package_in(f_types_);
+ package_in(f_vars_);
+
+ if (!no_asd) {
+ string f_asd_name = program_dir + "/" + system_prefix + program_name_ + ".asd";
+ f_asd_.open(f_asd_name);
+ f_asd_ << cl_autogen_comment() << endl;
+ asdf_def(f_asd_);
+ }
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_cl_generator::render_includes() {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ result += ":depends-on (:thrift";
+ for (auto include : includes) {
+ result += " :" + system_prefix + underscore(include->get_name());
+ }
+ result += ")\n";
+ return result;
+}
+
+string t_cl_generator::package_of(t_program* program) {
+ string prefix = program->get_namespace("cl");
+ return prefix.empty() ? "thrift-generated" : prefix;
+}
+
+string t_cl_generator::package() {
+ return package_of(program_);
+}
+
+string t_cl_generator::prefix(string symbol) {
+ return "\"" + symbol + "\"";
+}
+
+string t_cl_generator::cl_autogen_comment() {
+ return
+ std::string(";;; ") + "Autogenerated by Thrift\n" +
+ ";;; DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" +
+ ";;; options string: " + copy_options_ + "\n";
+}
+
+string t_cl_generator::cl_docstring(string raw) {
+ replace(raw.begin(), raw.end(), '"', '\'');
+ return raw;
+}
+
+
+void t_cl_generator::close_generator() {
+ f_asd_.close();
+ f_types_.close();
+ f_vars_.close();
+}
+
+string t_cl_generator::generated_package() {
+ return program_->get_namespace("cpp");
+}
+
+void t_cl_generator::asdf_def(std::ostream &out) {
+ out << "(asdf:defsystem #:" << system_prefix << program_name_ << endl;
+ indent_up();
+ out << indent() << render_includes()
+ << indent() << ":serial t" << endl
+ << indent() << ":components ("
+ << "(:file \"" << program_name_ << "-types\") "
+ << "(:file \"" << program_name_ << "-vars\")))" << endl;
+ indent_down();
+}
+
+/***
+ * Generate a package definition. Add use references equivalent to the idl file's include statements.
+ */
+void t_cl_generator::package_def(std::ostream &out) {
+ const vector<t_program*>& includes = program_->get_includes();
+
+ out << "(thrift:def-package :" << package();
+ if ( includes.size() > 0 ) {
+ out << " :use (";
+ for (auto include : includes) {
+ out << " :" << include->get_name();
+ }
+ out << ")";
+ }
+ out << ")" << endl << endl;
+}
+
+void t_cl_generator::package_in(std::ostream &out) {
+ out << "(cl:in-package :" << package() << ")" << endl << endl;
+}
+
+/**
+ * Generates a typedef. This is not done in Common Lisp, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_cl_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+void t_cl_generator::generate_enum(t_enum* tenum) {
+ f_types_ << "(thrift:def-enum " << prefix(tenum->get_name()) << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ int value = -1;
+
+ indent_up();
+ f_types_ << indent() << "(";
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ value = (*c_iter)->get_value();
+
+ if(c_iter != constants.begin()) f_types_ << endl << indent() << " ";
+
+ f_types_ << "(\"" << (*c_iter)->get_name() << "\" . " << value << ")";
+ }
+ indent_down();
+ f_types_ << "))" << endl << endl;
+}
+
+/**
+ * Generate a constant value
+ */
+void t_cl_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ f_vars_ << "(thrift:def-constant " << prefix(name) << " " << render_const_value(type, value) << ")"
+ << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_cl_generator::render_const_value(t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream out;
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << "\"" << value->get_string() << "\"";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "t" : "nil");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ indent(out) << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << (type->is_struct() ? "(make-instance '" : "(make-exception '") <<
+ lowercase(type->get_name()) << " " << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = nullptr;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == nullptr) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ out << indent() << ":" << v_iter->first->get_string() << " " <<
+ render_const_value(field_type, v_iter->second) << endl;
+ }
+ out << indent() << ")";
+
+ indent_down();
+ } else if (type->is_map()) {
+ // emit an hash form with both keys and values to be evaluated
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "(thrift:map ";
+ indent_up();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << endl << indent()
+ << "(cl:cons " << render_const_value(ktype, v_iter->first) << " "
+ << render_const_value(vtype, v_iter->second) << ")";
+ }
+ indent_down();
+ out << indent() << ")";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ if (type->is_set()) {
+ out << "(thrift:set" << endl;
+ } else {
+ out << "(thrift:list" << endl;
+ }
+ indent_up();
+ indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << render_const_value(etype, *v_iter) << endl;
+ }
+ out << indent() << ")";
+ indent_down();
+ indent_down();
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+ return out.str();
+}
+
+void t_cl_generator::generate_struct(t_struct* tstruct) {
+ generate_cl_struct(f_types_, tstruct, false);
+}
+
+void t_cl_generator::generate_xception(t_struct* txception) {
+ generate_cl_struct(f_types_, txception, true);
+}
+
+void t_cl_generator::generate_cl_struct_internal(std::ostream& out, t_struct* tstruct, bool is_exception) {
+ (void)is_exception;
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << "(";
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_const_value* value = (*m_iter)->get_value();
+ t_type* type = (*m_iter)->get_type();
+
+ if (m_iter != members.begin()) {
+ out << endl << indent() << " ";
+ }
+ out << "(" << prefix((*m_iter)->get_name()) << " " <<
+ ( (nullptr != value) ? render_const_value(type, value) : "nil" ) <<
+ " :id " << (*m_iter)->get_key();
+ if ( type->is_base_type() && "string" == typespec(type) )
+ if ( ((t_base_type*)type)->is_binary() )
+ out << " :type binary";
+ else
+ out << " :type string";
+ else
+ out << " :type " << typespec(type);
+ if ( (*m_iter)->get_req() == t_field::T_OPTIONAL ) {
+ out << " :optional t";
+ }
+ if ( (*m_iter)->has_doc()) {
+ out << " :documentation \"" << cl_docstring((*m_iter)->get_doc()) << "\"";
+ }
+ out <<")";
+ }
+
+ out << ")";
+}
+
+void t_cl_generator::generate_cl_struct(std::ostream& out, t_struct* tstruct, bool is_exception = false) {
+ std::string name = type_name(tstruct);
+ out << (is_exception ? "(thrift:def-exception " : "(thrift:def-struct ") <<
+ prefix(name) << endl;
+ indent_up();
+ if ( tstruct->has_doc() ) {
+ out << indent() ;
+ out << "\"" << cl_docstring(tstruct->get_doc()) << "\"" << endl;
+ }
+ out << indent() ;
+ generate_cl_struct_internal(out, tstruct, is_exception);
+ indent_down();
+ out << ")" << endl << endl;
+}
+
+void t_cl_generator::generate_exception_sig(std::ostream& out, t_function* f) {
+ generate_cl_struct_internal(out, f->get_xceptions(), true);
+}
+
+void t_cl_generator::generate_service(t_service* tservice) {
+ string extends_client;
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ if (tservice->get_extends() != nullptr) {
+ extends_client = type_name(tservice->get_extends());
+ }
+
+ extends_client = extends_client.empty() ? "nil" : prefix(extends_client);
+
+ f_types_ << "(thrift:def-service " << prefix(service_name_) << " "
+ << extends_client;
+
+ indent_up();
+
+ if ( tservice->has_doc()) {
+ f_types_ << endl << indent()
+ << "(:documentation \"" << cl_docstring(tservice->get_doc()) << "\")";
+ }
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_function* function = *f_iter;
+ string fname = function->get_name();
+ string signature = function_signature(function);
+ t_struct* exceptions = function->get_xceptions();
+ const vector<t_field*>& xmembers = exceptions->get_members();
+
+ f_types_ << endl << indent() << "(:method " << prefix(fname);
+ f_types_ << " (" << signature << " " << typespec((*f_iter)->get_returntype()) << ")";
+ if (xmembers.size() > 0) {
+ f_types_ << endl << indent() << " :exceptions " ;
+ generate_exception_sig(f_types_, function);
+ }
+ if ( (*f_iter)->is_oneway() ) {
+ f_types_ << endl << indent() << " :oneway t";
+ }
+ if ( (*f_iter)->has_doc() ) {
+ f_types_ << endl << indent() << " :documentation \""
+ << cl_docstring((*f_iter)->get_doc()) << "\"";
+ }
+ f_types_ << ")";
+ }
+
+ f_types_ << ")" << endl << endl;
+
+ indent_down();
+}
+
+string t_cl_generator::typespec(t_type *t) {
+ t = get_true_type(t);
+
+ if (t -> is_binary()){
+ return "binary";
+ } else if (t->is_base_type()) {
+ return type_name(t);
+ } else if (t->is_map()) {
+ t_map *m = (t_map*) t;
+ return "(thrift:map " + typespec(m->get_key_type()) + " " +
+ typespec(m->get_val_type()) + ")";
+ } else if (t->is_struct() || t->is_xception()) {
+ return "(struct " + prefix(type_name(t)) + ")";
+ } else if (t->is_list()) {
+ return "(thrift:list " + typespec(((t_list*) t)->get_elem_type()) + ")";
+ } else if (t->is_set()) {
+ return "(thrift:set " + typespec(((t_set*) t)->get_elem_type()) + ")";
+ } else if (t->is_enum()) {
+ return "(enum \"" + ((t_enum*) t)->get_name() + "\")";
+ } else {
+ throw "Sorry, I don't know how to generate this: " + type_name(t);
+ }
+}
+
+string t_cl_generator::function_signature(t_function* tfunction) {
+ return argument_list(tfunction->get_arglist());
+}
+
+string t_cl_generator::argument_list(t_struct* tstruct) {
+ stringstream res;
+ res << "(";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ res << " ";
+ }
+ res << "(" + prefix((*f_iter)->get_name()) << " " <<
+ typespec((*f_iter)->get_type()) << " " <<
+ (*f_iter)->get_key() << ")";
+
+
+ }
+ res << ")";
+ return res.str();
+}
+
+string t_cl_generator::type_name(t_type* ttype) {
+ string prefix = "";
+ t_program* program = ttype->get_program();
+
+ if (program != nullptr && program != program_)
+ prefix = package_of(program) == package() ? "" : package_of(program) + ":";
+
+ string name = ttype->get_name();
+
+ if (ttype->is_struct() || ttype->is_xception())
+ name = lowercase(ttype->get_name());
+
+ return prefix + name;
+}
+
+THRIFT_REGISTER_GENERATOR(
+ cl,
+ "Common Lisp",
+ " no_asd: Do not define ASDF systems for each generated Thrift program.\n"
+ " sys_pref= The prefix to give ASDF system names. Default: thrift-gen-\n")
diff --git a/compiler/cpp/src/thrift/generate/t_generator.cc b/compiler/cpp/src/thrift/generate/t_generator.cc
index a2bc9d6c2..f26690b9a 100644
--- a/compiler/cpp/src/thrift/generate/t_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_generator.cc
@@ -237,7 +237,7 @@ t_generator* t_generator_registry::get_generator(t_program* program,
if ((language == "csharp") || (language == "netcore")) {
failure("The '%s' target is no longer available. Use 'netstd' instead.", language.c_str());
}
-
+
if (iter == the_map.end()) {
return nullptr;
}
diff --git a/compiler/cpp/tests/CMakeLists.txt b/compiler/cpp/tests/CMakeLists.txt
index fd8bed2eb..0e8254158 100644
--- a/compiler/cpp/tests/CMakeLists.txt
+++ b/compiler/cpp/tests/CMakeLists.txt
@@ -98,6 +98,7 @@ endmacro()
# The following compiler with unit tests can be enabled or disabled
THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" OFF)
+THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" OFF)
THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" OFF)
THRIFT_ADD_COMPILER(d "Enable compiler for D" OFF)
THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" OFF)