Skip to content

Instantly share code, notes, and snippets.

@luncliff
Last active May 19, 2024 01:38
Show Gist options
  • Save luncliff/2a892ce06cd319207720434966ded7fc to your computer and use it in GitHub Desktop.
Save luncliff/2a892ce06cd319207720434966ded7fc to your computer and use it in GitHub Desktop.
C++, Swift build with CMake
#include "logger.hpp"
#include "logger_binding.h"
#define FMT_HEADER_ONLY
#define SPDLOG_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/xchar.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/spdlog.h>
auto make_logger(const char* name, FILE* fout) noexcept(false) {
spdlog::sink_ptr sink0 = [fout]() -> spdlog::sink_ptr {
using mutex_t = spdlog::details::console_nullmutex;
if (fout == stdout || fout == stderr)
return std::make_shared<spdlog::sinks::stdout_color_sink_st>();
using sink_t = spdlog::sinks::stdout_sink_base<mutex_t>;
return std::make_shared<sink_t>(fout);
}();
return std::make_shared<spdlog::logger>(name, spdlog::sinks_init_list{sink0});
}
void setup_logger(const char* name, FILE* fout) {
auto logger = make_logger(name, fout);
logger->set_pattern("%T.%e [%L] %8t %v");
logger->set_level(spdlog::level::level_enum::debug);
spdlog::set_default_logger(logger);
}
void setup_logger(const char* name) {
return setup_logger(name, stdout);
}
#pragma once
#include <cstdio>
#if __has_include("logger_binding.h")
extern "C" {
#include "logger_binding.h"
}
#endif
void setup_logger(const char* name, FILE* fout);
#pragma once
void setup_logger(const char* name);
import Darwin
import Foundation
// public func setup_logger(_ name: UnsafePointer<CChar>!) -> Void
/// @see https://developer.apple.com/documentation/swift/using-imported-c-functions-in-swift
class CustomLogger {
init() {
changeName("test")
}
func changeName(_ name: String) {
let u8str = name.cString(using: .utf8)
setup_logger(u8str!)
// let sym: UnsafeMutableRawPointer? = dlsym(handle, "_setup_logger")
// if sym == nil {
// return
// }
// typealias FnSetup = @convention(c) (UnsafePointer<CChar>) -> Void
// let fn = unsafeBitCast(sym!, to: FnSetup.self)
// fn(u8str!)
}
}
import Darwin
import Foundation
import XCTest
@testable import logger1
class CustomLoggerTestCase: XCTestCase {
var handle: UnsafeMutableRawPointer?
override func setUp() {
handle = dlopen("liblogger1.dylib", RTLD_NOW)
if handle == nil {
handle = dlopen(nil, RTLD_NOW)
print("Using dlopen(nil)...")
}
XCTAssertNotNil(handle)
}
override func tearDown() {
dlclose(handle)
}
func testFunctions() {
let fn = dlsym(handle, "setup_logger")
XCTAssertNotNil(fn)
}
func testBindingClass() {
let logger = CustomLogger()
logger.changeName("XCTestCase")
}
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(cpp-swift-test VERSION 1.0 LANGUAGES CXX OBJCXX Swift)
message(STATUS "Using system: ${CMAKE_SYSTEM_VERSION}")
option(BUILD_TESTING "Build XCTest bundle" ON)
include(GNUInstallDirs)
enable_testing()
find_package(spdlog CONFIG REQUIRED) # spdlog::spdlog_header_only
# C++/Objective-C++ library
add_library(logger0 STATIC
logger.cpp
)
# some public interfaces
list(APPEND ifcs
logger.hpp
)
target_sources(logger0 PRIVATE ${ifcs})
set_target_properties(logger0 PROPERTIES
CXX_STANDARD 17
OBJCXX_STANDARD 17
PUBLIC_HEADER "${ifcs}"
XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC ON # -fobjc-arc
)
target_compile_options(logger0 PRIVATE
-g
)
target_link_libraries(logger0 PRIVATE
spdlog::spdlog_header_only
)
# Swift framework with some C++ libraries
add_library(logger1 SHARED
${ifcs}
logger_binding.swift
)
# Swift-C bridge headers
list(APPEND bridges
logger_binding.h
)
target_sources(logger1 PRIVATE ${bridges})
# It sould be `.framework` so we can `import` it in Swift sources
set_target_properties(logger1 PROPERTIES
LINKER_LANGUAGE Swift
Swift_LANGUAGE_VERSION 5.4
FRAMEWORK TRUE
FRAMEWORK_VERSION A
PUBLIC_HEADER "${ifcs};${bridges}"
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${bridges}" #
XCODE_ATTRIBUTE_ENABLE_TESTABILITY "YES" # for XCTest
)
target_include_directories(logger1 PRIVATE
${PROJECT_SOURCE_DIR}
)
target_link_libraries(logger1 PRIVATE
logger0
"-framework Foundation"
)
if(NOT BUILD_TESTING)
return()
endif()
# https://cmake.org/cmake/help/latest/module/FindXCTest.html
find_package(XCTest REQUIRED) # XCTest_INCLUDE_DIRS, XCTest_LIBRARIES
xctest_add_bundle(test_suite logger1)
xctest_add_test(run_test_suite test_suite)
target_include_directories(test_suite PRIVATE ${XCTest_INCLUDE_DIRS})
target_link_libraries(test_suite PRIVATE ${XCTest_LIBRARIES})
# Swift sources may need customized compiler/linker option...
list(APPEND TEST_SRCS
logger_test.swift
)
set_source_files_properties(${TEST_SRCS} PROPERTIES
LANGUAGE Swift
COMPILE_OPTIONS "-Wall;-Wextra"
LINK_OPTIONS "-Wall"
)
target_sources(test_suite PRIVATE ${TEST_SRCS})
# installation under CMAKE_INSTALL_PREFIX
install(TARGETS logger0 logger1 test_suite
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment