CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
514 lines
17 KiB
C++
514 lines
17 KiB
C++
/* ************************************************************************
|
|
* Copyright (C) 2020-2022 Advanced Micro Devices, Inc. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-
|
|
* ies of the Software, and to permit persons to whom the Software is furnished
|
|
* to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-
|
|
* PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-
|
|
* CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*
|
|
* ************************************************************************ */
|
|
// This emulates the required functionality of boost::program_options
|
|
|
|
#pragma once
|
|
|
|
#include <cinttypes>
|
|
#include <cstdio>
|
|
#include <iomanip>
|
|
#include <map>
|
|
#include <ostream>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace roc
|
|
{
|
|
// Regular expression for token delimiters (whitespace and commas)
|
|
static const std::regex program_options_regex{"[, \\f\\n\\r\\t\\v]+",
|
|
std::regex_constants::optimize};
|
|
|
|
// Polymorphic base class to use with dynamic_cast
|
|
class value_base
|
|
{
|
|
protected:
|
|
bool m_has_actual = false;
|
|
bool m_has_default = false;
|
|
|
|
public:
|
|
virtual ~value_base() = default;
|
|
|
|
bool has_actual() const
|
|
{
|
|
return m_has_actual;
|
|
}
|
|
|
|
bool has_default() const
|
|
{
|
|
return m_has_default;
|
|
}
|
|
};
|
|
|
|
// Value parameters
|
|
template <typename T>
|
|
class value : public value_base
|
|
{
|
|
T m_var; // Variable to be modified if no pointer provided
|
|
T* m_var_ptr; // Pointer to variable to be modified
|
|
|
|
public:
|
|
// Constructor
|
|
explicit value()
|
|
: m_var_ptr(nullptr)
|
|
{
|
|
}
|
|
|
|
explicit value(const T& var, bool defaulted)
|
|
: m_var(var)
|
|
, m_var_ptr(nullptr)
|
|
{
|
|
m_has_actual = !defaulted;
|
|
m_has_default = defaulted;
|
|
}
|
|
|
|
explicit value(T* var_ptr)
|
|
: m_var_ptr(var_ptr)
|
|
{
|
|
}
|
|
|
|
// Allows actual_value() and default_value()
|
|
value* operator->()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
// Get the value
|
|
const T& get_value() const
|
|
{
|
|
if(m_var_ptr)
|
|
return *m_var_ptr;
|
|
else
|
|
return m_var;
|
|
}
|
|
|
|
// Set actual value
|
|
value& actual_value(T val)
|
|
{
|
|
if(m_var_ptr)
|
|
*m_var_ptr = std::move(val);
|
|
else
|
|
m_var = std::move(val);
|
|
m_has_actual = true;
|
|
return *this;
|
|
}
|
|
|
|
// Set default value
|
|
value& default_value(T val)
|
|
{
|
|
if(!m_has_actual)
|
|
{
|
|
if(m_var_ptr)
|
|
*m_var_ptr = std::move(val);
|
|
else
|
|
m_var = std::move(val);
|
|
m_has_default = true;
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
// bool_switch is a value<bool>, which is handled specially
|
|
using bool_switch = value<bool>;
|
|
|
|
class variable_value
|
|
{
|
|
std::shared_ptr<value_base> m_val;
|
|
|
|
public:
|
|
// Constructor
|
|
explicit variable_value() = default;
|
|
|
|
template <typename T>
|
|
explicit variable_value(const T& xv, bool xdefaulted)
|
|
: m_val(std::make_shared<value<T>>(xv, xdefaulted))
|
|
{
|
|
}
|
|
|
|
explicit variable_value(std::shared_ptr<value_base> val)
|
|
: m_val(val)
|
|
{
|
|
}
|
|
|
|
// Member functions
|
|
bool empty() const
|
|
{
|
|
return !m_val.get() || (!m_val->has_actual() && !m_val->has_default());
|
|
}
|
|
|
|
bool defaulted() const
|
|
{
|
|
return m_val.get() && !m_val->has_actual() && m_val->has_default();
|
|
}
|
|
|
|
template <typename T>
|
|
const T& as() const
|
|
{
|
|
if(value<T>* val = dynamic_cast<value<T>*>(m_val.get()))
|
|
return val->get_value();
|
|
else
|
|
throw std::logic_error("Internal error: Invalid cast");
|
|
}
|
|
};
|
|
|
|
using variables_map = std::map<std::string, variable_value>;
|
|
|
|
class options_description
|
|
{
|
|
// desc_option describes a particular option
|
|
class desc_option
|
|
{
|
|
std::string m_opts;
|
|
std::shared_ptr<value_base> m_val;
|
|
std::string m_desc;
|
|
|
|
public:
|
|
// Constructor with options, value and description
|
|
template <typename T>
|
|
desc_option(std::string opts, value<T> val, std::string desc)
|
|
: m_opts(std::move(opts))
|
|
, m_val(new auto(std::move(val)))
|
|
, m_desc(std::move(desc))
|
|
{
|
|
}
|
|
|
|
// Constructor with options and description
|
|
desc_option(std::string opts, std::string desc)
|
|
: m_opts(std::move(opts))
|
|
, m_val(nullptr)
|
|
, m_desc(std::move(desc))
|
|
{
|
|
}
|
|
|
|
// Copy constructor is deleted
|
|
desc_option(const desc_option&) = delete;
|
|
|
|
// Move constructor
|
|
desc_option(desc_option&& other) = default;
|
|
|
|
// Accessors
|
|
const std::string& get_opts() const
|
|
{
|
|
return m_opts;
|
|
}
|
|
|
|
const std::shared_ptr<value_base> get_val() const
|
|
{
|
|
return m_val;
|
|
}
|
|
|
|
const std::string& get_desc() const
|
|
{
|
|
return m_desc;
|
|
}
|
|
|
|
// Set a value
|
|
void set_val(int& argc, char**& argv, const std::string& inopt) const
|
|
{
|
|
// We test all supported types with dynamic_cast and parse accordingly
|
|
bool match = false;
|
|
if(auto* ptr = dynamic_cast<value<int32_t>*>(m_val.get()))
|
|
{
|
|
int32_t val;
|
|
match = argc && sscanf(*argv, "%" SCNd32, &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<uint32_t>*>(m_val.get()))
|
|
{
|
|
uint32_t val;
|
|
match = argc && sscanf(*argv, "%" SCNu32, &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<int64_t>*>(m_val.get()))
|
|
{
|
|
int64_t val;
|
|
match = argc && sscanf(*argv, "%" SCNd64, &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<uint64_t>*>(m_val.get()))
|
|
{
|
|
uint64_t val;
|
|
match = argc && sscanf(*argv, "%" SCNu64, &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<float>*>(m_val.get()))
|
|
{
|
|
float val;
|
|
match = argc && sscanf(*argv, "%f", &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<double>*>(m_val.get()))
|
|
{
|
|
double val;
|
|
match = argc && sscanf(*argv, "%lf", &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<char>*>(m_val.get()))
|
|
{
|
|
char val;
|
|
match = argc && sscanf(*argv, " %c", &val) == 1;
|
|
ptr->actual_value(val);
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<bool>*>(m_val.get()))
|
|
{
|
|
// We handle bool specially, setting the value to true without argument
|
|
ptr->actual_value(true);
|
|
return;
|
|
}
|
|
else if(auto* ptr = dynamic_cast<value<std::string>*>(m_val.get()))
|
|
{
|
|
if(argc)
|
|
{
|
|
ptr->actual_value(*argv);
|
|
match = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::logic_error("Internal error: Unsupported data type");
|
|
}
|
|
|
|
if(!match)
|
|
throw std::invalid_argument(argc ? "Invalid value for " + inopt
|
|
: "Missing required value for " + inopt);
|
|
|
|
// Skip past the argument's value
|
|
++argv;
|
|
--argc;
|
|
}
|
|
};
|
|
|
|
// Description and option list
|
|
std::string m_desc;
|
|
std::vector<desc_option> m_optlist;
|
|
|
|
// desc_optionlist allows chains of options to be parenthesized
|
|
class desc_optionlist
|
|
{
|
|
std::vector<desc_option>& m_list;
|
|
|
|
public:
|
|
explicit desc_optionlist(std::vector<desc_option>& list)
|
|
: m_list(list)
|
|
{
|
|
}
|
|
|
|
template <typename... Ts>
|
|
desc_optionlist operator()(Ts&&... arg)
|
|
{
|
|
m_list.push_back(desc_option(std::forward<Ts>(arg)...));
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
// Parse an option at the current (argc, argv) position
|
|
void parse_option(int& argc, char**& argv, variables_map& vm, bool ignoreUnknown) const
|
|
{
|
|
// Iterate across all options
|
|
for(const auto& opt : m_optlist)
|
|
{
|
|
// Canonical name used for map
|
|
std::string canonical_name;
|
|
|
|
// Iterate across tokens in the opts
|
|
for(std::sregex_token_iterator tok{
|
|
opt.get_opts().begin(), opt.get_opts().end(), program_options_regex, -1};
|
|
tok != std::sregex_token_iterator();
|
|
++tok)
|
|
{
|
|
// The first option in a list of options is the canonical name
|
|
if(!canonical_name.length())
|
|
canonical_name = tok->str();
|
|
|
|
// If the length of the option is 1, it is single-dash; otherwise double-dash
|
|
const char* prefix = tok->length() == 1 ? "-" : "--";
|
|
|
|
// If option matches
|
|
if(*argv == prefix + tok->str())
|
|
{
|
|
++argv;
|
|
--argc;
|
|
|
|
// If option has a value, set it
|
|
if(opt.get_val().get())
|
|
opt.set_val(argc, argv, prefix + tok->str());
|
|
|
|
// Add seen options to map
|
|
vm[canonical_name] = variable_value(opt.get_val());
|
|
|
|
return; // Return successfully
|
|
}
|
|
}
|
|
}
|
|
|
|
// No options were matched
|
|
if(ignoreUnknown)
|
|
{
|
|
++argv;
|
|
--argc;
|
|
}
|
|
else
|
|
throw std::invalid_argument("Option " + std::string(argv[0]) + " is not defined.");
|
|
}
|
|
|
|
public:
|
|
// Constructor
|
|
explicit options_description(std::string desc)
|
|
: m_desc(std::move(desc))
|
|
{
|
|
}
|
|
|
|
// Start a desc_optionlist chain
|
|
desc_optionlist add_options() &
|
|
{
|
|
return desc_optionlist(m_optlist);
|
|
}
|
|
|
|
// Parse all options
|
|
void parse_options(int& argc,
|
|
char**& argv,
|
|
variables_map& vm,
|
|
bool ignoreUnknown = false) const
|
|
{
|
|
// Add options with default values to map
|
|
for(const auto& opt : m_optlist)
|
|
{
|
|
std::sregex_token_iterator tok{
|
|
opt.get_opts().begin(), opt.get_opts().end(), program_options_regex, -1};
|
|
|
|
// Canonical name used for map
|
|
std::string canonical_name = tok->str();
|
|
|
|
if(opt.get_val().get() && opt.get_val()->has_default())
|
|
vm[canonical_name] = variable_value(opt.get_val());
|
|
}
|
|
|
|
// Parse options
|
|
while(argc)
|
|
parse_option(argc, argv, vm, ignoreUnknown);
|
|
}
|
|
|
|
// Formatted output of command-line arguments description
|
|
friend std::ostream& operator<<(std::ostream& os, const options_description& d)
|
|
{
|
|
// Iterate across all options
|
|
for(const auto& opt : d.m_optlist)
|
|
{
|
|
bool first = true, printvalue = true;
|
|
const char* delim = "";
|
|
std::ostringstream left;
|
|
|
|
// Iterate across tokens in the opts
|
|
for(std::sregex_token_iterator tok{opt.get_opts().begin(),
|
|
opt.get_opts().end(),
|
|
program_options_regex,
|
|
-1};
|
|
tok != std::sregex_token_iterator();
|
|
++tok, first = false, delim = " ")
|
|
{
|
|
// If the length of the option is 1, it is single-dash; otherwise double-dash
|
|
const char* prefix = tok->length() == 1 ? "-" : "--";
|
|
left << delim << (first ? "" : "|") << prefix << tok->str();
|
|
|
|
if(tok->str() == "help" || tok->str() == "h")
|
|
printvalue = false;
|
|
}
|
|
|
|
if(printvalue)
|
|
left << " <value>";
|
|
os << std::setw(26) << std::left << left.str() << " " << opt.get_desc() << " ";
|
|
left.str(std::string());
|
|
|
|
// Print the default value of the variable type if it exists
|
|
// We do not print the default value for bool
|
|
const value_base* val = opt.get_val().get();
|
|
if(val && !dynamic_cast<const value<bool>*>(val))
|
|
{
|
|
if(val->has_default())
|
|
{
|
|
// We test all supported types with dynamic_cast and print accordingly
|
|
left << " (Default value is: ";
|
|
if(dynamic_cast<const value<int32_t>*>(val))
|
|
left << dynamic_cast<const value<int32_t>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<uint32_t>*>(val))
|
|
left << dynamic_cast<const value<uint32_t>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<int64_t>*>(val))
|
|
left << dynamic_cast<const value<int64_t>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<uint64_t>*>(val))
|
|
left << dynamic_cast<const value<uint64_t>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<float>*>(val))
|
|
left << dynamic_cast<const value<float>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<double>*>(val))
|
|
left << dynamic_cast<const value<double>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<char>*>(val))
|
|
left << dynamic_cast<const value<char>*>(val)->get_value();
|
|
else if(dynamic_cast<const value<std::string>*>(val))
|
|
left << dynamic_cast<const value<std::string>*>(val)->get_value();
|
|
else
|
|
throw std::logic_error("Internal error: Unsupported data type");
|
|
left << ")";
|
|
}
|
|
}
|
|
os << left.str() << "\n\n";
|
|
}
|
|
return os << std::flush;
|
|
}
|
|
};
|
|
|
|
// Class representing command line parser
|
|
class parse_command_line
|
|
{
|
|
variables_map m_vm;
|
|
|
|
public:
|
|
parse_command_line(int argc,
|
|
char** argv,
|
|
const options_description& desc,
|
|
bool ignoreUnknown = false)
|
|
{
|
|
++argv; // Skip argv[0]
|
|
--argc;
|
|
desc.parse_options(argc, argv, m_vm, ignoreUnknown);
|
|
}
|
|
|
|
// Copy the variables_map
|
|
friend void store(const parse_command_line& p, variables_map& vm)
|
|
{
|
|
vm = p.m_vm;
|
|
}
|
|
|
|
// Move the variables_map
|
|
friend void store(parse_command_line&& p, variables_map& vm)
|
|
{
|
|
vm = std::move(p.m_vm);
|
|
}
|
|
};
|
|
|
|
// We can define the notify() function as a no-op for our purposes
|
|
inline void notify(const variables_map&) {}
|
|
|
|
}
|