|
66 | 66 | #include "Framework/ServiceMetricsInfo.h" |
67 | 67 | #include "Framework/DataTakingContext.h" |
68 | 68 | #include "Framework/WorkflowDefinitionContext.h" |
| 69 | +#include "Framework/WorkflowCustomizationHelpers.h" |
69 | 70 | #include "Framework/CommonServices.h" |
70 | 71 | #include "Framework/DefaultsHelpers.h" |
71 | 72 | #include "ProcessingPoliciesHelpers.h" |
@@ -202,19 +203,35 @@ int doMain(int argc, char** argv, o2::framework::WorkflowDefinitionContext& work |
202 | 203 | int callMain(int argc, char** argv, char const* pluginSpec, DPLPluginHandle* (*embeddedPlugins)(DPLPluginHandle*)) |
203 | 204 | { |
204 | 205 | auto mainNoCatch = [pluginSpec, embeddedPlugins](int argc, char** argv) { |
205 | | - auto plugins = PluginManager::parsePluginSpecString(pluginSpec); |
| 206 | + // --workflow-plugin replaces the plugin embedded in the executable. |
| 207 | + char const* spec = pluginSpec; |
| 208 | + for (int ai = 1; ai < argc; ++ai) { |
| 209 | + if (strcmp(argv[ai], "--workflow-plugin") == 0 && ai + 1 < argc) { |
| 210 | + spec = argv[ai + 1]; |
| 211 | + } else if (strncmp(argv[ai], "--workflow-plugin=", 18) == 0) { |
| 212 | + spec = argv[ai] + 18; |
| 213 | + } |
| 214 | + } |
| 215 | + auto plugins = PluginManager::parsePluginSpecString(spec); |
206 | 216 | std::vector<WorkflowDefinition> workflows; |
207 | | - if (plugins.size() == 1 && plugins[0].library.empty()) { |
208 | | - if (auto* plugin = PluginManager::getByName<WorkflowPlugin>(embeddedPlugins(nullptr), plugins[0].name.c_str())) { |
209 | | - workflows.push_back(*plugin->create()); |
| 217 | + std::vector<std::string> names; |
| 218 | + for (auto& plugin : plugins) { |
| 219 | + if (plugin.library.empty()) { |
| 220 | + if (auto* embedded = PluginManager::getByName<WorkflowPlugin>(embeddedPlugins(nullptr), plugin.name.c_str())) { |
| 221 | + workflows.push_back(*embedded->create()); |
| 222 | + } |
| 223 | + } else { |
| 224 | + PluginManager::loadFromPlugin<WorkflowDefinition, WorkflowPlugin>({plugin}, workflows); |
| 225 | + } |
| 226 | + names.push_back(plugin.library + ":" + plugin.name); |
| 227 | + if (workflows.size() != names.size()) { |
| 228 | + throw runtime_error_f("Could not load the workflow plugin %s.", names.back().c_str()); |
210 | 229 | } |
211 | | - } else { |
212 | | - PluginManager::loadFromPlugin<WorkflowDefinition, WorkflowPlugin>(plugins, workflows); |
213 | 230 | } |
214 | | - if (workflows.size() != 1) { |
215 | | - throw runtime_error_f("Expected exactly one workflow plugin in %s, found %zu.", pluginSpec, workflows.size()); |
| 231 | + if (workflows.empty()) { |
| 232 | + throw runtime_error_f("No workflow plugin in %s.", spec); |
216 | 233 | } |
217 | | - auto workflowContext = workflows.back().defineWorkflow(argc, argv); |
| 234 | + auto workflowContext = defineWorkflow(workflows, names, argc, argv); |
218 | 235 | return doMain(argc, argv, workflowContext); |
219 | 236 | }; |
220 | 237 | static bool noCatch = getenv("O2_NO_CATCHALL_EXCEPTIONS") && strcmp(getenv("O2_NO_CATCHALL_EXCEPTIONS"), "0"); |
@@ -1085,6 +1102,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, |
1085 | 1102 | ("data-processing-timeout", bpo::value<std::string>()->default_value(defaultDataProcessingTimeout), "how many second to wait before stopping data processing and allowing data calibration") // |
1086 | 1103 | ("timeframes-rate-limit", bpo::value<std::string>()->default_value("0"), "how many timeframe can be in flight at the same moment (0 disables)") // |
1087 | 1104 | ("configuration,cfg", bpo::value<std::string>()->default_value("command-line"), "configuration backend") // |
| 1105 | + ("workflow-plugin", bpo::value<std::string>(), "workflow plugins to load") // |
1088 | 1106 | ("infologger-mode", bpo::value<std::string>()->default_value(defaultInfologgerMode), "O2_INFOLOGGER_MODE override") // |
1089 | 1107 | ("log-timestamp-us", bpo::value<bool>()->zero_tokens()->default_value(false), "enable microsecond timestamps in log messages"); |
1090 | 1108 | r.fConfig.AddToCmdLineOptions(optsDesc, true); |
@@ -2926,6 +2944,59 @@ std::unique_ptr<o2::framework::ServiceRegistry> createRegistry() |
2926 | 2944 | return std::make_unique<o2::framework::ServiceRegistry>(); |
2927 | 2945 | } |
2928 | 2946 |
|
| 2947 | +o2::framework::WorkflowDefinitionContext o2::framework::defineWorkflow(std::vector<WorkflowDefinition> const& definitions, std::vector<std::string> const& names, int argc, char** argv) |
| 2948 | +{ |
| 2949 | + auto append = [](auto& to, auto const& from) { to.insert(to.end(), from.begin(), from.end()); }; |
| 2950 | + auto toString = [](Variant const& value) { std::ostringstream oss; oss << value; return oss.str(); }; |
| 2951 | + WorkflowDefinitionContext result; |
| 2952 | + std::vector<WorkflowDefinitionContext> contexts(definitions.size()); |
| 2953 | + for (size_t di = 0; di < definitions.size(); ++di) { |
| 2954 | + auto& context = contexts[di]; |
| 2955 | + definitions[di].customize(context); |
| 2956 | + // Options already declared by a previous definition are shared, if they agree. |
| 2957 | + auto previous = result.workflowOptions.size(); |
| 2958 | + for (auto& option : context.workflowOptions) { |
| 2959 | + auto end = result.workflowOptions.begin() + previous; |
| 2960 | + auto found = std::find_if(result.workflowOptions.begin(), end, [&option](auto const& other) { return other.name == option.name; }); |
| 2961 | + if (found == end) { |
| 2962 | + result.workflowOptions.push_back(option); |
| 2963 | + } else if (found->type != option.type || toString(found->defaultValue) != toString(option.defaultValue)) { |
| 2964 | + throw runtime_error_f("Workflow option %s of %s has a different type or default than in a previous plugin.", option.name.c_str(), names[di].c_str()); |
| 2965 | + } |
| 2966 | + } |
| 2967 | + append(result.completionPolicies, context.completionPolicies); |
| 2968 | + append(result.dispatchPolicies, context.dispatchPolicies); |
| 2969 | + append(result.resourcePolicies, context.resourcePolicies); |
| 2970 | + append(result.callbacksPolicies, context.callbacksPolicies); |
| 2971 | + append(result.sendingPolicies, context.sendingPolicies); |
| 2972 | + } |
| 2973 | + append(result.workflowOptions, WorkflowCustomizationHelpers::requiredWorkflowOptions()); |
| 2974 | + append(result.completionPolicies, CompletionPolicy::createDefaultPolicies()); |
| 2975 | + append(result.dispatchPolicies, DispatchPolicy::createDefaultPolicies()); |
| 2976 | + append(result.resourcePolicies, ResourcePolicy::createDefaultPolicies()); |
| 2977 | + append(result.callbacksPolicies, CallbacksPolicy::createDefaultPolicies()); |
| 2978 | + append(result.sendingPolicies, SendingPolicy::createDefaultPolicies()); |
| 2979 | + |
| 2980 | + result.configRegistry = createRegistry(); |
| 2981 | + result.configContext = createConfigContext(result.workflowOptionsRegistry, *result.configRegistry, result.workflowOptions, result.extraOptions, argc, argv); |
| 2982 | + |
| 2983 | + std::map<std::string, size_t> definedBy; |
| 2984 | + for (size_t di = 0; di < definitions.size(); ++di) { |
| 2985 | + auto& context = contexts[di]; |
| 2986 | + definitions[di].defineWorkflow(context, *result.configContext); |
| 2987 | + for (auto& spec : context.specs) { |
| 2988 | + auto [it, inserted] = definedBy.try_emplace(spec.name, di); |
| 2989 | + if (!inserted && it->second != di) { |
| 2990 | + throw runtime_error_f("Data processor %s is defined by both %s and %s.", spec.name.c_str(), names[it->second].c_str(), names[di].c_str()); |
| 2991 | + } |
| 2992 | + } |
| 2993 | + append(result.specs, context.specs); |
| 2994 | + append(result.channelPolicies, context.channelPolicies); |
| 2995 | + } |
| 2996 | + append(result.channelPolicies, ChannelConfigurationPolicy::createDefaultPolicies(*result.configContext)); |
| 2997 | + return result; |
| 2998 | +} |
| 2999 | + |
2929 | 3000 | // This is a toy executor for the workflow spec |
2930 | 3001 | // What it needs to do is: |
2931 | 3002 | // |
@@ -3010,6 +3081,7 @@ int doMain(int argc, char** argv, o2::framework::WorkflowDefinitionContext& work |
3010 | 3081 | ("id,i", bpo::value<std::string>(), "device id for child spawning") // |
3011 | 3082 | ("channel-config", bpo::value<std::vector<std::string>>(), "channel configuration") // |
3012 | 3083 | ("control", "control plugin") // |
| 3084 | + ("workflow-plugin", bpo::value<std::string>(), "workflow plugins to load") // |
3013 | 3085 | ("log-color", "logging color scheme")("color", "logging color scheme"); |
3014 | 3086 |
|
3015 | 3087 | bpo::options_description visibleOptions; |
|
0 commit comments