Skip to content

Commit da39842

Browse files
author
Marcel Hecko
committed
Add out-of-tree CMake plugin example for issue #237
Provides a working example demonstrating the correct way to build SEMS plugins with CMake when building outside the SEMS source tree. Files: - MyApp.cpp: Simple example plugin implementation - CMakeLists.txt: Correct configuration using SemsPlugin.cmake helper - README.md: Build instructions and explanation This example uses the SemsPlugin.cmake helper to ensure: - MODULE library type (not SHARED) - No static linking of SEMS core - Correct symbol visibility for dlsym() - Platform-specific linker flags Includes verification target to check that session_factory_create symbol is properly exported.
1 parent 712484d commit da39842

File tree

3 files changed

+295
-0
lines changed

3 files changed

+295
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Example: Out-of-Tree SEMS Plugin with CMake
2+
#
3+
# This demonstrates the CORRECT way to build a SEMS plugin using CMake
4+
# when building outside the SEMS source tree.
5+
#
6+
# Build instructions:
7+
# mkdir build && cd build
8+
# cmake -DSEMS_SOURCE_DIR=/path/to/sems ..
9+
# make
10+
# nm -D myapp_cmake.so | grep session_factory_create # verify symbol
11+
12+
cmake_minimum_required(VERSION 3.15)
13+
project(myapp_cmake_example)
14+
15+
set(CMAKE_CXX_STANDARD 17)
16+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
17+
18+
# ============================================================================
19+
# SEMS Configuration
20+
# ============================================================================
21+
22+
# Path to SEMS source directory (required)
23+
set(SEMS_SOURCE_DIR "" CACHE PATH "Path to SEMS source directory")
24+
25+
if(NOT SEMS_SOURCE_DIR)
26+
message(FATAL_ERROR
27+
"SEMS_SOURCE_DIR not set. Please specify:\n"
28+
" cmake -DSEMS_SOURCE_DIR=/path/to/sems .."
29+
)
30+
endif()
31+
32+
if(NOT EXISTS "${SEMS_SOURCE_DIR}/cmake/SemsPlugin.cmake")
33+
message(FATAL_ERROR
34+
"SEMS source directory invalid: ${SEMS_SOURCE_DIR}\n"
35+
"Cannot find cmake/SemsPlugin.cmake"
36+
)
37+
endif()
38+
39+
# Include the SEMS plugin helper
40+
include(${SEMS_SOURCE_DIR}/cmake/SemsPlugin.cmake)
41+
42+
# ============================================================================
43+
# Plugin Configuration
44+
# ============================================================================
45+
46+
# Add the plugin using the helper function
47+
# This automatically:
48+
# - Creates a MODULE library (for dlopen)
49+
# - Sets MODULE_NAME definition
50+
# - Configures proper symbol visibility
51+
# - Sets platform-specific linker flags
52+
sems_add_plugin(myapp_cmake
53+
MyApp.cpp
54+
)
55+
56+
# Add SEMS core include directory
57+
sems_plugin_include_directories(myapp_cmake
58+
${SEMS_SOURCE_DIR}/core
59+
)
60+
61+
# Link external libraries only
62+
# DO NOT link SEMS core libraries statically!
63+
# They will be resolved from the SEMS binary at runtime.
64+
sems_plugin_link_libraries(myapp_cmake
65+
pthread
66+
# Add your external dependencies here (PocoNet, etc.)
67+
)
68+
69+
# Install to standard location
70+
sems_install_plugin(myapp_cmake)
71+
72+
# ============================================================================
73+
# Verification
74+
# ============================================================================
75+
76+
# Custom target to verify the symbol is exported correctly
77+
find_program(NM_PROGRAM nm)
78+
if(NM_PROGRAM)
79+
add_custom_target(verify_symbol
80+
COMMAND ${CMAKE_COMMAND} -E echo "Verifying session_factory_create symbol..."
81+
COMMAND ${NM_PROGRAM} -D ${CMAKE_CURRENT_BINARY_DIR}/myapp_cmake.so | grep session_factory_create || (echo "ERROR: Symbol not found!" && exit 1)
82+
COMMAND ${CMAKE_COMMAND} -E echo "✓ Symbol exported correctly!"
83+
DEPENDS sems_myapp_cmake
84+
COMMENT "Verifying plugin symbol export"
85+
)
86+
87+
message(STATUS "Run 'make verify_symbol' after building to verify the plugin")
88+
endif()
89+
90+
# ============================================================================
91+
# Summary
92+
# ============================================================================
93+
94+
message(STATUS "========================================")
95+
message(STATUS "Out-of-Tree Plugin Example")
96+
message(STATUS "========================================")
97+
message(STATUS "Plugin name: myapp_cmake")
98+
message(STATUS "SEMS source: ${SEMS_SOURCE_DIR}")
99+
message(STATUS "Output: ${CMAKE_CURRENT_BINARY_DIR}/myapp_cmake.so")
100+
message(STATUS "========================================")
101+
message(STATUS "After building:")
102+
message(STATUS " 1. Run: make verify_symbol")
103+
message(STATUS " 2. Install: sudo cp myapp_cmake.so /usr/lib/sems/plug-in/")
104+
message(STATUS " 3. Configure: Add 'load_plugins=myapp_cmake' to sems.conf")
105+
message(STATUS "========================================")
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Example out-of-tree SEMS plugin built with CMake
3+
*
4+
* This demonstrates the correct way to build a SEMS plugin
5+
* using CMake when building outside the SEMS source tree.
6+
*
7+
* See CMakeLists.txt for the build configuration.
8+
*/
9+
10+
#include "AmApi.h"
11+
#include "AmSession.h"
12+
#include "log.h"
13+
14+
#include <string>
15+
#include <map>
16+
17+
#define MOD_NAME "myapp_cmake"
18+
19+
using std::string;
20+
using std::map;
21+
22+
class MyAppFactory: public AmSessionFactory
23+
{
24+
public:
25+
MyAppFactory(const string& _app_name)
26+
: AmSessionFactory(_app_name)
27+
{
28+
DBG("MyAppFactory created\n");
29+
}
30+
31+
int onLoad() {
32+
DBG("MyAppFactory::onLoad()\n");
33+
return 0;
34+
}
35+
36+
AmSession* onInvite(const AmSipRequest& req, const string& app_name,
37+
const map<string,string>& app_params);
38+
};
39+
40+
class MyAppDialog : public AmSession
41+
{
42+
public:
43+
MyAppDialog() {
44+
DBG("MyAppDialog created\n");
45+
}
46+
47+
~MyAppDialog() {
48+
DBG("MyAppDialog destroyed\n");
49+
}
50+
51+
void onSessionStart() {
52+
DBG("MyAppDialog::onSessionStart - Hello from CMake-built plugin!\n");
53+
// Just log and hang up for this example
54+
dlg->bye();
55+
setStopped();
56+
}
57+
58+
void onBye(const AmSipRequest& req) {
59+
DBG("MyAppDialog::onBye\n");
60+
setStopped();
61+
}
62+
};
63+
64+
AmSession* MyAppFactory::onInvite(const AmSipRequest& req, const string& app_name,
65+
const map<string,string>& app_params)
66+
{
67+
DBG("MyAppFactory::onInvite\n");
68+
return new MyAppDialog();
69+
}
70+
71+
// This macro creates the extern "C" function that SEMS will call
72+
// The symbol "session_factory_create" must be visible via dlsym()
73+
EXPORT_SESSION_FACTORY(MyAppFactory, MOD_NAME);
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Out-of-Tree SEMS Plugin with CMake
2+
3+
This example demonstrates the **correct** way to build a SEMS plugin using CMake when building outside the SEMS source tree.
4+
5+
## The Issue (GitHub #237)
6+
7+
When building SEMS plugins with CMake incorrectly, they compile fine but fail at runtime:
8+
```
9+
[AmPlugIn.cpp:818] INFO: could not find any application matching configured criteria
10+
```
11+
12+
This happens when the `session_factory_create` symbol (created by `EXPORT_SESSION_FACTORY`) is not properly exported.
13+
14+
## This Example
15+
16+
This example shows the **correct** approach using the SEMS plugin helper.
17+
18+
### Files
19+
20+
- `MyApp.cpp` - Simple plugin implementation
21+
- `CMakeLists.txt` - Correct CMake configuration using `SemsPlugin.cmake`
22+
23+
### Key Points
24+
25+
1. **Uses `MODULE` library type** (not `SHARED`)
26+
2. **Does NOT statically link SEMS core**
27+
3. **Sets correct symbol visibility**
28+
4. **Includes platform-specific settings**
29+
30+
## Building
31+
32+
```bash
33+
# Create build directory
34+
mkdir build && cd build
35+
36+
# Configure (point to SEMS source)
37+
cmake -DSEMS_SOURCE_DIR=/path/to/sems/source ..
38+
39+
# Build
40+
make
41+
42+
# Verify the symbol is exported
43+
make verify_symbol
44+
```
45+
46+
Expected output from verification:
47+
```
48+
✓ Symbol exported correctly!
49+
```
50+
51+
## Testing
52+
53+
1. Copy the plugin:
54+
```bash
55+
sudo cp myapp_cmake.so /usr/lib/sems/plug-in/
56+
```
57+
58+
2. Configure SEMS (`/etc/sems/sems.conf`):
59+
```
60+
load_plugins=myapp_cmake
61+
application=myapp_cmake
62+
```
63+
64+
3. Start SEMS and make a test call
65+
66+
4. Check logs for:
67+
```
68+
[sems] DEBUG: application 'myapp_cmake' loaded.
69+
[myapp_cmake] DEBUG: MyAppFactory created
70+
[myapp_cmake] DEBUG: MyAppDialog::onSessionStart - Hello from CMake-built plugin!
71+
```
72+
73+
## How It Works
74+
75+
The `CMakeLists.txt` uses `SemsPlugin.cmake` helper:
76+
77+
```cmake
78+
include(${SEMS_SOURCE_DIR}/cmake/SemsPlugin.cmake)
79+
80+
sems_add_plugin(myapp_cmake MyApp.cpp)
81+
sems_plugin_include_directories(myapp_cmake ${SEMS_SOURCE_DIR}/core)
82+
sems_plugin_link_libraries(myapp_cmake pthread)
83+
```
84+
85+
This automatically:
86+
- Creates a `MODULE` library (for `dlopen()`)
87+
- Sets `MODULE_NAME` definition
88+
- Configures symbol visibility to `default`
89+
- Adds platform-specific linker flags
90+
- Links only external libraries (not SEMS core)
91+
92+
## Common Mistakes (What NOT to Do)
93+
94+
### ❌ Wrong: Using SHARED
95+
```cmake
96+
add_library(myapp SHARED MyApp.cpp) # WRONG!
97+
```
98+
99+
### ❌ Wrong: Statically linking SEMS core
100+
```cmake
101+
target_link_libraries(myapp
102+
"-Wl,--whole-archive"
103+
${SEMS_DIR}/core/libsems_core.a
104+
"-Wl,--no-whole-archive") # WRONG!
105+
```
106+
107+
### ❌ Wrong: Hidden symbol visibility
108+
```cmake
109+
set_target_properties(myapp PROPERTIES
110+
CXX_VISIBILITY_PRESET hidden) # WRONG!
111+
```
112+
113+
## See Also
114+
115+
- `cmake/SemsPlugin.cmake` - Plugin helper functions
116+
- `doc/BuildingOutOfTreePlugins.md` - Detailed documentation
117+
- `cmake/module.rules.txt` - How SEMS builds its own plugins

0 commit comments

Comments
 (0)