Skip to content

Commit 382457a

Browse files
committed
Upgrade use of Boost.Process
Boost 1.88 has effectively deprecated Boost.Process.v1, which was replaced by Boost.Process.v2. This breaking change was repaired by creating a `Process` class that hides the intricacies of using either Boost.Process.v1/.v2 depending on the Boost version used to configure ecFlow build: - version < 1.66, not supported - 1.66 <= version < 1.86, Process.v1 is used - 1.86 <= version, Process.v2 is used The CMake build was also repaired, since Process.v2 is no longer a header only library and required linking with Boost::process.
1 parent 509a6e4 commit 382457a

File tree

6 files changed

+182
-24
lines changed

6 files changed

+182
-24
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ project( ecflow LANGUAGES CXX VERSION 5.13.8 )
3737
# The CMake project version is used, as generated CMake variables, to filter .../ecflow/core/ecflow_version.h.in
3838
#
3939

40-
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
40+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
4141

4242
include( ecbuild_system NO_POLICY_SCOPE )
4343
ecbuild_requires_macro_version( 1.6 )

cmake/Dependencies.cmake

+7
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ if (ENABLE_PYTHON)
166166
endif()
167167
set(ECFLOW_BOOST_PYTHON_COMPONENT "python${_python_base_version}")
168168

169+
if ( Boost_MINOR_VERSION GREATER_EQUAL 86 )
170+
list(APPEND _boost_needed_libs process)
171+
endif()
172+
169173
list(APPEND _boost_needed_libs ${ECFLOW_BOOST_PYTHON_COMPONENT})
170174
endif()
171175

@@ -175,11 +179,14 @@ endif()
175179

176180
find_package( Boost ${ECFLOW_BOOST_VERSION} QUIET REQUIRED COMPONENTS ${_boost_needed_libs})
177181

182+
set(SELECTED_BOOST_VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}")
183+
178184
ecbuild_info( " * Boost_FOUND : ${Boost_FOUND}" )
179185
ecbuild_info( " * Boost_NO_BOOST_CMAKE : ${Boost_NO_BOOST_CMAKE}" )
180186
ecbuild_info( " * Boost_USE_MULTITHREADED : ${Boost_USE_MULTITHREADED}" )
181187
ecbuild_info( " * Boost_NO_SYSTEM_PATHS : ${Boost_NO_SYSTEM_PATHS}" )
182188
ecbuild_info( " * Boost_DETAILED_FAILURE_MSG : ${Boost_DETAILED_FAILURE_MSG}" )
189+
ecbuild_info( " * Boost_VERSION : ${Boost_VERSION}" )
183190
ecbuild_info( " * Boost_MAJOR_VERSION : ${Boost_MAJOR_VERSION}" )
184191
ecbuild_info( " * Boost_MINOR_VERSION : ${Boost_MINOR_VERSION}" )
185192
ecbuild_info( " * Boost_SUBMINOR_VERSION : ${Boost_SUBMINOR_VERSION}" )

libs/udp/CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,18 @@ set(TEST_TARGET s_udp)
6767

6868
list(APPEND ${TEST_TARGET}_srcs
6969
# HEADERS
70+
test/Process.hpp
7071
test/TestSupport.hpp
7172
# SOURCES
73+
test/Process.cpp
7274
test/TestUDP_main.cpp
7375
test/TestUDPServer.cpp
7476
)
7577

