Commit 6095da5a authored by Otto Kekäläinen's avatar Otto Kekäläinen

New upstream version 25.3.25

parent 98cd892b
......@@ -141,7 +141,7 @@ deterministic_tests = int(ARGUMENTS.get('deterministic_tests', 0))
strict_build_flags = int(ARGUMENTS.get('strict_build_flags', 0))
GALERA_VER = ARGUMENTS.get('version', '3.24')
GALERA_VER = ARGUMENTS.get('version', '3.25')
GALERA_REV = ARGUMENTS.get('revno', 'XXXX')
# Attempt to read from file if not given
......@@ -279,8 +279,13 @@ def CheckSystemASIOVersion(context):
system_asio_test_source_file = """
#include <asio.hpp>
#define XSTR(x) STR(x)
#define STR(x) #x
#pragma message "Asio version:" XSTR(ASIO_VERSION)
#if ASIO_VERSION < 101001
#error "Included asio version is too old"
#error Included asio version is too old
#elif ASIO_VERSION >= 101100
#error Included asio version is too new
#endif
int main()
......@@ -289,7 +294,7 @@ int main()
}
"""
context.Message('Checking ASIO version (> 1.10.1) ... ')
context.Message('Checking ASIO version (>= 1.10.1 and < 1.11.0) ... ')
result = context.TryLink(system_asio_test_source_file, '.cpp')
context.Result(result)
return result
......@@ -351,6 +356,16 @@ int main() { SSL_CTX* ctx=NULL; return !SSL_CTX_set_ecdh_auto(ctx, 1); }
context.Result(result)
return result
def CheckSetTmpEcdh(context):
test_source = """
#include <openssl/ssl.h>
int main() { SSL_CTX* ctx=NULL; EC_KEY* ecdh=NULL; return !SSL_CTX_set_tmp_ecdh(ctx,ecdh); }
"""
context.Message('Checking for SSL_CTX_set_tmp_ecdh_() ... ')
result = context.TryLink(test_source, '.cpp')
context.Result(result)
return result
#
# Construct configuration context
#
......@@ -361,7 +376,8 @@ conf = Configure(env, custom_tests = {
'CheckTr1SharedPtr': CheckTr1SharedPtr,
'CheckTr1UnorderedMap': CheckTr1UnorderedMap,
'CheckWeffcpp': CheckWeffcpp,
'CheckSetEcdhAuto': CheckSetEcdhAuto
'CheckSetEcdhAuto': CheckSetEcdhAuto,
'CheckSetTmpEcdh': CheckSetTmpEcdh
})
conf.env.Append(CPPPATH = [ '#/wsrep/src' ])
......@@ -562,6 +578,8 @@ if not conf.CheckLib('crypto'):
# advanced SSL features
if conf.CheckSetEcdhAuto():
conf.env.Append(CPPFLAGS = ' -DOPENSSL_HAS_SET_ECDH_AUTO')
elif conf.CheckSetTmpEcdh():
conf.env.Append(CPPFLAGS = ' -DOPENSSL_HAS_SET_TMP_ECDH')
# these will be used only with our software
if strict_build_flags == 1:
......
......@@ -19,11 +19,12 @@ static const bool cert_debug_on(false);
else log_info << "cert debug: "
#define CERT_PARAM_LOG_CONFLICTS galera::Certification::PARAM_LOG_CONFLICTS
#define CERT_PARAM_OPTIMISTIC_PA galera::Certification::PARAM_OPTIMISTIC_PA
static std::string const CERT_PARAM_PREFIX("cert.");
std::string const galera::Certification::PARAM_LOG_CONFLICTS(CERT_PARAM_PREFIX +
"log_conflicts");
std::string const CERT_PARAM_LOG_CONFLICTS(CERT_PARAM_PREFIX + "log_conflicts");
std::string const CERT_PARAM_OPTIMISTIC_PA(CERT_PARAM_PREFIX + "optimistic_pa");
static std::string const CERT_PARAM_MAX_LENGTH (CERT_PARAM_PREFIX +
"max_length");
......@@ -31,6 +32,7 @@ static std::string const CERT_PARAM_LENGTH_CHECK (CERT_PARAM_PREFIX +
"length_check");
static std::string const CERT_PARAM_LOG_CONFLICTS_DEFAULT("no");
static std::string const CERT_PARAM_OPTIMISTIC_PA_DEFAULT("yes");
/*** It is EXTREMELY important that these constants are the same on all nodes.
*** Don't change them ever!!! ***/
......@@ -41,6 +43,7 @@ void
galera::Certification::register_params(gu::Config& cnf)
{
cnf.add(CERT_PARAM_LOG_CONFLICTS, CERT_PARAM_LOG_CONFLICTS_DEFAULT);
cnf.add(CERT_PARAM_OPTIMISTIC_PA, CERT_PARAM_OPTIMISTIC_PA_DEFAULT);
/* The defaults below are deliberately not reflected in conf: people
* should not know about these dangerous setting unless they read RTFM. */
cnf.add(CERT_PARAM_MAX_LENGTH);
......@@ -548,7 +551,7 @@ certify_and_depend_v3to4(const galera::KeyEntryNG* const found,
galera::TrxHandle* const trx,
bool const log_conflict)
{
wsrep_seqno_t depends_seqno(-1);
wsrep_seqno_t depends_seqno(trx->depends_seqno());
wsrep_key_type_t const key_type(key.wsrep_type(trx->version()));
/*
......@@ -579,7 +582,8 @@ certify_and_depend_v3to4(const galera::KeyEntryNG* const found,
}
else
{
trx->set_depends_seqno(std::max(trx->depends_seqno(), depends_seqno));
if (depends_seqno > trx->depends_seqno())
trx->set_depends_seqno(depends_seqno);
return false;
}
}
......@@ -587,9 +591,10 @@ certify_and_depend_v3to4(const galera::KeyEntryNG* const found,
/* returns true on collision, false otherwise */
static bool
certify_v3to4(galera::Certification::CertIndexNG& cert_index_ng,
const galera::KeySet::KeyPart& key,
galera::TrxHandle* trx,
bool const store_keys, bool const log_conflicts)
const galera::KeySet::KeyPart& key,
galera::TrxHandle* trx,
bool const store_keys,
bool const log_conflicts)
{
galera::KeyEntryNG ke(key);
galera::Certification::CertIndexNG::iterator ci(cert_index_ng.find(&ke));
......@@ -799,6 +804,10 @@ galera::Certification::do_test(TrxHandle* trx, bool store_keys)
{
trx->set_depends_seqno(
trx_map_.begin()->second->global_seqno() - 1);
if (optimistic_pa_ == false &&
trx->last_seen_seqno() > trx->depends_seqno())
trx->set_depends_seqno(trx->last_seen_seqno());
}
switch (version_)
......@@ -875,6 +884,7 @@ galera::Certification::do_test_preordered(TrxHandle* trx)
galera::Certification::Certification(gu::Config& conf, ServiceThd& thd)
:
version_ (-1),
conf_ (conf),
trx_map_ (),
cert_index_ (),
cert_index_ng_ (),
......@@ -899,7 +909,8 @@ galera::Certification::Certification(gu::Config& conf, ServiceThd& thd)
max_length_ (max_length(conf)),
max_length_check_ (length_check(conf)),
log_conflicts_ (conf.get<bool>(CERT_PARAM_LOG_CONFLICTS))
log_conflicts_ (conf.get<bool>(CERT_PARAM_LOG_CONFLICTS)),
optimistic_pa_ (conf.get<bool>(CERT_PARAM_OPTIMISTIC_PA))
{}
......@@ -1156,23 +1167,46 @@ galera::TrxHandle* galera::Certification::get_trx(wsrep_seqno_t seqno)
}
void
galera::Certification::set_log_conflicts(const std::string& str)
set_boolean_parameter(bool& param,
const std::string& value,
const std::string& param_name,
const std::string& change_msg)
{
try
{
bool const old(log_conflicts_);
log_conflicts_ = gu::Config::from_config<bool>(str);
if (old != log_conflicts_)
bool const old(param);
param = gu::Config::from_config<bool>(value);
if (old != param)
{
log_info << (log_conflicts_ ? "Enabled" : "Disabled")
<< " logging of certification conflicts.";
log_info << (param ? "Enabled " : "Disabled ") << change_msg;
}
}
catch (gu::NotFound& e)
{
gu_throw_error(EINVAL) << "Bad value '" << str
gu_throw_error(EINVAL) << "Bad value '" << value
<< "' for boolean parameter '"
<< CERT_PARAM_LOG_CONFLICTS << '\'';
<< param_name << '\'';
}
}
void
galera::Certification::param_set(const std::string& key,
const std::string& value)
{
if (key == Certification::PARAM_LOG_CONFLICTS)
{
set_boolean_parameter(log_conflicts_, value, CERT_PARAM_LOG_CONFLICTS,
"logging of certification conflicts.");
}
else if (key == Certification::PARAM_OPTIMISTIC_PA)
{
set_boolean_parameter(optimistic_pa_, value, CERT_PARAM_OPTIMISTIC_PA,
"\"optimistic\" parallel applying.");
}
else
{
throw gu::NotFound();
}
conf_.set(key, value);
}
......@@ -24,6 +24,7 @@ namespace galera
public:
static std::string const PARAM_LOG_CONFLICTS;
static std::string const PARAM_OPTIMISTIC_PA;
static void register_params(gu::Config&);
......@@ -105,7 +106,7 @@ namespace galera
index_size_ = 0;
}
void set_log_conflicts(const std::string& str);
void param_set(const std::string& key, const std::string& value);
private:
......@@ -178,6 +179,7 @@ namespace galera
};
int version_;
gu::Config& conf_;
TrxMap trx_map_;
CertIndex cert_index_;
CertIndexNG cert_index_ng_;
......@@ -211,6 +213,7 @@ namespace galera
unsigned int const max_length_check_; /* Mask how often to check */
bool log_conflicts_;
bool optimistic_pa_;
};
}
......
......@@ -1881,9 +1881,12 @@ galera::ReplicatorSMM::update_state_uuid (const wsrep_uuid_t& uuid)
*(const_cast<wsrep_uuid_t*>(&state_uuid_)) = uuid;
std::ostringstream os; os << state_uuid_;
strncpy(const_cast<char*>(state_uuid_str_), os.str().c_str(),
sizeof(state_uuid_str_));
// Copy only non-nil terminated part of the source string
// and terminate the string explicitly to silence a warning
// generated by Wstringop-truncation
char* str(const_cast<char*>(state_uuid_str_));
strncpy(str, os.str().c_str(), sizeof(state_uuid_str_) - 1);
str[sizeof(state_uuid_str_) - 1] = '\0';
}
st_.set(uuid, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_);
......
......@@ -208,19 +208,21 @@ galera::ReplicatorSMM::param_set (const std::string& key,
if (defaults.map_.find(key) != defaults.map_.end() ||
key == Param::base_host) // is my key?
{
found = true;
set_param (key, value);
config_.set (key, value);
found = true;
config_.set(key, value);
}
if (key == Certification::PARAM_LOG_CONFLICTS)
{
cert_.set_log_conflicts(value);
return;
}
// this key might be for another module
else if (0 != key.find(common_prefix))
{
try
{
cert_.param_set (key, value);
found = true;
}
catch (gu::NotFound&) {}
try
{
gcs_.param_set (key, value);
......
......@@ -144,12 +144,28 @@ void gu::ssl_prepare_context(const gu::Config& conf, asio::ssl::context& ctx,
try
{
#ifdef OPENSSL_HAS_SET_ECDH_AUTO
// In some older OpenSSL versions ECDH engines must be enabled
// explicitly. Here we use SSL_CTX_set_ecdh_auto() or
// SSL_CTX_set_tmp_ecdh() if present.
#if defined(OPENSSL_HAS_SET_ECDH_AUTO)
if (!SSL_CTX_set_ecdh_auto(ctx.impl(), 1))
{
throw_last_SSL_error("SSL_CTX_set_ecdh_auto() failed");
}
#endif /* OPENSSL_HAS_SET_ECDH_AUTO */
#elif defined(OPENSSL_HAS_SET_TMP_ECDH)
{
EC_KEY* const ecdh(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
if (ecdh == NULL)
{
throw_last_SSL_error("EC_KEY_new_by_curve_name() failed");
}
if (!SSL_CTX_set_tmp_ecdh(ctx.impl(),ecdh))
{
throw_last_SSL_error("SSL_CTX_set_tmp_ecdh() failed");
}
EC_KEY_free(ecdh);
}
#endif /* OPENSSL_HAS_SET_ECDH_AUTO | OPENSSL_HAS_SET_TMP_ECDH */
param = conf::ssl_key;
ctx.use_private_key_file(conf.get(param), asio::ssl::context::pem);
param = conf::ssl_cert;
......
......@@ -9,7 +9,6 @@
#define GU_DIGEST_HPP
#include "gu_hash.h"
#include "gu_vec16.h"
#include "gu_byteswap.hpp"
#include "gu_serializable.hpp"
#include "gu_macros.hpp"
......
......@@ -69,8 +69,8 @@ gu_str2dbl (const char* str, double* dbl)
const char*
gu_str2bool (const char* str, bool* b)
{
size_t len = strlen(str);
int res = -1; /* no conversion */
size_t const len = strlen(str);
int res = -1; /* no conversion */
switch (len)
{
......@@ -82,17 +82,20 @@ gu_str2bool (const char* str, bool* b)
}
break;
case 2:
if (!strcasecmp(str, "on")) res = 1;
if (!strcasecmp(str, "no")) res = 0;
if (!strcasecmp(str, "on")) res = 1;
else if (!strcasecmp(str, "no")) res = 0;
break;
case 3:
if (!strcasecmp(str, "off")) res = 0;
if (!strcasecmp(str, "yes")) res = 1;
if (!strcasecmp(str, "off")) res = 0;
else if (!strcasecmp(str, "yes")) res = 1;
else if (!strcasecmp(str, "yep")) res = 1;
break;
case 4:
if (!strcasecmp(str, "true")) res = 1;
if (!strcasecmp(str, "sure")) res = 1;
if (!strcasecmp(str, "nope")) res = 0;
if (!strcasecmp(str, "true")) res = 1;
else if (!strcasecmp(str, "sure")) res = 1;
else if (!strcasecmp(str, "none")) res = 0;
else if (!strcasecmp(str, "nope")) res = 0;
else if (!strcasecmp(str, "yeah")) res = 1;
break;
case 5:
if (!strcasecmp(str, "false")) res = 0;
......
// Copyright (C) 2013 Codership Oy <info@codership.com>
/**
* @file 16-byte "vector" type and operations - mostly for checksums/hashes
*
* $Id$
*/
#ifndef _gu_vec16_h_
#define _gu_vec16_h_
#include "gu_macros.h"
#include "gu_byteswap.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h> /* bool */
#ifdef __cplusplus
extern "C" {
#endif
/* this type will generate SIMD instructions where possible: good for XORing */
typedef unsigned long gu_vec16__ __attribute__ ((vector_size (16)));
typedef union gu_vec16
{
gu_vec16__ vec_;
uint64_t int_[2]; /* for equality we better use scalar type
* since we need scalar return */
} gu_vec16_t;
static GU_FORCE_INLINE gu_vec16_t
gu_vec16_from_byte (unsigned char b)
{
gu_vec16_t ret;
memset (&ret, b, sizeof(ret));
return ret;
}
static GU_FORCE_INLINE gu_vec16_t
gu_vec16_from_ptr (const void* ptr)
{
gu_vec16_t ret;
memcpy (&ret, ptr, sizeof(ret));
return ret;
}
static GU_FORCE_INLINE gu_vec16_t
gu_vec16_xor (gu_vec16_t l, gu_vec16_t r)
{
gu_vec16_t ret;
ret.vec_ = (l.vec_ ^ r.vec_);
return ret;
}
static GU_FORCE_INLINE bool
gu_vec16_neq (gu_vec16_t l, gu_vec16_t r)
{
return (l.int_[0] != r.int_[0] || l.int_[1] != r.int_[1]);
}
static GU_FORCE_INLINE bool
gu_vec16_eq (gu_vec16_t l, gu_vec16_t r)
{
return !(gu_vec16_neq (l, r));
}
static GU_FORCE_INLINE gu_vec16_t
gu_vec16_bswap (gu_vec16_t x)
{
gu_vec16_t ret;
ret.int_[0] = gu_bswap64 (x.int_[1]);
ret.int_[1] = gu_bswap64 (x.int_[0]);
return ret;
}
#ifdef __cplusplus
}
static GU_FORCE_INLINE gu_vec16_t
operator^ (const gu_vec16_t& l, const gu_vec16_t& r)
{
return (gu_vec16_xor (l, r));
}
static GU_FORCE_INLINE bool
operator== (const gu_vec16_t& l, const gu_vec16_t& r)
{
return (gu_vec16_eq (l, r));
}
#endif
#endif /* _gu_vec16_h_ */
......@@ -16,7 +16,6 @@ gu_tests = env.Program(target = 'gu_tests',
source = Split('''
gu_tests.c
gu_mem_test.c
gu_vec_test.c
gu_bswap_test.c
gu_fnv_test.c
gu_mmh3_test.c
......@@ -73,7 +72,7 @@ avalanche = env.Program(target = 'avalanche',
avalanche.c
'''))
copy_vs_assignment = env.Program(target = 'copy_vs_assignment',
source = Split('''
copy_vs_assignment.cpp
'''))
# copy_vs_assignment = env.Program(target = 'copy_vs_assignment',
# source = Split('''
# copy_vs_assignment.cpp
# '''))
......@@ -10,7 +10,6 @@
#include "../src/gu_conf.h"
#include "gu_mem_test.h"
#include "gu_vec_test.h"
#include "gu_bswap_test.h"
#include "gu_fnv_test.h"
#include "gu_mmh3_test.h"
......@@ -30,7 +29,6 @@ typedef Suite *(*suite_creator_t)(void);
static suite_creator_t suites[] =
{
gu_mem_suite,
gu_vec_suite,
gu_bswap_suite,
gu_fnv_suite,
gu_mmh3_suite,
......
/* Copyright (C) 2013 Codership Oy <info@codership.com>
*
* $Id$
*/
#include "gu_vec_test.h"
#include "../src/gu_vec16.h"
START_TEST (vec16_test)
{
gu_vec16_t v1 = gu_vec16_from_byte (0);
gu_vec16_t v2 = gu_vec16_from_byte (0);
gu_vec16_t v3 = gu_vec16_from_byte (7);
fail_if (!gu_vec16_eq(v1, v2));
fail_if (gu_vec16_eq(v1, v3));
unsigned char a1[16], a2[16], a3[16];
fail_if (sizeof(v1) != sizeof(a1));
unsigned int i;
for (i = 0; i < sizeof(a1); i++)
{
a1[i] = i;
a2[i] = i * i;
a3[i] = a1[i] ^ a2[i];
}
v1 = gu_vec16_from_ptr (a1);
v2 = gu_vec16_from_ptr (a2);
fail_if (gu_vec16_eq(v1, v2));
v3 = gu_vec16_xor (v1, v2);
fail_if (memcmp (&v3, a3, sizeof(a3)));
}
END_TEST
Suite*
gu_vec_suite(void)
{
TCase* t = tcase_create ("vec16");
tcase_add_test (t, vec16_test);
Suite* s = suite_create ("Vector math");
suite_add_tcase (s, t);
return s;
}
/* Copyright (C) 2013 Codership Oy <info@codership.com>
*
* $Id$
*/
#ifndef __gu_vec_test__
#define __gu_vec_test__
#include <check.h>
extern Suite *gu_vec_suite(void);
#endif /* __gu_vec_test__ */
......@@ -14,7 +14,7 @@ struct gcs_act
const void* buf;
ssize_t buf_len;
gcs_act_type_t type;
gcs_act() { }
gcs_act() : buf(), buf_len(), type() { }
gcs_act(const void* b, ssize_t bl, gcs_act_type_t t)
:
buf(b),
......@@ -35,7 +35,7 @@ struct gcs_act_rcvd
const struct gu_buf* local; // local buffer vector if any
gcs_seqno_t id; // global total order seqno
int sender_idx;
gcs_act_rcvd() { }
gcs_act_rcvd() : act(), local(), id(), sender_idx() { }
gcs_act_rcvd(const gcs_act& a, const struct gu_buf* loc,
gcs_seqno_t i, int si)
:
......
......@@ -474,8 +474,8 @@ gcs_group_handle_comp_msg (gcs_group_t* group, const gcs_comp_msg_t* comp)
/* initialize node ID to the one given by the backend - this way
* we'll be recognized as coming from prev. conf. in node array
* remap below */
strncpy ((char*)group->nodes[0].id, new_nodes[0].id,
sizeof (new_nodes[0].id) - 1);
strncpy (group->nodes[0].id, new_nodes[0].id,
sizeof (group->nodes[0].id));
group->nodes[0].segment = new_nodes[0].segment;
}
}
......
......@@ -415,7 +415,7 @@ state_report_uuids (char* buf, size_t buf_len,
* @retval (void*)-1 in case of fatal error */
static const gcs_state_msg_t*
state_quorum_inherit (const gcs_state_msg_t* states[],
long states_num,
size_t states_num,
gcs_state_quorum_t* quorum)
{
/* They all must have the same group_uuid or otherwise quorum is impossible.
......@@ -425,7 +425,7 @@ state_quorum_inherit (const gcs_state_msg_t* states[],
* Of those with the status >= GCS_STATE_JOINED we choose the most
* representative: with the highest act_seqno and prim_seqno.
*/
long i, j;
size_t i, j;
const gcs_state_msg_t* rep = NULL;
// find at least one JOINED/DONOR (donor was once joined)
......@@ -786,13 +786,13 @@ state_quorum_bootstrap (const gcs_state_msg_t* const states[],
/* Get quorum decision from state messages */
long
gcs_state_msg_get_quorum (const gcs_state_msg_t* states[],
long states_num,
size_t states_num,
gcs_state_quorum_t* quorum)
{
assert (states_num > 0);
assert (NULL != states);
long i;
size_t i;
const gcs_state_msg_t* rep = NULL;
*quorum = GCS_QUORUM_NON_PRIMARY; // pessimistic assumption
......
......@@ -165,7 +165,7 @@ gcs_state_msg_flags (const gcs_state_msg_t* state);
* quorum parameter */
extern long
gcs_state_msg_get_quorum (const gcs_state_msg_t* states[],
long states_num,
size_t states_num,
gcs_state_quorum_t* quorum);
/* Print state message contents to buffer */
......
......@@ -153,8 +153,8 @@ START_TEST (gcs_group_configuration)
ret = gcs_group_handle_act_msg (&group, &frg, &(msg), &r_act, true);
// 1. Try fragment that is not the first
memset (&r_act, 0, sizeof(r_act));
// ret = gcs_group_handle_act_msg (&group, &frg, &msg3, &r_act);
r_act = gcs_act_rcvd();
// ret = gcs_group_handle_act_msg (&group, &frg, &msg3, &r_act);
TRY_MESSAGE(msg3);
fail_if (ret != -EPROTO);
fail_if (act->buf != NULL);
......@@ -205,7 +205,7 @@ START_TEST (gcs_group_configuration)
fail_if (r_act.id != seqno, "Expected seqno %llu, found %llu", seqno, r_act.id);
seqno++;
// cleanup
memset (&r_act, 0, sizeof(r_act));
r_act = gcs_act_rcvd();
// 10. New component message
gcs_comp_msg_delete (comp);
......@@ -243,7 +243,7 @@ START_TEST (gcs_group_configuration)
seqno++;
// cleanup
free ((void*)act->buf);
memset (&r_act, 0, sizeof(r_act));
r_act = gcs_act_rcvd();
// 12. Try foreign action with a new node joined in the middle.
gcs_comp_msg_delete (comp);
......@@ -288,7 +288,7 @@ START_TEST (gcs_group_configuration)
seqno++;
// cleanup
free ((void*)act->buf);
memset (&r_act, 0, sizeof(r_act));
r_act = gcs_act_rcvd();
// 13. Try to send an action with one node disappearing in the middle
// and order of nodes changed
......@@ -367,7 +367,7 @@ return;
seqno++;
// cleanup
free ((void*)act->buf);
memset (&r_act, 0, sizeof(r_act));
r_act = gcs_act_rcvd();
// Leave group
comp = gcs_comp_msg_new (FALSE, false, -1, 0, 0);
......
......@@ -477,6 +477,8 @@ then
cmake \
-DCMAKE_C_COMPILER=$(basename $CC) \
-DCMAKE_CXX_COMPILER=$(basename $CXX) \
-DCMAKE_C_FLAGS=$(CFLAGS) \
-DCMAKE_CXX_FLAGS=$(CXXFLAGS) \
-DBUILD_CONFIG=mysql_release \
"${CMAKE_LAYOUT_OPTIONS[@]}" \
$BUILD_OPT \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment