test: Introduce Quassel::Test::Global library
[quassel.git] / cmake / QuasselMacros.cmake
index 318defb..9f576cd 100644 (file)
@@ -7,21 +7,37 @@
 ###################################################################################################
 
 include(CMakeParseArguments)
+include(GenerateExportHeader)
+include(QuasselCompileFeatures)
 
 ###################################################################################################
 # Adds a library target for a Quassel module.
 #
-# It expects the (CamelCased) module name as a parameter, and derives various
+# quassel_add_module(Module [STATIC] [EXPORT])
+#
+# The function expects the (CamelCased) module name as a parameter, and derives various
 # strings from it. For example, quassel_add_module(Client) produces
 #  - a library target named quassel_client with output name (lib)quassel-client(.so)
 #  - an alias target named Quassel::Client in global scope
 #
+# If the optional argument STATIC is given, a static library is built; otherwise, on
+# platforms other than Windows, a shared library is created. For shared libraries, also
+# an install rule is added.
+#
+# To generate an export header for the library, specify EXPORT. The header will be named
+# ${module}-export.h (where ${module} is the lower-case name of the module).
+#
 # The function exports the TARGET variable which can be used in the current scope
 # for setting source files, properties, link dependencies and so on.
 # To refer to the target outside of the current scope, e.g. for linking, use
 # the alias name.
 #
 function(quassel_add_module _module)
+    set(options EXPORT STATIC NOINSTALL)
+    set(oneValueArgs )
+    set(multiValueArgs )
+    cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
     # Derive target, alias target, output name from the given module name
     set(alias "Quassel::${_module}")
     set(target ${alias})
@@ -29,17 +45,46 @@ function(quassel_add_module _module)
     string(REPLACE "::" "_" target ${target})
     string(REPLACE "_" "-" output_name ${target})
 
-    add_library(${target} STATIC "")
-    add_library(${alias} ALIAS ${target})
+    if (ARG_STATIC)
+        set(buildmode STATIC)
+    else()
+        set(buildmode SHARED)
+    endif()
 
-    set_target_properties(${target} PROPERTIES
-        OUTPUT_NAME ${output_name}
-    )
+    add_library(${target} ${buildmode} "")
+    add_library(${alias} ALIAS ${target})
 
+    target_link_libraries(${target} PRIVATE Qt5::Core)
     target_include_directories(${target}
         PUBLIC  ${CMAKE_CURRENT_SOURCE_DIR}
         PRIVATE ${CMAKE_CURRENT_BINARY_DIR} # for generated files
     )
+    target_compile_features(${target} PUBLIC ${QUASSEL_COMPILE_FEATURES})
+
+    set_target_properties(${target} PROPERTIES
+        OUTPUT_NAME ${output_name}
+        VERSION ${QUASSEL_MAJOR}.${QUASSEL_MINOR}.${QUASSEL_PATCH}
+    )
+
+    if (buildmode STREQUAL "SHARED" AND NOT ${ARG_NOINSTALL})
+        install(TARGETS ${target}
+            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+    endif()
+
+    if (ARG_EXPORT)
+        string(TOLOWER ${_module} lower_module)
+        string(TOUPPER ${_module} upper_module)
+        string(REPLACE "::" "-" header_base ${lower_module})
+        string(REPLACE "::" "_" macro_base ${upper_module})
+        generate_export_header(${target}
+            BASE_NAME ${macro_base}
+            EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/export/${header_base}-export.h
+        )
+        target_include_directories(${target} PUBLIC ${CMAKE_BINARY_DIR}/export)
+    endif()
 
     # Export the target name for further use
     set(TARGET ${target} PARENT_SCOPE)
@@ -113,8 +158,9 @@ function(quassel_add_resource _name)
         WORKING_DIRECTORY ${basedir}
     )
 
-    # Generate library target that can be referenced elsewhere
-    quassel_add_module(Resource::${_name})
+    # Generate library target that can be referenced elsewhere. Force static, because
+    # we can't easily export symbols from the generated sources.
+    quassel_add_module(Resource::${_name} STATIC)
     target_sources(${TARGET} PRIVATE ${qrc_srcpath})
     set_target_properties(${TARGET} PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF)
 
