From 83e95631cf42e3e83746ff593e196d96a4ee69b6 Mon Sep 17 00:00:00 2001 From: ansurya <50609411+ansurya@users.noreply.github.com> Date: Fri, 9 Aug 2019 17:22:10 +0530 Subject: [PATCH] [dtests[ Fix build issues with hipEnvVar*.cpp on windows (#1299) * replace getopt with clara based command line options * Removed header getopt.h [ROCm/hip commit: cbe9f8dc6bc2c582caca33ee91b8854725b41353] --- projects/hip/tests/src/clara/clara.hpp | 1165 ++++++++++++++++++++ projects/hip/tests/src/hipEnvVar.cpp | 86 +- projects/hip/tests/src/hipEnvVarDriver.cpp | 8 + 3 files changed, 1214 insertions(+), 45 deletions(-) create mode 100644 projects/hip/tests/src/clara/clara.hpp diff --git a/projects/hip/tests/src/clara/clara.hpp b/projects/hip/tests/src/clara/clara.hpp new file mode 100644 index 0000000000..10b70da644 --- /dev/null +++ b/projects/hip/tests/src/clara/clara.hpp @@ -0,0 +1,1165 @@ +// v1.0-develop.2 +// See https://github.com/philsquared/Clara + +#ifndef CLARA_HPP_INCLUDED +#define CLARA_HPP_INCLUDED + +#ifndef CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// This work is licensed under the BSD 2-Clause license. +// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// +// This project is hosted at https://github.com/philsquared/textflowcpp + +#ifndef CLARA_TEXTFLOW_HPP_INCLUDED +#define CLARA_TEXTFLOW_HPP_INCLUDED + +#include +#include +#include +#include + +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + + +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector m_strings; + size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + while (m_end < line().size() && line()[m_end] != '\n') ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = + m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const& plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + explicit iterator(Column const& column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || + m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) m_stringIndex++; // Empty string + } + + auto operator*() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + if (m_pos + m_column.m_width < m_end) + return addIndentAndSuffix(line().substr(m_pos, m_len)); + else + return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); + } + + auto operator++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) calcLength(); + return *this; + } + auto operator++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator==(iterator const& other) const -> bool { + return m_pos == other.m_pos && m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator!=(iterator const& other) const -> bool { return !operator==(other); } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return {*this, m_strings.size()}; } + + inline friend std::ostream& operator<<(std::ostream& os, Column const& col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator+(Column const& other) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + public: + explicit Spacer(size_t spaceWidth) : Column("") { width(spaceWidth); } +}; + +class Columns { + std::vector m_columns; + + public: + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator(Columns const& columns, EndTag) + : m_columns(columns.m_columns), m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) m_iterators.push_back(col.end()); + } + + public: + explicit iterator(Columns const& columns) + : m_columns(columns.m_columns), m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) m_iterators.push_back(col.begin()); + } + + auto operator==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator!=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator*() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) ++m_iterators[i]; + } + return *this; + } + auto operator++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return {*this, iterator::EndTag()}; } + + auto operator+=(Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator+(Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator<<(std::ostream& os, Columns const& cols) { + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator+(Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} // namespace TextFlow +} // namespace clara + +#endif // CLARA_TEXTFLOW_HPP_INCLUDED + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + + +#include +#include +#include + +#if !defined(CLARA_PLATFORM_WINDOWS) && \ + (defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)) +#define CLARA_PLATFORM_WINDOWS +#endif + +namespace clara { +namespace detail { + +// Traits for extracting arg and return type of lambdas (for single argument lambdas) +template +struct UnaryLambdaTraits : UnaryLambdaTraits {}; + +template +struct UnaryLambdaTraits { + static const bool isValid = false; +}; + +template +struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + ; + using ReturnType = ReturnT; +}; + +class TokenStream; + +// Transport for raw args (copied from main args, or supplied via init list for testing) +class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args(int argc, char* argv[]) { + m_exeName = argv[0]; + for (int i = 1; i < argc; ++i) m_args.push_back(argv[i]); + } + + Args(std::initializer_list args) + : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) {} + + auto exeName() const -> std::string { return m_exeName; } +}; + +// Wraps a token coming from a token stream. These may not directly correspond to strings as a +// single string may encode an option + its argument if the : or = form is used +enum class TokenType { Option, Argument }; +struct Token { + TokenType type; + std::string token; +}; + +inline auto isOptPrefix(char c) -> bool { + return c == '-' +#ifdef CLARA_PLATFORM_WINDOWS + || c == '/' +#endif + ; +} + +// Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled +class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize(0); + + // Skip any empty strings + while (it != itEnd && it->empty()) ++it; + + if (it != itEnd) { + auto const& next = *it; + if (isOptPrefix(next[0])) { + auto delimiterPos = next.find_first_of(" :="); + if (delimiterPos != std::string::npos) { + m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); + m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); + } else { + if (next[1] != '-' && next.size() > 2) { + std::string opt = "- "; + for (size_t i = 1; i < next.size(); ++i) { + opt[1] = next[i]; + m_tokenBuffer.push_back({TokenType::Option, opt}); + } + } else { + m_tokenBuffer.push_back({TokenType::Option, next}); + } + } + } else { + m_tokenBuffer.push_back({TokenType::Argument, next}); + } + } + } + + public: + explicit TokenStream(Args const& args) : TokenStream(args.m_args.begin(), args.m_args.end()) {} + + TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) { loadBuffer(); } + + explicit operator bool() const { return !m_tokenBuffer.empty() || it != itEnd; } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert(!m_tokenBuffer.empty()); + return m_tokenBuffer.front(); + } + + auto operator-> () const -> Token const* { + assert(!m_tokenBuffer.empty()); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream& { + if (m_tokenBuffer.size() >= 2) { + m_tokenBuffer.erase(m_tokenBuffer.begin()); + } else { + if (it != itEnd) ++it; + loadBuffer(); + } + return *this; + } +}; + + +class ResultBase { + public: + enum Type { Ok, LogicError, RuntimeError }; + + protected: + ResultBase(Type type) : m_type(type) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; +}; + +template +class ResultValueBase : public ResultBase { + public: + auto value() const -> T const& { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase(Type type) : ResultBase(type) {} + + ResultValueBase(ResultValueBase const& other) : ResultBase(other) { + if (m_type == ResultBase::Ok) new (&m_value) T(other.m_value); + } + + ResultValueBase(Type, T const& value) : ResultBase(Ok) { new (&m_value) T(value); } + + auto operator=(ResultValueBase const& other) -> ResultValueBase& { + if (m_type == ResultBase::Ok) m_value.~T(); + ResultBase::operator=(other); + if (m_type == ResultBase::Ok) new (&m_value) T(other.m_value); + return *this; + } + + ~ResultValueBase() { + if (m_type == Ok) m_value.~T(); + } + + union { + T m_value; + }; +}; + +template <> +class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; +}; + +template +class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult(BasicResult const& other) + : ResultValueBase(other.type()), m_errorMessage(other.errorMessage()) { + assert(type() != ResultBase::Ok); + } + + template + static auto ok(U const& value) -> BasicResult { + return {ResultBase::Ok, value}; + } + static auto ok() -> BasicResult { return {ResultBase::Ok}; } + static auto logicError(std::string const& message) -> BasicResult { + return {ResultBase::LogicError, message}; + } + static auto runtimeError(std::string const& message) -> BasicResult { + return {ResultBase::RuntimeError, message}; + } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + virtual void enforceOk() const { + // !TBD: If no exceptions, std::terminate here or something + switch (m_type) { + case ResultBase::LogicError: + throw std::logic_error(m_errorMessage); + case ResultBase::RuntimeError: + throw std::runtime_error(m_errorMessage); + case ResultBase::Ok: + break; + } + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult(ResultBase::Type type, std::string const& message) + : ResultValueBase(type), m_errorMessage(message) { + assert(m_type != ResultBase::Ok); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; +}; + +enum class ParseResultType { Matched, NoMatch, ShortCircuitAll, ShortCircuitSame }; + +class ParseState { + public: + ParseState(ParseResultType type, TokenStream const& remainingTokens) + : m_type(type), m_remainingTokens(remainingTokens) {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; +}; + +using Result = BasicResult; +using ParserResult = BasicResult; +using InternalParseResult = BasicResult; + +struct HelpColumns { + std::string left; + std::string right; +}; + +template +inline auto convertInto(std::string const& source, T& target) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if (ss.fail()) + return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); + else + return ParserResult::ok(ParseResultType::Matched); +} +inline auto convertInto(std::string const& source, std::string& target) -> ParserResult { + target = source; + return ParserResult::ok(ParseResultType::Matched); +} +inline auto convertInto(std::string const& source, bool& target) -> ParserResult { + std::string srcLC = source; + std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), + [](char c) { return static_cast(::tolower(c)); }); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError("Expected a boolean value but did not recognise: '" + + source + "'"); + return ParserResult::ok(ParseResultType::Matched); +} + +struct BoundRefBase { + BoundRefBase() = default; + BoundRefBase(BoundRefBase const&) = delete; + BoundRefBase(BoundRefBase&&) = delete; + BoundRefBase& operator=(BoundRefBase const&) = delete; + BoundRefBase& operator=(BoundRefBase&&) = delete; + + virtual ~BoundRefBase() = default; + + virtual auto isFlag() const -> bool = 0; + virtual auto isContainer() const -> bool { return false; } + virtual auto setValue(std::string const& arg) -> ParserResult = 0; + virtual auto setFlag(bool flag) -> ParserResult = 0; +}; + +struct BoundValueRefBase : BoundRefBase { + auto isFlag() const -> bool override { return false; } + + auto setFlag(bool) -> ParserResult override { + return ParserResult::logicError("Flags can only be set on boolean fields"); + } +}; + +struct BoundFlagRefBase : BoundRefBase { + auto isFlag() const -> bool override { return true; } + + auto setValue(std::string const& arg) -> ParserResult override { + bool flag; + auto result = convertInto(arg, flag); + if (result) setFlag(flag); + return result; + } +}; + +template +struct BoundRef : BoundValueRefBase { + T& m_ref; + + explicit BoundRef(T& ref) : m_ref(ref) {} + + auto setValue(std::string const& arg) -> ParserResult override { + return convertInto(arg, m_ref); + } +}; + +template +struct BoundRef> : BoundValueRefBase { + std::vector& m_ref; + + explicit BoundRef(std::vector& ref) : m_ref(ref) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue(std::string const& arg) -> ParserResult override { + T temp; + auto result = convertInto(arg, temp); + if (result) m_ref.push_back(temp); + return result; + } +}; + +struct BoundFlagRef : BoundFlagRefBase { + bool& m_ref; + + explicit BoundFlagRef(bool& ref) : m_ref(ref) {} + + auto setFlag(bool flag) -> ParserResult override { + m_ref = flag; + return ParserResult::ok(ParseResultType::Matched); + } +}; + +template +struct LambdaInvoker { + static_assert(std::is_same::value, + "Lambda must return void or clara::ParserResult"); + + template + static auto invoke(L const& lambda, ArgType const& arg) -> ParserResult { + return lambda(arg); + } +}; + +template <> +struct LambdaInvoker { + template + static auto invoke(L const& lambda, ArgType const& arg) -> ParserResult { + lambda(arg); + return ParserResult::ok(ParseResultType::Matched); + } +}; + +template +inline auto invokeLambda(L const& lambda, std::string const& arg) -> ParserResult { + ArgType temp; + auto result = convertInto(arg, temp); + return !result ? result + : LambdaInvoker::ReturnType>::invoke(lambda, temp); +}; + + +template +struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + explicit BoundLambda(L const& lambda) : m_lambda(lambda) {} + + auto setValue(std::string const& arg) -> ParserResult override { + return invokeLambda::ArgType>(m_lambda, arg); + } +}; + +template +struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ArgType, bool>::value, + "flags must be boolean"); + + explicit BoundFlagLambda(L const& lambda) : m_lambda(lambda) {} + + auto setFlag(bool flag) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + } +}; + +enum class Optionality { Optional, Required }; + +struct Parser; + +class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse(std::string const& exeName, TokenStream const& tokens) const + -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse(Args const& args) const -> InternalParseResult { + return parse(args.exeName(), TokenStream(args)); + } +}; + +template +class ComposableParserImpl : public ParserBase { + public: + template + auto operator|(T const& other) const -> Parser; +}; + +// Common code and state for Args and Opts +template +class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl(std::shared_ptr const& ref) : m_ref(ref) {} + + public: + template + ParserRefImpl(T& ref, std::string const& hint) + : m_ref(std::make_shared>(ref)), m_hint(hint) {} + + template + ParserRefImpl(LambdaT const& ref, std::string const& hint) + : m_ref(std::make_shared>(ref)), m_hint(hint) {} + + auto operator()(std::string const& description) -> DerivedT& { + m_description = description; + return static_cast(*this); + } + + auto optional() -> DerivedT& { + m_optionality = Optionality::Optional; + return static_cast(*this); + }; + + auto required() -> DerivedT& { + m_optionality = Optionality::Required; + return static_cast(*this); + }; + + auto isOptional() const -> bool { return m_optionality == Optionality::Optional; } + + auto cardinality() const -> size_t override { + if (m_ref->isContainer()) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } +}; + +class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const& lambda) -> std::shared_ptr { + return std::make_shared>(lambda); + } + + public: + ExeName() : m_name(std::make_shared("")) {} + + explicit ExeName(std::string& ref) : ExeName() { + m_ref = std::make_shared>(ref); + } + + template + explicit ExeName(LambdaT const& lambda) : ExeName() { + m_ref = std::make_shared>(lambda); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse(std::string const&, TokenStream const& tokens) const + -> InternalParseResult override { + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + } + + auto name() const -> std::string { return *m_name; } + auto set(std::string const& newName) -> ParserResult { + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) ? newName : newName.substr(lastSlash + 1); + + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return ParserResult::ok(ParseResultType::Matched); + } +}; + +class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse(std::string const&, TokenStream const& tokens) const + -> InternalParseResult override { + auto validationResult = validate(); + if (!validationResult) return InternalParseResult(validationResult); + + auto remainingTokens = tokens; + auto const& token = *remainingTokens; + if (token.type != TokenType::Argument) + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); + + auto result = m_ref->setValue(remainingTokens->token); + if (!result) + return InternalParseResult(result); + else + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); + } +}; + +inline auto normaliseOpt(std::string const& optName) -> std::string { +#ifdef CLARA_PLATFORM_WINDOWS + if (optName[0] == '/') + return "-" + optName.substr(1); + else +#endif + return optName; +} + +class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt(LambdaT const& ref) + : ParserRefImpl(std::make_shared>(ref)) {} + + explicit Opt(bool& ref) : ParserRefImpl(std::make_shared(ref)) {} + + template + Opt(LambdaT const& ref, std::string const& hint) : ParserRefImpl(ref, hint) {} + + template + Opt(T& ref, std::string const& hint) : ParserRefImpl(ref, hint) {} + + auto operator[](std::string const& optName) -> Opt& { + m_optNames.push_back(optName); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for (auto const& opt : m_optNames) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if (!m_hint.empty()) oss << " <" << m_hint << ">"; + return {{oss.str(), m_description}}; + } + + auto isMatch(std::string const& optToken) const -> bool { + auto normalisedToken = normaliseOpt(optToken); + for (auto const& name : m_optNames) { + if (normaliseOpt(name) == normalisedToken) return true; + } + return false; + } + + using ParserBase::parse; + + auto parse(std::string const&, TokenStream const& tokens) const + -> InternalParseResult override { + auto validationResult = validate(); + if (!validationResult) return InternalParseResult(validationResult); + + auto remainingTokens = tokens; + if (remainingTokens && remainingTokens->type == TokenType::Option) { + auto const& token = *remainingTokens; + if (isMatch(token.token)) { + if (m_ref->isFlag()) { + auto result = m_ref->setFlag(true); + if (!result) return InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } else { + ++remainingTokens; + if (!remainingTokens) + return InternalParseResult::runtimeError("Expected argument following " + + token.token); + auto const& argToken = *remainingTokens; + if (argToken.type != TokenType::Argument) + return InternalParseResult::runtimeError("Expected argument following " + + token.token); + auto result = m_ref->setValue(argToken.token); + if (!result) return InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } + return InternalParseResult::ok( + ParseState(ParseResultType::Matched, ++remainingTokens)); + } + } + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); + } + + auto validate() const -> Result override { + if (m_optNames.empty()) return Result::logicError("No options supplied to Opt"); + for (auto const& name : m_optNames) { + if (name.empty()) return Result::logicError("Option name cannot be empty"); +#ifdef CLARA_PLATFORM_WINDOWS + if (name[0] != '-' && name[0] != '/') + return Result::logicError("Option name must begin with '-' or '/'"); +#else + if (name[0] != '-') return Result::logicError("Option name must begin with '-'"); +#endif + } + return ParserRefImpl::validate(); + } +}; + +struct Help : Opt { + Help(bool& showHelpFlag) + : Opt([&](bool flag) { + showHelpFlag = flag; + return ParserResult::ok(ParseResultType::ShortCircuitAll); + }) { + static_cast (*this)("display usage information")["-?"]["-h"]["--help"].optional(); + } +}; + + +struct Parser : ParserBase { + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=(ExeName const& exeName) -> Parser& { + m_exeName = exeName; + return *this; + } + + auto operator|=(Arg const& arg) -> Parser& { + m_args.push_back(arg); + return *this; + } + + auto operator|=(Opt const& opt) -> Parser& { + m_options.push_back(opt); + return *this; + } + + auto operator|=(Parser const& other) -> Parser& { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|(T const& other) const -> Parser { + return Parser(*this) |= other; + } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const& o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert(cols.end(), childCols.begin(), childCols.end()); + } + return cols; + } + + void writeToStream(std::ostream& os) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" + << " " << m_exeName.name() << " "; + bool required = true, first = true; + for (auto const& arg : m_args) { + if (first) + first = false; + else + os << " "; + if (arg.isOptional() && required) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if (arg.cardinality() == 0) os << " ... "; + } + if (!required) os << "]"; + if (!m_options.empty()) os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for (auto const& cols : rows) optWidth = (std::max)(optWidth, cols.left.size() + 2); + + for (auto const& cols : rows) { + auto row = TextFlow::Column(cols.left).width(optWidth).indent(2) + TextFlow::Spacer(4) + + TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); + os << row << std::endl; + } + } + + friend auto operator<<(std::ostream& os, Parser const& parser) -> std::ostream& { + parser.writeToStream(os); + return os; + } + + auto validate() const -> Result override { + for (auto const& opt : m_options) { + auto result = opt.validate(); + if (!result) return result; + } + for (auto const& arg : m_args) { + auto result = arg.validate(); + if (!result) return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse(std::string const& exeName, TokenStream const& tokens) const + -> InternalParseResult override { + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert(totalParsers < 512); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const& opt : m_options) parseInfos[i++].parser = &opt; + for (auto const& arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set(exeName); + + auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + while (result.value().remainingTokens()) { + bool tokenParsed = false; + + for (size_t i = 0; i < totalParsers; ++i) { + auto& parseInfo = parseInfos[i]; + if (parseInfo.parser->cardinality() == 0 || + parseInfo.count < parseInfo.parser->cardinality()) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if (result.value().type() == ParseResultType::ShortCircuitAll) return result; + if (!tokenParsed) + return InternalParseResult::runtimeError("Unrecognised token: " + + result.value().remainingTokens()->token); + } + // !TBD Check missing required options + return result; + } +}; + +template +template +auto ComposableParserImpl::operator|(T const& other) const -> Parser { + return Parser() | static_cast(*this) | other; +} +} // namespace detail + + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + + +} // namespace clara + +#endif // CLARA_HPP_INCLUDED diff --git a/projects/hip/tests/src/hipEnvVar.cpp b/projects/hip/tests/src/hipEnvVar.cpp index 2626e838dc..7c261b1fa3 100644 --- a/projects/hip/tests/src/hipEnvVar.cpp +++ b/projects/hip/tests/src/hipEnvVar.cpp @@ -27,57 +27,56 @@ THE SOFTWARE. #include #include -#include #include +#include "clara/clara.hpp" #include #include "hip/hip_runtime.h" +#ifdef _WIN64 +#include +#endif + +#ifdef _WIN64 +#define setenv(x,y,z) _putenv_s(x,y) +#endif + using namespace std; +using namespace clara; +inline clara::Parser cmdline_parser(bool& help, std::string& env, int &device, bool &retDevCnt) { + return clara::Opt{retDevCnt} + ["-c"] + ("total number of GPUs available") | -void usage() { - printf( - "hipEnvVar [otpions]\n\ - -c,\t\ttotal number of available GPUs and their pciBusID\n\ - -d,\t\tselect one GPU and return its pciBusID\n\ - -v,\t\tsend the list to HIP_VISIBLE_DEVICES env var\n\ - -h,\t\tshow this help message\n\ - "); + clara::Help{help} | + + clara::Opt{device,"device"} + ["-d"]["--device"] + ("select one GPU and return its pciBusID") | + + clara::Opt{env,"Set Env Value"} + ["-v"]["--EnvValue"] + ("send the list to HIP_VISIBLE_DEVICES env var, syntax -v="); } -int main(int argc, char** argv) { - // string str = getenv("HIP_VISIBLE_DEVICES"); - // std::cout << "The current env HIP_VISIBLE_DEVICES is"<= 0"< devCount - 1) { + if (device != INT_MAX && (device < 0 || device > devCount - 1)) { printf("Selected device %d is out of bound. Devices on your system are in range %d - %d\n", device, 0, devCount - 1); return -1; } if (retDevCnt) { - // std::cout << "Total number of devices visible in system is "<< devCount << std::endl; std::cout << devCount << std::endl; } - if (retDevInfo) { + if (device != INT_MAX) { hipDevice_t deviceT; hipDeviceGet(&deviceT, device); diff --git a/projects/hip/tests/src/hipEnvVarDriver.cpp b/projects/hip/tests/src/hipEnvVarDriver.cpp index 5063319ead..37e51faa31 100644 --- a/projects/hip/tests/src/hipEnvVarDriver.cpp +++ b/projects/hip/tests/src/hipEnvVarDriver.cpp @@ -30,6 +30,14 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "hip/hip_runtime.h" #include #include + +#ifdef _WIN64 +#define popen(x,y) _popen(x,y) +#define pclose(x) _pclose(x) +#define setenv(x,y,z) _putenv_s(x,y) +#define unsetenv(x) _putenv_s(x,"") +#endif + using namespace std; int getDeviceNumber() {