78+
if(${SELECTED_BOOST_VERSION} VERSION_GREATER_EQUAL 1.86.0)
79+
set(ADDITIONAL_BOOST_COMPONENTS Boost::process)
80+
endif()
81+
7682
ecbuild_add_test(
7783
TARGET
7884
${TEST_TARGET}
@@ -86,6 +92,7 @@ ecbuild_add_test(
8692
test_scaffold
8793
Boost::boost
8894
Boost::unit_test_framework
95+
${ADDITIONAL_BOOST_COMPONENTS}
8996
$<$<BOOL:${OPENSSL_FOUND}>:OpenSSL::SSL>
9097
DEPENDS
9198
ecflow_server # the server is launched to support tests

libs/udp/test/Process.cpp

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2009- ECMWF.
3+
*
4+
* This software is licensed under the terms of the Apache Licence version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
* In applying this licence, ECMWF does not waive the privileges and immunities
7+
* granted to it by virtue of its status as an intergovernmental organisation
8+
* nor does it submit to any jurisdiction.
9+
*/
10+
11+
#include "Process.hpp"
12+
13+
#include <boost/version.hpp>
14+
#if BOOST_VERSION < 106600
15+
16+
#error "Boost version >= 1.66.0 is required"
17+
18+
#elif BOOST_VERSION >= 106600 && BOOST_VERSION < 108600
19+
20+
#define BOOST_PROCESS_VERSION 1
21+
#include <boost/process.hpp>
22+
23+
namespace bp = boost::process;
24+
25+
#else // BOOST_VERSION >= 108600
26+
27+
#define BOOST_PROCESS_VERSION 2
28+
#include <boost/asio.hpp>
29+
#include <boost/process.hpp>
30+
31+
namespace ba = boost::asio;
32+
#if BOOST_VERSION < 108800
33+
namespace bp = boost::process::v2;
34+
#else
35+
namespace bp = boost::process;
36+
#endif
37+
38+
#endif
39+
40+
namespace ecf::test {
41+
42+
#if BOOST_PROCESS_VERSION == 1
43+
struct Process::Impl
44+
{
45+
Impl(std::string_view executable, std::vector<std::string_view> args) : handle_{} {
46+
// Determine the invocation command
47+
auto invoke_command = std::string(executable);
48+
for (const auto& arg : args) {
49+
invoke_command += " " + std::string(arg);
50+
}
51+
52+
// Start the process
53+
handle_ = bp::child{invoke_command};
54+
}
55+
~Impl() = default;
56+
57+
bp::child handle_;
58+
};
59+
#elif BOOST_PROCESS_VERSION == 2
60+
struct Process::Impl
61+
{
62+
static std::vector<boost::string_view> to_boost_string_view(std::vector<std::string_view> args) {
63+
std::vector<boost::string_view> result;
64+
result.reserve(args.size());
65+
for (const auto& arg : args) {
66+
result.emplace_back(arg.data(), arg.size());
67+
}
68+
return result;
69+
}
70+
71+
Impl(std::string_view executable, std::vector<std::string_view> args)
72+
: ctx_{},
73+
handle_{ctx_, executable, to_boost_string_view(args)} {
74+
// The process is started in the constructor
75+
}
76+
~Impl() = default;
77+
78+
ba::io_context ctx_;
79+
bp::process handle_;
80+
};
81+
82+
#else
83+
#error "Unsupported boost::process version"
84+
#endif
85+
86+
Process::Process() : impl_{nullptr} {
87+
}
88+
89+
Process::Process(Process&& rhs) : impl_{std::move(rhs.impl_)} {
90+
rhs.impl_ = nullptr;
91+
}
92+
93+
Process::Process(std::string_view executable, std::vector<std::string_view> args)
94+
: impl_{std::make_unique<Impl>(executable, args)} {
95+
}
96+
97+
Process& Process::operator=(Process&& rhs) {
98+
if (this != &rhs) {
99+
impl_ = std::move(rhs.impl_);
100+
rhs.impl_ = nullptr;
101+
}
102+
return *this;
103+
}
104+
105+
Process::~Process() = default;
106+
107+
void Process::terminate() {
108+
impl_ = nullptr;
109+
}
110+
111+
} // namespace ecf::test

libs/udp/test/Process.hpp

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2009- ECMWF.
3+
*
4+
* This software is licensed under the terms of the Apache Licence version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
* In applying this licence, ECMWF does not waive the privileges and immunities
7+
* granted to it by virtue of its status as an intergovernmental organisation
8+
* nor does it submit to any jurisdiction.
9+
*/
10+
11+
#ifndef ecflow_udp_test_Process_HPP
12+
#define ecflow_udp_test_Process_HPP
13+
14+
#include <memory>
15+
#include <string_view>
16+
#include <vector>
17+
18+
namespace ecf::test {
19+
20+
class Process {
21+
public:
22+
Process();
23+
Process(const Process& rhs) = delete;
24+
Process(Process&& rhs);
25+
26+
Process(std::string_view executable, std::vector<std::string_view> args);
27+
28+
Process& operator=(const Process& rhs) = delete;
29+
Process& operator=(Process&& rhs);
30+
31+
void terminate();
32+
33+
~Process();
34+
35+
private:
36+
struct Impl;
37+
std::unique_ptr<Impl> impl_;
38+
};
39+
40+
} // namespace ecf::test
41+
42+
#endif /* ecflow_udp_test_Process_HPP */

libs/udp/test/TestSupport.hpp

+14-23
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,21 @@
1414
#include <memory>
1515
#include <thread>
1616

17-
#include <boost/asio.hpp>
18-
#include <boost/process.hpp>
1917
#include <boost/test/unit_test.hpp>
2018

19+
#include "Process.hpp"
2120
#include "ecflow/attribute/NodeAttr.hpp"
2221
#include "ecflow/client/ClientInvoker.hpp"
2322
#include "ecflow/core/EcfPortLock.hpp"
2423
#include "ecflow/core/File.hpp"
24+
#include "ecflow/core/Filesystem.hpp"
2525
#include "ecflow/core/Host.hpp"
2626
#include "ecflow/core/Str.hpp"
2727
#include "ecflow/node/Defs.hpp"
2828
#include "ecflow/node/Node.hpp"
2929
#include "ecflow/test/scaffold/Naming.hpp"
3030
#include "ecflow/udp/UDPClient.hpp"
3131

32-
namespace bp = boost::process;
33-
3432
namespace ecf::test {
3533

3634
template <typename SERVER>
@@ -46,7 +44,7 @@ class BaseMockServer {
4644
BOOST_REQUIRE_MESSAGE(!host_.empty(), "determiner host name");
4745
BOOST_REQUIRE_MESSAGE(port_ > 0, "port is be larger than 0");
4846

49-
server_ = SERVER::launch(host_, port_, std::forward<Args>(args)...);
47+
server_ = std::move(SERVER::launch(host_, port_, std::forward<Args>(args)...));
5048
ECF_TEST_DBG(<< " MOCK: " << SERVER::designation << " has been started!");
5149
}
5250
BaseMockServer(const BaseMockServer&) = delete;
@@ -65,7 +63,7 @@ class BaseMockServer {
6563
hostname_t host_;
6664
uint16_t port_;
6765

68-
bp::child server_;
66+
Process server_;
6967
};
7068

7169
/**
@@ -78,7 +76,7 @@ class MockServer : public BaseMockServer<MockServer> {
7876
void load_definition(const std::string& defs) const {
7977
ClientInvoker client(ecf::Str::LOCALHOST(), port());
8078
try {
81-
BOOST_REQUIRE_MESSAGE(fs::exists(defs), "definitions file exists at: " + defs);
79+
BOOST_REQUIRE_MESSAGE(fs::exists(defs), "definitions file doesn't exist at: " + defs);
8280
auto error = client.loadDefs(defs);
8381
BOOST_REQUIRE_MESSAGE(!error, "load definitions, without error ");
8482
}
@@ -132,7 +130,7 @@ class MockServer : public BaseMockServer<MockServer> {
132130
public:
133131
static constexpr const char* designation = "ecFlow server";
134132

135-
static bp::child launch(const hostname_t& host, port_t port) {
133+
static Process launch(const hostname_t& host, port_t port) {
136134
// Just for precaution, in case a previous run didn't clean up...
137135
cleanup(host, port);
138136

@@ -141,20 +139,16 @@ class MockServer : public BaseMockServer<MockServer> {
141139
BOOST_REQUIRE_MESSAGE(!invoke_command.empty(), "The server program could not be found");
142140
BOOST_REQUIRE_MESSAGE(fs::exists(invoke_command), "Server exe does not exist at:" << invoke_command);
143141

144-
invoke_command += " --port ";
145-
invoke_command += std::to_string(port);
146-
invoke_command += " -d &";
147-
148142
ECF_TEST_DBG(<< "Launching ecflow_server @" << host << ":" << port << ", with: " << invoke_command);
149143

150-
bp::child child(invoke_command);
144+
auto server = Process(invoke_command, {"--port", std::to_string(port), "-d"});
151145

152146
ClientInvoker client(ecf::Str::LOCALHOST(), port);
153147
if (!client.wait_for_server_reply(5)) {
154148
BOOST_REQUIRE_MESSAGE(false, "could not launch ecflow server");
155149
}
156150

157-
return child;
151+
return server;
158152
}
159153

160154
static void cleanup(const hostname_t& host, port_t port) {
@@ -233,21 +227,18 @@ class MockUDPServer : public BaseMockServer<MockUDPServer> {
233227
public:
234228
static constexpr const char* designation = "ecFlow UDP";
235229

236-
static bp::child launch(const hostname_t& host, port_t port, port_t ecflow_port) {
230+
static Process launch(const hostname_t& host, port_t port, port_t ecflow_port) {
237231

238232
std::string invoke_command = ecf::File::root_build_dir() + "/bin/ecflow_udp";
239-
invoke_command += " --port ";
240-
invoke_command += std::to_string(port);
241-
invoke_command += " --ecflow_port ";
242-
invoke_command += std::to_string(ecflow_port);
243-
invoke_command += " --verbose";
244233

245234
ECF_TEST_DBG(<< " Launching ecflow_udp @" << host << ":" << port << ", with: " << invoke_command);
246235

247-
bp::child server(invoke_command);
236+
auto server =
237+
Process(invoke_command,
238+
{"--port", std::to_string(port), "--ecflow_port", std::to_string(ecflow_port), "--verbose"});
248239

249240
// Wait for server to start...
250-
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
241+
std::this_thread::sleep_for(std::chrono::milliseconds(2500));
251242

252243
return server;
253244
}
@@ -286,7 +277,7 @@ struct EnableServersFixture
286277
: ecflow_server(ecflow_server_port),
287278
ecflow_udp(ecflow_udp_port, ecflow_server_port) {
288279
// Load 'reference' suite for tests...
289-
ecflow_server.load_definition("data/reference.def");
280+
ecflow_server.load_definition(fs::absolute("data/reference.def").string());
290281
}
291282
};
292283

0 commit comments

Comments
 (0)