cmake_minimum_required(VERSION 3.13) include(CheckFunctionExists) include(CheckSymbolExists) project(ucode C) add_definitions(-Os -Wall -Werror --std=gnu99 -ffunction-sections -fwrapv -D_GNU_SOURCE) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6) add_definitions(-Wextra -Werror=implicit-function-declaration) add_definitions(-Wformat -Werror=format-security -Werror=format-nonliteral) endif() if(NOT CMAKE_C_FLAGS MATCHES -D_FORTIFY_SOURCE=) add_definitions(-D_FORTIFY_SOURCE=2) endif() add_definitions(-Wmissing-declarations -Wno-error=unused-variable -Wno-unused-parameter) include_directories(include) option(COMPILE_SUPPORT "Support compilation from source" ON) if(NOT COMPILE_SUPPORT) add_definitions(-DNO_COMPILE) endif() find_library(libuci NAMES uci) find_library(libubox NAMES ubox) find_library(libubus NAMES ubus) find_library(libblobmsg_json NAMES blobmsg_json) if(LINUX) find_library(libnl_tiny NAMES nl-tiny) if(libnl_tiny AND libubox) set(DEFAULT_NL_SUPPORT ON) endif() endif() if(libuci AND libubox) set(DEFAULT_UCI_SUPPORT ON) endif() if(libubus AND libblobmsg_json) set(DEFAULT_UBUS_SUPPORT ON) endif() if(libubox) set(DEFAULT_ULOOP_SUPPORT ON) endif() option(DEBUG_SUPPORT "Debug plugin support" ON) option(FS_SUPPORT "Filesystem plugin support" ON) option(MATH_SUPPORT "Math plugin support" ON) option(UBUS_SUPPORT "Ubus plugin support" ${DEFAULT_UBUS_SUPPORT}) option(UCI_SUPPORT "UCI plugin support" ${DEFAULT_UCI_SUPPORT}) option(RTNL_SUPPORT "Route Netlink plugin support" ${DEFAULT_NL_SUPPORT}) option(NL80211_SUPPORT "Wireless Netlink plugin support" ${DEFAULT_NL_SUPPORT}) option(RESOLV_SUPPORT "NS resolve plugin support" ON) option(STRUCT_SUPPORT "Struct plugin support" ON) option(ULOOP_SUPPORT "Uloop plugin support" ${DEFAULT_ULOOP_SUPPORT}) option(LOG_SUPPORT "Log plugin support" ON) option(SOCKET_SUPPORT "Socket plugin support" ON) set(LIB_SEARCH_PATH "${CMAKE_INSTALL_PREFIX}/lib/ucode/*.so:${CMAKE_INSTALL_PREFIX}/share/ucode/*.uc:./*.so:./*.uc" CACHE STRING "Default library search path") string(REPLACE ":" "\", \"" LIB_SEARCH_DEFINE "${LIB_SEARCH_PATH}") add_definitions(-DLIB_SEARCH_PATH="${LIB_SEARCH_DEFINE}") if(APPLE) set(UCODE_MODULE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup") add_definitions(-DBIND_8_COMPAT) else() set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--gc-sections") endif() if(DEBUG) add_definitions(-DDEBUG -g3 -Og) else() add_definitions(-DNDEBUG) endif() include(FindPkgConfig) pkg_check_modules(JSONC REQUIRED json-c) include_directories(${JSONC_INCLUDE_DIRS}) set(UCODE_SOURCES lexer.c lib.c vm.c chunk.c vallist.c compiler.c source.c types.c program.c platform.c) add_library(libucode SHARED ${UCODE_SOURCES}) set(SOVERSION 0 CACHE STRING "Override ucode library version") set_target_properties(libucode PROPERTIES OUTPUT_NAME ucode SOVERSION ${SOVERSION}) target_link_libraries(libucode ${JSONC_LINK_LIBRARIES}) set(CLI_SOURCES main.c) add_executable(ucode ${CLI_SOURCES}) target_link_libraries(ucode libucode ${JSONC_LINK_LIBRARIES}) check_function_exists(dlopen DLOPEN_FUNCTION_EXISTS) if(NOT DLOPEN_FUNCTION_EXISTS) target_link_libraries(libucode dl) endif() check_function_exists(fmod FMOD_FUNCTION_EXISTS) if(NOT FMOD_FUNCTION_EXISTS) target_link_libraries(libucode m) endif() list(APPEND CMAKE_REQUIRED_INCLUDES ${JSONC_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${JSONC_LINK_LIBRARIES}) check_symbol_exists(json_tokener_get_parse_end "json-c/json.h" HAVE_PARSE_END) if(HAVE_PARSE_END) add_definitions(-DHAVE_PARSE_END) endif() check_symbol_exists(json_object_new_array_ext "json-c/json.h" HAVE_ARRAY_EXT) if(HAVE_ARRAY_EXT) add_definitions(-DHAVE_ARRAY_EXT) endif() check_symbol_exists(json_object_new_uint64 "json-c/json.h" HAVE_JSON_UINT64) if(HAVE_JSON_UINT64) add_definitions(-DHAVE_JSON_UINT64) endif() set(LIBRARIES "") if(DEBUG_SUPPORT) set(LIBRARIES ${LIBRARIES} debug_lib) add_library(debug_lib MODULE lib/debug.c) set_target_properties(debug_lib PROPERTIES OUTPUT_NAME debug PREFIX "") target_link_options(debug_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) if(libubox) find_path(uloop_include_dir NAMES libubox/uloop.h) include_directories(${uloop_include_dir}) target_link_libraries(debug_lib ${libubox} ${libucode}) set_target_properties(debug_lib PROPERTIES COMPILE_DEFINITIONS HAVE_ULOOP) endif() endif() if(FS_SUPPORT) set(LIBRARIES ${LIBRARIES} fs_lib) add_library(fs_lib MODULE lib/fs.c) set_target_properties(fs_lib PROPERTIES OUTPUT_NAME fs PREFIX "") target_link_options(fs_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) endif() if(MATH_SUPPORT) set(LIBRARIES ${LIBRARIES} math_lib) add_library(math_lib MODULE lib/math.c) set_target_properties(math_lib PROPERTIES OUTPUT_NAME math PREFIX "") target_link_options(math_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) check_function_exists(ceil CEIL_FUNCTION_EXISTS) if(NOT CEIL_FUNCTION_EXISTS) target_link_libraries(math_lib m) endif() endif() if(UBUS_SUPPORT) find_path(ubus_include_dir NAMES libubus.h) include_directories(${ubus_include_dir}) set(LIBRARIES ${LIBRARIES} ubus_lib) add_library(ubus_lib MODULE lib/ubus.c) set_target_properties(ubus_lib PROPERTIES OUTPUT_NAME ubus PREFIX "") target_link_options(ubus_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) target_link_libraries(ubus_lib ${libubus} ${libblobmsg_json}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${libubox}) file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test.c" " #include int main() { return UBUS_STATUS_NO_MEMORY; } ") try_compile(HAVE_NEW_UBUS_STATUS_CODES ${CMAKE_BINARY_DIR} "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test.c") check_symbol_exists(uloop_fd_set_cb "libubox/uloop.h" FD_SET_CB_EXISTS) if(HAVE_NEW_UBUS_STATUS_CODES) add_definitions(-DHAVE_NEW_UBUS_STATUS_CODES) endif() if(FD_SET_CB_EXISTS) target_compile_definitions(ubus_lib PUBLIC HAVE_ULOOP_FD_SET_CB) endif() endif() if(UCI_SUPPORT) find_path(uci_include_dir uci.h) include_directories(${uci_include_dir}) set(LIBRARIES ${LIBRARIES} uci_lib) add_library(uci_lib MODULE lib/uci.c) set_target_properties(uci_lib PROPERTIES OUTPUT_NAME uci PREFIX "") target_link_options(uci_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) target_link_libraries(uci_lib ${libuci} ${libubox}) endif() if(RTNL_SUPPORT) find_path(nl_include_dir NAMES netlink/msg.h PATH_SUFFIXES libnl-tiny) include_directories(${nl_include_dir}) set(LIBRARIES ${LIBRARIES} rtnl_lib) add_library(rtnl_lib MODULE lib/rtnl.c) set_target_properties(rtnl_lib PROPERTIES OUTPUT_NAME rtnl PREFIX "") target_link_options(rtnl_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) target_link_libraries(rtnl_lib ${libnl_tiny} ${libubox}) endif() if(NL80211_SUPPORT) find_path(nl_include_dir NAMES netlink/msg.h PATH_SUFFIXES libnl-tiny) include_directories(${nl_include_dir}) set(LIBRARIES ${LIBRARIES} nl80211_lib) add_library(nl80211_lib MODULE lib/nl80211.c) set_target_properties(nl80211_lib PROPERTIES OUTPUT_NAME nl80211 PREFIX "") target_link_options(nl80211_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) target_link_libraries(nl80211_lib ${libnl_tiny} ${libubox}) endif() if(RESOLV_SUPPORT) set(LIBRARIES ${LIBRARIES} resolv_lib) add_library(resolv_lib MODULE lib/resolv.c) set_target_properties(resolv_lib PROPERTIES OUTPUT_NAME resolv PREFIX "") target_link_options(resolv_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) check_function_exists(res_mkquery RES_MKQUERY_FUNCTION_EXISTS) check_function_exists(ns_initparse NS_INITARSE_FUNCTION_EXISTS) check_function_exists(clock_gettime CLOCK_GETTIME_FUNCTION_EXISTS) if(NOT RES_MKQUERY_FUNCTION_EXISTS OR NOT NS_INITARSE_FUNCTION_EXISTS) target_link_libraries(resolv_lib resolv) endif() if(NOT CLOCK_GETTIME_FUNCTION_EXISTS) target_link_libraries(resolv_lib rt) endif() endif() if(STRUCT_SUPPORT) set(LIBRARIES ${LIBRARIES} struct_lib) add_library(struct_lib MODULE lib/struct.c) set_target_properties(struct_lib PROPERTIES OUTPUT_NAME struct PREFIX "") target_link_options(struct_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) check_function_exists(frexp FREXP_FUNCTION_EXISTS) if(NOT FREXP_FUNCTION_EXISTS) target_link_libraries(struct_lib m) endif() endif() if(ULOOP_SUPPORT) find_path(uloop_include_dir NAMES libubox/uloop.h) include_directories(${uloop_include_dir}) set(LIBRARIES ${LIBRARIES} uloop_lib) add_library(uloop_lib MODULE lib/uloop.c) set_target_properties(uloop_lib PROPERTIES OUTPUT_NAME uloop PREFIX "") target_link_options(uloop_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${libubox}) check_function_exists(uloop_timeout_remaining64 REMAINING64_FUNCTION_EXISTS) check_function_exists(uloop_interval_set INTERVAL_FUNCTION_EXISTS) check_function_exists(uloop_signal_add SIGNAL_FUNCTION_EXISTS) if(REMAINING64_FUNCTION_EXISTS) target_compile_definitions(uloop_lib PUBLIC HAVE_ULOOP_TIMEOUT_REMAINING64) endif() if(INTERVAL_FUNCTION_EXISTS) target_compile_definitions(uloop_lib PUBLIC HAVE_ULOOP_INTERVAL) endif() if(SIGNAL_FUNCTION_EXISTS) target_compile_definitions(uloop_lib PUBLIC HAVE_ULOOP_SIGNAL) endif() target_link_libraries(uloop_lib ${libubox}) endif() if(LOG_SUPPORT) set(LIBRARIES ${LIBRARIES} log_lib) add_library(log_lib MODULE lib/log.c) set_target_properties(log_lib PROPERTIES OUTPUT_NAME log PREFIX "") target_link_options(log_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) if(libubox) find_path(ulog_include_dir NAMES libubox/ulog.h) include_directories(${ulog_include_dir}) target_link_libraries(log_lib ${libubox}) set_target_properties(log_lib PROPERTIES COMPILE_DEFINITIONS HAVE_ULOG) endif() endif() if(SOCKET_SUPPORT) set(LIBRARIES ${LIBRARIES} socket_lib) add_library(socket_lib MODULE lib/socket.c) set_target_properties(socket_lib PROPERTIES OUTPUT_NAME socket PREFIX "") target_link_options(socket_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) endif() if(UNIT_TESTING) enable_testing() add_definitions(-DUNIT_TESTING) add_subdirectory(tests) list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") if(CMAKE_C_COMPILER_ID STREQUAL "Clang") add_executable(ucode-san ${CLI_SOURCES} ${UCODE_SOURCES}) set_property(TARGET ucode-san PROPERTY ENABLE_EXPORTS 1) target_link_libraries(ucode-san ${JSONC_LINK_LIBRARIES}) target_compile_options(ucode-san PRIVATE -g -fno-omit-frame-pointer -fsanitize=undefined,address,leak -fno-sanitize-recover=all) target_link_options(ucode-san PRIVATE -fsanitize=undefined,address,leak) endif() endif() install(TARGETS ucode RUNTIME DESTINATION bin) install(TARGETS libucode LIBRARY DESTINATION lib) install(TARGETS ${LIBRARIES} LIBRARY DESTINATION lib/ucode) add_custom_target(utpl ALL COMMAND ${CMAKE_COMMAND} -E create_symlink ucode utpl) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/utpl DESTINATION bin) if(COMPILE_SUPPORT) add_custom_target(ucc ALL COMMAND ${CMAKE_COMMAND} -E create_symlink ucode ucc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ucc DESTINATION bin) endif() file(GLOB UCODE_HEADERS "include/ucode/*.h") install(FILES ${UCODE_HEADERS} DESTINATION include/ucode) add_subdirectory(examples)