Skip to content

Commit 6f69769

Browse files
committed
DPL: allow building a workflow as a workflow plugin
A workflow opts in with WORKFLOW_PLUGIN, on o2_add_dpl_workflow or on o2_add_executable. Its sources are then compiled once, into the DSO libO2<Name>Plugin holding the workflow plugin <Name>, the CamelCase of the executable name without o2-: o2-testworkflows-diamond-workflow gives O2TestworkflowsDiamondWorkflowPlugin:TestworkflowsDiamondWorkflow. The executable keeps its name and options, and only has a main() which calls dpl_workflow_main() of the plugin it links. That one runs the plugin as the one embedded in the executable, exactly as main() does for any other workflow, so its behaviour and dumps are the same. The plugin is installed in lib with the executable. WORKFLOW_PLUGIN, rather than PLUGIN, because DPL has several kinds of plugins and this one is DplPluginKind::Workflow, loaded with --workflow-plugin; the same name works on both functions. The existing o2-dpl-run, whose embedded workflow is empty, is the generic loader: o2-dpl-run --workflow-plugin A:X,B:Y runs the workflows of the plugins, and its children are again o2-dpl-run. Workflows which do not opt in are built exactly as before. The diamond workflow, the simple source, processor and sink, and the two test plugins of test_WorkflowPlugins opt in. test_Framework_test_WorkflowPluginDump checks that the plugin of the diamond workflow, run by o2-dpl-run, defines the same workflow as its executable, apart from the executable, plugin and command line.
1 parent 13cd2ac commit 6f69769

7 files changed

Lines changed: 98 additions & 26 deletions

File tree

‎Framework/Core/CMakeLists.txt‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -459,18 +459,16 @@ o2_add_test(
459459
TARGETVARNAME workflowPluginsTest
460460
NO_BOOST_TEST
461461
COMMAND_LINE_ARGS
462-
--workflow-plugin O2FrameworkTestWorkflowPluginA:TestWorkflowPluginA,O2FrameworkTestWorkflowPluginB:TestWorkflowPluginB
462+
--workflow-plugin O2TestFrameworkWorkflowPluginAPlugin:TestFrameworkWorkflowPluginA,O2TestFrameworkWorkflowPluginBPlugin:TestFrameworkWorkflowPluginB
463463
--shared 3 --run --shm-segment-size 20000000 ${DPL_WORKFLOW_TESTS_EXTRA_OPTIONS}
464464
ENVIRONMENT "DYLD_LIBRARY_PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}:$ENV{DYLD_LIBRARY_PATH};LD_LIBRARY_PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}:$ENV{LD_LIBRARY_PATH}"
465465
)
466466
if(workflowPluginsTest)
467467
foreach(p A B)
468-
add_library(O2FrameworkTestWorkflowPlugin${p} SHARED test/test_WorkflowPlugin${p}.cxx)
469-
target_link_libraries(O2FrameworkTestWorkflowPlugin${p} PRIVATE O2::Framework)
470-
target_compile_definitions(O2FrameworkTestWorkflowPlugin${p} PRIVATE
471-
DPL_WORKFLOW_PLUGIN_NAME=TestWorkflowPlugin${p}
472-
DPL_WORKFLOW_PLUGIN_LIBRARY=O2FrameworkTestWorkflowPlugin${p})
473-
add_dependencies(${workflowPluginsTest} O2FrameworkTestWorkflowPlugin${p})
468+
o2_add_executable(WorkflowPlugin${p} SOURCES test/test_WorkflowPlugin${p}.cxx
469+
COMPONENT_NAME Framework PUBLIC_LINK_LIBRARIES O2::Framework
470+
IS_TEST NO_INSTALL WORKFLOW_PLUGIN TARGETVARNAME workflowPlugin)
471+
add_dependencies(${workflowPluginsTest} ${workflowPlugin})
474472
endforeach()
475473
add_test(NAME test_Framework_test_WorkflowPluginsDump
476474
COMMAND ${CMAKE_CURRENT_LIST_DIR}/test/test_WorkflowPluginsDump.sh $<TARGET_FILE:${workflowPluginsTest}>)

‎Framework/Core/include/Framework/runDataProcessing.h‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,12 @@ char* getIdString(int argc, char** argv);
170170
#define DPL_STRINGIZE(A) DPL_STRINGIZE_NX(A)
171171

