From 5ad7f878a63a4bafea1d416f4fc22ce958188961 Mon Sep 17 00:00:00 2001 From: bugdea1er Date: Wed, 3 Jan 2024 17:22:16 +0300 Subject: [PATCH] Initial commit --- .github/workflows/LinuxBuild.yml | 20 +++++++ .github/workflows/MacBuildWithFlags.yml | 20 +++++++ .github/workflows/MacBuildWithoutFlags.yml | 15 +++++ AbstractPlugin.cpp | 6 ++ AbstractPlugin.hpp | 13 +++++ CMakeLists.txt | 16 ++++++ ModulePlugin.cpp | 14 +++++ README.md | 66 ++++++++++++++++++++++ main.cpp | 14 +++++ 9 files changed, 184 insertions(+) create mode 100644 .github/workflows/LinuxBuild.yml create mode 100644 .github/workflows/MacBuildWithFlags.yml create mode 100644 .github/workflows/MacBuildWithoutFlags.yml create mode 100644 AbstractPlugin.cpp create mode 100644 AbstractPlugin.hpp create mode 100644 CMakeLists.txt create mode 100644 ModulePlugin.cpp create mode 100644 README.md create mode 100644 main.cpp diff --git a/.github/workflows/LinuxBuild.yml b/.github/workflows/LinuxBuild.yml new file mode 100644 index 0000000..7b5f5db --- /dev/null +++ b/.github/workflows/LinuxBuild.yml @@ -0,0 +1,20 @@ +name: Linux Build +on: + push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build + - name: Build + run: cmake --build ${{github.workspace}}/build + - name: Check that AbstractPlugin::preface is defined in the executable + run: nm -g ${{github.workspace}}/build/dynamic_lookup | grep AbstractPlugin.preface | grep T + - name: Check that AbstractPlugin::preface is undefined in the module + run: nm -g ${{github.workspace}}/build/libModulePlugin.so | grep AbstractPlugin.preface | grep U + - name: Run the executable + run: cd ${{github.workspace}}/build && ./dynamic_lookup diff --git a/.github/workflows/MacBuildWithFlags.yml b/.github/workflows/MacBuildWithFlags.yml new file mode 100644 index 0000000..ee1e6aa --- /dev/null +++ b/.github/workflows/MacBuildWithFlags.yml @@ -0,0 +1,20 @@ +name: Mac Build With Flags +on: + push + +jobs: + build: + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DDYNAMIC_LOOKUP=ON + - name: Build + run: cmake --build ${{github.workspace}}/build + - name: Check that AbstractPlugin::preface is defined in the executable + run: nm -g ${{github.workspace}}/build/dynamic_lookup | grep AbstractPlugin.preface | grep T + - name: Check that AbstractPlugin::preface is undefined in the module + run: nm -g ${{github.workspace}}/build/libModulePlugin.so | grep AbstractPlugin.preface | grep U + - name: Run the executable + run: cd ${{github.workspace}}/build && ./dynamic_lookup diff --git a/.github/workflows/MacBuildWithoutFlags.yml b/.github/workflows/MacBuildWithoutFlags.yml new file mode 100644 index 0000000..3659817 --- /dev/null +++ b/.github/workflows/MacBuildWithoutFlags.yml @@ -0,0 +1,15 @@ +name: Mac Build Without Flags +on: + push + +jobs: + build: + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build + - name: Fail to build + run: cmake --build ${{github.workspace}}/build + continue-on-error: true diff --git a/AbstractPlugin.cpp b/AbstractPlugin.cpp new file mode 100644 index 0000000..8127636 --- /dev/null +++ b/AbstractPlugin.cpp @@ -0,0 +1,6 @@ +#include "AbstractPlugin.hpp" +#include + +void AbstractPlugin::preface() { + std::cout << "Hello world" << std::endl; +} diff --git a/AbstractPlugin.hpp b/AbstractPlugin.hpp new file mode 100644 index 0000000..9f90b6e --- /dev/null +++ b/AbstractPlugin.hpp @@ -0,0 +1,13 @@ +#pragma once + +class AbstractPlugin { +public: + /// This symbol must be defined in the module + virtual void run() = 0; + + virtual ~AbstractPlugin() = default; + +protected: + /// This symbol should not be defined in the module + void preface(); +}; diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4bff293 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.12) +project(dynamic_lookup) +set(CMAKE_CXX_STANDARD 17) + +option(DYNAMIC_LOOKUP "Adds dynamic lookup linker flag" OFF) + +add_library(AbstractPlugin OBJECT AbstractPlugin.cpp) +add_library(ModulePlugin MODULE ModulePlugin.cpp) +if (${DYNAMIC_LOOKUP}) + target_link_options(ModulePlugin PRIVATE -undefined dynamic_lookup) +endif() + +add_executable(dynamic_lookup main.cpp) +target_link_libraries(dynamic_lookup PUBLIC AbstractPlugin ${CMAKE_DL_LIBS}) + +set_property(TARGET dynamic_lookup PROPERTY ENABLE_EXPORTS ON) diff --git a/ModulePlugin.cpp b/ModulePlugin.cpp new file mode 100644 index 0000000..cbf9df5 --- /dev/null +++ b/ModulePlugin.cpp @@ -0,0 +1,14 @@ +#include "AbstractPlugin.hpp" +#include + +class ModulePlugin final : public AbstractPlugin { +public: + void run() override { + preface(); + std::cout << "Bye world" << std::endl; + } +}; + +extern "C" { +AbstractPlugin* createPlugin() { return new ModulePlugin(); } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..bfb89fc --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +This example shows different behaviour of cmake project build when building MODULEs on Linux and Mac OS + +The example consists of three parts: +- `AbstractPlugin` OBJECT library defines a virtual class AbstractPlugin. It has one defined method `preface` and one pure virtual method `run`. The `preface` method **required** to be defined in the object library +- `dynamic_lookup` executable links with the AbstractPlugin library and therefore contains a definition for `AbstractPlugin::preface` method +- `ModulePlugin` MODULE library defines an implementation of AbstractPlugin. The resulting `.so` file contains the `run` definition and does not contain the `preface` method definition. + +The CMakeLists.txt file describes an option DYNAMIC_LOOKUP (off by default) which adds a `-undefined dynamic_lookup` linker flag for the MODULE target. + +`dynamic_lookup` executable lists `preface` symbol as defined: +```shell +$ nm -g build/dynamic_lookup | grep AbstractPlugin.preface | grep T +0000000100003010 T __ZN14AbstractPlugin7prefaceEv +``` + +and `libModulePlugin` module lists `preface` symbol as undefined: +```shell +$ nm -g build/libModulePlugin.so | grep AbstractPlugin.preface | grep T + U __ZN14AbstractPlugin7prefaceEv +``` + +Linux build ([Linux Build CI](https://github.com/bugdea1er/dynamic_lookup/actions/workflows/LinuxBuild.yml)) completes just fine: no need for extra flags on top of marking the libs as OBJECT and MODULE where needed: +```shell +$ cmake -B build +$ cmake --build build +[ 20%] Building CXX object CMakeFiles/AbstractPlugin.dir/AbstractPlugin.cpp.o +[ 20%] Built target AbstractPlugin +[ 40%] Building CXX object CMakeFiles/ModulePlugin.dir/ModulePlugin.cpp.o +[ 60%] Linking CXX shared module libModulePlugin.so +[ 60%] Built target ModulePlugin +[ 80%] Building CXX object CMakeFiles/dynamic_lookup.dir/main.cpp.o +[100%] Linking CXX executable dynamic_lookup +[100%] Built target dynamic_lookup +``` + +Mac OS build ([Mac Build Without Flags CI](https://github.com/bugdea1er/dynamic_lookup/actions/workflows/MacBuildWithoutFlags.yml)) fails under the same conditions: +```shell +$ cmake -B build +$ cmake --build build +[ 20%] Building CXX object CMakeFiles/AbstractPlugin.dir/AbstractPlugin.cpp.o +[ 20%] Built target AbstractPlugin +[ 40%] Building CXX object CMakeFiles/ModulePlugin.dir/ModulePlugin.cpp.o +[ 60%] Linking CXX shared module libModulePlugin.so +Undefined symbols for architecture x86_64: + "AbstractPlugin::preface()", referenced from: + ModulePlugin::run() in ModulePlugin.cpp.o +ld: symbol(s) not found for architecture x86_64 +clang: error: linker command failed with exit code 1 (use -v to see invocation) +make[2]: *** [libModulePlugin.so] Error 1 +make[1]: *** [CMakeFiles/ModulePlugin.dir/all] Error 2 +make: *** [all] Error 2 +``` + +Mac OS build needs a separate linker flag `-undefined dynamic_lookup` in order to achive the same results ([Mac Build With Flags CI](https://github.com/bugdea1er/dynamic_lookup/actions/workflows/MacBuildWithFlags.yml)): +```shell +$ cmake -B build -DDYNAMIC_LOOKUP=ON +$ cmake --build build +[ 20%] Building CXX object CMakeFiles/AbstractPlugin.dir/AbstractPlugin.cpp.o +[ 20%] Built target AbstractPlugin +[ 40%] Building CXX object CMakeFiles/ModulePlugin.dir/ModulePlugin.cpp.o +[ 60%] Linking CXX shared module libModulePlugin.so +[ 60%] Built target ModulePlugin +[ 80%] Building CXX object CMakeFiles/dynamic_lookup.dir/main.cpp.o +[100%] Linking CXX executable dynamic_lookup +[100%] Built target dynamic_lookup +``` diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..766d57f --- /dev/null +++ b/main.cpp @@ -0,0 +1,14 @@ +#include "AbstractPlugin.hpp" +#include +#include + +int main() { + auto module = std::filesystem::absolute("libModulePlugin.so"); + auto* handle = dlopen(module.c_str(), RTLD_NOW); + auto* constructor = (AbstractPlugin*(*)()) dlsym(handle, "createPlugin"); + auto* plugin = constructor(); + plugin->run(); + + delete plugin; + dlclose(handle); +}