diff options
author | Nobuaki Sukegawa <nsuke@apache.org> | 2016-09-10 14:02:19 +0900 |
---|---|---|
committer | Nobuaki Sukegawa <nsuke@apache.org> | 2016-09-26 01:42:01 +0900 |
commit | 11da87e6f30c237869a09e28ab44fe4a59db270e (patch) | |
tree | 4c81e09a6c5647cf8b4216379c30ec8ec2cb5517 | |
parent | 1e3cf9b9fdcf9a7b7ffc18a2e6148465b154e66b (diff) | |
download | thrift-11da87e6f30c237869a09e28ab44fe4a59db270e.tar.gz |
THRIFT-2835 Add possibility to distribute generators separately from thrift core, and load them dynamically
Client: Compiler
Patch: Nobuaki Sukegawa, rebased by dtmuller
Also fixed by dtmuller:
* Add plugin namespace for erlang language binding
* Fix unit test test_const_value
* Don't clear type cache with every t_program conversion
* Type "wb" may not be supported by popen on non-Windows platforms
* Fix constness of AST type signatures
63 files changed, 2701 insertions, 169 deletions
diff --git a/.gitignore b/.gitignore index 286d7394f..94e851967 100644 --- a/.gitignore +++ b/.gitignore @@ -47,9 +47,18 @@ erl_crash.dump /aclocal/lt*.m4 /autoscan.log /autoscan-*.log +/compiler/cpp/test/plugin/t_cpp_generator.cc +/compiler/cpp/src/plugin/plugin_constants.cpp +/compiler/cpp/src/plugin/plugin_constants.h +/compiler/cpp/src/plugin/plugin_types.cpp +/compiler/cpp/src/plugin/plugin_types.h +/compiler/cpp/test/*test +/compiler/cpp/test/thrift-gen-* +/compiler/cpp/src/thrift-bootstrap +/compiler/cpp/src/plugin/gen.stamp /compiler/cpp/Debug /compiler/cpp/Release -/compiler/cpp/libparse.a +/compiler/cpp/src/libparse.a /compiler/cpp/src/thriftl.cc /compiler/cpp/src/thrifty.cc /compiler/cpp/src/thrifty.hh @@ -60,7 +69,7 @@ erl_crash.dump /compiler/cpp/lex.yythriftl.cc /compiler/cpp/thrifty.h /compiler/cpp/thrifty.hh -/compiler/cpp/version.h +/compiler/cpp/src/version.h /config.* /configure /configure.lineno diff --git a/CMakeLists.txt b/CMakeLists.txt index 37e89766f..93ed8d2ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,9 @@ if(BUILD_COMPILER) set(THRIFT_COMPILER $<TARGET_FILE:thrift-compiler>) endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/compiler/cpp) +elseif(EXISTS ${THRIFT_COMPILER}) + add_executable(thrift-compiler IMPORTED) + set_property(TARGET thrift-compiler PROPERTY IMPORTED_LOCATION ${THRIFT_COMPILER}) endif() if(BUILD_CPP) diff --git a/Makefile.am b/Makefile.am index 38dd8c9d6..ed58265ac 100755 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,15 @@ ACLOCAL_AMFLAGS = -I ./aclocal +if WITH_PLUGIN +# To enable bootstrap, build order is lib/cpp -> compiler -> others +SUBDIRS = lib/cpp compiler/cpp lib +if WITH_TESTS +SUBDIRS += lib/cpp/test +endif +else SUBDIRS = compiler/cpp lib +endif if WITH_TESTS SUBDIRS += test diff --git a/build/cmake/DefineOptions.cmake b/build/cmake/DefineOptions.cmake index 6dd59e0ff..171c9fefa 100644 --- a/build/cmake/DefineOptions.cmake +++ b/build/cmake/DefineOptions.cmake @@ -40,6 +40,13 @@ option(BUILD_LIBRARIES "Build Thrift libraries" ON) # and enables the library if all are found. This means the default is to build as # much as possible but leaving out libraries if their dependencies are not met. +CMAKE_DEPENDENT_OPTION(WITH_BOOST_STATIC "Build with Boost static link library" OFF "NOT MSVC" ON) +set(Boost_USE_STATIC_LIBS ${WITH_BOOST_STATIC}) +if (NOT WITH_BOOST_STATIC) + add_definitions(-DBOOST_ALL_DYN_LINK) + add_definitions(-DBOOST_TEST_DYN_LINK) +endif() + # C++ option(WITH_CPP "Build C++ Thrift library" ON) if(WITH_CPP) @@ -77,6 +84,8 @@ if(WITH_CPP) endif() CMAKE_DEPENDENT_OPTION(BUILD_CPP "Build C++ library" ON "BUILD_LIBRARIES;WITH_CPP;Boost_FOUND" OFF) +CMAKE_DEPENDENT_OPTION(WITH_PLUGIN "Build compiler plugin support" ON + "BUILD_COMPILER;BUILD_CPP" OFF) # C GLib option(WITH_C_GLIB "Build C (GLib) Thrift library" ON) @@ -86,6 +95,21 @@ endif() CMAKE_DEPENDENT_OPTION(BUILD_C_GLIB "Build C (GLib) library" ON "BUILD_LIBRARIES;WITH_C_GLIB;GLIB_FOUND" OFF) +if(BUILD_CPP) + set(boost_components) + if(WITH_BOOSTTHREADS OR BUILD_TESTING) + list(APPEND boost_components system thread) + endif() + if(BUILD_TESTING) + list(APPEND boost_components unit_test_framework filesystem chrono program_options) + endif() + if(boost_components) + find_package(Boost 1.53 REQUIRED COMPONENTS ${boost_components}) + endif() +elseif(BUILD_C_GLIB AND BUILD_TESTING) + find_package(Boost 1.53 REQUIRED) +endif() + # Java option(WITH_JAVA "Build Java Thrift library" ON) if(ANDROID) @@ -120,8 +144,6 @@ if (NOT WITH_SHARED_LIB AND NOT WITH_STATIC_LIB) message(FATAL_ERROR "Cannot build with both shared and static outputs disabled!") endif() -option(WITH_DYN_LINK_TEST "Build with Boost dynamic link test library" OFF) - #NOTE: C++ compiler options are defined in the lib/cpp/CMakeLists.txt # Visual Studio only options @@ -141,6 +163,7 @@ message(STATUS "Thrift version: ${thrift_VERSION} message(STATUS "Thrift package version: ${PACKAGE_VERSION}") message(STATUS "Build configuration Summary") message(STATUS " Build Thrift compiler: ${BUILD_COMPILER}") +message(STATUS " Build compiler plugin support: ${WITH_PLUGIN}") message(STATUS " Build with unit tests: ${BUILD_TESTING}") MESSAGE_DEP(HAVE_COMPILER "Disabled because BUILD_THRIFT=OFF and no valid THRIFT_COMPILER is given") message(STATUS " Build examples: ${BUILD_EXAMPLES}") @@ -178,6 +201,9 @@ message(STATUS " Build with Qt5 support: ${WITH_QT5}") message(STATUS " Build with OpenSSL support: ${WITH_OPENSSL}") message(STATUS " Build with Boost thread support: ${WITH_BOOSTTHREADS}") message(STATUS " Build with C++ std::thread support: ${WITH_STDTHREADS}") -message(STATUS " Build with Boost dynamic link test library: ${WITH_DYN_LINK_TEST}") +message(STATUS " Build with Boost static link library: ${WITH_BOOST_STATIC}") +if(MSVC) + message(STATUS " - Enabled for Visual C++") +endif() message(STATUS "----------------------------------------------------------") endmacro(PRINT_CONFIG_SUMMARY) diff --git a/build/cmake/ThriftMacros.cmake b/build/cmake/ThriftMacros.cmake index 265659814..f837f9482 100644 --- a/build/cmake/ThriftMacros.cmake +++ b/build/cmake/ThriftMacros.cmake @@ -25,7 +25,6 @@ macro(ADD_LIBRARY_THRIFT name) if(WITH_SHARED_LIB) add_library(${name} SHARED ${ARGN}) - #target_link_libraries(${name} ${SYSLIBS}) set_target_properties(${name} PROPERTIES OUTPUT_NAME ${name} VERSION ${thrift_VERSION} @@ -40,7 +39,6 @@ endif() if(WITH_STATIC_LIB) add_library(${name}_static STATIC ${ARGN}) - #target_link_libraries(${name}_static ${SYSLIBS}) set_target_properties(${name}_static PROPERTIES OUTPUT_NAME ${name}${STATIC_POSTFIX} VERSION ${thrift_VERSION} @@ -55,6 +53,19 @@ endif() endmacro(ADD_LIBRARY_THRIFT) +macro(TARGET_INCLUDE_DIRECTORIES_THRIFT name) + +if(WITH_SHARED_LIB) + target_include_directories(${name} ${ARGN}) +endif() + +if(WITH_STATIC_LIB) + target_include_directories(${name}_static ${ARGN}) +endif() + +endmacro(TARGET_INCLUDE_DIRECTORIES_THRIFT) + + macro(TARGET_LINK_LIBRARIES_THRIFT name) if(WITH_SHARED_LIB) @@ -84,11 +95,11 @@ endmacro(LINK_AGAINST_THRIFT_LIBRARY) macro(TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY target libname) if(WITH_SHARED_LIB) - target_link_libraries(${target} ${libname}) + target_link_libraries(${target} ${ARGN} ${libname}) endif() if(WITH_STATIC_LIB) - target_link_libraries(${target}_static ${libname}_static) + target_link_libraries(${target}_static ${ARGN} ${libname}_static) endif() endmacro(TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY) diff --git a/compiler/cpp/CMakeLists.txt b/compiler/cpp/CMakeLists.txt index b7ed6ea08..48cadbc1d 100644 --- a/compiler/cpp/CMakeLists.txt +++ b/compiler/cpp/CMakeLists.txt @@ -21,7 +21,7 @@ if(MSVC) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/windows/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) else() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) endif() find_package(FLEX REQUIRED) @@ -47,39 +47,17 @@ set(libparse_SOURCES add_library(libparse STATIC ${libparse_SOURCES}) # Create the thrift compiler -set( thrift_SOURCES - src/main.cc +set(compiler_core + src/common.cc src/generate/t_generator.cc - src/generate/t_generator_registry.h - src/globals.h - src/main.h - src/platform.h - src/audit/t_audit.cpp - src/parse/t_doc.h - src/parse/t_type.h - src/parse/t_base_type.h - src/parse/t_enum.h - src/parse/t_enum_value.h - src/parse/t_typedef.h src/parse/t_typedef.cc - src/parse/t_container.h - src/parse/t_list.h - src/parse/t_set.h - src/parse/t_map.h - src/parse/t_struct.h - src/parse/t_field.h - src/parse/t_service.h - src/parse/t_function.h - src/parse/t_program.h - src/parse/t_scope.h - src/parse/t_const.h - src/parse/t_const_value.h src/parse/parse.cc - src/generate/t_generator.h - src/generate/t_oop_generator.h - src/generate/t_html_generator.h - src/windows/config.h - version.h + ${CMAKE_CURRENT_BINARY_DIR}/version.h +) + +set(thrift-compiler_SOURCES + src/main.cc + src/audit/t_audit.cpp ) # This macro adds an option THRIFT_COMPILER_${NAME} @@ -89,7 +67,7 @@ macro(THRIFT_ADD_COMPILER name description initial) set(src "src/generate/t_${name}_generator.cc") option(${enabler} ${description} ${initial}) if(${enabler}) - list(APPEND thrift_SOURCES ${src}) + list(APPEND thrift-compiler_SOURCES ${src}) endif() endmacro() @@ -127,9 +105,56 @@ THRIFT_ADD_COMPILER(xml "Enable compiler for XML" ON) # we also add the current binary directory for generated files include_directories(${CMAKE_CURRENT_BINARY_DIR} src) -add_executable(thrift-compiler ${thrift_SOURCES}) +if(NOT ${WITH_PLUGIN}) + list(APPEND thrift-compiler_SOURCES ${compiler_core}) +endif() +add_executable(thrift-compiler ${thrift-compiler_SOURCES}) + +if(${WITH_PLUGIN}) + add_executable(thrift-bootstrap ${compiler_core} + src/main.cc + src/audit/t_audit.cpp + src/generate/t_cpp_generator.cc + ) + target_link_libraries(thrift-bootstrap libparse) + + set(PLUGIN_GEN_SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_types.h + ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_types.cpp + ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_constants.h + ${CMAKE_CURRENT_BINARY_DIR}/plugin/plugin_constants.cpp + ) + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugin) + add_custom_command(OUTPUT ${PLUGIN_GEN_SOURCES} + DEPENDS thrift-bootstrap src/plugin/plugin.thrift + COMMAND thrift-bootstrap -gen cpp + -out ${CMAKE_CURRENT_BINARY_DIR}/plugin + ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/plugin.thrift + ) + + include_directories(../../lib/cpp/src) + + include(ThriftMacros) + ADD_LIBRARY_THRIFT(thriftc + ${compiler_core} + ${PLUGIN_GEN_SOURCES} + src/logging.cc + src/plugin/plugin_output.cc + src/plugin/plugin.cc + ) + TARGET_INCLUDE_DIRECTORIES_THRIFT(thriftc PUBLIC ${Boost_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftc thrift PUBLIC) + target_compile_definitions(thrift-compiler PUBLIC THRIFT_ENABLE_PLUGIN) + LINK_AGAINST_THRIFT_LIBRARY(thrift-compiler thriftc) +endif() + set_target_properties(thrift-compiler PROPERTIES OUTPUT_NAME thrift) target_link_libraries(thrift-compiler libparse) install(TARGETS thrift-compiler DESTINATION "${BIN_INSTALL_DIR}") + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index 49ec26e3b..f7a717165 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -21,25 +21,34 @@ # Please see doc/old-thrift-license.txt in the Thrift distribution for # details. -AM_YFLAGS = -d -LIBS = -BUILT_SOURCES = src/thrifty.cc +AUTOMAKE_OPTIONS = subdir-objects -bin_PROGRAMS = thrift +# Note on why we have src and src/plugin directories: +# Since Automake supports only one set of BUILT_SOURCES per file and does not allow +# SUBDIRS built before BUILT_SOURCES, we end up separate Makefile.am for each source +# code generation, i.e. lex-yacc and Thrift, to achieve stable parallel make. + +SUBDIRS = src src/plugin . +if WITH_TESTS +SUBDIRS += test +endif -noinst_LIBRARIES = libparse.a +bin_PROGRAMS = thrift thrift_OBJDIR = obj -thrift_SOURCES = src/main.cc \ +plugin_gen = src/plugin/plugin_types.h \ + src/plugin/plugin_types.cpp \ + src/plugin/plugin_constants.h \ + src/plugin/plugin_constants.cpp + +compiler_core = src/common.h \ + src/common.cc \ src/generate/t_generator.cc \ src/generate/t_generator_registry.h \ src/globals.h \ - src/main.h \ src/platform.h \ src/logging.h \ - src/audit/t_audit.cpp \ - src/audit/t_audit.h \ src/parse/t_doc.h \ src/parse/t_type.h \ src/parse/t_base_type.h \ @@ -66,6 +75,11 @@ thrift_SOURCES = src/main.cc \ src/windows/config.h \ src/windows/version.h +thrift_SOURCES = src/main.h \ + src/main.cc \ + src/audit/t_audit.cpp \ + src/audit/t_audit.h + # Specific client generator source thrift_SOURCES += src/generate/t_c_glib_generator.cc \ src/generate/t_cpp_generator.cc \ @@ -98,13 +112,32 @@ thrift_SOURCES += src/generate/t_c_glib_generator.cc \ thrift_CPPFLAGS = -I$(srcdir)/src thrift_CXXFLAGS = -Wall -Wextra -pedantic -thrift_LDADD = @LEXLIB@ libparse.a +thrift_LDADD = @LEXLIB@ src/libparse.a + +if !WITH_PLUGIN +thrift_SOURCES += $(compiler_core) +else + +lib_LTLIBRARIES = libthriftc.la + +thrift_CPPFLAGS += -DTHRIFT_ENABLE_PLUGIN=1 +thrift_LDADD += libthriftc.la + +nodist_libthriftc_la_SOURCES = $(plugin_gen) +libthriftc_la_SOURCES = $(compiler_core) \ + src/plugin/type_util.h \ + src/plugin/plugin.h \ + src/plugin/plugin.cc \ + src/plugin/plugin_output.h \ + src/plugin/plugin_output.cc \ + src/plugin/plugin.thrift \ + src/logging.cc -libparse_a_CPPFLAGS = -I$(srcdir)/src -libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused -libparse_a_SOURCES = src/thrifty.yy \ - src/thriftl.ll +libthriftc_la_CPPFLAGS = -I$(srcdir)/src -Isrc -I$(top_builddir)/lib/cpp/src -DTHRIFT_ENABLE_PLUGIN=1 +libthriftc_la_CXXFLAGS = -Wall -Wextra -pedantic +libthriftc_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la +endif WINDOWS_DIST = \ compiler.sln \ @@ -118,9 +151,9 @@ EXTRA_DIST = \ $(WINDOWS_DIST) clean-local: - $(RM) thriftl.cc thrifty.cc thrifty.h thrifty.hh version.h windows/version.h + $(RM) version.h windows/version.h $(plugin_gen) -src/main.cc: version.h +src/main.cc: src/version.h style-local: $(CPPSTYLE_CMD) diff --git a/compiler/cpp/compiler.vcxproj b/compiler/cpp/compiler.vcxproj index a2548d4ab..3dbe42c9b 100644 --- a/compiler/cpp/compiler.vcxproj +++ b/compiler/cpp/compiler.vcxproj @@ -20,6 +20,7 @@ </ItemGroup> <ItemGroup> <ClInclude Include="src\audit\t_audit.h" /> + <ClInclude Include="src\common.h" /> <ClInclude Include="src\generate\t_generator.h" /> <ClInclude Include="src\generate\t_generator_registry.h" /> <ClInclude Include="src\generate\t_oop_generator.h" /> @@ -50,7 +51,8 @@ <ClInclude Include="src\windows\version.h" /> </ItemGroup> <ItemGroup> - <ClCompile Include="src\audit\t_audit.cpp" /> + <ClCompile Include="src\audit\t_audit.cpp"/> + <ClCompile Include="src\common.cc" /> <ClCompile Include="src\generate\t_as3_generator.cc" /> <ClCompile Include="src\generate\t_cocoa_generator.cc" /> <ClCompile Include="src\generate\t_cpp_generator.cc" /> @@ -245,4 +247,4 @@ bison -y -o "src\thrifty.cc" --defines="src/thrifty.hh" src/thrifty.yy</Command> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/compiler/cpp/src/Makefile.am b/compiler/cpp/src/Makefile.am new file mode 100644 index 000000000..e2fae709d --- /dev/null +++ b/compiler/cpp/src/Makefile.am @@ -0,0 +1,87 @@ +# +# 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. +# +# +# Contains some contributions under the Thrift Software License. +# Please see doc/old-thrift-license.txt in the Thrift distribution for +# details. + +AUTOMAKE_OPTIONS = subdir-objects + +AM_YFLAGS = -d + +BUILT_SOURCES = thrifty.cc + +noinst_LIBRARIES = libparse.a + +libparse_a_CPPFLAGS = -I$(srcdir) +libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused + +libparse_a_SOURCES = thrifty.yy \ + thriftl.ll + +clean-local: + $(RM) thriftl.cc thrifty.cc thrifty.h thrifty.hh + +if WITH_PLUGIN +noinst_PROGRAMS = thrift-bootstrap + +thrift_bootstrap_SOURCES = \ + common.h \ + common.cc \ + audit/t_audit.h \ + audit/t_audit.cpp \ + generate/t_generator.cc \ + generate/t_generator_registry.h \ + globals.h \ + platform.h \ + logging.h \ + parse/t_doc.h \ + parse/t_type.h \ + parse/t_base_type.h \ + parse/t_enum.h \ + parse/t_enum_value.h \ + parse/t_typedef.h \ + parse/t_typedef.cc \ + parse/t_container.h \ + parse/t_list.h \ + parse/t_set.h \ + parse/t_map.h \ + parse/t_struct.h \ + parse/t_field.h \ + parse/t_service.h \ + parse/t_function.h \ + parse/t_program.h \ + parse/t_scope.h \ + parse/t_const.h \ + parse/t_const_value.h \ + parse/parse.cc \ + generate/t_generator.h \ + generate/t_oop_generator.h \ + generate/t_html_generator.h \ + windows/config.h \ + windows/version.h \ + generate/t_cpp_generator.cc \ + main.h \ + main.cc + +main.cc: version.h + +thrift_bootstrap_CXXFLAGS = -Wall -Wextra -pedantic +thrift_bootstrap_LDADD = @LEXLIB@ libparse.a +endif diff --git a/compiler/cpp/src/common.cc b/compiler/cpp/src/common.cc new file mode 100644 index 000000000..6e11bb241 --- /dev/null +++ b/compiler/cpp/src/common.cc @@ -0,0 +1,69 @@ +/* + * 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 "common.h" +#include "parse/t_base_type.h" + +t_type* g_type_void; +t_type* g_type_string; +t_type* g_type_binary; +t_type* g_type_slist; +t_type* g_type_bool; +t_type* g_type_i8; +t_type* g_type_i16; +t_type* g_type_i32; +t_type* g_type_i64; +t_type* g_type_double; + +void initGlobals() { + g_type_void = new t_base_type("void", t_base_type::TYPE_VOID); + g_type_string = new t_base_type("string", t_base_type::TYPE_STRING); + g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING); + ((t_base_type*)g_type_binary)->set_binary(true); + g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING); + ((t_base_type*)g_type_slist)->set_string_list(true); + g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL); + g_type_i8 = new t_base_type("i8", t_base_type::TYPE_I8); + g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16); + g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32); + g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64); + g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE); +} + +void clearGlobals() { + delete g_type_void; + delete g_type_string; + delete g_type_bool; + delete g_type_i8; + delete g_type_i16; + delete g_type_i32; + delete g_type_i64; + delete g_type_double; +} + +/** + * Those are not really needed for plugins but causes link errors without + */ + +/** + * The location of the last parsed doctext comment. + */ +int g_doctext_lineno; +int g_program_doctext_lineno = 0; +PROGDOCTEXT_STATUS g_program_doctext_status = INVALID; diff --git a/compiler/cpp/src/common.h b/compiler/cpp/src/common.h new file mode 100644 index 000000000..ab7c42327 --- /dev/null +++ b/compiler/cpp/src/common.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef T_COMMON_H +#define T_COMMON_H + +#include "parse/t_type.h" + +/** + * Global types for the parser to be able to reference + */ + +extern t_type* g_type_void; +extern t_type* g_type_string; +extern t_type* g_type_binary; +extern t_type* g_type_slist; +extern t_type* g_type_bool; +extern t_type* g_type_i8; +extern t_type* g_type_i16; +extern t_type* g_type_i32; +extern t_type* g_type_i64; +extern t_type* g_type_double; + +void initGlobals(); +void clearGlobals(); + +#endif diff --git a/compiler/cpp/src/generate/t_generator.cc b/compiler/cpp/src/generate/t_generator.cc index e7760d7dc..7f5daf442 100644 --- a/compiler/cpp/src/generate/t_generator.cc +++ b/compiler/cpp/src/generate/t_generator.cc @@ -127,11 +127,12 @@ void t_generator_registry::register_generator(t_generator_factory* factory) { the_map[factory->get_short_name()] = factory; } -t_generator* t_generator_registry::get_generator(t_program* program, const string& options) { +void t_generator::parse_options(const string& options, + string& language, + map<string, string>& parsed_options) { string::size_type colon = options.find(':'); - string language = options.substr(0, colon); + language = options.substr(0, colon); - map<string, string> parsed_options; if (colon != string::npos) { string::size_type pos = colon + 1; while (pos != string::npos && pos < options.size()) { @@ -152,7 +153,12 @@ t_generator* t_generator_registry::get_generator(t_program* program, const strin parsed_options[key] = value; } } +} +t_generator* t_generator_registry::get_generator(t_program* program, + const string& language, + const map<string, string>& parsed_options, + const std::string& options) { gen_map_t& the_map = get_generator_map(); gen_map_t::iterator iter = the_map.find(language); @@ -163,6 +169,13 @@ t_generator* t_generator_registry::get_generator(t_program* program, const strin return iter->second->get_generator(program, parsed_options, options); } +t_generator* t_generator_registry::get_generator(t_program* program, const string& options) { + string language; + map<string, string> parsed_options; + t_generator::parse_options(options, language, parsed_options); + return get_generator(program, language, parsed_options, options); +} + t_generator_registry::gen_map_t& t_generator_registry::get_generator_map() { // http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 static gen_map_t* the_map = new gen_map_t(); diff --git a/compiler/cpp/src/generate/t_generator.h b/compiler/cpp/src/generate/t_generator.h index 4136ab66c..590efdbae 100644 --- a/compiler/cpp/src/generate/t_generator.h +++ b/compiler/cpp/src/generate/t_generator.h @@ -25,7 +25,7 @@ #include <fstream> #include <sstream> #include "parse/t_program.h" -#include "globals.h" +#include "common.h" #include "t_generator_registry.h" #include "version.h" @@ -66,6 +66,9 @@ public: const std::string& contents, const std::string& comment_end); + static void parse_options(const std::string& options, std::string& language, + std::map<std::string, std::string>& parsed_options); + /** * check whether sub-namespace declaraction is used by generator. * e.g. allow @@ -257,6 +260,7 @@ public: /** * Get the true type behind a series of typedefs. */ + static const t_type* get_true_type(const t_type* type) { return type->get_true_type(); } static t_type* get_true_type(t_type* type) { return type->get_true_type(); } protected: diff --git a/compiler/cpp/src/generate/t_generator_registry.h b/compiler/cpp/src/generate/t_generator_registry.h index a85238592..1f02167bc 100644 --- a/compiler/cpp/src/generate/t_generator_registry.h +++ b/compiler/cpp/src/generate/t_generator_registry.h @@ -81,6 +81,10 @@ public: static void register_generator(t_generator_factory* factory); static t_generator* get_generator(t_program* program, const std::string& options); + static t_generator* get_generator(t_program* program, + const std::string& laugnage, + const std::map<std::string, std::string>& parsed_options, + const std::string& options); typedef std::map<std::string, t_generator_factory*> gen_map_t; static gen_map_t& get_generator_map(); diff --git a/compiler/cpp/src/generate/t_oop_generator.h b/compiler/cpp/src/generate/t_oop_generator.h index 07c9d85a3..e5a469842 100644 --- a/compiler/cpp/src/generate/t_oop_generator.h +++ b/compiler/cpp/src/generate/t_oop_generator.h @@ -23,7 +23,7 @@ #include <string> #include <iostream> -#include "globals.h" +#include "common.h" #include "t_generator.h" #include <algorithm> diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc index 49bf7e184..abd3320f8 100644 --- a/compiler/cpp/src/generate/t_rb_generator.cc +++ b/compiler/cpp/src/generate/t_rb_generator.cc @@ -195,14 +195,14 @@ public: std::string render_require_thrift(); std::string render_includes(); std::string declare_field(t_field* tfield); - std::string type_name(t_type* ttype); - std::string full_type_name(t_type* ttype); + std::string type_name(const t_type* ttype); + std::string full_type_name(const t_type* ttype); std::string function_signature(t_function* tfunction, std::string prefix = ""); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); std::string rb_namespace_to_path_prefix(std::string rb_namespace); - std::vector<std::string> ruby_modules(t_program* p) { + std::vector<std::string> ruby_modules(const t_program* p) { std::string ns = p->get_namespace("rb"); std::vector<std::string> modules; if (ns.empty()) { @@ -1101,7 +1101,7 @@ string t_rb_generator::argument_list(t_struct* tstruct) { return result; } -string t_rb_generator::type_name(t_type* ttype) { +string t_rb_generator::type_name(const t_type* ttype) { string prefix = ""; string name = ttype->get_name(); @@ -1112,7 +1112,7 @@ string t_rb_generator::type_name(t_type* ttype) { return prefix + name; } -string t_rb_generator::full_type_name(t_type* ttype) { +string t_rb_generator::full_type_name(const t_type* ttype) { string prefix = "::"; vector<std::string> modules = ruby_modules(ttype->get_program()); for (vector<std::string>::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { diff --git a/compiler/cpp/src/globals.h b/compiler/cpp/src/globals.h index c5c039468..961c6ef8a 100644 --- a/compiler/cpp/src/globals.h +++ b/compiler/cpp/src/globals.h @@ -62,21 +62,6 @@ extern int g_strict; extern t_program* g_program; /** - * Global types for the parser to be able to reference - */ - -extern t_type* g_type_void; -extern t_type* g_type_string; -extern t_type* g_type_binary; -extern t_type* g_type_slist; -extern t_type* g_type_bool; -extern t_type* g_type_i8; -extern t_type* g_type_i16; -extern t_type* g_type_i32; -extern t_type* g_type_i64; -extern t_type* g_type_double; - -/** * The scope that we are currently parsing into */ extern t_scope* g_scope; diff --git a/compiler/cpp/src/logging.cc b/compiler/cpp/src/logging.cc new file mode 100644 index 000000000..2daaaec10 --- /dev/null +++ b/compiler/cpp/src/logging.cc @@ -0,0 +1,77 @@ +/* + * 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. + */ + +/** + * Logging functions copied from main.cc to avoid link errors for plugins + */ + +#include "logging.h" +#include "globals.h" +#include <cstdarg> +#include <cstdio> +#include <cstdlib> + +// TODO: make plugins accept log options from main compiler +int g_debug = 0; +int g_warn = 1; +int g_verbose = 0; + +void pdebug(const char* fmt, ...) { + if (g_debug == 0) { + return; + } + va_list args; + // printf("[PARSE:%d] ", yylineno); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +void pverbose(const char* fmt, ...) { + if (g_verbose == 0) { + return; + } + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +void pwarning(int level, const char* fmt, ...) { + if (g_warn < level) { + return; + } + va_list args; + // printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +void failure(const char* fmt, ...) { + va_list args; + // fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + printf("\n"); + exit(1); +} diff --git a/compiler/cpp/src/logging.h b/compiler/cpp/src/logging.h index 3f1fce87d..ebefbf229 100644 --- a/compiler/cpp/src/logging.h +++ b/compiler/cpp/src/logging.h @@ -20,7 +20,9 @@ #ifndef T_LOGGING_H #define T_LOGGING_H -#include <string> +extern int g_debug; +extern int g_warn; +extern int g_verbose; /** * Parse debugging output, used to print helpful info diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc index 89dd9f97e..510d69f47 100644 --- a/compiler/cpp/src/main.cc +++ b/compiler/cpp/src/main.cc @@ -44,6 +44,7 @@ #endif // Careful: must include globals first for extern definitions +#include "common.h" #include "globals.h" #include "platform.h" @@ -52,6 +53,9 @@ #include "parse/t_scope.h" #include "generate/t_generator.h" #include "audit/t_audit.h" +#ifdef THRIFT_ENABLE_PLUGIN +#include "plugin/plugin_output.h" +#endif #include "version.h" @@ -63,21 +67,6 @@ using namespace std; t_program* g_program; /** - * Global types - */ - -t_type* g_type_void; -t_type* g_type_string; -t_type* g_type_binary; -t_type* g_type_slist; -t_type* g_type_bool; -t_type* g_type_i8; -t_type* g_type_i16; -t_type* g_type_i32; -t_type* g_type_i64; -t_type* g_type_double; - -/** * Global scope */ t_scope* g_scope; @@ -143,16 +132,9 @@ char* g_time_str; char* g_doctext; /** - * The location of the last parsed doctext comment. - */ -int g_doctext_lineno; - -/** * The First doctext comment */ char* g_program_doctext_candidate; -int g_program_doctext_lineno = 0; -PROGDOCTEXT_STATUS g_program_doctext_status = INVALID; /** * Whether or not negative field keys are accepted. @@ -179,6 +161,7 @@ bool g_audit = false; */ bool g_return_failure = false; bool g_audit_fatal = true; +bool g_generator_failure = false; /** * Win32 doesn't have realpath, so use fallback implementation in that case, @@ -1024,8 +1007,27 @@ void generate(t_program* program, const vector<string>& generator_strings) { t_generator* generator = t_generator_registry::get_generator(program, *iter); if (generator == NULL) { +#ifdef THRIFT_ENABLE_PLUGIN + switch (plugin_output::delegateToPlugin(program, *iter)) { + case plugin_output::PLUGIN_NOT_FOUND: + pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str()); + g_generator_failure = true; + break; + case plugin_output::PLUGIN_FAILURE: + pwarning(1, "Plugin generator for \"%s\" failed.\n", iter->c_str()); + g_generator_failure = true; + break; + case plugin_output::PLUGIN_SUCCEESS: + break; + default: + assert(false); + break; + } +#else pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str()); - } else { + g_generator_failure = true; +#endif + } else if (generator) { pverbose("Generating \"%s\"\n", iter->c_str()); generator->generate_program(); delete generator; @@ -1208,18 +1210,7 @@ int main(int argc, char** argv) { } // Initialize global types - g_type_void = new t_base_type("void", t_base_type::TYPE_VOID); - g_type_string = new t_base_type("string", t_base_type::TYPE_STRING); - g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING); - ((t_base_type*)g_type_binary)->set_binary(true); - g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING); - ((t_base_type*)g_type_slist)->set_string_list(true); - g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL); - g_type_i8 = new t_base_type("i8", t_base_type::TYPE_I8); - g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16); - g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32); - g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64); - g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE); + initGlobals(); if (g_audit) { // Audit operation @@ -1302,20 +1293,15 @@ int main(int argc, char** argv) { // Clean up. Who am I kidding... this program probably orphans heap memory // all over the place, but who cares because it is about to exit and it is // all referenced and used by this wacky parse tree up until now anyways. - - delete g_type_void; - delete g_type_string; - delete g_type_bool; - delete g_type_i8; - delete g_type_i16; - delete g_type_i32; - delete g_type_i64; - delete g_type_double; + clearGlobals(); // Finished if (g_return_failure && g_audit_fatal) { exit(2); } + if (g_generator_failure) { + exit(3); + } // Finished return 0; } diff --git a/compiler/cpp/src/parse/parse.cc b/compiler/cpp/src/parse/parse.cc index b22ee5281..81a557b15 100644 --- a/compiler/cpp/src/parse/parse.cc +++ b/compiler/cpp/src/parse/parse.cc @@ -29,3 +29,11 @@ t_type* t_type::get_true_type() { } return type; } + +const t_type* t_type::get_true_type() const { + const t_type* type = this; + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + return type; +} diff --git a/compiler/cpp/src/parse/t_const_value.h b/compiler/cpp/src/parse/t_const_value.h index 7e6e3f6b4..15366ad77 100644 --- a/compiler/cpp/src/parse/t_const_value.h +++ b/compiler/cpp/src/parse/t_const_value.h @@ -26,6 +26,11 @@ #include <vector> #include <string> +namespace plugin_output { +template <typename From, typename To> +void convert(From*, To&); +} + /** * A const value is something parsed that could be a map, set, list, struct * or whatever. @@ -141,6 +146,10 @@ private: t_enum* enum_; t_const_value_type valType_; + + // to read enum_ + template <typename From, typename To> + friend void plugin_output::convert(From*, To&); }; #endif diff --git a/compiler/cpp/src/parse/t_container.h b/compiler/cpp/src/parse/t_container.h index 0d992b723..2cdcf7edd 100644 --- a/compiler/cpp/src/parse/t_container.h +++ b/compiler/cpp/src/parse/t_container.h @@ -33,9 +33,9 @@ public: has_cpp_name_ = true; } - bool has_cpp_name() { return has_cpp_name_; } + bool has_cpp_name() const { return has_cpp_name_; } - std::string get_cpp_name() { return cpp_name_; } + std::string get_cpp_name() const { return cpp_name_; } bool is_container() const { return true; } diff --git a/compiler/cpp/src/parse/t_doc.h b/compiler/cpp/src/parse/t_doc.h index 9d310b72d..621513a3d 100644 --- a/compiler/cpp/src/parse/t_doc.h +++ b/compiler/cpp/src/parse/t_doc.h @@ -31,6 +31,7 @@ class t_doc { public: t_doc() : has_doc_(false) {} + virtual ~t_doc() {} void set_doc(const std::string& doc) { doc_ = doc; diff --git a/compiler/cpp/src/parse/t_enum.h b/compiler/cpp/src/parse/t_enum.h index 64f4ff4c8..268f89ff0 100644 --- a/compiler/cpp/src/parse/t_enum.h +++ b/compiler/cpp/src/parse/t_enum.h @@ -20,9 +20,11 @@ #ifndef T_ENUM_H #define T_ENUM_H -#include "t_enum_value.h" #include <vector> +#include "t_enum_value.h" +#include "t_type.h" + /** * An enumerated type. A list of constant objects with a name for the type. * @@ -35,7 +37,7 @@ public: void append(t_enum_value* constant) { constants_.push_back(constant); } - const std::vector<t_enum_value*>& get_constants() { return constants_; } + const std::vector<t_enum_value*>& get_constants() const { return constants_; } t_enum_value* get_constant_by_name(const std::string& name) { const std::vector<t_enum_value*>& enum_values = get_constants(); diff --git a/compiler/cpp/src/parse/t_enum_value.h b/compiler/cpp/src/parse/t_enum_value.h index 5979f06a7..296029b24 100644 --- a/compiler/cpp/src/parse/t_enum_value.h +++ b/compiler/cpp/src/parse/t_enum_value.h @@ -20,6 +20,7 @@ #ifndef T_ENUM_VALUE_H #define T_ENUM_VALUE_H +#include <map> #include <string> #include "t_doc.h" diff --git a/compiler/cpp/src/parse/t_field.h b/compiler/cpp/src/parse/t_field.h index eece7bb14..8b459a351 100644 --- a/compiler/cpp/src/parse/t_field.h +++ b/compiler/cpp/src/parse/t_field.h @@ -20,10 +20,12 @@ #ifndef T_FIELD_H #define T_FIELD_H +#include <map> #include <string> #include <sstream> #include "t_doc.h" +#include "t_type.h" // Forward declare for xsd_attrs class t_struct; @@ -58,7 +60,9 @@ public: ~t_field() {} - t_type* get_type() const { return type_; } + t_type* get_type() { return type_; } + + const t_type* get_type() const { return type_; } const std::string& get_name() const { return name_; } @@ -74,6 +78,8 @@ public: t_const_value* get_value() { return value_; } + const t_const_value* get_value() const { return value_; } + void set_xsd_optional(bool xsd_optional) { xsd_optional_ = xsd_optional; } bool get_xsd_optional() const { return xsd_optional_; } diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h index 812106cb4..563e9e038 100644 --- a/compiler/cpp/src/parse/t_program.h +++ b/compiler/cpp/src/parse/t_program.h @@ -58,9 +58,7 @@ class t_program : public t_doc { public: t_program(std::string path, std::string name) - : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false) { - scope_ = new t_scope(); - } + : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope) {} t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) { name_ = program_name(path); @@ -250,6 +248,10 @@ public: // Includes + void add_include(t_program* program) { + includes_.push_back(program); + } + void add_include(std::string path, std::string include_site) { t_program* program = new t_program(path); diff --git a/compiler/cpp/src/parse/t_scope.h b/compiler/cpp/src/parse/t_scope.h index 5acb94e5b..565fd8fde 100644 --- a/compiler/cpp/src/parse/t_scope.h +++ b/compiler/cpp/src/parse/t_scope.h @@ -32,6 +32,11 @@ #include "t_map.h" #include "t_list.h" +namespace plugin_output { +template <typename From, typename To> +void convert(From*, To&); +} + /** * This represents a variable scope used for looking up predefined types and * services. Typically, a scope is associated with a t_program. Scopes are not @@ -167,6 +172,10 @@ private: // Map of names to services std::map<std::string, t_service*> services_; + + // to list map entries + template <typename From, typename To> + friend void plugin_output::convert(From*, To&); }; #endif diff --git a/compiler/cpp/src/parse/t_service.h b/compiler/cpp/src/parse/t_service.h index 2b01f9c45..6fa8398f1 100644 --- a/compiler/cpp/src/parse/t_service.h +++ b/compiler/cpp/src/parse/t_service.h @@ -51,6 +51,8 @@ public: t_service* get_extends() { return extends_; } + const t_service* get_extends() const { return extends_; } + private: std::vector<t_function*> functions_; t_service* extends_; diff --git a/compiler/cpp/src/parse/t_struct.h b/compiler/cpp/src/parse/t_struct.h index d19447c36..1f48f91bf 100644 --- a/compiler/cpp/src/parse/t_struct.h +++ b/compiler/cpp/src/parse/t_struct.h @@ -127,7 +127,7 @@ public: return true; } - const members_type& get_members() { return members_; } + const members_type& get_members() const { return members_; } const members_type& get_sorted_members() { return members_in_id_order_; } @@ -147,6 +147,16 @@ public: return NULL; } + const t_field* get_field_by_name(std::string field_name) const { + members_type::const_iterator m_iter; + for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) { + if ((*m_iter)->get_name() == field_name) { + return *m_iter; + } + } + return NULL; + } + private: members_type members_; members_type members_in_id_order_; diff --git a/compiler/cpp/src/parse/t_type.h b/compiler/cpp/src/parse/t_type.h index 416cc6fa3..bea4ee171 100644 --- a/compiler/cpp/src/parse/t_type.h +++ b/compiler/cpp/src/parse/t_type.h @@ -63,6 +63,7 @@ public: const t_program* get_program() const { return program_; } t_type* get_true_type(); + const t_type* get_true_type() const; // This function will break (maybe badly) unless 0 <= num <= 16. static char nybble_to_xdigit(int num) { diff --git a/compiler/cpp/src/plugin/Makefile.am b/compiler/cpp/src/plugin/Makefile.am new file mode 100644 index 000000000..7e3c82d8b --- /dev/null +++ b/compiler/cpp/src/plugin/Makefile.am @@ -0,0 +1,47 @@ +# +# 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. +# +# +# Contains some contributions under the Thrift Software License. +# Please see doc/old-thrift-license.txt in the Thrift distribution for +# details. + +AUTOMAKE_OPTIONS = subdir-objects + +if WITH_PLUGIN +plugin_gen = plugin_types.h \ + plugin_types.cpp \ + plugin_constants.h \ + plugin_constants.cpp + +BUILT_SOURCES = $(plugin_gen) +gen.stamp: plugin.thrift $(top_builddir)/compiler/cpp/src/thrift-bootstrap + @$(RM) -f gen.tmp + @touch gen.tmp + $(top_builddir)/compiler/cpp/src/thrift-bootstrap -gen cpp -out . $< + @mv -f gen.tmp $@ + +$(plugin_gen): gen.stamp + @if test -f $@; then :; else \ + $(RM) -f gen.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) gen.stamp; \ + fi + +clean-local: + $(RM) version.h windows/version.h $(plugin_gen) +endif diff --git a/compiler/cpp/src/plugin/plugin.cc b/compiler/cpp/src/plugin/plugin.cc new file mode 100644 index 000000000..d969f5028 --- /dev/null +++ b/compiler/cpp/src/plugin/plugin.cc @@ -0,0 +1,503 @@ +/* + * 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 "plugin/plugin.h" + +#ifdef _WIN32 +#include <fcntl.h> +#include <io.h> +#endif + +#include <cassert> +#include <iostream> + +#include <boost/bind.hpp> +#include <boost/range/adaptor/map.hpp> +#include <boost/range/algorithm/for_each.hpp> +#include <boost/smart_ptr.hpp> + +#include "generate/t_generator.h" +#include "plugin/type_util.h" +#include "thrift/protocol/TBinaryProtocol.h" +#include "thrift/transport/TBufferTransports.h" +#include "thrift/transport/TFDTransport.h" + +#include "plugin/plugin_types.h" + +namespace apache { +namespace thrift { +namespace plugin { + +using apache::thrift::protocol::TBinaryProtocol; +using apache::thrift::transport::TFDTransport; +using apache::thrift::transport::TFramedTransport; + +#define THRIFT_CONVERT_FORWARD(from_type) \ + template <> \ + typename ToType<from_type>::type* convert_forward<from_type>(const from_type& from) + +#define THRIFT_CONVERT_COMPLETE_DECL(from_type) \ + template <> \ + void convert(const from_type& from, ToType<from_type>::type* to) + +#define THRIFT_CONVERT_UNARY_DECL(from_type) \ + template <> \ + typename ToType<from_type>::type* convert<from_type>(const from_type& from) + +#define THRIFT_CONVERSION_DECL(from_type) \ + THRIFT_CONVERT_FORWARD(from_type); \ + THRIFT_CONVERT_COMPLETE_DECL(from_type); \ + THRIFT_CONVERT_UNARY_DECL(from_type) + +#define THRIFT_CONVERT_COMPLETE(from_type) \ + THRIFT_CONVERSION_DECL(from_type) { \ + ToType<from_type>::type* to = convert_forward(from); \ + convert(from, to); \ + return to; \ + } \ + THRIFT_CONVERT_COMPLETE_DECL(from_type) + +#define THRIFT_CONVERSION(from_type, ...) \ + THRIFT_CONVERT_FORWARD(from_type) { \ + (void)from; \ + return new ToType<from_type>::type(__VA_ARGS__); \ + } \ + THRIFT_CONVERT_COMPLETE(from_type) + +#define THRIFT_ASSIGN_DOC() \ + do { \ + if (from.__isset.doc) \ + to->set_doc(from.doc); \ + } while (0) + +#define THRIFT_ASSIGN_ANNOTATIONS() \ + THRIFT_ASSIGN_DOC(); \ + do { \ + if (from.__isset.annotations) \ + to->annotations_ = from.annotations; \ + } while (0) + +#define THRIFT_ASSIGN_METADATA() \ + do { \ + to->set_name(from.metadata.name); \ + if (from.metadata.__isset.doc) \ + to->set_doc(from.metadata.doc); \ + if (from.metadata.__isset.annotations) \ + to->annotations_ = from.metadata.annotations; \ + } while (0) + +::t_program* g_program = 0; + +template <typename C, typename S> +struct TypeCache { + C* operator[](const int64_t& k) { + typename std::map<int64_t, C*>::iterator it = cache.find(k); + if (it != cache.end()) { + return it->second; + } else { + typename std::map<int64_t, S>::const_iterator cit = source->find(k); + if (cit == source->end()) { + throw ThriftPluginError("Type not found"); + } + return (cache)[k] = convert_forward(cit->second); + } + } + + void compileAll() { + boost::for_each(*source | boost::adaptors::map_keys, + boost::bind(&TypeCache::compile, this, _1)); + } + + std::map<int64_t, S> const* source; + +protected: + std::map<int64_t, C*> cache; + +private: + void compile(const int64_t& k) { + typename std::map<int64_t, S>::const_iterator cit = source->find(k); + if (cit == source->end()) { + throw ThriftPluginError("Type not found "); + } + convert(cit->second, (*this)[k]); + } +}; +std::map<int64_t, ::t_program*> g_program_cache; +TypeCache< ::t_type, t_type> g_type_cache; +TypeCache< ::t_const, t_const> g_const_cache; +TypeCache< ::t_service, t_service> g_service_cache; + +void set_global_cache(const TypeRegistry& from) { + g_type_cache.source = &from.types; + g_const_cache.source = &from.constants; + g_service_cache.source = &from.services; + + g_type_cache.compileAll(); + g_const_cache.compileAll(); + g_service_cache.compileAll(); +} + +template <typename T> +T* resolve_type(int64_t name) { + return reinterpret_cast<T*>(g_type_cache[name]); +} + +::t_const* resolve_const(int64_t name) { + return g_const_cache[name]; +} + +::t_service* resolve_service(int64_t name) { + return g_service_cache[name]; +} + +THRIFT_CONVERT_FORWARD(t_base_type) { +#define T_BASETYPE_CASE(type) \ + case t_base::TYPE_##type: \ + t = ::t_base_type::TYPE_##type; \ + break + + ::t_base_type::t_base t = ::t_base_type::TYPE_VOID; + bool is_binary = false; + switch (from.value) { + T_BASETYPE_CASE(VOID); + T_BASETYPE_CASE(STRING); + T_BASETYPE_CASE(BOOL); + T_BASETYPE_CASE(I8); + T_BASETYPE_CASE(I16); + T_BASETYPE_CASE(I32); + T_BASETYPE_CASE(I64); + T_BASETYPE_CASE(DOUBLE); + case t_base::TYPE_BINARY: + t = ::t_base_type::TYPE_STRING; + is_binary = true; + break; + } + ::t_base_type* to = new ::t_base_type(from.metadata.name, t); + to->set_binary(is_binary); + return to; +#undef T_BASETYPE_CASE +} +THRIFT_CONVERT_COMPLETE(t_base_type) { + THRIFT_ASSIGN_METADATA(); +} + +THRIFT_CONVERT_FORWARD(t_typedef) { + ::t_typedef* to; + if (from.forward) { + to = new ::t_typedef(g_program_cache[from.metadata.program_id], from.symbolic, true); + } else { + to = new ::t_typedef(g_program_cache[from.metadata.program_id], + resolve_type< ::t_type>(from.type), from.symbolic); + } + return to; +} +THRIFT_CONVERT_COMPLETE(t_typedef) { + THRIFT_ASSIGN_METADATA(); +} +THRIFT_CONVERSION(t_enum_value, from.name, from.value) { + assert(to); + THRIFT_ASSIGN_ANNOTATIONS(); +} +THRIFT_CONVERSION(t_enum, g_program_cache[from.metadata.program_id]) { + assert(to); + THRIFT_ASSIGN_METADATA(); + boost::for_each(from.constants | boost::adaptors::transformed(convert<t_enum_value>), + boost::bind(&::t_enum::append, to, _1)); +} +THRIFT_CONVERSION(t_list, resolve_type< ::t_type>(from.elem_type)) { + assert(to); + THRIFT_ASSIGN_METADATA(); + if (from.__isset.cpp_name) + to->set_cpp_name(from.cpp_name); +} +THRIFT_CONVERSION(t_set, resolve_type< ::t_type>(from.elem_type)) { + assert(to); + THRIFT_ASSIGN_METADATA(); + if (from.__isset.cpp_name) + to->set_cpp_name(from.cpp_name); +} +THRIFT_CONVERSION(t_map, + resolve_type< ::t_type>(from.key_type), + resolve_type< ::t_type>(from.val_type)) { + assert(to); + THRIFT_ASSIGN_METADATA(); + if (from.__isset.cpp_name) + to->set_cpp_name(from.cpp_name); +} +THRIFT_CONVERSION(t_const_value, ) { +#define T_CONST_VALUE_CASE(type) \ + if (from.__isset.type##_val) \ + to->set_##type(from.type##_val) + + assert(to); + if (from.__isset.map_val) { + to->set_map(); + for (std::map<t_const_value, t_const_value>::const_iterator it = from.map_val.begin(); + it != from.map_val.end(); it++) { + to->add_map(convert(it->first), convert(it->second)); + } + } else if (from.__isset.list_val) { + to->set_list(); + boost::for_each(from.list_val | boost::adaptors::transformed(&convert<t_const_value>), + boost::bind(&::t_const_value::add_list, to, _1)); + } else + T_CONST_VALUE_CASE(string); + else T_CONST_VALUE_CASE(integer); + else T_CONST_VALUE_CASE(double); + else { + T_CONST_VALUE_CASE(identifier); + if (from.__isset.enum_val) + to->set_enum(resolve_type< ::t_enum>(from.enum_val)); + } +#undef T_CONST_VALUE_CASE +} +THRIFT_CONVERSION(t_field, resolve_type< ::t_type>(from.type), from.name, from.key) { + assert(to); + THRIFT_ASSIGN_ANNOTATIONS(); + to->set_reference(from.reference); + to->set_req(static_cast< ::t_field::e_req>(from.req)); + if (from.__isset.value) { + to->set_value(convert(from.value)); + } +} +THRIFT_CONVERSION(t_struct, g_program_cache[from.metadata.program_id]) { + assert(to); + THRIFT_ASSIGN_METADATA(); + to->set_union(from.is_union); + to->set_xception(from.is_xception); + boost::for_each(from.members | boost::adaptors::transformed(convert<t_field>), + boost::bind(&::t_struct::append, to, _1)); +} +THRIFT_CONVERSION(t_const, + resolve_type< ::t_type>(from.type), + from.name, + convert<t_const_value>(from.value)) { + assert(to); + THRIFT_ASSIGN_DOC(); +} + +THRIFT_CONVERSION(t_function, + resolve_type< ::t_type>(from.returntype), + from.name, + resolve_type< ::t_struct>(from.arglist), + resolve_type< ::t_struct>(from.xceptions), + from.is_oneway) { + assert(to); + THRIFT_ASSIGN_DOC(); +} + +THRIFT_CONVERSION(t_service, g_program_cache[from.metadata.program_id]) { + assert(to); + assert(from.metadata.program_id); + assert(g_program_cache[from.metadata.program_id]); + THRIFT_ASSIGN_METADATA(); + + boost::for_each(from.functions | boost::adaptors::transformed(convert<t_function>), + boost::bind(&::t_service::add_function, to, _1)); + + if (from.__isset.extends_) + to->set_extends(resolve_service(from.extends_)); +} + +THRIFT_CONVERT_FORWARD(t_type) { +#define T_TYPE_CASE_FW_T(case, type) \ + if (from.__isset.case##_val) \ + return convert_forward<type>(from.case##_val) +#define T_TYPE_CASE_FW(case) T_TYPE_CASE_FW_T(case, t_##case) + + T_TYPE_CASE_FW(base_type); + T_TYPE_CASE_FW(typedef); + T_TYPE_CASE_FW(enum); + T_TYPE_CASE_FW(struct); + T_TYPE_CASE_FW_T(xception, t_struct); + T_TYPE_CASE_FW(list); + T_TYPE_CASE_FW(set); + T_TYPE_CASE_FW(map); + T_TYPE_CASE_FW(service); + throw ThriftPluginError("Invalid data: Type union has no value."); +#undef T_TYPE_CASE_FW_T +#undef T_TYPE_CASE_FW +} +THRIFT_CONVERT_COMPLETE(t_type) { +#define T_TYPE_CASE_T(case, type) \ + else if (from.__isset.case##_val) \ + convert<type, ::type>(from.case##_val, reinterpret_cast< ::type*>(to)) +#define T_TYPE_CASE(case) T_TYPE_CASE_T(case, t_##case) + + if (false) { + } + T_TYPE_CASE(base_type); + T_TYPE_CASE(typedef); + T_TYPE_CASE(enum); + T_TYPE_CASE(struct); + T_TYPE_CASE_T(xception, t_struct); + T_TYPE_CASE(list); + T_TYPE_CASE(set); + T_TYPE_CASE(map); + T_TYPE_CASE(service); + else { + throw ThriftPluginError("Invalid data: Type union has no value."); + } +#undef T_TYPE_CASE_T +#undef T_TYPE_CASE +} + +THRIFT_CONVERSION(t_scope, ) { + assert(to); +#define T_SCOPE_RESOLVE(type, name, a) \ + for (std::vector<int64_t>::const_iterator it = from.name##s.begin(); it != from.name##s.end(); \ + it++) { \ + ::t_##type* t = resolve_##type a(*it); \ + to->add_##name(t->get_name(), t); \ + } + T_SCOPE_RESOLVE(type, type, < ::t_type>); + T_SCOPE_RESOLVE(const, constant, ); + T_SCOPE_RESOLVE(service, service, ); +#undef T_SCOPE_RESOLVE +} + +THRIFT_CONVERT_FORWARD(t_program) { + ::t_program* to = new ::t_program(from.path, from.name); + for (std::vector<t_program>::const_iterator it = from.includes.begin(); it != from.includes.end(); + it++) { + to->add_include(convert_forward(*it)); + } + g_program_cache[from.program_id] = to; + return to; +} +THRIFT_CONVERT_COMPLETE(t_program) { + assert(to); + g_program = to; + convert<t_scope, ::t_scope>(from.scope, to->scope()); + THRIFT_ASSIGN_DOC(); + + to->set_out_path(from.out_path, from.out_path_is_absolute); + + boost::for_each(from.typedefs | boost::adaptors::transformed(&resolve_type< ::t_typedef>), + boost::bind(&::t_program::add_typedef, to, _1)); + boost::for_each(from.enums | boost::adaptors::transformed(&resolve_type< ::t_enum>), + boost::bind(&::t_program::add_enum, to, _1)); + for (std::vector<int64_t>::const_iterator it = from.objects.begin(); it != from.objects.end(); + it++) { + ::t_struct* t2 = resolve_type< ::t_struct>(*it); + if (t2->is_xception()) { + to->add_xception(t2); + } else { + to->add_struct(t2); + } + } + boost::for_each(from.consts | boost::adaptors::transformed(&resolve_const), + boost::bind(&::t_program::add_const, to, _1)); + boost::for_each(from.services | boost::adaptors::transformed(&resolve_service), + boost::bind(&::t_program::add_service, to, _1)); + + for (std::vector<t_program>::const_iterator it = from.includes.begin(); it != from.includes.end(); + it++) { + convert(*it, g_program_cache[it->program_id]); + } + std::for_each(from.c_includes.begin(), from.c_includes.end(), + boost::bind(&::t_program::add_c_include, to, _1)); + std::for_each(from.cpp_includes.begin(), from.cpp_includes.end(), + boost::bind(&::t_program::add_cpp_include, to, _1)); + for (std::map<std::string, std::string>::const_iterator it = from.namespaces.begin(); + it != from.namespaces.end(); it++) { + to->set_namespace(it->first, it->second); + } + + to->set_include_prefix(from.include_prefix); + to->set_namespace(from.namespace_); +} + +int GeneratorPlugin::exec(int, char* []) { +#ifdef _WIN32 + _setmode(fileno(stdin), _O_BINARY); +#endif + boost::shared_ptr<TFramedTransport> transport( + new TFramedTransport(boost::make_shared<TFDTransport>(fileno(stdin)))); + TBinaryProtocol proto(transport); + GeneratorInput input; + try { + input.read(&proto); + } catch (std::exception& err) { + std::cerr << "Error while receiving plugin data: " << err.what() << std::endl; + return -1; + } + initGlobals(); + ::t_program* p = g_program = convert_forward(input.program); + set_global_cache(input.type_registry); + convert(input.program, p); + + int ret = generate(p, input.parsed_options); + clearGlobals(); + + return ret; +} + +::t_const_value::t_const_value_type const_value_case(const t_const_value& v) { + if (v.__isset.map_val) + return ::t_const_value::CV_MAP; + if (v.__isset.list_val) + return ::t_const_value::CV_LIST; + if (v.__isset.string_val) + return ::t_const_value::CV_STRING; + if (v.__isset.integer_val) + return ::t_const_value::CV_INTEGER; + if (v.__isset.double_val) + return ::t_const_value::CV_DOUBLE; + if (v.__isset.identifier_val) + return ::t_const_value::CV_IDENTIFIER; + if (v.__isset.enum_val) + return ::t_const_value::CV_IDENTIFIER; + throw ThriftPluginError("Unknown const value type"); +} + +bool t_const_value::operator<(const t_const_value& that) const { + ::t_const_value::t_const_value_type t1 = const_value_case(*this); + ::t_const_value::t_const_value_type t2 = const_value_case(that); + if (t1 != t2) + return t1 < t2; + switch (t1) { + case ::t_const_value::CV_INTEGER: + return integer_val < that.integer_val; + case ::t_const_value::CV_DOUBLE: + return double_val < that.double_val; + case ::t_const_value::CV_STRING: + return string_val < that.string_val; + case ::t_const_value::CV_MAP: + if (that.map_val.empty()) + return false; + else if (map_val.empty()) + return true; + else + return map_val.begin()->first < that.map_val.begin()->first; + case ::t_const_value::CV_LIST: + if (that.list_val.empty()) + return false; + else if (list_val.empty()) + return true; + else + return list_val.front() < that.list_val.front(); + case ::t_const_value::CV_IDENTIFIER: + return integer_val < that.integer_val; + } + throw ThriftPluginError("Unknown const value type"); +} +} +} +} diff --git a/compiler/cpp/src/plugin/plugin.h b/compiler/cpp/src/plugin/plugin.h new file mode 100644 index 000000000..705cd41ff --- /dev/null +++ b/compiler/cpp/src/plugin/plugin.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef T_PLUGIN_PLUGIN_H +#define T_PLUGIN_PLUGIN_H + +#include "thrift/Thrift.h" + +class t_program; + +namespace apache { +namespace thrift { +namespace plugin { + +struct ThriftPluginError : public apache::thrift::TException { + ThriftPluginError(const std::string& msg) : apache::thrift::TException(msg) {} +}; + +class GeneratorPlugin { +public: + int exec(int argc, char* argv[]); + virtual int generate(::t_program*, const std::map<std::string, std::string>&) = 0; +}; +} +} +} + +#endif diff --git a/compiler/cpp/src/plugin/plugin.thrift b/compiler/cpp/src/plugin/plugin.thrift new file mode 100644 index 000000000..a93873da1 --- /dev/null +++ b/compiler/cpp/src/plugin/plugin.thrift @@ -0,0 +1,202 @@ +/* + * 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. + */ + +namespace as3 org.apache.thrift.plugin +namespace cpp apache.thrift.plugin +namespace csharp Thrift.Plugin +namespace d thrift.plugin +namespace delphi Thrift.Plugin +namespace erl thrift.plugin +namespace go thrift +namespace haxe org.apache.thrift.plugin +namespace hs Thrift.Plugin +namespace java org.apache.thrift.plugin +namespace ocaml Thrift +namespace perl Thrift.Plugin +namespace php thrift.plugin +namespace py thrift.plugin +namespace rb Thrift + +typedef i64 t_program_id +typedef i64 t_type_id +typedef i64 t_const_id +typedef i64 t_service_id + +enum t_base { + TYPE_VOID + TYPE_STRING + TYPE_BOOL + TYPE_I8 + TYPE_I16 + TYPE_I32 + TYPE_I64 + TYPE_DOUBLE + TYPE_BINARY +} + +struct TypeMetadata { + 1: required string name + 2: required t_program_id program_id + 99: optional map<string, string> annotations + 100: optional string doc +} + +struct t_base_type { + 1: required TypeMetadata metadata + 2: required t_base value +} + +struct t_list { + 1: required TypeMetadata metadata + 2: optional string cpp_name + 3: required t_type_id elem_type +} + +struct t_set { + 1: required TypeMetadata metadata + 2: optional string cpp_name + 3: required t_type_id elem_type +} + +struct t_map { + 1: required TypeMetadata metadata + 2: optional string cpp_name + 3: required t_type_id key_type + 4: required t_type_id val_type +} + +struct t_typedef { + 1: required TypeMetadata metadata + 2: required t_type_id type + 3: required string symbolic + 4: required bool forward +} + +struct t_enum_value { + 1: required string name + 2: required i32 value + 99: optional map<string, string> annotations + 100: optional string doc +} +struct t_enum { + 1: required TypeMetadata metadata + 2: required list<t_enum_value> constants +} + +enum Requiredness { + T_REQUIRED = 0 + T_OPTIONAL = 1 + T_OPT_IN_REQ_OUT = 2 +} + +union t_const_value { + 1: optional map<t_const_value, t_const_value> map_val + 2: optional list<t_const_value> list_val + 3: optional string string_val + 4: optional i64 integer_val + 5: optional double double_val + 6: optional string identifier_val + 7: optional t_type_id enum_val +} +struct t_const { + 1: required string name + 2: required t_type_id type + 3: required t_const_value value + 100: optional string doc +} +struct t_struct { + 1: required TypeMetadata metadata + 2: required list<t_field> members + 3: required bool is_union + 4: required bool is_xception +} +struct t_field { + 1: required string name + 2: required t_type_id type + 3: required i32 key + 4: required Requiredness req + 5: optional t_const_value value + 10: required bool reference + 99: optional map<string, string> annotations + 100: optional string doc +} +struct t_function { + 1: required string name + 2: required t_type_id returntype + 3: required t_type_id arglist + 4: required t_type_id xceptions + 5: required bool is_oneway + 100: optional string doc +} +struct t_service { + 1: required TypeMetadata metadata + 2: required list<t_function> functions + 3: optional t_service_id extends_ +} +union t_type { + 1: optional t_base_type base_type_val + 2: optional t_typedef typedef_val + 3: optional t_enum enum_val + 4: optional t_struct struct_val + 5: optional t_struct xception_val + 6: optional t_list list_val + 7: optional t_set set_val + 8: optional t_map map_val + 9: optional t_service service_val +} +struct t_scope { + 1: required list<t_type_id> types + 2: required list<t_const_id> constants + 3: required list<t_service_id> services +} + +struct TypeRegistry { + 1: required map<t_type_id, t_type> types + 2: required map<t_const_id, t_const> constants + 3: required map<t_service_id, t_service> services +} + +struct t_program { + 1: required string name + 2: required t_program_id program_id + 3: required string path + 4: required string namespace_ + 5: required string out_path + 6: required bool out_path_is_absolute + 8: required list<t_program> includes + 9: required string include_prefix + 10: required t_scope scope + + 11: required list<t_type_id> typedefs + 12: required list<t_type_id> enums + 13: required list<t_const_id> consts + 14: required list<t_type_id> objects + 15: required list<t_service_id> services + + 16: required map<string, string> namespaces + 17: required list<string> cpp_includes + 18: required list<string> c_includes + 100: optional string doc +} + +struct GeneratorInput { + 1: required t_program program + 2: required TypeRegistry type_registry + 3: required map<string, string> parsed_options +} diff --git a/compiler/cpp/src/plugin/plugin_output.cc b/compiler/cpp/src/plugin/plugin_output.cc new file mode 100644 index 000000000..1ab015e84 --- /dev/null +++ b/compiler/cpp/src/plugin/plugin_output.cc @@ -0,0 +1,410 @@ +/* + * 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. + */ + +#ifdef _WIN32 +#include <cstdio> +#include <fcntl.h> +#include <io.h> +#include <iostream> +#define THRIFT_POPEN(cmd) _popen(cmd, "wb") +#define THRIFT_PCLOSE _pclose +#else +#define THRIFT_POPEN(cmd) popen(cmd, "w") +#define THRIFT_PCLOSE pclose +#endif + +#include "plugin/plugin_output.h" + +#include <boost/range/adaptor/map.hpp> +#include <boost/range/algorithm/copy.hpp> +#include <boost/range/algorithm/transform.hpp> +#include <boost/smart_ptr.hpp> + +#include "generate/t_generator.h" +#include "plugin/plugin.h" +#include "plugin/type_util.h" +#include "thrift/protocol/TBinaryProtocol.h" +#include "thrift/transport/TBufferTransports.h" +#include "thrift/transport/TFDTransport.h" + +#include "plugin/plugin_types.h" + +namespace plugin_output { + +template <typename From> +typename apache::thrift::plugin::ToType<From>::type convert(From* from) { + typename apache::thrift::plugin::ToType<From>::type to; + convert(from, to); + return to; +} + +using apache::thrift::protocol::TBinaryProtocol; +using apache::thrift::transport::TFDTransport; +using apache::thrift::transport::TFramedTransport; + +using namespace apache::thrift; + +#define THRIFT_CONVERSION_N(from_type, to_type) \ + template <> \ + void convert<from_type, to_type>(from_type * from, to_type & to) +#define THRIFT_CONVERSION(type) THRIFT_CONVERSION_N(::type, plugin::type) + +#define THRIFT_ASSIGN_N(from_name, to_name, prefix) \ + do { \ + if (from) \ + to.__set_##to_name(prefix(from->from_name)); \ + } while (0) + +#define THRIFT_ASSIGN(name) THRIFT_ASSIGN_N(get_##name(), name, ) +#define THRIFT_ASSIGN_CONVERT(type, from_name, to_name) \ + do { \ + if (from && from->from_name) { \ + to.__set_##to_name(convert(from->from_name)); \ + } \ + } while (0) + +#define THRIFT_ASSIGN_OPT(name) \ + do { \ + if (from->has_##name()) \ + THRIFT_ASSIGN(name); \ + } while (0) + +#define THRIFT_ASSIGN_LIST_N(type, from_name, to_name) \ + do { \ + if (from && !from->from_name.empty()) { \ + std::transform(from->from_name.begin(), \ + from->from_name.end(), \ + std::back_inserter(to.to_name), \ + convert< ::type>); \ + } \ + } while (0) + +#define THRIFT_ASSIGN_METADATA() convert(reinterpret_cast<t_type*>(from), to.metadata) + +// To avoid multiple instances of same type, t_type, t_const and t_service are stored in one place +// and referenced by ID. +template <typename T> +struct TypeCache { + typedef typename plugin::ToType<T>::type to_type; + std::map<int64_t, to_type> cache; + + template <typename T2> + int64_t store(T2* t) { + intptr_t id = reinterpret_cast<intptr_t>(t); + if (id) { + typename std::map<int64_t, to_type>::iterator it = cache.find(id); + if (it == cache.end()) { + // HACK: fake resolve for recursive type + cache.insert(std::make_pair(id, to_type())); + // overwrite with true value + cache[id] = convert(t); + } + } + return static_cast<int64_t>(id); + } + + void clear() { cache.clear(); } +}; + +template <typename T> +int64_t store_type(T* t); + +#define T_STORE(type) \ + TypeCache<t_##type> type##_cache; \ + template <> \ + plugin::t_##type##_id store_type<t_##type>(t_##type * t) { \ + return type##_cache.store<t_##type>(t); \ + } +T_STORE(type) +T_STORE(const) +T_STORE(service) +#undef T_STORE + +#define THRIFT_ASSIGN_ID_N(t, from_name, to_name) \ + do { \ + if (from && from->from_name) \ + to.__set_##to_name(store_type<t>(from->from_name)); \ + } while (0) + +#define THRIFT_ASSIGN_ID(name) THRIFT_ASSIGN_ID_N(t_type, get_##name(), name) + +#define THRIFT_ASSIGN_LIST_ID(t, name) \ + do { \ + if (from && !from->get_##name##s().empty()) { \ + std::transform(from->get_##name##s().begin(), \ + from->get_##name##s().end(), \ + std::back_inserter(to.name##s), \ + &store_type<t>); \ + } \ + } while (0) + +THRIFT_CONVERSION_N(::t_type, plugin::TypeMetadata) { + to.program_id = reinterpret_cast<int64_t>(from->get_program()); + THRIFT_ASSIGN_N(annotations_, annotations, ); + if (from->has_doc()) { + to.__set_doc(from->get_doc()); + } + THRIFT_ASSIGN(name); +} + +THRIFT_CONVERSION(t_typedef) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_ID(type); + THRIFT_ASSIGN(symbolic); + THRIFT_ASSIGN_N(is_forward_typedef(), forward, ); +} + +THRIFT_CONVERSION(t_enum_value) { + THRIFT_ASSIGN_OPT(doc); + THRIFT_ASSIGN(name); + THRIFT_ASSIGN(value); +} + +THRIFT_CONVERSION(t_enum) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_LIST_N(t_enum_value, get_constants(), constants); +} + +THRIFT_CONVERSION(t_const_value) { + switch (from->get_type()) { + case t_const_value::CV_INTEGER: + THRIFT_ASSIGN_N(get_integer(), integer_val, ); + break; + case t_const_value::CV_DOUBLE: + THRIFT_ASSIGN_N(get_double(), double_val, ); + break; + case t_const_value::CV_STRING: + THRIFT_ASSIGN_N(get_string(), string_val, ); + break; + case t_const_value::CV_IDENTIFIER: + THRIFT_ASSIGN_ID_N(t_type, enum_, enum_val); + THRIFT_ASSIGN_N(get_identifier(), identifier_val, ); + break; + case t_const_value::CV_MAP: + to.__isset.map_val = true; + if (from && !from->get_map().empty()) { + for (std::map< ::t_const_value*, ::t_const_value*>::const_iterator it + = from->get_map().begin(); + it != from->get_map().end(); + it++) { + to.map_val.insert(std::make_pair(convert(it->first), convert(it->second))); + } + } + break; + case t_const_value::CV_LIST: + to.__isset.list_val = true; + THRIFT_ASSIGN_LIST_N(t_const_value, get_list(), list_val); + break; + default: + throw plugin::ThriftPluginError("const value has no value"); + } +} +THRIFT_CONVERSION(t_const) { + THRIFT_ASSIGN_OPT(doc); + THRIFT_ASSIGN(name); + THRIFT_ASSIGN_ID(type); + THRIFT_ASSIGN_CONVERT(t_const_value, get_value(), value); +} +THRIFT_CONVERSION(t_field) { + THRIFT_ASSIGN_OPT(doc); + THRIFT_ASSIGN(name); + THRIFT_ASSIGN(key); + THRIFT_ASSIGN_N(get_req(), req, (plugin::Requiredness::type)); + THRIFT_ASSIGN(reference); + THRIFT_ASSIGN_ID(type); + THRIFT_ASSIGN_CONVERT(t_const_value, get_value(), value); +} +THRIFT_CONVERSION(t_struct) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_LIST_N(t_field, get_members(), members); + THRIFT_ASSIGN_N(is_union(), is_union, ); + THRIFT_ASSIGN_N(is_xception(), is_xception, ); +} +THRIFT_CONVERSION(t_function) { + THRIFT_ASSIGN_OPT(doc); + THRIFT_ASSIGN(name); + THRIFT_ASSIGN_ID(returntype); + THRIFT_ASSIGN_N(is_oneway(), is_oneway, ); + THRIFT_ASSIGN_ID(arglist); + THRIFT_ASSIGN_ID(xceptions); +} + +THRIFT_CONVERSION(t_list) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_OPT(cpp_name); + THRIFT_ASSIGN_ID(elem_type); +} +THRIFT_CONVERSION(t_set) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_OPT(cpp_name); + THRIFT_ASSIGN_ID(elem_type); +} +THRIFT_CONVERSION(t_map) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_OPT(cpp_name); + THRIFT_ASSIGN_ID(key_type); + THRIFT_ASSIGN_ID(val_type); +} + +THRIFT_CONVERSION(t_service) { + THRIFT_ASSIGN_METADATA(); + THRIFT_ASSIGN_LIST_N(t_function, get_functions(), functions); + THRIFT_ASSIGN_ID_N(t_service, get_extends(), extends_); +} + +THRIFT_CONVERSION(t_base_type) { + THRIFT_ASSIGN_METADATA(); + if (from->is_binary()) { + to.value = plugin::t_base::TYPE_BINARY; + } else { + switch (from->get_base()) { +#define T_BASETYPE_CASE(name) \ + case t_base_type::TYPE_##name: \ + to.value = plugin::t_base::TYPE_##name; \ + break + T_BASETYPE_CASE(VOID); + T_BASETYPE_CASE(STRING); + T_BASETYPE_CASE(BOOL); + T_BASETYPE_CASE(I8); + T_BASETYPE_CASE(I16); + T_BASETYPE_CASE(I32); + T_BASETYPE_CASE(I64); + T_BASETYPE_CASE(DOUBLE); + default: + throw plugin::ThriftPluginError("Base type union has no value"); + break; +#undef T_BASETYPE_CASE + } + } +} +THRIFT_CONVERSION(t_type) { +#define T_CONVERT_UNION_N(name, type) \ + else if (from->is_##name()) { \ + to.__isset.name##_val = true; \ + convert(reinterpret_cast< ::type*>(from), to.name##_val); \ + } +#define T_CONVERT_UNION(name) T_CONVERT_UNION_N(name, t_##name) + if (false) { + } + T_CONVERT_UNION(base_type) + T_CONVERT_UNION(typedef) + T_CONVERT_UNION(enum) + T_CONVERT_UNION(struct) + T_CONVERT_UNION_N(xception, t_struct) + T_CONVERT_UNION(list) + T_CONVERT_UNION(set) + T_CONVERT_UNION(map) + T_CONVERT_UNION(service) + else { + throw plugin::ThriftPluginError("Type union has no value"); + } +#undef T_CONVERT_UNION_N +#undef T_CONVERT_UNION +} + +THRIFT_CONVERSION(t_scope) { +#define T_SCOPE_ASSIGN(name, type) \ + boost::copy(from->name##s_ | boost::adaptors::map_values \ + | boost::adaptors::transformed(&store_type<type>), \ + std::back_inserter(to.name##s)) + T_SCOPE_ASSIGN(type, t_type); + T_SCOPE_ASSIGN(constant, t_const); + T_SCOPE_ASSIGN(service, t_service); +#undef T_SCOPE_ASSIGN +} + +void get_global_cache(plugin::TypeRegistry& reg) { + reg.types = type_cache.cache; + reg.constants = const_cache.cache; + reg.services = service_cache.cache; +} + +void clear_global_cache() { + type_cache.clear(); + const_cache.clear(); + service_cache.clear(); +} + +THRIFT_CONVERSION(t_program) { + THRIFT_ASSIGN_CONVERT(t_scope, scope(), scope); + THRIFT_ASSIGN(path); + THRIFT_ASSIGN(out_path); + THRIFT_ASSIGN(name); + THRIFT_ASSIGN(include_prefix); + THRIFT_ASSIGN(cpp_includes); + THRIFT_ASSIGN(c_includes); + THRIFT_ASSIGN(namespaces); + THRIFT_ASSIGN_N(is_out_path_absolute(), out_path_is_absolute, ); + THRIFT_ASSIGN_N(get_namespace(), namespace_, ); + THRIFT_ASSIGN_LIST_ID(t_type, typedef); + THRIFT_ASSIGN_LIST_ID(t_type, enum); + THRIFT_ASSIGN_LIST_ID(t_type, object); + THRIFT_ASSIGN_LIST_ID(t_const, const); + THRIFT_ASSIGN_LIST_ID(t_service, service); + THRIFT_ASSIGN_LIST_N(t_program, get_includes(), includes); + to.program_id = reinterpret_cast<plugin::t_program_id>(from); +} + +PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options) { + std::string language; + std::map<std::string, std::string> parsed_options; + t_generator::parse_options(options, language, parsed_options); + std::string cmd = "thrift-gen-"; + if (language.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789") + != std::string::npos) { + std::cerr << "Invalid language name" << std::endl; + return PLUGIN_FAILURE; + } + cmd.append(language); + FILE* fd = THRIFT_POPEN(cmd.c_str()); + if (fd) { +#ifdef _WIN32 + _setmode(fileno(fd), _O_BINARY); +#endif + boost::shared_ptr<TFramedTransport> transport( + new TFramedTransport(boost::make_shared<TFDTransport>(fileno(fd)))); + TBinaryProtocol proto(transport); + + plugin::GeneratorInput input; + input.__set_parsed_options(parsed_options); + clear_global_cache(); + convert(program, input.program); + get_global_cache(input.type_registry); + try { + input.write(&proto); + transport->flush(); + } catch (std::exception& err) { + std::cerr << "Error while sending data to plugin: " << err.what() << std::endl; + THRIFT_PCLOSE(fd); + return PLUGIN_FAILURE; + } + + // TODO: be prepared for hang or crash of child process + int ret = THRIFT_PCLOSE(fd); + if (!ret) { + return PLUGIN_SUCCEESS; + } else { + std::cerr << "plugin process returned non zero exit code: " << ret << std::endl; + return PLUGIN_FAILURE; + } + } + clear_global_cache(); + return PLUGIN_NOT_FOUND; +} +} diff --git a/compiler/cpp/src/plugin/plugin_output.h b/compiler/cpp/src/plugin/plugin_output.h new file mode 100644 index 000000000..eab2d1bfc --- /dev/null +++ b/compiler/cpp/src/plugin/plugin_output.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef T_PLUGIN_PLUGIN_OUTPUT_H +#define T_PLUGIN_PLUGIN_OUTPUT_H + +#include <string> + +class t_program; + +namespace plugin_output { + +enum PluginDelegateResult { + PLUGIN_NOT_FOUND, + PLUGIN_FAILURE, + PLUGIN_SUCCEESS, +}; + +PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options); +} + +#endif diff --git a/compiler/cpp/src/plugin/type_util.h b/compiler/cpp/src/plugin/type_util.h new file mode 100644 index 000000000..508b74181 --- /dev/null +++ b/compiler/cpp/src/plugin/type_util.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef T_PLUGIN_TYPE_UTIL_H +#define T_PLUGIN_TYPE_UTIL_H + +namespace apache { +namespace thrift { +namespace plugin { + +template <typename From> +struct ToType {}; + +template <typename From> +typename ToType<From>::type* convert_forward(const From&); + +template <typename From, typename To> +void convert(const From&, To*); + +template <typename From> +typename ToType<From>::type* convert(const From& from); + +class TypeRegistry; +void set_global_cache(const TypeRegistry&); +} +} +} + +// conversion from raw compiler types to plugin wire type +namespace plugin_output { + +template <typename From, typename To> +void convert(From* from, To& to); + +template <typename From> +typename apache::thrift::plugin::ToType<From>::type convert(From* from); + +void get_global_cache(apache::thrift::plugin::TypeRegistry&); +void clear_global_cache(); +} + +#define THRIFT_TYPE_MAPPING(TYPE) \ + class TYPE; \ + namespace apache { \ + namespace thrift { \ + namespace plugin { \ + class TYPE; \ + template <> \ + struct ToType< ::TYPE> { \ + typedef TYPE type; \ + }; \ + template <> \ + struct ToType<TYPE> { \ + typedef ::TYPE type; \ + }; \ + } \ + } \ + } +THRIFT_TYPE_MAPPING(t_base_type) +THRIFT_TYPE_MAPPING(t_const) +THRIFT_TYPE_MAPPING(t_const_value) +THRIFT_TYPE_MAPPING(t_container) +THRIFT_TYPE_MAPPING(t_doc) +THRIFT_TYPE_MAPPING(t_enum) +THRIFT_TYPE_MAPPING(t_enum_value) +THRIFT_TYPE_MAPPING(t_field) +THRIFT_TYPE_MAPPING(t_function) +THRIFT_TYPE_MAPPING(t_list) +THRIFT_TYPE_MAPPING(t_map) +THRIFT_TYPE_MAPPING(t_program) +THRIFT_TYPE_MAPPING(t_scope) +THRIFT_TYPE_MAPPING(t_service) +THRIFT_TYPE_MAPPING(t_set) +THRIFT_TYPE_MAPPING(t_struct) +THRIFT_TYPE_MAPPING(t_type) +THRIFT_TYPE_MAPPING(t_typedef) +#undef THRIFT_TYPE_MAPPING +#endif diff --git a/compiler/cpp/src/thriftl.ll b/compiler/cpp/src/thriftl.ll index 5c3187d96..f47928013 100644 --- a/compiler/cpp/src/thriftl.ll +++ b/compiler/cpp/src/thriftl.ll @@ -56,6 +56,7 @@ #include "windows/config.h" #endif #include "main.h" +#include "common.h" #include "globals.h" #include "parse/t_program.h" diff --git a/compiler/cpp/src/thrifty.yy b/compiler/cpp/src/thrifty.yy index 292670d59..51de58f8e 100644 --- a/compiler/cpp/src/thrifty.yy +++ b/compiler/cpp/src/thrifty.yy @@ -38,6 +38,7 @@ #include "windows/config.h" #endif #include "main.h" +#include "common.h" #include "globals.h" #include "parse/t_program.h" #include "parse/t_scope.h" diff --git a/compiler/cpp/version.h.in b/compiler/cpp/src/version.h.in index 5770ec9b9..5770ec9b9 100644 --- a/compiler/cpp/version.h.in +++ b/compiler/cpp/src/version.h.in diff --git a/compiler/cpp/test/CMakeLists.txt b/compiler/cpp/test/CMakeLists.txt new file mode 100644 index 000000000..047e23ec5 --- /dev/null +++ b/compiler/cpp/test/CMakeLists.txt @@ -0,0 +1,77 @@ +# +# 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. +# + + +if(${WITH_PLUGIN}) + include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") + + # Make sure gen-cpp files can be included + include_directories("${CMAKE_CURRENT_BINARY_DIR}") + + set(plugintest_SOURCES + plugin/conversion_test.cc + ) + add_executable(plugintest ${plugintest_SOURCES}) + if(WITH_SHARED_LIB AND NOT MSVC) + target_link_libraries(plugintest + thriftc + ${Boost_LIBRARIES} + ) + else() + target_link_libraries(plugintest + thriftc_static + thrift_static + ${Boost_LIBRARIES} + ) + endif() + add_test(NAME PluginUnitTest COMMAND plugintest) + + set(thrift-gen-mycpp_SOURCES + ../src/generate/t_cpp_generator.cc + plugin/cpp_plugin.cc + ) + add_executable(thrift-gen-mycpp ${thrift-gen-mycpp_SOURCES}) + if(WITH_SHARED_LIB AND NOT MSVC) + target_link_libraries(thrift-gen-mycpp thriftc) + else() + target_link_libraries(thrift-gen-mycpp thriftc_static thrift_static) + endif() + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(BUILDTYPE "Debug") + else() + # RelWithDebInfo generates binaries in "Release" directory too + set(BUILDTYPE "Release") + endif() + + set_directory_properties(PROPERTIES + ADDITIONAL_MAKE_CLEAN_FILES gen-cpp + ADDITIONAL_MAKE_CLEAN_FILES gen-mycpp) + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen-mycpp) + add_test(NAME PluginIntegrationTest + COMMAND ${CMAKE_COMMAND} + -DTHRIFT_COMPILER=${THRIFT_COMPILER} + -DBINDIR=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + -DBUILDTYPE=${BUILDTYPE} + -DCURDIR=${CMAKE_CURRENT_BINARY_DIR} + -DSRCDIR=${CMAKE_CURRENT_SOURCE_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cpp_plugin_test.cmake) +endif() diff --git a/compiler/cpp/test/Makefile.am b/compiler/cpp/test/Makefile.am new file mode 100644 index 000000000..5a232029a --- /dev/null +++ b/compiler/cpp/test/Makefile.am @@ -0,0 +1,51 @@ +# +# 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. +# +# +# Contains some contributions under the Thrift Software License. +# Please see doc/old-thrift-license.txt in the Thrift distribution for +# details. + +AUTOMAKE_OPTIONS = subdir-objects serial-tests + +AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/compiler/cpp/src +AM_LDFLAGS = $(BOOST_LDFLAGS) +AM_CXXFLAGS = -Wall -Wextra -pedantic + +if WITH_PLUGIN +check_PROGRAMS = plugintest + +noinst_PROGRAMS = thrift-gen-mycpp + +AM_CPPFLAGS += -I$(top_srcdir)/lib/cpp/src -I$(top_builddir)/lib/cpp/src + +plugintest_SOURCES = plugin/conversion_test.cc +plugintest_LDADD = $(top_builddir)/compiler/cpp/libthriftc.la + +thrift_gen_mycpp_SOURCES = plugin/cpp_plugin.cc \ + plugin/t_cpp_generator.cc +thrift_gen_mycpp_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/compiler/cpp -I$(top_srcdir)/compiler/cpp/src/generate +thrift_gen_mycpp_LDADD = $(top_builddir)/compiler/cpp/libthriftc.la + +cpp_plugin_test.sh: thrift-gen-mycpp +TESTS = $(check_PROGRAMS) cpp_plugin_test.sh + +clean-local: + $(RM) -rf gen-cpp gen-mycpp + +endif diff --git a/compiler/cpp/test/cpp_plugin_test.cmake b/compiler/cpp/test/cpp_plugin_test.cmake new file mode 100644 index 000000000..fd182818d --- /dev/null +++ b/compiler/cpp/test/cpp_plugin_test.cmake @@ -0,0 +1,45 @@ +# +# 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. +# + +file(MAKE_DIRECTORY ${CURDIR}/gen-cpp) +execute_process(COMMAND ${THRIFT_COMPILER} -r -out ${CURDIR}/gen-cpp -gen cpp ${SRCDIR}/../../../test/Include.thrift) +if(EXITCODE) + message(FATAL_ERROR "FAILED: \"${ARGV}\": \"${EXITCODE}\"") +endif() +if(WIN32) + set(ENV{PATH} "${BINDIR}/${BUILDTYPE};${BINDIR};$ENV{PATH}") +else() + set(ENV{PATH} "${BINDIR}:$ENV{PATH}") +endif() + +file(MAKE_DIRECTORY ${CURDIR}/gen-mycpp) +execute_process(COMMAND ${THRIFT_COMPILER} -r -out ${CURDIR}/gen-mycpp -gen mycpp ${SRCDIR}/../../../test/Include.thrift RESULT_VARIABLE EXITCODE) +if(EXITCODE) + message(FATAL_ERROR "FAILED: \"${EXITCODE}\"") +endif() + +find_program(DIFF diff) +if(DIFF) + execute_process(COMMAND ${DIFF} -urN gen-cpp gen-mycpp RESULT_VARIABLE EXITCODE) + if(EXITCODE) + message(FATAL_ERROR "FAILED: \"${EXITCODE}\"") + endif() +else() + message(WARNING "diff executable is not available. Not validating plugin-generated code.") +endif() diff --git a/compiler/cpp/test/cpp_plugin_test.sh b/compiler/cpp/test/cpp_plugin_test.sh new file mode 100755 index 000000000..ddb2e0a09 --- /dev/null +++ b/compiler/cpp/test/cpp_plugin_test.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# +# 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. +# + +# this file is intended to be invoked by make. +set -e +mkdir -p gen-cpp gen-mycpp +PATH=.:"$PATH" ../thrift -r -out gen-cpp -gen cpp ../../../test/Include.thrift +PATH=.:"$PATH" ../thrift -r -out gen-mycpp -gen mycpp ../../../test/Include.thrift +diff -urN gen-cpp gen-mycpp diff --git a/compiler/cpp/test/plugin/conversion_test.cc b/compiler/cpp/test/plugin/conversion_test.cc new file mode 100644 index 000000000..f2d140797 --- /dev/null +++ b/compiler/cpp/test/plugin/conversion_test.cc @@ -0,0 +1,496 @@ +/* + * 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 "parse/t_program.h" +#include "plugin/type_util.h" +#include "plugin/plugin_types.h" + +#include <map> +#include <vector> + +#include <boost/preprocessor.hpp> +#include <boost/test/included/unit_test.hpp> +#include <boost/test/parameterized_test.hpp> + +using namespace apache::thrift; +using namespace boost::unit_test; + +namespace test_data { +#define T_TEST_TYPES \ + BOOST_PP_TUPLE_TO_LIST(14, \ + (program, \ + base_type, \ + enum_value, \ + enum, \ + const_value, \ + const, \ + list, \ + set, \ + map, \ + field, \ + struct, \ + typedef, \ + function, \ + service)) +#define T_DELETE_TESTDATA(r, d, elem) \ + for (std::vector<t_##elem*>::reverse_iterator it = elem##s.rbegin(); it != elem##s.rend(); it++) \ + delete *it; +#define T_DECL_TESTDATA(r, d, elem) static std::vector< ::t_##elem*> elem##s; +BOOST_PP_LIST_FOR_EACH(T_DECL_TESTDATA, _, T_TEST_TYPES) +#undef T_DECL_TESTDATA + +bool has_data = false; +void cleanup() { + if (has_data) { + has_data = false; + BOOST_PP_LIST_FOR_EACH(T_DELETE_TESTDATA, _, T_TEST_TYPES) + } +} + +void init_programs() { + programs.push_back(new t_program("prog path", "prog_name")); +} + +void init_base_types() { + base_types.push_back(new ::t_base_type("name0", ::t_base_type::TYPE_VOID)); + base_types.push_back(new ::t_base_type("name1", ::t_base_type::TYPE_STRING)); + base_types.push_back(new ::t_base_type("name2", ::t_base_type::TYPE_BOOL)); + base_types.push_back(new ::t_base_type("name3", ::t_base_type::TYPE_I8)); + base_types.push_back(new ::t_base_type("name4", ::t_base_type::TYPE_I16)); + base_types.push_back(new ::t_base_type("name5", ::t_base_type::TYPE_I32)); + base_types.push_back(new ::t_base_type("name6", ::t_base_type::TYPE_I64)); + base_types.push_back(new ::t_base_type("name7", ::t_base_type::TYPE_DOUBLE)); +} + +void init_const_values() { + const_values.push_back(new t_const_value(42)); + const_values.push_back(new t_const_value("foo")); + { + t_const_value* x = new t_const_value; + x->set_double(3.1415); + const_values.push_back(x); + } + { + t_const_value* x = new t_const_value; + x->set_identifier("bar"); + x->set_enum(enums[0]); + const_values.push_back(x); + } + { + t_const_value* x = new t_const_value; + x->set_map(); + x->add_map(const_values[0], const_values[1]); + x->add_map(const_values[1], const_values[0]); + const_values.push_back(x); + } + { + t_const_value* x = new t_const_value; + x->set_list(); + x->add_list(const_values[0]); + x->add_list(const_values[1]); + const_values.push_back(x); + } +} + +void init_consts() { + // base_type/enum indexes for this and other tests are arbitrary + consts.push_back(new t_const(base_types[2], "aaa", const_values[0])); + consts.back()->set_doc("soem doc"); + consts.push_back(new t_const(base_types[3], "bbb", const_values[1])); +} + +void init_enum_values() { + enum_values.push_back(new t_enum_value("VAL1", 11)); + enum_values.back()->set_doc("enum doc 1"); + enum_values.back()->annotations_.insert(std::make_pair("anno1", "val1")); + + enum_values.push_back(new t_enum_value("VAL2", 22)); +} + +void init_enums() { + enums.push_back(new t_enum(programs[0])); + enums.back()->set_doc("enum doc 1"); + enums.back()->annotations_.insert(std::make_pair("anno1", "val1")); + enums.back()->set_name("fooo"); + enums.back()->append(enum_values[0]); + enums.back()->append(enum_values[1]); +} + +void init_lists() { + lists.push_back(new t_list(enums[0])); + lists.push_back(new t_list(base_types[5])); + lists.back()->set_cpp_name("list_cpp_name_1"); +} +void init_sets() { + sets.push_back(new t_set(base_types[4])); + sets.push_back(new t_set(enums[0])); + sets.back()->set_cpp_name("set_cpp_name_1"); +} +void init_maps() { + maps.push_back(new t_map(base_types[4], base_types[1])); + maps.push_back(new t_map(base_types[5], enums[0])); + maps.back()->set_cpp_name("map_cpp_name_1"); +} + +void init_typedefs() { + typedefs.push_back(new t_typedef(programs[0], base_types[3], "VAL1")); +} +void init_fields() { + fields.push_back(new t_field(base_types[1], "f1")); + fields.back()->set_reference(false); + fields.back()->set_req(t_field::T_OPTIONAL); + fields.push_back(new t_field(base_types[2], "f2", 9)); + fields.back()->set_reference(true); + fields.push_back(new t_field(base_types[3], "f3", 11)); + fields.back()->set_req(t_field::T_REQUIRED); + fields.back()->set_value(const_values[0]); +} +void init_structs() { + structs.push_back(new t_struct(programs[0], "struct1")); + structs.back()->append(fields[0]); + structs.back()->append(fields[1]); + structs.push_back(new t_struct(programs[0], "union1")); + structs.back()->append(fields[0]); + structs.back()->append(fields[1]); + structs.back()->set_union(true); + structs.push_back(new t_struct(programs[0], "xcept1")); + structs.back()->set_xception(true); +} +void init_functions() { + structs.push_back(new t_struct(programs[0], "errs1")); + t_struct* errors = structs.back(); + structs.push_back(new t_struct(programs[0], "args1")); + t_struct* arglist = structs.back(); + functions.push_back(new t_function(base_types[0], "func1", errors, arglist, false)); + functions.push_back(new t_function(base_types[0], "func2", errors, arglist, true)); +} +void init_services() { + services.push_back(new t_service(programs[0])); + services.back()->set_doc("srv1 doc"); + services.back()->set_name("srv1"); + services.back()->add_function(functions[0]); + services.back()->add_function(functions[1]); + + services.push_back(new t_service(programs[0])); + services.back()->set_name("srv2"); + services.back()->set_extends(services[0]); +} + +std::vector<t_type*> types; +void init_types() { +#define T_COPY_TYPES(type) std::copy(type##s.begin(), type##s.end(), std::back_inserter(types)) + T_COPY_TYPES(base_type); + T_COPY_TYPES(enum); + T_COPY_TYPES(typedef); + T_COPY_TYPES(struct); + T_COPY_TYPES(list); + T_COPY_TYPES(set); + T_COPY_TYPES(map); +// T_COPY_TYPES(service); +#undef T_COPY_TYPES +} + +void init() { + if (!has_data) { + has_data = true; +#define T_INIT_TESTDATA(r, d, elem) init_##elem##s(); + BOOST_PP_LIST_FOR_EACH(T_INIT_TESTDATA, _, T_TEST_TYPES) + init_types(); +#undef T_INIT_TESTDATA + } +} +} +struct GlobalFixture { + ~GlobalFixture() { test_data::cleanup(); } +}; +#if (BOOST_VERSION >= 105900) +BOOST_GLOBAL_FIXTURE(GlobalFixture); +#else +BOOST_GLOBAL_FIXTURE(GlobalFixture) +#endif + +void migrate_global_cache() { + plugin::TypeRegistry reg; + plugin_output::get_global_cache(reg); + plugin::set_global_cache(reg); + plugin_output::clear_global_cache(); +} +template <typename T> +T* round_trip(T* t) { + typename plugin::ToType<T>::type p; + plugin_output::convert(t, p); + migrate_global_cache(); + return plugin::convert(p); +} + +void test_base_type(::t_base_type* sut) { + plugin::t_base_type p; + plugin_output::convert(sut, p); + boost::scoped_ptr< ::t_base_type> sut2(plugin::convert(p)); + +#define THRIFT_CHECK(r, data, elem) BOOST_PP_EXPAND(BOOST_CHECK_EQUAL(data elem, sut2->elem)); + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(7, + (is_void(), + is_string(), + is_bool(), + is_string_list(), + is_binary(), + is_string_enum(), + is_base_type()))) +} + +void test_const_value(t_const_value* sut) { + boost::scoped_ptr<t_const_value> sut2(round_trip(sut)); + + BOOST_CHECK_EQUAL(sut->get_type(), sut2->get_type()); + switch (sut->get_type()) { +#define T_CONST_VALUE_CASE(type, name) \ + case t_const_value::type: \ + BOOST_CHECK_EQUAL(sut->get_##name(), sut2->get_##name()); \ + break + T_CONST_VALUE_CASE(CV_INTEGER, integer); + T_CONST_VALUE_CASE(CV_DOUBLE, double); + T_CONST_VALUE_CASE(CV_STRING, string); + T_CONST_VALUE_CASE(CV_IDENTIFIER, identifier); +#undef T_CONST_VALUE_CASE + case t_const_value::CV_MAP: + BOOST_CHECK_EQUAL(sut->get_map().size(), sut2->get_map().size()); + { + std::map<t_const_value::t_const_value_type, t_const_value::t_const_value_type> sut_values; + for (std::map<t_const_value*, t_const_value*>::const_iterator it = sut->get_map().begin(); + it != sut->get_map().end(); it++) { + sut_values[it->first->get_type()] = it->second->get_type(); + } + std::map<t_const_value::t_const_value_type, t_const_value::t_const_value_type> sut2_values; + for (std::map<t_const_value*, t_const_value*>::const_iterator it = sut2->get_map().begin(); + it != sut2->get_map().end(); it++) { + sut2_values[it->first->get_type()] = it->second->get_type(); + } + BOOST_CHECK_EQUAL(sut_values.begin()->first, sut2_values.begin()->first); + BOOST_CHECK_EQUAL(sut_values.begin()->second, sut2_values.begin()->second); + } + break; + case t_const_value::CV_LIST: + BOOST_CHECK_EQUAL(sut->get_list().size(), sut2->get_list().size()); + BOOST_CHECK_EQUAL(sut->get_list().front()->get_type(), sut2->get_list().front()->get_type()); + break; + default: + BOOST_ASSERT(false); + break; + } +} + +void test_const(t_const* sut) { + boost::scoped_ptr< ::t_const> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(4, + (get_type()->get_name(), + get_name(), + get_value()->get_type(), + get_doc()))) +} + +void test_enum_value(t_enum_value* sut) { + boost::scoped_ptr<t_enum_value> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(3, (get_name(), get_value(), get_doc()))) +} + +void test_enum(t_enum* sut) { + boost::scoped_ptr< ::t_enum> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(6, + (get_name(), + get_min_value()->get_value(), + get_max_value()->get_value(), + get_constant_by_value(11)->get_value(), + get_constant_by_name("VAL1")->get_value(), + get_doc()))) +} + +void test_list(t_list* sut) { + boost::scoped_ptr<t_list> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(4, + (get_elem_type()->get_name(), + has_cpp_name(), + get_doc(), + get_name()))) + if (sut->has_cpp_name()) + BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name()); +} +void test_set(t_set* sut) { + boost::scoped_ptr<t_set> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(4, + (get_elem_type()->get_name(), + has_cpp_name(), + get_doc(), + get_name()))) + if (sut->has_cpp_name()) + BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name()); +} +void test_map(t_map* sut) { + boost::scoped_ptr<t_map> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(5, + (get_key_type()->get_name(), + get_val_type()->get_name(), + has_cpp_name(), + get_doc(), + get_name()))) + if (sut->has_cpp_name()) + BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name()); +} + +void test_typedef(t_typedef* sut) { + boost::scoped_ptr<t_typedef> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(4, + (get_doc(), + get_name(), + get_symbolic(), + is_forward_typedef()))) +} + +void test_type(t_type* sut) { + boost::scoped_ptr<t_type> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(15, + (is_void(), + is_base_type(), + is_string(), + is_bool(), + is_typedef(), + is_enum(), + is_struct(), + is_xception(), + is_container(), + is_list(), + is_set(), + is_map(), + is_service(), + get_doc(), + get_name()))) +} + +void test_field(t_field* sut) { + boost::scoped_ptr<t_field> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(5, + (get_req(), + get_reference(), + get_key(), + get_doc(), + get_name()))) + if (sut->get_value()) { + THRIFT_CHECK(, sut->, get_value()->get_type()); + } else { + BOOST_CHECK(!sut2->get_value()); + } + if (sut->get_type()) { + THRIFT_CHECK(, sut->, get_type()->get_name()); + } else { + BOOST_CHECK(!sut2->get_type()); + } +} +void test_struct(t_struct* sut) { + boost::scoped_ptr<t_struct> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(5, + (is_union(), + is_xception(), + is_struct(), + get_doc(), + get_name()))) +} + +void test_function(t_function* sut) { + boost::scoped_ptr<t_function> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH( + THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(4, (get_doc(), get_name(), get_returntype()->get_name(), is_oneway()))) +} +void test_service(t_service* sut) { + boost::scoped_ptr<t_service> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, + sut->, + BOOST_PP_TUPLE_TO_LIST(3, (get_doc(), get_name(), get_functions().size()))) + if (sut->get_extends()) { + THRIFT_CHECK(, sut->, get_extends()->get_name()); + } else { + BOOST_CHECK(!sut2->get_extends()); + } +} + +void test_program(t_program* sut) { + boost::scoped_ptr<t_program> sut2(round_trip(sut)); + + BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, sut->, BOOST_PP_TUPLE_TO_LIST(2, (get_doc(), get_name()))) +} +boost::unit_test::test_suite* do_init_unit_test_suite() { + test_data::init(); + test_suite* ts = BOOST_TEST_SUITE("PluginConversionTest"); + +#define T_TEST_CASE(r, d, type) \ + ts->add(BOOST_PARAM_TEST_CASE(test_##type, test_data::type##s.begin(), test_data::type##s.end())); + BOOST_PP_LIST_FOR_EACH(T_TEST_CASE, _, T_TEST_TYPES) + T_TEST_CASE(_, _, type) +#undef T_TEST_CASE + return ts; +} + +#ifdef BOOST_TEST_DYN_LINK +bool init_unit_test_suite() { + framework::master_test_suite().add(do_init_unit_test_suite()); + return true; +} +int main(int argc, char* argv[]) { + return ::boost::unit_test::unit_test_main(&init_unit_test_suite, argc, argv); +} +#else +boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { + return do_init_unit_test_suite(); +} +#endif diff --git a/compiler/cpp/test/plugin/cpp_plugin.cc b/compiler/cpp/test/plugin/cpp_plugin.cc new file mode 100644 index 000000000..43a62d4a8 --- /dev/null +++ b/compiler/cpp/test/plugin/cpp_plugin.cc @@ -0,0 +1,43 @@ +/* + * 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 "plugin/plugin.h" +#include "generate/t_generator.h" + +namespace apache { +namespace thrift { +namespace plugin { + +class MyCppGenerator : public GeneratorPlugin { + virtual int generate(::t_program* program, + const std::map<std::string, std::string>& parsed_options) { + t_generator* gen = t_generator_registry::get_generator(program, "cpp", parsed_options, ""); + gen->generate_program(); + delete gen; + return 0; + } +}; +} +} +} + +int main(int argc, char* argv[]) { + apache::thrift::plugin::MyCppGenerator p; + return p.exec(argc, argv); +} diff --git a/configure.ac b/configure.ac index b79094c7b..e11636f95 100755 --- a/configure.ac +++ b/configure.ac @@ -516,6 +516,22 @@ if test "$enable_tests" = "no"; then fi AM_CONDITIONAL(WITH_TESTS, [test "$have_tests" = "yes"]) +AC_ARG_ENABLE([plugin], + AS_HELP_STRING([--enable-plugin], [build compiler plugin support [default=yes]]), + [], enable_plugin=yes +) +have_plugin=yes +if test "$have_cpp" = "no" ; then + have_plugin="no" +fi +if test "$enable_plugin" = "no"; then + have_plugin="no" +fi +if test "$have_plugin" = "yes" ; then + AC_CONFIG_LINKS([compiler/cpp/test/plugin/t_cpp_generator.cc:compiler/cpp/src/generate/t_cpp_generator.cc]) +fi +AM_CONDITIONAL(WITH_PLUGIN, [test "$have_plugin" = "yes"]) + AC_ARG_ENABLE([tutorial], AS_HELP_STRING([--enable-tutorial], [build tutorial [default=yes]]), [], enable_tutorial=yes @@ -712,7 +728,10 @@ AH_BOTTOM([ AC_CONFIG_FILES([ Makefile compiler/cpp/Makefile - compiler/cpp/version.h + compiler/cpp/src/Makefile + compiler/cpp/src/plugin/Makefile + compiler/cpp/test/Makefile + compiler/cpp/src/version.h compiler/cpp/src/windows/version.h lib/Makefile lib/cpp/Makefile @@ -823,6 +842,7 @@ AC_OUTPUT echo echo "$PACKAGE $VERSION" echo +echo "Building Plugin Support ...... : $have_plugin" echo "Building C++ Library ......... : $have_cpp" echo "Building C (GLib) Library .... : $have_c_glib" echo "Building Java Library ........ : $have_java" diff --git a/debian/rules b/debian/rules index a31881d77..84cba1f10 100755 --- a/debian/rules +++ b/debian/rules @@ -42,12 +42,12 @@ build: build-arch build-indep build-arch: build-arch-stamp $(CURDIR)/compiler/cpp/thrift build-arch-stamp: configure-stamp - # Compile compiler - $(MAKE) -C $(CURDIR)/compiler/cpp - # Compile C++ library $(MAKE) -C $(CURDIR)/lib/cpp + # Compile compiler + $(MAKE) -C $(CURDIR)/compiler/cpp + # Compile C (glib) library $(MAKE) -C $(CURDIR)/lib/c_glib diff --git a/lib/Makefile.am b/lib/Makefile.am index f12e09219..5f17fca88 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -21,8 +21,11 @@ SUBDIRS = json xml PRECROSS_TARGET = if WITH_CPP +# cpp dir is picked directly by plugin build +if !WITH_PLUGIN SUBDIRS += cpp endif +endif if WITH_C_GLIB SUBDIRS += c_glib diff --git a/lib/c_glib/test/CMakeLists.txt b/lib/c_glib/test/CMakeLists.txt index 45ef41f3c..2c87dbcc9 100644 --- a/lib/c_glib/test/CMakeLists.txt +++ b/lib/c_glib/test/CMakeLists.txt @@ -18,12 +18,12 @@ # -#Make sure gen-cpp and gen-c_glib files can be included -include_directories("${CMAKE_CURRENT_BINARY_DIR}") - set(TEST_PREFIX "c_glib") -include_directories(${Boost_INCLUDE_DIRS}) +include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) + +#Make sure gen-cpp and gen-c_glib files can be included +include_directories("${CMAKE_CURRENT_BINARY_DIR}") # Create the thrift C test library set(testgenc_SOURCES diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt index 33cefcb1b..d07b4008c 100755 --- a/lib/cpp/CMakeLists.txt +++ b/lib/cpp/CMakeLists.txt @@ -17,13 +17,6 @@ # under the License. # -# Find required packages -if(WITH_BOOSTTHREADS) - find_package(Boost 1.53.0 REQUIRED COMPONENTS system thread) -else() - find_package(Boost 1.53.0 REQUIRED) -endif() - include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") include_directories(src) diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am index 6fd15d26e..2a1cca8ae 100755 --- a/lib/cpp/Makefile.am +++ b/lib/cpp/Makefile.am @@ -28,8 +28,12 @@ moc__%.cpp: %.h SUBDIRS = . if WITH_TESTS +# This file is needed by compiler with plugin, while test/Makefile.am needs compiler +# So test directory is directly picked by top level Makefile.am for plugin build +if !WITH_PLUGIN SUBDIRS += test endif +endif pkgconfigdir = $(libdir)/pkgconfig diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp index c46cc142a..d3ec72286 100644 --- a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp @@ -524,7 +524,7 @@ namespace { std::string doubleToString(double d) { std::ostringstream str; str.imbue(std::locale::classic()); - const double max_digits10 = 2 + std::numeric_limits<double>::digits10; + const int max_digits10 = 2 + std::numeric_limits<double>::digits10; str.precision(max_digits10); str << d; return str.str(); diff --git a/lib/cpp/src/thrift/protocol/TProtocol.h b/lib/cpp/src/thrift/protocol/TProtocol.h index 1b46faf32..448c4fe5a 100644 --- a/lib/cpp/src/thrift/protocol/TProtocol.h +++ b/lib/cpp/src/thrift/protocol/TProtocol.h @@ -20,6 +20,11 @@ #ifndef _THRIFT_PROTOCOL_TPROTOCOL_H_ #define _THRIFT_PROTOCOL_TPROTOCOL_H_ 1 +#ifdef _WIN32 +// Need to come before any Windows.h includes +#include <Winsock2.h> +#endif + #include <thrift/transport/TTransport.h> #include <thrift/protocol/TProtocolException.h> diff --git a/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp b/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp index fba077c95..820828f32 100644 --- a/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp +++ b/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp @@ -21,8 +21,6 @@ #include <thrift/thrift-config.h> // win32 -#include <time.h> - #if defined(__MINGW32__) #include <sys/time.h> #endif diff --git a/lib/cpp/src/thrift/windows/GetTimeOfDay.h b/lib/cpp/src/thrift/windows/GetTimeOfDay.h index 6e90ba183..762ac5e24 100644 --- a/lib/cpp/src/thrift/windows/GetTimeOfDay.h +++ b/lib/cpp/src/thrift/windows/GetTimeOfDay.h @@ -29,6 +29,7 @@ #endif #include <thrift/thrift-config.h> +#include <time.h> struct thrift_timespec { int64_t tv_sec; diff --git a/lib/cpp/src/thrift/windows/config.h b/lib/cpp/src/thrift/windows/config.h index 108e05b29..8650103a8 100644 --- a/lib/cpp/src/thrift/windows/config.h +++ b/lib/cpp/src/thrift/windows/config.h @@ -30,6 +30,7 @@ // use std::thread in MSVC11 (2012) or newer #if _MSC_VER >= 1700 +#define HAVE_STDINT_H 1 #define USE_STD_THREAD 1 // otherwise use boost threads #else diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index 7c7e32071..cbeff0884 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -17,15 +17,8 @@ # under the License. # -# Find required packages -set(Boost_USE_STATIC_LIBS ON) # Force the use of static boost test framework -find_package(Boost 1.53.0 REQUIRED COMPONENTS chrono filesystem system thread unit_test_framework) include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") -if (WITH_DYN_LINK_TEST) - add_definitions( -DBOOST_TEST_DYN_LINK ) -endif() - #Make sure gen-cpp files can be included include_directories("${CMAKE_CURRENT_BINARY_DIR}") diff --git a/lib/cpp/test/Makefile.am b/lib/cpp/test/Makefile.am index 4e76ce8b4..6f4611745 100755 --- a/lib/cpp/test/Makefile.am +++ b/lib/cpp/test/Makefile.am @@ -358,7 +358,7 @@ gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cp gen-cpp/ChildService.cpp gen-cpp/ChildService.h gen-cpp/ParentService.cpp gen-cpp/ParentService.h gen-cpp/proc_types.cpp gen-cpp/proc_types.h: processor/proc.thrift $(THRIFT) --gen cpp:templates,cob_style $< -AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -D__STDC_LIMIT_MACROS +AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -D__STDC_LIMIT_MACROS -I. AM_LDFLAGS = $(BOOST_LDFLAGS) AM_CXXFLAGS = -Wall -Wextra -pedantic diff --git a/test/cpp/CMakeLists.txt b/test/cpp/CMakeLists.txt index 11db68ee8..09850a814 100755 --- a/test/cpp/CMakeLists.txt +++ b/test/cpp/CMakeLists.txt @@ -20,8 +20,6 @@ # Contains the thrift specific LINK_AGAINST_THRIFT_LIBRARY include(ThriftMacros) -set(Boost_USE_STATIC_LIBS ON) -find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options system filesystem) include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") find_package(OpenSSL REQUIRED) diff --git a/tutorial/cpp/CMakeLists.txt b/tutorial/cpp/CMakeLists.txt index 8a3d08551..1feec2c35 100644 --- a/tutorial/cpp/CMakeLists.txt +++ b/tutorial/cpp/CMakeLists.txt @@ -17,7 +17,6 @@ # under the License. # -find_package(Boost 1.53.0 REQUIRED) include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") #Make sure gen-cpp files can be included @@ -27,7 +26,7 @@ include_directories("${PROJECT_SOURCE_DIR}/lib/cpp/src") include(ThriftMacros) -set(tutorialgencpp_SOURCES +set(tutorialgencpp_SOURCES gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_constants.cpp |