172172
// The workflow definition is a plugin. By default it is embedded in the
173-
// executable as DPL_WORKFLOW_PLUGIN_NAME, with an empty library name.
174-
// To build it in a DSO instead, both DPL_WORKFLOW_PLUGIN_NAME and
175-
// DPL_WORKFLOW_PLUGIN_LIBRARY must be specified at build time.
173+
// executable as DPL_WORKFLOW_PLUGIN_NAME, and main() runs it. A workflow built
174+
// as a plugin library gets DPL_WORKFLOW_PLUGIN_NAME at build time: it has no
175+
// main(), and the executable which links it calls dpl_workflow_main() instead.
176176
#ifndef DPL_WORKFLOW_PLUGIN_NAME
177177
#define DPL_WORKFLOW_PLUGIN_NAME InternalCustomWorkflow
178-
#ifdef DPL_WORKFLOW_PLUGIN_LIBRARY
179-
#error Missing DPL_WORKFLOW_PLUGIN_NAME
180-
#endif
181-
#define DPL_WORKFLOW_PLUGIN_LIBRARY
178+
#define DPL_WORKFLOW_PLUGIN_EMBEDDED
182179
#endif
183180

184181
struct DPL_WORKFLOW_PLUGIN_NAME : o2::framework::WorkflowPlugin {
@@ -216,11 +213,15 @@ DPLPluginHandle* dpl_plugin_callback(DPLPluginHandle* previous)
216213
// An empty library means the plugin embedded in the executable, found via @a embeddedPlugins.
217214
int callMain(int argc, char** argv, char const* pluginSpec, DPLPluginHandle* (*embeddedPlugins)(DPLPluginHandle*));
218215

216+
#ifdef DPL_WORKFLOW_PLUGIN_EMBEDDED
219217
int main(int argc, char** argv)
218+
#else
219+
extern "C" int dpl_workflow_main(int argc, char** argv)
220+
#endif
220221
{
221222
using namespace o2::framework;
222223

223-
int result = callMain(argc, argv, DPL_STRINGIZE(DPL_WORKFLOW_PLUGIN_LIBRARY) ":" DPL_STRINGIZE(DPL_WORKFLOW_PLUGIN_NAME), dpl_plugin_callback);
224+
int result = callMain(argc, argv, ":" DPL_STRINGIZE(DPL_WORKFLOW_PLUGIN_NAME), dpl_plugin_callback);
224225

225226
char* idstring = getIdString(argc, argv);
226227
o2::framework::OnWorkflowTerminationHook onWorkflowTerminationHook;

‎Framework/Core/test/test_WorkflowPluginsDump.sh‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# Dumps the workflow of the two test plugins and checks that it records,
33
# for each data processor, the plugin which defines it.
44
# usage: test_WorkflowPluginsDump.sh <o2-test-framework-WorkflowPlugins>
5-
A=O2FrameworkTestWorkflowPluginA:TestWorkflowPluginA
6-
B=O2FrameworkTestWorkflowPluginB:TestWorkflowPluginB
5+
A=O2TestFrameworkWorkflowPluginAPlugin:TestFrameworkWorkflowPluginA
6+
B=O2TestFrameworkWorkflowPluginBPlugin:TestFrameworkWorkflowPluginB
77
dump=$("$1" -b --workflow-plugin $A,$B --dump-workflow < /dev/null)
88
echo "$dump" | grep -q "\"plugin\": \"$A\""
99
echo "$dump" | grep -q "\"plugin\": \"$B\""

‎Framework/TestWorkflows/CMakeLists.txt‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ o2_add_dpl_workflow(o2rootmessage-workflow
3131

3232
o2_add_dpl_workflow(diamond-workflow
3333
SOURCES src/o2DiamondWorkflow.cxx
34-
COMPONENT_NAME TestWorkflows)
34+
COMPONENT_NAME TestWorkflows
35+
WORKFLOW_PLUGIN)
3536

3637
o2_add_dpl_workflow(dummy-populator-workflow
3738
SOURCES src/o2DummyPopulatorWorkflow.cxx
@@ -114,7 +115,8 @@ o2_add_dpl_workflow(ccdb-fetch-to-timeframe
114115

115116
o2_add_dpl_workflow(simple-source
116117
SOURCES src/o2SimpleSource.cxx
117-
COMPONENT_NAME TestWorkflows)
118+
COMPONENT_NAME TestWorkflows
119+
WORKFLOW_PLUGIN)
118120

119121
o2_add_dpl_workflow(simple-fairmq-source
120122
SOURCES src/o2SimpleFairMQSource.cxx
@@ -126,11 +128,13 @@ o2_add_dpl_workflow(simple-fairmq-sink
126128

127129
o2_add_dpl_workflow(simple-sink
128130
SOURCES src/o2SimpleSink.cxx
129-
COMPONENT_NAME TestWorkflows)
131+
COMPONENT_NAME TestWorkflows
132+
WORKFLOW_PLUGIN)
130133

131134
o2_add_dpl_workflow(simple-processor
132135
SOURCES src/o2SimpleProcessor.cxx
133-
COMPONENT_NAME TestWorkflows)
136+
COMPONENT_NAME TestWorkflows
137+
WORKFLOW_PLUGIN)
134138

135139
o2_add_dpl_workflow(analysis-workflow
136140
SOURCES src/o2AnalysisWorkflow.cxx
@@ -156,3 +160,13 @@ o2_add_executable(fairmq-header-size-test
156160
add_executable(o2-test-deadlock src/o2DeadlockReproducer.cxx)
157161
target_link_libraries(o2-test-deadlock PUBLIC FairMQ::FairMQ)
158162
add_test(NAME o2-test-deadlock COMMAND o2-test-deadlock --channel-config "name=data,type=sub,method=bind,address=ipc://127.0.0.1,rateLogging=0" --id foo --transport zeromq --control static)
163+
164+
if(BUILD_TESTING)
165+
add_test(NAME test_Framework_test_WorkflowPluginDump
166+
COMMAND ${CMAKE_CURRENT_LIST_DIR}/test/test_WorkflowPluginDump.sh
167+
$<TARGET_FILE:O2exe-testworkflows-diamond-workflow> $<TARGET_FILE:O2exe-dpl-run>
168+
O2TestworkflowsDiamondWorkflowPlugin:TestworkflowsDiamondWorkflow)
169+
set_property(TEST test_Framework_test_WorkflowPluginDump PROPERTY LABELS framework)
170+
set_property(TEST test_Framework_test_WorkflowPluginDump PROPERTY ENVIRONMENT
171+
"LD_LIBRARY_PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}:$ENV{LD_LIBRARY_PATH}")
172+
endif()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/sh -e
2+
# Checks that the workflow plugin of an executable, run by o2-dpl-run,
3+
# defines the same workflow as the executable itself.
4+
# usage: test_WorkflowPluginDump.sh <executable> <o2-dpl-run> <library>:<plugin>
5+
strip() { grep -v '"executable"\|"plugin"\|"command"\|"--workflow-plugin"\|"'"$1"'"'; }
6+
exe=$("$1" -b --dump-workflow < /dev/null | strip "$3")
7+
dso=$("$2" -b --workflow-plugin "$3" --dump-workflow < /dev/null | strip "$3")
8+
test -n "$exe"
9+
test "$exe" = "$dso"
10+
test "$("$2" -b --workflow-plugin "$3" --dump-workflow < /dev/null | grep -c "\"plugin\": \"$3\"")" -gt 0

‎cmake/O2AddExecutable.cmake‎

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ include_guard()
3030
# regular executables (e.g. o2-tpc-... or o2-mch-...) or the origin target for
3131
# tests (to help locate the source file in the source directory hierarchy,
3232
# e.g. o2-test-DataFormats-...)
33+
# * WORKFLOW_PLUGIN (optional), for a DPL workflow: its sources are compiled
34+
# into the workflow plugin <Name>, in libO2<Name>Plugin, which o2-dpl-run can
35+
# load with --workflow-plugin, and the executable is only a main() linking
36+
# it. <Name> is the CamelCase of the executable name without o2-, e.g.
37+
# o2-tpc-reco-workflow gives O2TpcRecoWorkflowPlugin:TpcRecoWorkflow.
3338
#
3439
# Note that the _target_ corresponding to the executable will be named
3540
# O2exe[-exeType][component]-basename and can be retrieved with the
@@ -48,7 +53,7 @@ function(o2_add_executable baseTargetName)
4853
cmake_parse_arguments(PARSE_ARGV
4954
1
5055
A
51-
"IS_TEST;NO_INSTALL;IS_BENCHMARK"
56+
"IS_TEST;NO_INSTALL;IS_BENCHMARK;WORKFLOW_PLUGIN"
5257
"COMPONENT_NAME;TARGETVARNAME"
5358
"SOURCES;PUBLIC_LINK_LIBRARIES;JOB_POOL")
5459

@@ -92,7 +97,18 @@ function(o2_add_executable baseTargetName)
9297
endif()
9398

9499
# add the executable with its sources
95-
add_executable(${target} ${A_SOURCES})
100+
if(A_WORKFLOW_PLUGIN)
101+
o2_add_workflow_plugin(${exeName} SOURCES ${A_SOURCES}
102+
PUBLIC_LINK_LIBRARIES ${A_PUBLIC_LINK_LIBRARIES}
103+
TARGETVARNAME plugin)
104+
add_executable(${target} $<TARGET_OBJECTS:O2FrameworkWorkflowMain>)
105+
target_link_libraries(${target} PUBLIC ${plugin})
106+
if(NOT A_NO_INSTALL)
107+
install(TARGETS ${plugin} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
108+
endif()
109+
else()
110+
add_executable(${target} ${A_SOURCES})
111+
endif()
96112

97113
# set the executable output name
98114
set_property(TARGET ${target} PROPERTY OUTPUT_NAME ${exeName})
@@ -132,3 +148,32 @@ function(o2_add_executable baseTargetName)
132148
endif()
133149

134150
endfunction()
151+
152+
# o2_add_workflow_plugin(exeName SOURCES ... PUBLIC_LINK_LIBRARIES ...
153+
# TARGETVARNAME var) builds the workflow plugin of the executable exeName, see
154+
# WORKFLOW_PLUGIN above, and the main() of such executables, once.
155+
function(o2_add_workflow_plugin exeName)
156+
cmake_parse_arguments(PARSE_ARGV 1 A "" "TARGETVARNAME" "SOURCES;PUBLIC_LINK_LIBRARIES")
157+
string(REGEX REPLACE "^o2-" "" name ${exeName})
158+
string(REPLACE "-" ";" parts ${name})
159+
set(name "")
160+
foreach(part IN LISTS parts)
161+
string(SUBSTRING ${part} 0 1 first)
162+
string(SUBSTRING ${part} 1 -1 rest)
163+
string(TOUPPER ${first} first)
164+
string(APPEND name ${first}${rest})
165+
endforeach()
166+
string(MAKE_C_IDENTIFIER ${name} name)
167+
set(plugin O2${name}Plugin)
168+
add_library(${plugin} SHARED ${A_SOURCES})
169+
target_link_libraries(${plugin} PUBLIC ${A_PUBLIC_LINK_LIBRARIES})
170+
target_compile_definitions(${plugin} PRIVATE DPL_WORKFLOW_PLUGIN_NAME=${name})
171+
set(${A_TARGETVARNAME} ${plugin} PARENT_SCOPE)
172+
if(NOT TARGET O2FrameworkWorkflowMain)
173+
file(CONFIGURE OUTPUT ${CMAKE_BINARY_DIR}/O2FrameworkWorkflowMain.cxx CONTENT [[
174+
extern "C" int dpl_workflow_main(int argc, char** argv);
175+
int main(int argc, char** argv) { return dpl_workflow_main(argc, argv); }
176+
]])
177+
add_library(O2FrameworkWorkflowMain OBJECT ${CMAKE_BINARY_DIR}/O2FrameworkWorkflowMain.cxx)
178+
endif()
179+
endfunction()

‎cmake/O2AddWorkflow.cmake‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ include_guard()
1717
# file via <executable-name> --dump so that it can be easily registered and
1818
# deployed e.g. in the train infrastructure.
1919
#
20-
# For most of the options please see how o2_add_executable works.
20+
# For most of the options, e.g. WORKFLOW_PLUGIN, please see how
21+
# o2_add_executable works.
2122
#
2223
# The installed executable will be named as regular executables (see
2324
# o2_add_executable for details)
@@ -28,14 +29,17 @@ include_guard()
2829

2930
function(o2_add_dpl_workflow baseTargetName)
3031

31-
cmake_parse_arguments(PARSE_ARGV 1 A "" "COMPONENT_NAME;TARGETVARNAME"
32+
cmake_parse_arguments(PARSE_ARGV 1 A "WORKFLOW_PLUGIN" "COMPONENT_NAME;TARGETVARNAME"
3233
"SOURCES;PUBLIC_LINK_LIBRARIES;JOB_POOL")
3334

3435
if(A_UNPARSED_ARGUMENTS)
3536
message(FATAL_ERROR "Got trailing arguments ${A_UNPARSED_ARGUMENTS}")
3637
endif()
3738

38-
o2_add_executable(${baseTargetName}
39+
if(A_WORKFLOW_PLUGIN)
40+
set(workflowPlugin WORKFLOW_PLUGIN)
41+
endif()
42+
o2_add_executable(${baseTargetName} ${workflowPlugin}
3943
COMPONENT_NAME ${A_COMPONENT_NAME} TARGETVARNAME targetExeName
4044
SOURCES ${A_SOURCES}
4145
PUBLIC_LINK_LIBRARIES O2::Framework ${A_PUBLIC_LINK_LIBRARIES})

0 commit comments

Comments
 (0)