2011-07-13 04:13:33 +04:00
# - Function for generation of export macros for libraries
2013-09-02 12:57:55 +04:00
# This module provides the function GENERATE_EXPORT_HEADER().
2011-07-13 04:13:33 +04:00
#
# The GENERATE_EXPORT_HEADER function can be used to generate a file suitable
# for preprocessor inclusion which contains EXPORT macros to be used in
# library classes.
#
# GENERATE_EXPORT_HEADER( LIBRARY_TARGET
# [BASE_NAME <base_name>]
# [EXPORT_MACRO_NAME <export_macro_name>]
# [EXPORT_FILE_NAME <export_file_name>]
2011-08-24 01:19:31 +04:00
# [DEPRECATED_MACRO_NAME <deprecated_macro_name>]
2011-07-13 04:13:33 +04:00
# [NO_EXPORT_MACRO_NAME <no_export_macro_name>]
# [STATIC_DEFINE <static_define>]
2011-08-25 20:56:02 +04:00
# [NO_DEPRECATED_MACRO_NAME <no_deprecated_macro_name>]
# [DEFINE_NO_DEPRECATED]
# [PREFIX_NAME <prefix_name>]
2011-07-13 04:13:33 +04:00
# )
#
2013-09-02 12:57:55 +04:00
# The target properties CXX_VISIBILITY_PRESET and VISIBILITY_INLINES_HIDDEN
# can be used to add the appropriate compile flags for targets. See the
# documentation of those target properties, and the convenience variables
# CMAKE_CXX_VISIBILITY_PRESET and CMAKE_VISIBILITY_INLINES_HIDDEN.
2011-07-13 04:13:33 +04:00
#
# By default GENERATE_EXPORT_HEADER() generates macro names in a file name
2013-09-02 12:57:55 +04:00
# determined by the name of the library. This means that in the simplest case,
# users of generate_export_header will be equivalent to:
2011-07-13 04:13:33 +04:00
#
2013-09-02 12:57:55 +04:00
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
2011-07-13 04:13:33 +04:00
# add_library(somelib someclass.cpp)
# generate_export_header(somelib)
# install(TARGETS somelib DESTINATION ${LIBRARY_INSTALL_DIR})
# install(FILES
# someclass.h
# ${PROJECT_BINARY_DIR}/somelib_export.h DESTINATION ${INCLUDE_INSTALL_DIR}
# )
#
# And in the ABI header files:
#
# #include "somelib_export.h"
# class SOMELIB_EXPORT SomeClass {
2011-08-25 22:38:13 +04:00
# ...
2011-07-13 04:13:33 +04:00
# };
#
2013-02-20 12:26:20 +04:00
# The CMake fragment will generate a file in the ${CMAKE_CURRENT_BINARY_DIR}
2011-09-19 22:04:42 +04:00
# called somelib_export.h containing the macros SOMELIB_EXPORT, SOMELIB_NO_EXPORT,
2011-07-13 04:13:33 +04:00
# SOMELIB_DEPRECATED, SOMELIB_DEPRECATED_EXPORT and SOMELIB_DEPRECATED_NO_EXPORT.
# The resulting file should be installed with other headers in the library.
#
# The BASE_NAME argument can be used to override the file name and the names
# used for the macros
#
# add_library(somelib someclass.cpp)
# generate_export_header(somelib
# BASE_NAME other_name
# )
#
# Generates a file called other_name_export.h containing the macros
# OTHER_NAME_EXPORT, OTHER_NAME_NO_EXPORT and OTHER_NAME_DEPRECATED etc.
#
# The BASE_NAME may be overridden by specifiying other options in the function.
# For example:
#
# add_library(somelib someclass.cpp)
# generate_export_header(somelib
# EXPORT_MACRO_NAME OTHER_NAME_EXPORT
# )
#
# creates the macro OTHER_NAME_EXPORT instead of SOMELIB_EXPORT, but other macros
# and the generated file name is as default.
#
# add_library(somelib someclass.cpp)
# generate_export_header(somelib
2011-08-24 01:19:31 +04:00
# DEPRECATED_MACRO_NAME KDE_DEPRECATED
2011-07-13 04:13:33 +04:00
# )
#
# creates the macro KDE_DEPRECATED instead of SOMELIB_DEPRECATED.
#
# If LIBRARY_TARGET is a static library, macros are defined without values.
#
# If the same sources are used to create both a shared and a static library, the
# uppercased symbol ${BASE_NAME}_STATIC_DEFINE should be used when building the
# static library
#
# add_library(shared_variant SHARED ${lib_SRCS})
# add_library(static_variant ${lib_SRCS})
# generate_export_header(shared_variant BASE_NAME libshared_and_static)
2011-09-19 22:04:42 +04:00
# set_target_properties(static_variant PROPERTIES
# COMPILE_FLAGS -DLIBSHARED_AND_STATIC_STATIC_DEFINE)
2011-07-13 04:13:33 +04:00
#
2011-09-19 22:04:42 +04:00
# This will cause the export macros to expand to nothing when building the
# static library.
2011-08-25 20:56:02 +04:00
#
2011-09-19 22:04:42 +04:00
# If DEFINE_NO_DEPRECATED is specified, then a macro ${BASE_NAME}_NO_DEPRECATED
# will be defined
2011-08-25 20:56:02 +04:00
# This macro can be used to remove deprecated code from preprocessor output.
#
# option(EXCLUDE_DEPRECATED "Exclude deprecated parts of the library" FALSE)
# if (EXCLUDE_DEPRECATED)
# set(NO_BUILD_DEPRECATED DEFINE_NO_DEPRECATED)
# endif()
# generate_export_header(somelib ${NO_BUILD_DEPRECATED})
#
# And then in somelib:
#
# class SOMELIB_EXPORT SomeClass
# {
# public:
# #ifndef SOMELIB_NO_DEPRECATED
# SOMELIB_DEPRECATED void oldMethod();
# #endif
# };
#
# #ifndef SOMELIB_NO_DEPRECATED
# void SomeClass::oldMethod() { }
# #endif
#
# If PREFIX_NAME is specified, the argument will be used as a prefix to all
# generated macros.
#
# For example:
#
# generate_export_header(somelib PREFIX_NAME VTK_)
#
# Generates the macros VTK_SOMELIB_EXPORT etc.
2013-09-02 12:57:55 +04:00
#
#
# ADD_COMPILER_EXPORT_FLAGS( [<output_variable>] )
#
# The ADD_COMPILER_EXPORT_FLAGS function
# adds -fvisibility=hidden to CMAKE_CXX_FLAGS if supported, and is a no-op on
# Windows which does not need extra compiler flags for exporting support. You
# may optionally pass a single argument to ADD_COMPILER_EXPORT_FLAGS that will
# be populated with the required CXX_FLAGS required to enable visibility support
# for the compiler/architecture in use.
#
# This function is deprecated. Set the target properties CXX_VISIBILITY_PRESET
# and VISIBILITY_INLINES_HIDDEN instead.
#
2011-07-13 04:13:33 +04:00
2011-08-09 21:03:16 +04:00
#=============================================================================
# Copyright 2011 Stephen Kelly <steveire@gmail.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
2011-07-13 04:13:33 +04:00
include ( CMakeParseArguments )
include ( CheckCXXCompilerFlag )
2011-08-11 20:18:19 +04:00
# TODO: Install this macro separately?
macro ( _check_cxx_compiler_attribute _ATTRIBUTE _RESULT )
2011-09-19 22:04:42 +04:00
check_cxx_source_compiles ( " ${ _ATTRIBUTE } int somefunc( ) { r e t u r n 0 ; }
i n t main ( ) { r e t u r n somefunc ( ) ; } " $ { _ R E S U L T }
2011-08-11 20:18:19 +04:00
)
endmacro ( )
2011-07-13 04:13:33 +04:00
macro ( _test_compiler_hidden_visibility )
2011-08-10 23:13:33 +04:00
2012-02-24 20:48:02 +04:00
if ( CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.2" )
set ( GCC_TOO_OLD TRUE )
elseif ( CMAKE_COMPILER_IS_GNUC AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.2" )
set ( GCC_TOO_OLD TRUE )
elseif ( CMAKE_CXX_COMPILER_ID MATCHES Intel AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.0" )
set ( _INTEL_TOO_OLD TRUE )
2011-08-23 21:49:39 +04:00
endif ( )
2011-08-11 20:18:19 +04:00
# Exclude XL here because it misinterprets -fvisibility=hidden even though
# the check_cxx_compiler_flag passes
# http://www.cdash.org/CDash/testDetails.php?test=109109951&build=1419259
2011-09-19 22:04:42 +04:00
if ( NOT GCC_TOO_OLD
2011-08-23 22:04:56 +04:00
A N D N O T _ I N T E L _ T O O _ O L D
A N D N O T WIN32
A N D N O T CYGWIN
A N D N O T " $ { C M A K E _ C X X _ C O M P I L E R _ I D } " M A T C H E S X L
A N D N O T " $ { C M A K E _ C X X _ C O M P I L E R _ I D } " M A T C H E S P G I
A N D N O T " $ { C M A K E _ C X X _ C O M P I L E R _ I D } " M A T C H E S W a t c o m )
2011-08-10 23:13:33 +04:00
check_cxx_compiler_flag ( -fvisibility=hidden COMPILER_HAS_HIDDEN_VISIBILITY )
2011-09-19 22:04:42 +04:00
check_cxx_compiler_flag ( -fvisibility-inlines-hidden
C O M P I L E R _ H A S _ H I D D E N _ I N L I N E _ V I S I B I L I T Y )
option ( USE_COMPILER_HIDDEN_VISIBILITY
" U s e H I D D E N v i s i b i l i t y s u p p o r t i f a v a i l a b l e . " O N )
2011-08-10 23:13:33 +04:00
mark_as_advanced ( USE_COMPILER_HIDDEN_VISIBILITY )
endif ( )
2011-07-13 04:13:33 +04:00
endmacro ( )
2011-08-11 20:17:12 +04:00
macro ( _test_compiler_has_deprecated )
2011-08-23 22:04:56 +04:00
if ( "${CMAKE_CXX_COMPILER_ID}" MATCHES Borland
O R " $ { C M A K E _ C X X _ C O M P I L E R _ I D } " M A T C H E S H P
O R G C C _ T O O _ O L D
O R " $ { C M A K E _ C X X _ C O M P I L E R _ I D } " M A T C H E S P G I
O R " $ { C M A K E _ C X X _ C O M P I L E R _ I D } " M A T C H E S W a t c o m )
2011-09-19 22:04:42 +04:00
set ( COMPILER_HAS_DEPRECATED "" CACHE INTERNAL
" C o m p i l e r s u p p o r t f o r a d e p r e c a t e d a t t r i b u t e " )
2011-08-11 20:17:12 +04:00
else ( )
2011-09-19 22:04:42 +04:00
_check_cxx_compiler_attribute ( "__attribute__((__deprecated__))"
C O M P I L E R _ H A S _ D E P R E C A T E D _ A T T R )
2011-08-14 01:30:35 +04:00
if ( COMPILER_HAS_DEPRECATED_ATTR )
2011-09-19 22:04:42 +04:00
set ( COMPILER_HAS_DEPRECATED "${COMPILER_HAS_DEPRECATED_ATTR}"
C A C H E I N T E R N A L " C o m p i l e r s u p p o r t f o r a d e p r e c a t e d a t t r i b u t e " )
2011-08-14 01:30:35 +04:00
else ( )
2011-09-19 22:04:42 +04:00
_check_cxx_compiler_attribute ( "__declspec(deprecated)"
C O M P I L E R _ H A S _ D E P R E C A T E D )
2011-08-14 01:30:35 +04:00
endif ( )
2011-08-11 20:17:12 +04:00
endif ( )
endmacro ( )
2011-09-20 00:42:42 +04:00
get_filename_component ( _GENERATE_EXPORT_HEADER_MODULE_DIR
" $ { C M A K E _ C U R R E N T _ L I S T _ F I L E } " P A T H )
2011-07-13 04:13:33 +04:00
macro ( _DO_SET_MACRO_VALUES TARGET_LIBRARY )
set ( DEFINE_DEPRECATED )
set ( DEFINE_EXPORT )
set ( DEFINE_IMPORT )
set ( DEFINE_NO_EXPORT )
2011-08-14 17:37:24 +04:00
if ( COMPILER_HAS_DEPRECATED_ATTR )
2011-08-11 20:18:19 +04:00
set ( DEFINE_DEPRECATED "__attribute__ ((__deprecated__))" )
2011-08-14 17:37:24 +04:00
elseif ( COMPILER_HAS_DEPRECATED )
set ( DEFINE_DEPRECATED "__declspec(deprecated)" )
2011-07-13 04:13:33 +04:00
endif ( )
get_property ( type TARGET ${ TARGET_LIBRARY } PROPERTY TYPE )
if ( NOT ${ type } STREQUAL "STATIC_LIBRARY" )
2011-08-10 23:43:16 +04:00
if ( WIN32 )
2011-08-11 00:19:39 +04:00
set ( DEFINE_EXPORT "__declspec(dllexport)" )
set ( DEFINE_IMPORT "__declspec(dllimport)" )
2011-08-10 12:32:58 +04:00
elseif ( COMPILER_HAS_HIDDEN_VISIBILITY AND USE_COMPILER_HIDDEN_VISIBILITY )
set ( DEFINE_EXPORT "__attribute__((visibility(\" default\ ")))" )
set ( DEFINE_IMPORT "__attribute__((visibility(\" default\ ")))" )
set ( DEFINE_NO_EXPORT "__attribute__((visibility(\" hidden\ ")))" )
2011-07-13 04:13:33 +04:00
endif ( )
endif ( )
endmacro ( )
macro ( _DO_GENERATE_EXPORT_HEADER TARGET_LIBRARY )
# Option overrides
2011-08-24 01:26:43 +04:00
set ( options DEFINE_NO_DEPRECATED )
2011-09-19 22:04:42 +04:00
set ( oneValueArgs PREFIX_NAME BASE_NAME EXPORT_MACRO_NAME EXPORT_FILE_NAME
D E P R E C A T E D _ M A C R O _ N A M E N O _ E X P O R T _ M A C R O _ N A M E S T A T I C _ D E F I N E
N O _ D E P R E C A T E D _ M A C R O _ N A M E )
2011-07-13 04:13:33 +04:00
set ( multiValueArgs )
2011-09-19 22:04:42 +04:00
cmake_parse_arguments ( _GEH "${options}" "${oneValueArgs}" "${multiValueArgs}"
$ { A R G N } )
2011-07-13 04:13:33 +04:00
set ( BASE_NAME "${TARGET_LIBRARY}" )
if ( _GEH_BASE_NAME )
set ( BASE_NAME ${ _GEH_BASE_NAME } )
endif ( )
string ( TOUPPER ${ BASE_NAME } BASE_NAME_UPPER )
string ( TOLOWER ${ BASE_NAME } BASE_NAME_LOWER )
# Default options
2011-08-25 20:51:37 +04:00
set ( EXPORT_MACRO_NAME "${_GEH_PREFIX_NAME}${BASE_NAME_UPPER}_EXPORT" )
set ( NO_EXPORT_MACRO_NAME "${_GEH_PREFIX_NAME}${BASE_NAME_UPPER}_NO_EXPORT" )
2011-07-13 04:13:33 +04:00
set ( EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME_LOWER}_export.h" )
2011-08-25 20:51:37 +04:00
set ( DEPRECATED_MACRO_NAME "${_GEH_PREFIX_NAME}${BASE_NAME_UPPER}_DEPRECATED" )
set ( STATIC_DEFINE "${_GEH_PREFIX_NAME}${BASE_NAME_UPPER}_STATIC_DEFINE" )
2011-09-19 22:04:42 +04:00
set ( NO_DEPRECATED_MACRO_NAME
" $ { _ G E H _ P R E F I X _ N A M E } $ { B A S E _ N A M E _ U P P E R } _ N O _ D E P R E C A T E D " )
2011-07-13 04:13:33 +04:00
if ( _GEH_UNPARSED_ARGUMENTS )
message ( FATAL_ERROR "Unknown keywords given to GENERATE_EXPORT_HEADER(): \" ${ _GEH_UNPARSED_ARGUMENTS } \"")
endif ( )
if ( _GEH_EXPORT_MACRO_NAME )
2011-08-25 20:51:37 +04:00
set ( EXPORT_MACRO_NAME ${ _GEH_PREFIX_NAME } ${ _GEH_EXPORT_MACRO_NAME } )
2011-07-13 04:13:33 +04:00
endif ( )
2013-05-23 09:44:04 +04:00
string ( MAKE_C_IDENTIFIER ${ EXPORT_MACRO_NAME } EXPORT_MACRO_NAME )
2011-07-13 04:13:33 +04:00
if ( _GEH_EXPORT_FILE_NAME )
2012-01-01 00:31:40 +04:00
if ( IS_ABSOLUTE ${ _GEH_EXPORT_FILE_NAME } )
2011-07-13 04:13:33 +04:00
set ( EXPORT_FILE_NAME ${ _GEH_EXPORT_FILE_NAME } )
else ( )
set ( EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${_GEH_EXPORT_FILE_NAME}" )
endif ( )
endif ( )
2011-08-24 01:19:31 +04:00
if ( _GEH_DEPRECATED_MACRO_NAME )
2011-08-25 20:51:37 +04:00
set ( DEPRECATED_MACRO_NAME ${ _GEH_PREFIX_NAME } ${ _GEH_DEPRECATED_MACRO_NAME } )
2011-07-13 04:13:33 +04:00
endif ( )
2013-05-23 09:44:04 +04:00
string ( MAKE_C_IDENTIFIER ${ DEPRECATED_MACRO_NAME } DEPRECATED_MACRO_NAME )
2011-07-13 04:13:33 +04:00
if ( _GEH_NO_EXPORT_MACRO_NAME )
2011-08-25 20:51:37 +04:00
set ( NO_EXPORT_MACRO_NAME ${ _GEH_PREFIX_NAME } ${ _GEH_NO_EXPORT_MACRO_NAME } )
2011-07-13 04:13:33 +04:00
endif ( )
2013-05-23 09:44:04 +04:00
string ( MAKE_C_IDENTIFIER ${ NO_EXPORT_MACRO_NAME } NO_EXPORT_MACRO_NAME )
2011-07-13 04:13:33 +04:00
if ( _GEH_STATIC_DEFINE )
2011-08-25 20:51:37 +04:00
set ( STATIC_DEFINE ${ _GEH_PREFIX_NAME } ${ _GEH_STATIC_DEFINE } )
2011-07-13 04:13:33 +04:00
endif ( )
2013-05-23 09:44:04 +04:00
string ( MAKE_C_IDENTIFIER ${ STATIC_DEFINE } STATIC_DEFINE )
2011-07-13 04:13:33 +04:00
2011-09-19 22:04:42 +04:00
if ( _GEH_DEFINE_NO_DEPRECATED )
2011-08-24 01:26:43 +04:00
set ( DEFINE_NO_DEPRECATED TRUE )
endif ( )
2011-09-19 22:04:42 +04:00
if ( _GEH_NO_DEPRECATED_MACRO_NAME )
set ( NO_DEPRECATED_MACRO_NAME
$ { _ G E H _ P R E F I X _ N A M E } $ { _ G E H _ N O _ D E P R E C A T E D _ M A C R O _ N A M E } )
2011-08-24 01:26:43 +04:00
endif ( )
2013-05-23 09:44:04 +04:00
string ( MAKE_C_IDENTIFIER ${ NO_DEPRECATED_MACRO_NAME } NO_DEPRECATED_MACRO_NAME )
2011-08-24 01:26:43 +04:00
2011-08-25 20:51:37 +04:00
set ( INCLUDE_GUARD_NAME "${EXPORT_MACRO_NAME}_H" )
2011-07-13 04:13:33 +04:00
2011-08-08 04:08:10 +04:00
get_target_property ( EXPORT_IMPORT_CONDITION ${ TARGET_LIBRARY } DEFINE_SYMBOL )
2011-09-19 22:04:42 +04:00
if ( NOT EXPORT_IMPORT_CONDITION )
2011-08-08 04:08:10 +04:00
set ( EXPORT_IMPORT_CONDITION ${ TARGET_LIBRARY } _EXPORTS )
endif ( )
2013-05-23 09:44:04 +04:00
string ( MAKE_C_IDENTIFIER ${ EXPORT_IMPORT_CONDITION } EXPORT_IMPORT_CONDITION )
2011-08-08 04:08:10 +04:00
2011-09-19 22:04:42 +04:00
configure_file ( "${_GENERATE_EXPORT_HEADER_MODULE_DIR}/exportheader.cmake.in"
" $ { E X P O R T _ F I L E _ N A M E } " @ O N L Y )
2011-07-13 04:13:33 +04:00
endmacro ( )
function ( GENERATE_EXPORT_HEADER TARGET_LIBRARY )
get_property ( type TARGET ${ TARGET_LIBRARY } PROPERTY TYPE )
2013-03-14 19:37:49 +04:00
if ( NOT ${ type } STREQUAL "STATIC_LIBRARY"
A N D N O T $ { t y p e } S T R E Q U A L " S H A R E D _ L I B R A R Y "
A N D N O T $ { t y p e } S T R E Q U A L " M O D U L E _ L I B R A R Y " )
2011-07-13 04:13:33 +04:00
message ( WARNING "This macro can only be used with libraries" )
return ( )
endif ( )
_test_compiler_hidden_visibility ( )
2011-08-11 20:17:12 +04:00
_test_compiler_has_deprecated ( )
2011-07-13 04:13:33 +04:00
_do_set_macro_values ( ${ TARGET_LIBRARY } )
_do_generate_export_header ( ${ TARGET_LIBRARY } ${ ARGN } )
endfunction ( )
2011-09-20 08:16:16 +04:00
function ( add_compiler_export_flags )
2013-09-02 12:57:55 +04:00
if ( NOT CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12 )
2013-10-08 20:14:21 +04:00
message ( DEPRECATION "The add_compiler_export_flags function is obsolete. Use the CXX_VISIBILITY_PRESET and VISIBILITY_INLINES_HIDDEN target properties instead." )
2013-09-02 12:57:55 +04:00
endif ( )
2011-07-13 04:13:33 +04:00
2011-08-10 12:26:16 +04:00
_test_compiler_hidden_visibility ( )
2011-08-11 20:17:12 +04:00
_test_compiler_has_deprecated ( )
2011-08-10 12:26:16 +04:00
2011-08-11 12:39:51 +04:00
if ( NOT ( USE_COMPILER_HIDDEN_VISIBILITY AND COMPILER_HAS_HIDDEN_VISIBILITY ) )
2011-09-19 22:55:19 +04:00
# Just return if there are no flags to add.
2011-07-13 04:13:33 +04:00
return ( )
endif ( )
2011-08-10 12:28:15 +04:00
set ( EXTRA_FLAGS "-fvisibility=hidden" )
2011-07-13 04:13:33 +04:00
2011-08-10 12:28:15 +04:00
if ( COMPILER_HAS_HIDDEN_INLINE_VISIBILITY )
set ( EXTRA_FLAGS "${EXTRA_FLAGS} -fvisibility-inlines-hidden" )
2011-07-13 04:13:33 +04:00
endif ( )
2011-09-20 00:42:42 +04:00
# Either return the extra flags needed in the supplied argument, or to the
# CMAKE_CXX_FLAGS if no argument is supplied.
if ( ARGV0 )
2011-09-20 08:16:16 +04:00
set ( ${ ARGV0 } "${EXTRA_FLAGS}" PARENT_SCOPE )
2011-09-20 00:42:42 +04:00
else ( )
2011-09-20 08:16:16 +04:00
set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_FLAGS}" PARENT_SCOPE )
2011-09-20 00:42:42 +04:00
endif ( )
2011-09-20 08:16:16 +04:00
endfunction ( )