@@ -122,6 +168,64 @@ function(quassel_add_resource _name)
     set(RESOURCE_TARGET ${TARGET} PARENT_SCOPE)
 endfunction()
 
+###################################################################################################
+# Adds a unit test case
+#
+# quassel_add_test(TestName
+#                  [LIBRARIES lib1 lib2...]
+# )
+#
+# The test name is given in CamelCase as first and mandatory parameter. The corresponding source file
+# is expected the lower-cased test name plus the .cpp extension.
+# The test case is automatically linked against Qt5::Test, GMock, Quassel::Common and
+# Quassel::Test::Main, which contains the main function. This main function also instantiates a
+# QCoreApplication, so the event loop can be used in test cases.
+#
+# Additional libraries can be given using the LIBRARIES argument.
+#
+# Test cases should include testglobal.h, which transitively includes the GTest/GMock headers and
+# exports the main function.
+#
+# The compiled test case binary is located in the unit/ directory in the build directory.
+#
+function(quassel_add_test _target)
+    set(options )
+    set(oneValueArgs )
+    set(multiValueArgs LIBRARIES)
+    cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    string(TOLOWER ${_target} lower_target)
+    set(srcfile ${lower_target}.cpp)
+
+    list(APPEND ARG_LIBRARIES
+        Qt5::Test
+        Quassel::Common
+        Quassel::Test::Global
+        Quassel::Test::Main
+    )
+
+    if (WIN32)
+        # On Windows, tests need to be built in the same directory that contains the libraries
+        set(output_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
+    else()
+        # On other platforms, separate the test cases out
+        set(output_dir "${CMAKE_BINARY_DIR}/unit")
+    endif()
+
+    add_executable(${_target} ${srcfile})
+    set_target_properties(${_target} PROPERTIES
+        OUTPUT_NAME ${_target}
+        RUNTIME_OUTPUT_DIRECTORY "${output_dir}"
+    )
+    target_link_libraries(${_target} PUBLIC ${ARG_LIBRARIES})
+
+    add_test(
+        NAME ${_target}
+        COMMAND $<TARGET_FILE:${_target}>
+        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+    )
+endfunction()
+
 ###################################################################################################
 # target_link_if_exists(Target
 #                       [PUBLIC dep1 dep2...]
@@ -158,38 +262,3 @@ function(target_link_if_exists _target)
         endforeach()
     endif()
 endfunction()
-
-######################################
-# Macros for dealing with translations
-######################################
-
-# This generates a .ts from a .po file
-macro(generate_ts outvar basename)
-  set(input ${basename}.po)
-  set(output ${CMAKE_BINARY_DIR}/po/${basename}.ts)
-  add_custom_command(OUTPUT ${output}
-          COMMAND $<TARGET_PROPERTY:Qt5::lconvert,LOCATION>
-          ARGS -i ${input}
-               -of ts
-               -o ${output}
-          WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/po
-# This is a workaround to add (duplicate) strings that lconvert missed to the .ts
-          COMMAND $<TARGET_PROPERTY:Qt5::lupdate,LOCATION>
-          ARGS -silent
-               ${CMAKE_SOURCE_DIR}/src/
-               -ts ${output}
-          DEPENDS ${basename}.po)
-  set(${outvar} ${output})
-endmacro(generate_ts outvar basename)
-
-# This generates a .qm from a .ts file
-macro(generate_qm outvar basename)
-  set(input ${CMAKE_BINARY_DIR}/po/${basename}.ts)
-  set(output ${CMAKE_BINARY_DIR}/po/${basename}.qm)
-  add_custom_command(OUTPUT ${output}
-          COMMAND $<TARGET_PROPERTY:Qt5::lrelease,LOCATION>
-          ARGS -silent
-               ${input}
-          DEPENDS ${basename}.ts)
-  set(${outvar} ${output})
-endmacro(generate_qm outvar basename)