From 838cf14637f0c50fd8596a3de50c0fe79ab21735 Mon Sep 17 00:00:00 2001 From: Tudor Bosman Date: Thu, 23 Jul 2015 07:30:41 -0700 Subject: [PATCH] Make gflags and boost::program_options play nice with each other Summary: GFlags is useful for global program options. boost::program_options makes it easier to write command-line utilities. They don't, unfortunately, play very nicely with each other. Add a addGFlags() function to convert all GFlags to boost::program_options options; you can then use boost::program_options::parse_command_line() to parse all arguments, GFlags or not. Also add a helper function to make parsing nested command lines easier. Reviewed By: @fugalh Differential Revision: D2215285 --- folly/Makefile.am | 2 + folly/configure.ac | 3 +- folly/experimental/ProgramOptions.cpp | 326 ++++++++++++++++++ folly/experimental/ProgramOptions.h | 87 +++++ .../experimental/test/ProgramOptionsTest.cpp | 173 ++++++++++ .../test/ProgramOptionsTestHelper.cpp | 78 +++++ folly/m4/ax_boost_program_options.m4 | 108 ++++++ 7 files changed, 776 insertions(+), 1 deletion(-) create mode 100644 folly/experimental/ProgramOptions.cpp create mode 100644 folly/experimental/ProgramOptions.h create mode 100644 folly/experimental/test/ProgramOptionsTest.cpp create mode 100644 folly/experimental/test/ProgramOptionsTestHelper.cpp create mode 100644 folly/m4/ax_boost_program_options.m4 diff --git a/folly/Makefile.am b/folly/Makefile.am index 1b29351c..1250f4b7 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -111,6 +111,7 @@ nobase_follyinclude_HEADERS = \ experimental/io/FsUtil.h \ experimental/JSONSchema.h \ experimental/LockFreeRingBuffer.h \ + experimental/ProgramOptions.h \ experimental/Select64.h \ experimental/StringKeyedCommon.h \ experimental/StringKeyedUnorderedMap.h \ @@ -371,6 +372,7 @@ libfolly_la_SOURCES = \ experimental/FunctionScheduler.cpp \ experimental/io/FsUtil.cpp \ experimental/JSONSchema.cpp \ + experimental/ProgramOptions.cpp \ experimental/Select64.cpp \ experimental/TestUtil.cpp diff --git a/folly/configure.ac b/folly/configure.ac index b24e575f..97437f63 100644 --- a/folly/configure.ac +++ b/folly/configure.ac @@ -107,8 +107,9 @@ AC_CHECK_LIB(ssl, # check for boost libs AX_BOOST_BASE([1.51.0], [], [AC_MSG_ERROR( - [Please install boost >= 1.51.0 (context, thread, regex, and system)])]) + [Please install boost >= 1.51.0 (context, thread, program_options, regex, and system)])]) AX_BOOST_CONTEXT +AX_BOOST_PROGRAM_OPTIONS AX_BOOST_THREAD AX_BOOST_REGEX AX_BOOST_SYSTEM diff --git a/folly/experimental/ProgramOptions.cpp b/folly/experimental/ProgramOptions.cpp new file mode 100644 index 00000000..c5d37152 --- /dev/null +++ b/folly/experimental/ProgramOptions.cpp @@ -0,0 +1,326 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace po = ::boost::program_options; + +namespace folly { + +namespace { + +// Information about one GFlag. Handled via shared_ptr, as, in the case +// of boolean flags, two boost::program_options options (--foo and --nofoo) +// may share the same GFlag underneath. +// +// We're slightly abusing the boost::program_options interface; the first +// time we (successfully) parse a value that matches this GFlag, we'll set +// it and remember not to set it again; this prevents, for example, the +// default value of --foo from overwriting the GFlag if --nofoo is set. +template +class GFlagInfo { + public: + explicit GFlagInfo(gflags::CommandLineFlagInfo info) + : info_(std::move(info)), + isSet_(false) { } + + void set(const T& value) { + if (isSet_) { + return; + } + + auto strValue = folly::to(value); + auto msg = gflags::SetCommandLineOption( + info_.name.c_str(), + strValue.c_str()); + if (msg.empty()) { + throw po::invalid_option_value(strValue); + } + isSet_ = true; + } + + T get() const { + std::string str; + CHECK(gflags::GetCommandLineOption(info_.name.c_str(), &str)); + return folly::to(str); + } + + const gflags::CommandLineFlagInfo& info() const { return info_; } + + private: + gflags::CommandLineFlagInfo info_; + bool isSet_; +}; + +template +class GFlagValueSemanticBase : public po::value_semantic { + public: + explicit GFlagValueSemanticBase(std::shared_ptr> info) + : info_(std::move(info)) { } + + std::string name() const override { return "arg"; } + bool is_composing() const override { return false; } + bool is_required() const override { return false; } + // We handle setting the GFlags from parse(), so notify() does nothing. + void notify(const boost::any& valueStore) const override { } + bool apply_default(boost::any& valueStore) const override { + // We're using the *current* rather than *default* value here, and + // this is intentional; GFlags-using programs assign to FLAGS_foo + // before ParseCommandLineFlags() in order to change the default value, + // and we obey that. + auto val = info_->get(); + this->transform(val); + valueStore = val; + return true; + } + + void parse(boost::any& valueStore, + const std::vector& tokens, + bool utf8) const override; + + private: + virtual T parseValue(const std::vector& tokens) const = 0; + virtual void transform(T& val) const { } + + mutable std::shared_ptr> info_; +}; + +template +void GFlagValueSemanticBase::parse(boost::any& valueStore, + const std::vector& tokens, + bool utf8) const { + T val; + try { + val = this->parseValue(tokens); + this->transform(val); + } catch (const std::exception& e) { + throw po::invalid_option_value( + tokens.empty() ? std::string() : tokens.front()); + } + this->info_->set(val); + valueStore = val; +} + +template +class GFlagValueSemantic : public GFlagValueSemanticBase { + public: + explicit GFlagValueSemantic(std::shared_ptr> info) + : GFlagValueSemanticBase(std::move(info)) { } + + unsigned min_tokens() const override { return 1; } + unsigned max_tokens() const override { return 1; } + + T parseValue(const std::vector& tokens) const override { + DCHECK(tokens.size() == 1); + return folly::to(tokens.front()); + } +}; + +class BoolGFlagValueSemantic : public GFlagValueSemanticBase { + public: + explicit BoolGFlagValueSemantic(std::shared_ptr> info) + : GFlagValueSemanticBase(std::move(info)) { } + + unsigned min_tokens() const override { return 0; } + unsigned max_tokens() const override { return 0; } + + bool parseValue(const std::vector& tokens) const override { + DCHECK(tokens.empty()); + return true; + } +}; + +class NegativeBoolGFlagValueSemantic : public BoolGFlagValueSemantic { + public: + explicit NegativeBoolGFlagValueSemantic(std::shared_ptr> info) + : BoolGFlagValueSemantic(std::move(info)) { } + + private: + void transform(bool& val) const override { + val = !val; + } +}; + +static const std::unordered_set gSkipFlags { + "flagfile", + "fromenv", + "tryfromenv", + "undefok", + "help", + "helpfull", + "helpshort", + "helpon", + "helpmatch", + "helppackage", + "helpxml", + "version", + "tab_completion_columns", + "tab_completion_word", +}; + +static const std::unordered_map gFlagOverrides { + // Allow -v in addition to --v + {"v", "v,v"}, +}; + +const std::string& getName(const std::string& name) { + auto pos = gFlagOverrides.find(name); + return pos != gFlagOverrides.end() ? pos->second : name; +} + +template +void addGFlag(gflags::CommandLineFlagInfo&& flag, + po::options_description& desc, + ProgramOptionsStyle style) { + auto gflagInfo = std::make_shared>(std::move(flag)); + auto& info = gflagInfo->info(); + auto name = getName(info.name); + + switch (style) { + case ProgramOptionsStyle::GFLAGS: + break; + case ProgramOptionsStyle::GNU: + std::replace(name.begin(), name.end(), '_', '-'); + break; + } + desc.add_options() + (name.c_str(), + new GFlagValueSemantic(gflagInfo), + info.description.c_str()); +} + +template <> +void addGFlag(gflags::CommandLineFlagInfo&& flag, + po::options_description& desc, + ProgramOptionsStyle style) { + auto gflagInfo = std::make_shared>(std::move(flag)); + auto& info = gflagInfo->info(); + auto name = getName(info.name); + std::string negationPrefix; + + switch (style) { + case ProgramOptionsStyle::GFLAGS: + negationPrefix = "no"; + break; + case ProgramOptionsStyle::GNU: + std::replace(name.begin(), name.end(), '_', '-'); + negationPrefix = "no-"; + break; + } + + desc.add_options() + (name.c_str(), + new BoolGFlagValueSemantic(gflagInfo), + info.description.c_str()) + ((negationPrefix + name).c_str(), + new NegativeBoolGFlagValueSemantic(gflagInfo), + folly::to("(no) ", info.description).c_str()); +} + +typedef void(*FlagAdder)(gflags::CommandLineFlagInfo&&, + po::options_description&, + ProgramOptionsStyle); + +const std::unordered_map gFlagAdders = { +#define X(NAME, TYPE) \ + {NAME, addGFlag}, + X("bool", bool) + X("int32", int32_t) + X("int64", int64_t) + X("uint64", uint64_t) + X("double", double) + X("string", std::string) +#undef X +}; + +} // namespace + +po::options_description getGFlags(ProgramOptionsStyle style) { + po::options_description desc("GFlags"); + + std::vector allFlags; + gflags::GetAllFlags(&allFlags); + + for (auto& f : allFlags) { + if (gSkipFlags.count(f.name)) { + continue; + } + auto pos = gFlagAdders.find(f.type); + CHECK(pos != gFlagAdders.end()) << "Invalid flag type: " << f.type; + (*pos->second)(std::move(f), desc, style); + } + + return desc; +} + +namespace { + +NestedCommandLineParseResult doParseNestedCommandLine( + po::command_line_parser&& parser, + const po::options_description& desc) { + NestedCommandLineParseResult result; + + result.options = parser.options(desc).allow_unregistered().run(); + + bool setCommand = true; + for (auto& opt : result.options.options) { + auto& tokens = opt.original_tokens; + auto tokensStart = tokens.begin(); + + if (setCommand && opt.position_key != -1) { + DCHECK(tokensStart != tokens.end()); + result.command = *(tokensStart++); + } + + if (opt.position_key != -1 || opt.unregistered) { + // If we see an unrecognized option before the first positional + // argument, assume we don't have a valid command name, because + // we don't know how to parse it otherwise. + // + // program --wtf foo bar + // + // Is "foo" an argument to "--wtf", or the command name? + setCommand = false; + result.rest.insert(result.rest.end(), tokensStart, tokens.end()); + } + } + + return result; +} + +} // namespace + +NestedCommandLineParseResult parseNestedCommandLine( + int argc, const char* const argv[], + const po::options_description& desc) { + return doParseNestedCommandLine(po::command_line_parser(argc, argv), desc); +} + +NestedCommandLineParseResult parseNestedCommandLine( + const std::vector& cmdline, + const po::options_description& desc) { + return doParseNestedCommandLine(po::command_line_parser(cmdline), desc); +} + +} // namespaces diff --git a/folly/experimental/ProgramOptions.h b/folly/experimental/ProgramOptions.h new file mode 100644 index 00000000..14abe9af --- /dev/null +++ b/folly/experimental/ProgramOptions.h @@ -0,0 +1,87 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOLLY_PROGRAMOPTIONS_H_ +#define FOLLY_PROGRAMOPTIONS_H_ + +#include +#include +#include + +namespace folly { + +enum class ProgramOptionsStyle { + GFLAGS, + GNU +}; + +// Add all GFlags to the given options_description. +// Use this *instead of* gflags::ParseCommandLineFlags(). +// +// in GFLAGS style, the flags are named as per gflags conventions: +// names_with_underscores +// boolean flags have a "no" prefix +// +// in GNU style, the flags are named as per GNU conventions: +// names-with-dashes +// boolean flags have a "no-" prefix +// +// Consider (for example) a boolean flag: +// DEFINE_bool(flying_pigs, false, "..."); +// +// In GFLAGS style, the corresponding flags are named +// flying_pigs +// noflying_pigs +// +// In GNU style, the corresponding flags are named +// flying-pigs +// no-flying-pigs +// +// You may not pass arguments to boolean flags, so you must use the +// "no" / "no-" prefix to set them to false; "--flying_pigs false" +// and "--flying_pigs=false" are not allowed, to prevent ambiguity. +boost::program_options::options_description getGFlags( + ProgramOptionsStyle style = ProgramOptionsStyle::GNU); + +// Helper when parsing nested command lines: +// +// program [--common_options...] command [--command_options...] args +// +// The result has "command" set to the first positional argument, if any, +// and "rest" set to the remaining options and arguments. Note that any +// unrecognized flags must appear after the command name. +// +// You may pass "rest" to parseNestedCommandLine again, etc. +struct NestedCommandLineParseResult { + NestedCommandLineParseResult() { } + + boost::program_options::parsed_options options {nullptr}; + + Optional command; + std::vector rest; +}; + +NestedCommandLineParseResult parseNestedCommandLine( + int argc, const char* const argv[], + const boost::program_options::options_description& desc); + +NestedCommandLineParseResult parseNestedCommandLine( + const std::vector& cmdline, + const boost::program_options::options_description& desc); + +} // namespaces + +#endif /* FOLLY_PROGRAMOPTIONS_H_ */ diff --git a/folly/experimental/test/ProgramOptionsTest.cpp b/folly/experimental/test/ProgramOptionsTest.cpp new file mode 100644 index 00000000..3a0f4ca1 --- /dev/null +++ b/folly/experimental/test/ProgramOptionsTest.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +namespace folly { namespace test { + +namespace { + +std::string getHelperPath() { + auto path = fs::executable_path(); + path.remove_filename() /= "program_options_test_helper"; + return path.native(); +} + +std::string callHelper(ProgramOptionsStyle style, + std::initializer_list args) { + static std::string helperPath = getHelperPath(); + + std::vector allArgs; + allArgs.reserve(args.size() + 1); + allArgs.push_back(helperPath); + allArgs.insert(allArgs.end(), args.begin(), args.end()); + + std::vector env; + switch (style) { + case ProgramOptionsStyle::GNU: + env.push_back("PROGRAM_OPTIONS_TEST_STYLE=GNU"); + break; + case ProgramOptionsStyle::GFLAGS: + env.push_back("PROGRAM_OPTIONS_TEST_STYLE=GFLAGS"); + break; + } + + Subprocess proc(allArgs, Subprocess::pipeStdout(), nullptr, &env); + auto p = proc.communicate(); + EXPECT_EQ(0, proc.wait().exitStatus()); + + return p.first; +} + +} // namespace + +// name value + +TEST(ProgramOptionsTest, GFlagsStyleDefaultValues) { + EXPECT_EQ( + "flag_bool_true 1\n" + "flag_bool_false 0\n" + "flag_int 42\n" + "flag_string foo\n", + callHelper(ProgramOptionsStyle::GFLAGS, {})); +} + +TEST(ProgramOptionsTest, GFlagsStyleFlagsSet) { + EXPECT_EQ( + "flag_bool_true 1\n" + "flag_bool_false 1\n" + "flag_int 43\n" + "flag_string bar\n", + callHelper(ProgramOptionsStyle::GFLAGS, { + "--flag_bool_true", + "--flag_bool_false", + "--flag_int", "43", + "--flag_string", "bar"})); +} + +TEST(ProgramOptionsTest, GFlagsStyleBoolFlagsNegation) { + EXPECT_EQ( + "flag_bool_true 0\n" + "flag_bool_false 0\n" + "flag_int 42\n" + "flag_string foo\n", + callHelper(ProgramOptionsStyle::GFLAGS, { + "--noflag_bool_true", + "--noflag_bool_false"})); +} + +TEST(ProgramOptionsTest, GNUStyleDefaultValues) { + EXPECT_EQ( + "flag-bool-true 1\n" + "flag-bool-false 0\n" + "flag-int 42\n" + "flag-string foo\n", + callHelper(ProgramOptionsStyle::GNU, {})); +} + +TEST(ProgramOptionsTest, GNUStyleFlagsSet) { + EXPECT_EQ( + "flag-bool-true 1\n" + "flag-bool-false 1\n" + "flag-int 43\n" + "flag-string bar\n", + callHelper(ProgramOptionsStyle::GNU, { + "--flag-bool-true", + "--flag-bool-false", + "--flag-int", "43", + "--flag-string", "bar"})); +} + +TEST(ProgramOptionsTest, GNUStyleBoolFlagsNegation) { + EXPECT_EQ( + "flag-bool-true 0\n" + "flag-bool-false 0\n" + "flag-int 42\n" + "flag-string foo\n", + callHelper(ProgramOptionsStyle::GNU, { + "--no-flag-bool-true", + "--no-flag-bool-false"})); +} + +TEST(ProgramOptionsTest, GNUStyleSubCommand) { + EXPECT_EQ( + "flag-bool-true 1\n" + "flag-bool-false 1\n" + "flag-int 43\n" + "flag-string foo\n" + "command hello\n" + "arg --wtf\n" + "arg 100\n" + "arg -x\n" + "arg -xy\n", + callHelper(ProgramOptionsStyle::GNU, { + "--flag-bool-false", + "hello", + "--wtf", + "--flag-int", "43", + "100", + "-x", + "-xy"})); +} + +TEST(ProgramOptionsTest, GNUStyleSubCommandUnrecognizedOptionFirst) { + EXPECT_EQ( + "flag-bool-true 1\n" + "flag-bool-false 1\n" + "flag-int 43\n" + "flag-string foo\n" + "arg --wtf\n" + "arg hello\n" + "arg 100\n" + "arg -x\n" + "arg -xy\n", + callHelper(ProgramOptionsStyle::GNU, { + "--flag-bool-false", + "--wtf", + "hello", + "--flag-int", "43", + "100", + "-x", + "-xy"})); +} + +}} // namespaces diff --git a/folly/experimental/test/ProgramOptionsTestHelper.cpp b/folly/experimental/test/ProgramOptionsTestHelper.cpp new file mode 100644 index 00000000..c683e9a5 --- /dev/null +++ b/folly/experimental/test/ProgramOptionsTestHelper.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +DEFINE_bool(flag_bool_true, true, "Bool with true default value"); +DEFINE_bool(flag_bool_false, false, "Bool with false default value"); +DEFINE_int32(flag_int, 42, "Integer flag"); +DEFINE_string(flag_string, "foo", "String flag"); + +namespace po = ::boost::program_options; + +namespace { +template +void print(const po::variables_map& vm, const std::string& name) { + auto& v = vm[name]; + printf("%s %s\n", + name.c_str(), + folly::to(v.as()).c_str()); +} +} // namespace + +int main(int argc, char *argv[]) { + po::options_description desc; + auto styleEnv = getenv("PROGRAM_OPTIONS_TEST_STYLE"); + + CHECK(styleEnv) << "PROGRAM_OPTIONS_TEST_STYLE is required"; + bool gnuStyle = !strcmp(styleEnv, "GNU"); + CHECK(gnuStyle || !strcmp(styleEnv, "GFLAGS")) + << "Invalid value for PROGRAM_OPTIONS_TEST_STYLE"; + + desc.add(getGFlags( + gnuStyle ? folly::ProgramOptionsStyle::GNU : + folly::ProgramOptionsStyle::GFLAGS)); + desc.add_options() + ("help,h", "help"); + + po::variables_map vm; + auto result = folly::parseNestedCommandLine(argc, argv, desc); + po::store(result.options, vm); + po::notify(vm); + + if (vm.count("help")) { + std::cout << desc; + return 1; + } + + print(vm, gnuStyle ? "flag-bool-true" : "flag_bool_true"); + print(vm, gnuStyle ? "flag-bool-false" : "flag_bool_false"); + print(vm, gnuStyle ? "flag-int" : "flag_int"); + print(vm, gnuStyle ? "flag-string" : "flag_string"); + + if (result.command) { + printf("command %s\n", result.command->c_str()); + } + + for (auto& arg : result.rest) { + printf("arg %s\n", arg.c_str()); + } + + return 0; +} diff --git a/folly/m4/ax_boost_program_options.m4 b/folly/m4/ax_boost_program_options.m4 new file mode 100644 index 00000000..30913f66 --- /dev/null +++ b/folly/m4/ax_boost_program_options.m4 @@ -0,0 +1,108 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_BOOST_PROGRAM_OPTIONS +# +# DESCRIPTION +# +# Test for program options library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) +# +# And sets: +# +# HAVE_BOOST_PROGRAM_OPTIONS +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 24 + +AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], +[ + AC_ARG_WITH([boost-program-options], + AS_HELP_STRING([--with-boost-program-options@<:@=special-lib@:>@], + [use the program options library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-program-options=boost_program_options-gcc-mt-1_33_1 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_program_options_lib="" + else + want_boost="yes" + ax_boost_user_program_options_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + export want_boost + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + AC_CACHE_CHECK([whether the Boost::Program_Options library is available], + ax_cv_boost_program_options, + [AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include + ]], + [[boost::program_options::error err("Error message"); + return 0;]])], + ax_cv_boost_program_options=yes, ax_cv_boost_program_options=no) + AC_LANG_POP([C++]) + ]) + if test "$ax_cv_boost_program_options" = yes; then + AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_program_options_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + if test "x$link_program_options" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + fi + else + for ax_lib in $ax_boost_user_program_options_lib boost_program_options-$ax_boost_user_program_options_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_program_options" != "xyes"; then + AC_MSG_ERROR([Could not link against [$ax_lib] !]) + fi + fi + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) -- 2.34.1