| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * 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 ART_LIBARTTOOLS_TOOLS_CMDLINE_BUILDER_H_ |
| #define ART_LIBARTTOOLS_TOOLS_CMDLINE_BUILDER_H_ |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "android-base/stringprintf.h" |
| |
| namespace art { |
| namespace tools { |
| |
| namespace internal { |
| |
| constexpr bool ContainsOneFormatSpecifier(std::string_view format, char specifier) { |
| int count = 0; |
| size_t pos = 0; |
| while ((pos = format.find('%', pos)) != std::string_view::npos) { |
| if (pos == format.length() - 1) { |
| // Invalid trailing '%'. |
| return false; |
| } |
| if (format[pos + 1] == specifier) { |
| count++; |
| } else if (format[pos + 1] != '%') { |
| // "%%" is okay. Otherwise, it's a wrong specifier. |
| return false; |
| } |
| pos += 2; |
| } |
| return count == 1; |
| } |
| |
| } // namespace internal |
| |
| // A util class that builds cmdline arguments. |
| class CmdlineBuilder { |
| public: |
| // Returns all arguments. |
| const std::vector<std::string>& Get() const { return elements_; } |
| |
| // Adds an argument as-is. |
| CmdlineBuilder& Add(std::string_view arg) { |
| elements_.push_back(std::string(arg)); |
| return *this; |
| } |
| |
| // Same as above but adds a runtime argument. |
| CmdlineBuilder& AddRuntime(std::string_view arg) { return Add("--runtime-arg").Add(arg); } |
| |
| // Adds a string value formatted by the format string. |
| // |
| // Usage: Add("--flag=%s", "value") |
| CmdlineBuilder& Add(const char* arg_format, const std::string& value) |
| __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'), |
| "'arg' must be a string literal that contains '%s'"))) { |
| return Add(android::base::StringPrintf(arg_format, value.c_str())); |
| } |
| |
| // Same as above but adds a runtime argument. |
| CmdlineBuilder& AddRuntime(const char* arg_format, const std::string& value) |
| __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'), |
| "'arg' must be a string literal that contains '%s'"))) { |
| return AddRuntime(android::base::StringPrintf(arg_format, value.c_str())); |
| } |
| |
| // Adds an integer value formatted by the format string. |
| // |
| // Usage: Add("--flag=%d", 123) |
| CmdlineBuilder& Add(const char* arg_format, int value) |
| __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 'd'), |
| "'arg' must be a string literal that contains '%d'"))) { |
| return Add(android::base::StringPrintf(arg_format, value)); |
| } |
| |
| // Same as above but adds a runtime argument. |
| CmdlineBuilder& AddRuntime(const char* arg_format, int value) |
| __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 'd'), |
| "'arg' must be a string literal that contains '%d'"))) { |
| return AddRuntime(android::base::StringPrintf(arg_format, value)); |
| } |
| |
| // Adds a string value formatted by the format string if the value is non-empty. Does nothing |
| // otherwise. |
| // |
| // Usage: AddIfNonEmpty("--flag=%s", "value") |
| CmdlineBuilder& AddIfNonEmpty(const char* arg_format, const std::string& value) |
| __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'), |
| "'arg' must be a string literal that contains '%s'"))) { |
| if (!value.empty()) { |
| Add(android::base::StringPrintf(arg_format, value.c_str())); |
| } |
| return *this; |
| } |
| |
| // Same as above but adds a runtime argument. |
| CmdlineBuilder& AddRuntimeIfNonEmpty(const char* arg_format, const std::string& value) |
| __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'), |
| "'arg' must be a string literal that contains '%s'"))) { |
| if (!value.empty()) { |
| AddRuntime(android::base::StringPrintf(arg_format, value.c_str())); |
| } |
| return *this; |
| } |
| |
| // Adds an argument as-is if the boolean value is true. Does nothing otherwise. |
| CmdlineBuilder& AddIf(bool value, std::string_view arg) { |
| if (value) { |
| Add(arg); |
| } |
| return *this; |
| } |
| |
| // Same as above but adds a runtime argument. |
| CmdlineBuilder& AddRuntimeIf(bool value, std::string_view arg) { |
| if (value) { |
| AddRuntime(arg); |
| } |
| return *this; |
| } |
| |
| // Concatenates this builder with another. Returns the concatenated result and nullifies the input |
| // builder. |
| CmdlineBuilder& Concat(CmdlineBuilder&& other) { |
| elements_.reserve(elements_.size() + other.elements_.size()); |
| std::move(other.elements_.begin(), other.elements_.end(), std::back_inserter(elements_)); |
| other.elements_.clear(); |
| return *this; |
| } |
| |
| private: |
| std::vector<std::string> elements_; |
| }; |
| |
| } // namespace tools |
| } // namespace art |
| |
| #endif // ART_LIBARTTOOLS_TOOLS_CMDLINE_BUILDER_H_ |