Browse Source

Add basic qmake build procedures support

- Add .pri files for protobuf and grpc
- Add qtprotobuf_generate for qmake build procedures
- Add sample qmake-based application to show usage
- Update README
TODO: well-known types will be integrated under separate task

Fixes: #52
Alexey Edelev 5 years ago
parent
commit
a541dc160a

+ 51 - 3
README.md

@@ -1,13 +1,27 @@
 # QtProtobuf
 
 gRPC and Protobuf generator and bindings for Qt framework
-
 > see [Protobuf](https://developers.google.com/protocol-buffers) and [gRPC](https://grpc.io/) for more information
 
-## [API Reference](https://semlanik.github.io/qtprotobuf)
-
 ![](https://github.com/semlanik/qtprotobuf/workflows/Test%20Verification/badge.svg?branch=master)
 
+# Table of contents
+
+[API Reference](https://semlanik.github.io/qtprotobuf)
+
+[Linux Build](#linux-build)
+
+[Windows Build](#windows-build)
+
+[Usage](#usage)
+
+[CMake functions reference](#cmake-functions-reference)
+
+[qmake integration](#qmake-integration)
+
+[Running tests](#running-tests)
+
+
 ## Linux Build
 ### Prerequesties
 Check installation of following packages in your system:
@@ -210,6 +224,40 @@ Due to cmake restrictions it's required to specify resulting artifacts manually
 
 *QTPROTOBUF_EXECUTABLE* - contains full path to QtProtobuf generator add_executable
 
+## qmake integration
+
+**Note:** Available in QtProtobuf version >=0.2.0
+
+QtProtobuf has limited qmake build procedures support. It's only available and tested on linux platforms. To use it in your qmake project, first you need build and install QtProtobuf as standalone project in your system paths:
+
+```bash
+mkdir build
+cd build
+cmake .. [-DCMAKE_PREFIX_PATH="<path/to/qt/installation>/Qt<qt_version>/<qt_version>/gcc_64/lib/cmake"] -DCMAKE_INSTALL_PREFIX=/usr -DQTPROTOBUF_MAKE_TESTS=OFF -DQTPROTOBUF_MAKE_EXAMPLES=OFF
+sudo cmake --build . [--config <RELEASE|DEBUG>] -- -j<N> install
+```
+
+Commands above will install qt protobuf into you system paths prefixed with */usr* folder.
+
+Once build and installation is finished, you may use QtProtobuf and QtGrpc in qmake project by adding following lines in .pro file:
+
+```bash
+QT += protobuf #for protobuf libraries support
+QT += grpc #for grpc libraries support
+```
+To generate source code and link it to you project use predefined *qtprotobuf_generate* function
+
+### qtprotobuf_generate([generate_qml=false])
+
+qtprotobuf_generate is qmake helper [test function](https://doc.qt.io/qt-5/qmake-language.html#test-functions) that generates QtProtobuf source code based on files provided by PROTO_FILES global context variable
+
+**Parameters:**
+
+*generate_qml* - Enables/disables QML code generation in protobuf classes. If set to `true` qml related code for lists and qml registration to be generated.
+
+
+**Note:** see examples/qmakeexample for details
+
 ## Running tests
 ```bash
 cd <build directory>

+ 36 - 0
examples/qmakeexample/main.cpp

@@ -0,0 +1,36 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <QCoreApplication>
+
+#include "qtprotobuf_global.qpb.h"
+
+int main(int argc, char *argv[])
+{
+    qtprotobufnamespace::tests::qRegisterProtobufTypes();
+    qtprotobufnamespace::tests2::qRegisterProtobufTypes();
+    QCoreApplication a(argc, argv);
+    return a.exec();
+}

+ 12 - 0
examples/qmakeexample/qmakeexample.pro

@@ -0,0 +1,12 @@
+QT -= gui
+QT += protobuf grpc core
+
+CONFIG += console
+CONFIG -= app_bundle
+
+SOURCES += \
+        main.cpp
+
+PROTO_FILES = test1.proto \
+              test2.proto
+qtprotobuf_generate(true)

+ 83 - 0
examples/qmakeexample/test1.proto

@@ -0,0 +1,83 @@
+syntax = "proto3";
+
+package qtprotobufnamespace.tests;
+
+message EmptyMessage {
+}
+
+message SimpleBoolMessage {
+    bool testFieldBool = 1;
+}
+
+message SimpleIntMessage {
+    int32 testFieldInt = 1;
+}
+
+message SimpleSIntMessage {
+    sint32 testFieldInt = 1;
+}
+
+message SimpleUIntMessage {
+    uint32 testFieldInt = 1;
+}
+
+message SimpleInt64Message {
+    int64 testFieldInt = 1;
+}
+
+message SimpleSInt64Message {
+    sint64 testFieldInt = 1;
+}
+
+message SimpleUInt64Message {
+    uint64 testFieldInt = 1;
+}
+
+message SimpleStringMessage {
+    string testFieldString = 6;
+}
+
+message SimpleFloatMessage {
+    float testFieldFloat = 7;
+}
+
+message SimpleDoubleMessage {
+    double testFieldDouble = 8;
+}
+
+message SimpleBytesMessage {
+    bytes testFieldBytes = 1;
+}
+
+message SimpleFixedInt32Message {
+    fixed32 testFieldFixedInt32 = 1;
+}
+
+message SimpleFixedInt64Message {
+    fixed64 testFieldFixedInt64 = 1;
+}
+
+message SimpleSFixedInt32Message {
+    sfixed32 testFieldFixedInt32 = 1;
+}
+
+message SimpleSFixedInt64Message {
+    sfixed64 testFieldFixedInt64 = 1;
+}
+
+message ComplexMessage {
+    int32 testFieldInt = 1;
+    SimpleStringMessage testComplexField = 2;
+}
+
+message RepeatedStringMessage {
+    repeated string testRepeatedString = 1;
+}
+
+message RepeatedDoubleMessage {
+    repeated double testRepeatedDouble = 1;
+}
+
+message RepeatedBytesMessage {
+    repeated bytes testRepeatedBytes = 1;
+}

+ 7 - 0
examples/qmakeexample/test2.proto

@@ -0,0 +1,7 @@
+syntax = "proto3";
+
+package qtprotobufnamespace.tests2;
+
+message RepeatedFloatMessage {
+    repeated float testRepeatedFloat = 1;
+}

+ 17 - 0
src/grpc/CMakeLists.txt

@@ -43,6 +43,20 @@ file(GLOB HEADERS qgrpcasyncoperationbase_p.h
 protobuf_generate_qt_headers(PUBLIC_HEADERS ${HEADERS} COMPONENT ${TARGET})
 
 add_library(${TARGET} SHARED ${SOURCES})
+
+if(NOT DEFINED QT_QMAKE_EXECUTABLE)
+    find_program(QT_QMAKE_EXECUTABLE "qmake")
+    if(QT_QMAKE_EXECUTABLE STREQUAL QT_QMAKE_EXECUTABLE-NOTFOUND)
+        message(FATAL_ERROR "Could not find qmake executable")
+    endif()
+endif()
+
+execute_process(
+    COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_HOST_DATA
+    OUTPUT_VARIABLE QT_HOST_DATA
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
 target_compile_definitions(${TARGET} PRIVATE QT_BUILD_GRPC_LIB)
 
 add_library(${QTPROTOBUF_COMMON_NAMESPACE}::${TARGET} ALIAS ${TARGET})
@@ -82,4 +96,7 @@ export(TARGETS ${TARGET} NAMESPACE ${QTPROTOBUF_COMMON_NAMESPACE}:: FILE ${TARGE
 configure_file("${QTPROTOBUF_CMAKE_DIR}/gRPCLookup.cmake" "${QTPROTOBUF_BINARY_DIR}/gRPCLookup.cmake" COPYONLY)
 install(FILES "${QTPROTOBUF_BINARY_DIR}/gRPCLookup.cmake" DESTINATION "${TARGET_CMAKE_DIR}")
 
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/qt_lib_grpc.pri.in" "${QTPROTOBUF_BINARY_DIR}/qt_lib_grpc.pri" @ONLY)
+install(FILES "${QTPROTOBUF_BINARY_DIR}/qt_lib_grpc.pri" DESTINATION "${QT_HOST_DATA}/mkspecs/modules")
+
 add_coverage_target(TARGET ${TARGET})

+ 1 - 0
src/grpc/qgrpccredentials.h

@@ -29,6 +29,7 @@
 #include "qabstractgrpccredentials.h"
 
 #include <memory>
+#include <type_traits>
 
 namespace QtProtobuf {
 

+ 8 - 0
src/grpc/qt_lib_grpc.pri.in

@@ -0,0 +1,8 @@
+QT.grpc.VERSION = @QTPROTOBUF_PROJECT_VERSION@
+QT.grpc.name = QtGrpc
+QT.grpc.module = QtGrpc
+QT.grpc.includes = @CMAKE_INSTALL_PREFIX@/@TARGET_INCLUDE_DIR@
+QT.grpc.private_includes =
+QT.grpc.libs = @CMAKE_INSTALL_PREFIX@/@TARGET_LIB_DIR@
+QT.grpc.depends = core network protobuf
+CONFIG += c++14

+ 16 - 0
src/protobuf/CMakeLists.txt

@@ -62,12 +62,25 @@ protobuf_generate_qt_headers(PUBLIC_HEADERS ${PUBLIC_HEADERS} COMPONENT ${TARGET
 
 add_library(${TARGET} SHARED ${SOURCES})
 
+if(NOT DEFINED QT_QMAKE_EXECUTABLE)
+    find_program(QT_QMAKE_EXECUTABLE "qmake")
+    if(QT_QMAKE_EXECUTABLE STREQUAL QT_QMAKE_EXECUTABLE-NOTFOUND)
+        message(FATAL_ERROR "Could not find qmake executable")
+    endif()
+endif()
+
 execute_process(
     COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_PLUGINS
     OUTPUT_VARIABLE QT_INSTALL_PLUGINS
     OUTPUT_STRIP_TRAILING_WHITESPACE
 )
 
+execute_process(
+    COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_HOST_DATA
+    OUTPUT_VARIABLE QT_HOST_DATA
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
 set_target_properties(${TARGET} PROPERTIES QT_PROTOBUF_PLUGIN_PATH "${QT_INSTALL_PLUGINS}/protobuf")
 target_compile_definitions(${TARGET} PUBLIC QT_PROTOBUF_PLUGIN_PATH="${QT_INSTALL_PLUGINS}/protobuf")
 
@@ -118,6 +131,9 @@ install(FILES "${QTPROTOBUF_BINARY_DIR}/parsemessages.go" DESTINATION "${TARGET_
 configure_file("${QTPROTOBUF_CMAKE_DIR}/ProtobufLookup.cmake" "${QTPROTOBUF_BINARY_DIR}/ProtobufLookup.cmake" COPYONLY)
 install(FILES "${QTPROTOBUF_BINARY_DIR}/ProtobufLookup.cmake" DESTINATION "${TARGET_CMAKE_DIR}")
 
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/qt_lib_protobuf.pri.in" "${QTPROTOBUF_BINARY_DIR}/qt_lib_protobuf.pri" @ONLY)
+install(FILES "${QTPROTOBUF_BINARY_DIR}/qt_lib_protobuf.pri" DESTINATION "${QT_HOST_DATA}/mkspecs/modules")
+
 add_subdirectory("quick")
 
 add_coverage_target(TARGET ${TARGET})

+ 35 - 0
src/protobuf/qt_lib_protobuf.pri.in

@@ -0,0 +1,35 @@
+QT.protobuf.VERSION = @QTPROTOBUF_PROJECT_VERSION@
+QT.protobuf.name = QtProtobuf
+QT.protobuf.module = QtProtobuf
+QT.protobuf.includes = @CMAKE_INSTALL_PREFIX@/@TARGET_INCLUDE_DIR@
+QT.protobuf.private_includes =
+QT.protobuf.libs = @CMAKE_INSTALL_PREFIX@/@TARGET_LIB_DIR@
+QT.protobuf.depends = core qml
+CONFIG += c++14
+
+defineTest(qtprotobuf_generate) {
+    OK = false
+
+    QML_ENABLED = $$1
+    $$QML_ENABLED:GENERATOR_OPTIONS="$$GENERATOR_OPTIONS:QML"
+
+    for(PROTO_FILE_REL, PROTO_FILES) {
+        PROTO_FILE_ABS = $$absolute_path($$PROTO_FILE_REL)
+        PROTO_FILES_PRIV *= $$PROTO_FILE_ABS
+        PROTO_INCLUDES_PRIV = "$$PROTO_INCLUDES_PRIV -I$$dirname(PROTO_FILE_ABS)"
+    }
+
+    GENERATED_OUT_DIR = $$OUT_PWD/generated
+    system("mkdir $$GENERATED_OUT_DIR")
+    message("QT_PROTOBUF_OPTIONS=$$GENERATOR_OPTIONS protoc --plugin=protoc-gen-@GENERATOR_TARGET@=@QTPROTOBUF_EXECUTABLE_INSTALL@ --@GENERATOR_TARGET@_out=$$GENERATED_OUT_DIR $$PROTO_INCLUDES_PRIV $$PROTO_FILES_PRIV")
+    system("QT_PROTOBUF_OPTIONS=$$GENERATOR_OPTIONS protoc --plugin=protoc-gen-@GENERATOR_TARGET@=@QTPROTOBUF_EXECUTABLE_INSTALL@ --@GENERATOR_TARGET@_out=$$GENERATED_OUT_DIR $$PROTO_INCLUDES_PRIV $$PROTO_FILES_PRIV"):OK=true
+    SOURCES += $$files($$GENERATED_OUT_DIR/*.cpp)
+    HEADERS += $$files($$GENERATED_OUT_DIR/*.h)
+    INCLUDEPATH += $$GENERATED_OUT_DIR
+
+    export(SOURCES)
+    export(HEADERS)
+    export(INCLUDEPATH)
+
+    return($$OK)
+}