refactor: new version of MakeSingleFiles (#546)

* refactor: new version of MakeSingleFiles

* fix: use CMake and set version
This commit is contained in:
Henry Schreiner 2021-01-04 17:47:06 -05:00 committed by GitHub
parent 32e6c3f347
commit f862849488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 222 additions and 140 deletions

View File

@ -22,11 +22,8 @@ jobs:
- uses: actions/setup-python@v2 - uses: actions/setup-python@v2
- name: Make header
run: python ./scripts/MakeSingleHeader.py CLI11.hpp
- name: Prepare CMake config - name: Prepare CMake config
run: cmake -S . -B build run: cmake -S . -B build -DCLI11_SINGLE_FILE=ON
- name: Make package - name: Make package
run: cmake --build build --target package_source run: cmake --build build --target package_source
@ -37,10 +34,13 @@ jobs:
cp build/CLI11-*-Source.* CLI11-Source cp build/CLI11-*-Source.* CLI11-Source
cp build/CLI11-*-Source.* . cp build/CLI11-*-Source.* .
- name: Make header
run: cmake --build build --target CLI11-generate-single-file
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: CLI11.hpp name: CLI11.hpp
path: CLI11.hpp path: build/include/CLI11.hpp
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:

67
CLI11.hpp.in Normal file
View File

@ -0,0 +1,67 @@
// CLI11: Version {version}
// Originally designed by Henry Schreiner
// https://github.com/CLIUtils/CLI11
//
// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
// from: {git}
//
// CLI11 {version} Copyright (c) 2017-2020 University of Cincinnati, developed by Henry
// Schreiner under NSF AWARD 1414736. All rights reserved.
//
// Redistribution and use in source and binary forms of CLI11, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Standard combined includes:
{public_includes}
{version_hpp}
{macros_hpp}
{validators_hpp_filesystem}
namespace {namespace} {{
{string_tools_hpp}
{error_hpp}
{type_tools_hpp}
{split_hpp}
{config_fwd_hpp}
{validators_hpp}
{formatter_fwd_hpp}
{option_hpp}
{app_hpp}
{config_hpp}
{formatter_hpp}
}} // namespace {namespace}

View File

@ -247,7 +247,10 @@ if(CLI11_SINGLE_FILE)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
COMMAND Python::Interpreter COMMAND Python::Interpreter
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py"
"${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" ${CLI11_headers}
--main "${CMAKE_CURRENT_SOURCE_DIR}/CLI11.hpp.in"
--output "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
--version "${CLI11_VERSION}"
DEPENDS DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp"
${CLI11_headers}) ${CLI11_headers})

View File

@ -6,6 +6,7 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
@ -18,6 +19,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
// CLI Library includes // CLI Library includes
#include "ConfigFwd.hpp" #include "ConfigFwd.hpp"
@ -30,6 +32,7 @@
#include "TypeTools.hpp" #include "TypeTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:app_hpp:verbatim]
#ifndef CLI11_PARSE #ifndef CLI11_PARSE
#define CLI11_PARSE(app, argc, argv) \ #define CLI11_PARSE(app, argc, argv) \
@ -3234,4 +3237,5 @@ struct AppFriend {
}; };
} // namespace detail } // namespace detail
// [CLI11:app_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,19 +6,21 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:set]
#include "App.hpp" #include "App.hpp"
#include "ConfigFwd.hpp" #include "ConfigFwd.hpp"
#include "StringTools.hpp" #include "StringTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:config_hpp:verbatim]
namespace detail { namespace detail {
inline std::string convert_arg_for_ini(const std::string &arg) { inline std::string convert_arg_for_ini(const std::string &arg) {
@ -348,4 +350,5 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
return out.str(); return out.str();
} }
// [CLI11:config_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,16 +6,19 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
#include "Error.hpp" #include "Error.hpp"
#include "StringTools.hpp" #include "StringTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:config_fwd_hpp:verbatim]
class App; class App;
@ -128,4 +131,5 @@ class ConfigINI : public ConfigTOML {
valueDelimiter = '='; valueDelimiter = '=';
} }
}; };
// [CLI11:config_fwd_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,16 +6,19 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
// CLI library includes // CLI library includes
#include "StringTools.hpp" #include "StringTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:error_hpp:verbatim]
// Use one of these on all error classes. // Use one of these on all error classes.
// These are temporary and are undef'd at the end of this file. // These are temporary and are undef'd at the end of this file.
@ -344,4 +347,5 @@ class OptionNotFound : public Error {
/// @} /// @}
// [CLI11:error_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,14 +6,17 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
#include "App.hpp" #include "App.hpp"
#include "FormatterFwd.hpp" #include "FormatterFwd.hpp"
namespace CLI { namespace CLI {
// [CLI11:formatter_hpp:verbatim]
inline std::string inline std::string
Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const { Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
@ -285,4 +288,5 @@ inline std::string Formatter::make_option_usage(const Option *opt) const {
return opt->get_required() ? out.str() : "[" + out.str() + "]"; return opt->get_required() ? out.str() : "[" + out.str() + "]";
} }
// [CLI11:formatter_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,14 +6,17 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <map> #include <map>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
#include "StringTools.hpp" #include "StringTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:formatter_fwd_hpp:verbatim]
class Option; class Option;
class App; class App;
@ -177,4 +180,5 @@ class Formatter : public FormatterBase {
///@} ///@}
}; };
// [CLI11:formatter_fwd_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,7 +6,7 @@
#pragma once #pragma once
// [CLI11:verbatim] // [CLI11:macros_hpp:verbatim]
// The following version macro is very similar to the one in PyBind11 // The following version macro is very similar to the one in PyBind11
#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
@ -41,4 +41,4 @@
#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) #define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
#endif #endif
// [CLI11:verbatim] // [CLI11:macros_hpp:end]

View File

@ -6,6 +6,7 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -14,6 +15,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
#include "Error.hpp" #include "Error.hpp"
#include "Macros.hpp" #include "Macros.hpp"
@ -22,6 +24,7 @@
#include "Validators.hpp" #include "Validators.hpp"
namespace CLI { namespace CLI {
// [CLI11:option_hpp:verbatim]
using results_t = std::vector<std::string>; using results_t = std::vector<std::string>;
/// callback function definition /// callback function definition
@ -1316,4 +1319,5 @@ class Option : public OptionBase<Option> {
} }
}; // namespace CLI }; // namespace CLI
// [CLI11:option_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,15 +6,19 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
#include "Error.hpp" #include "Error.hpp"
#include "StringTools.hpp" #include "StringTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:split_hpp:verbatim]
namespace detail { namespace detail {
// Returns false if not a short option. Otherwise, sets opt name and rest and returns true // Returns false if not a short option. Otherwise, sets opt name and rest and returns true
@ -135,4 +139,5 @@ get_names(const std::vector<std::string> &input) {
} }
} // namespace detail } // namespace detail
// [CLI11:split_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,6 +6,7 @@
#pragma once #pragma once
// [CLI11:public_includes:set]
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
#include <locale> #include <locale>
@ -14,9 +15,12 @@
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
namespace CLI { namespace CLI {
// [CLI11:string_tools_hpp:verbatim]
/// Include the items in this namespace to get free conversion of enums to/from streams. /// Include the items in this namespace to get free conversion of enums to/from streams.
/// (This is available inside CLI as well, so CLI11 will use this without a using statement). /// (This is available inside CLI as well, so CLI11 will use this without a using statement).
namespace enums { namespace enums {
@ -409,4 +413,6 @@ inline std::string &add_quotes_if_needed(std::string &str) {
} // namespace detail } // namespace detail
// [CLI11:string_tools_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,7 +6,7 @@
#pragma once #pragma once
#include "StringTools.hpp" // [CLI11:public_includes:set]
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <memory> #include <memory>
@ -14,8 +14,12 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
#include "StringTools.hpp"
namespace CLI { namespace CLI {
// [CLI11:type_tools_hpp:verbatim]
// Type tools // Type tools
@ -1540,4 +1544,5 @@ void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
#endif #endif
} // namespace detail } // namespace detail
// [CLI11:type_tools_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -10,6 +10,7 @@
#include "StringTools.hpp" #include "StringTools.hpp"
#include "TypeTools.hpp" #include "TypeTools.hpp"
// [CLI11:public_includes:set]
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
@ -20,8 +21,9 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
// [CLI11:public_includes:end]
// [CLI11:verbatim] // [CLI11:validators_hpp_filesystem:verbatim]
// C standard library // C standard library
// Only needed for existence checking // Only needed for existence checking
@ -55,9 +57,10 @@
#include <sys/types.h> #include <sys/types.h>
#endif #endif
// [CLI11:verbatim] // [CLI11:validators_hpp_filesystem:end]
namespace CLI { namespace CLI {
// [CLI11:validators_hpp:verbatim]
class Option; class Option;
@ -1112,4 +1115,5 @@ inline std::pair<std::string, std::string> split_program_name(std::string comman
} // namespace detail } // namespace detail
/// @} /// @}
// [CLI11:validators_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -6,11 +6,11 @@
#pragma once #pragma once
// [CLI11:verbatim] // [CLI11:version_hpp:verbatim]
#define CLI11_VERSION_MAJOR 1 #define CLI11_VERSION_MAJOR 1
#define CLI11_VERSION_MINOR 9 #define CLI11_VERSION_MINOR 9
#define CLI11_VERSION_PATCH 1 #define CLI11_VERSION_PATCH 1
#define CLI11_VERSION "1.9.1" #define CLI11_VERSION "1.9.1"
// [CLI11:verbatim] // [CLI11:version_hpp:end]

View File

@ -5,171 +5,136 @@ from __future__ import print_function, unicode_literals
import os import os
import re import re
from argparse import ArgumentParser from argparse import ArgumentParser
from operator import add
from copy import copy
from functools import reduce
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import warnings
includes_local = re.compile(r"""^#include "(.*)"$""", re.MULTILINE) tag_str = r"""
includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE) ^ # Begin of line
version_finder = re.compile(r"""^#define CLI11_VERSION \"(.*)\"$""", re.MULTILINE) [/\s]+ # Whitespace or comment // chars
verbatim_tag_str = r""" \[ # A literal [
^ # Begin of line {tag}: # The tag
[^\n^\[]+ # Some characters, not including [ or the end of a line (?P<name>[\w_]+) # name: group name
\[ # A literal [ : # Colon
[^\]^\n]* # Anything except a closing ] (?P<action>[\w_]+) # action: type of include
CLI11:verbatim # The tag \] # A literal ]
[^\]^\n]* # Anything except a closing ] \s* # Whitespace
\] # A literal ] $ # End of a line
[^\n]* # Up to end of line
$ # End of a line (?P<content>.*) # All
^ # Begin of line
[/\s]+ # Whitespace or comment // chars
\[ # A literal [
{tag}: # The tag
(?P=name) # Repeated name
: # Colon
end # Literal "end"
\] # A literal ]
\s* # Whitespace
$ # End of a line
""" """
verbatim_all = re.compile(
verbatim_tag_str + "(.*)" + verbatim_tag_str, re.MULTILINE | re.DOTALL | re.VERBOSE
)
DIR = os.path.dirname(os.path.abspath(__file__)) DIR = os.path.dirname(os.path.abspath(__file__))
class HeaderFile(object): class HeaderGroups(dict):
TAG = "Unknown git revision" def __init__(self, tag):
LICENSE = "// BSD 3 clause" self.re_matcher = re.compile(
VERSION = "Unknown" tag_str.format(tag=tag), re.MULTILINE | re.DOTALL | re.VERBOSE
)
super(HeaderGroups, self).__init__()
def __init__(self, base, inc): def read_header(self, filename):
with open(os.path.join(base, inc)) as f: with open(filename) as f:
inner = f.read() inner = f.read()
version = version_finder.search(inner) matches = self.re_matcher.findall(inner)
if version:
self.__class__.VERSION = version.groups()[0]
# add self.verbatim if not matches:
if "CLI11:verbatim" in inner: warnings.warn(
self.verbatim = ["\n\n// Verbatim copy from {0}:".format(inc)] "Failed to find any matches in {filename}".format(filename=filename)
self.verbatim += verbatim_all.findall(inner) )
inner = verbatim_all.sub("", inner)
else:
self.verbatim = []
self.headers = set(includes_system.findall(inner)) for name, action, content in matches:
if action == "verbatim":
assert (
name not in self
), "{name} read in more than once! Quitting.".format(name=name)
self[name] = content
elif action == "set":
self[name] = self.get(name, set()) | set(content.strip().splitlines())
else:
raise RuntimeError("Action not understood, must be verbatim or set")
self.body = "\n// From {0}:\n\n".format(inc) + inner[inner.find("namespace") :] def post_process(self):
for key in self:
self.namespace = None if isinstance(self[key], set):
self[key] = "\n".join(self[key])
def __add__(self, other):
out = copy(self)
out.headers |= other.headers
out.body += other.body
out.verbatim += other.verbatim
return out
@property
def header_str(self):
return "\n".join("#include <" + h + ">" for h in sorted(self.headers))
@property
def verbatim_str(self):
return "\n".join(self.verbatim)
def insert_namespace(self, namespace):
self.namespace = namespace
def macro_replacement(self, before, after):
self.verbatim = [x.replace(before, after) for x in self.verbatim]
self.body = self.body.replace(before, after)
def __str__(self):
result = """\
#pragma once
// CLI11: Version {self.VERSION}
// Originally designed by Henry Schreiner
// https://github.com/CLIUtils/CLI11
//
// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
// from: {self.TAG}
//
// From LICENSE:
//
{self.LICENSE}
// Standard combined includes:
{self.header_str}
""".format(
self=self
)
if self.namespace:
result += "\nnamespace " + self.namespace + " {\n\n"
result += "{self.verbatim_str}\n{self.body}\n".format(self=self)
if self.namespace:
result += "} // namespace " + self.namespace + "\n\n"
return result
def MakeHeader( def MakeHeader(output, main_header, files, tag, namespace, macro=None, version=None):
output, main_header, include_dir="../include", namespace=None, macro=None groups = HeaderGroups(tag)
):
# Set tag if possible to class variable # Set tag if possible to class variable
try: try:
proc = Popen( proc = Popen(
["git", "describe", "--tags", "--always"], cwd=str(DIR), stdout=PIPE ["git", "describe", "--tags", "--always"], cwd=str(DIR), stdout=PIPE
) )
out, _ = proc.communicate() out, _ = proc.communicate()
groups["git"] = out.decode("utf-8").strip() if proc.returncode == 0 else ""
except OSError: except OSError:
pass groups["git"] = ""
else:
if proc.returncode == 0:
HeaderFile.TAG = out.decode("utf-8").strip()
base_dir = os.path.abspath(os.path.join(DIR, include_dir)) for f in files:
main_header = os.path.join(base_dir, main_header) groups.read_header(f)
header_dir = os.path.dirname(main_header)
licence_file = os.path.abspath(os.path.join(DIR, "../LICENSE"))
with open(licence_file) as f: groups["namespace"] = namespace
HeaderFile.LICENSE = "".join("// " + line for line in f) groups["version"] = version or groups["git"]
groups.post_process()
with open(main_header) as f: with open(main_header) as f:
header = f.read() single_header = f.read().format(**groups)
include_files = includes_local.findall(header)
headers = [HeaderFile(header_dir, inc) for inc in include_files]
single_header = reduce(add, headers)
if macro is not None: if macro is not None:
before = "CLI11_" before, after = macro
print("Converting macros", before, "->", macro) print("Converting macros", before, "->", after)
single_header.macro_replacement(before, macro) single_header.replace(before, after)
if namespace: if output is not None:
print("Adding namespace", namespace) with open(output, "w") as f:
single_header.insert_namespace(namespace) f.write(single_header)
with open(output, "w") as f: print("Created", output)
f.write(str(single_header)) else:
print(single_header)
print("Created", output)
if __name__ == "__main__": if __name__ == "__main__":
parser = ArgumentParser( parser = ArgumentParser(
usage="Convert source to single header include. Can optionally add namespace and search-replace replacements (for macros)." usage="Convert source to single header include. Can optionally add namespace and search-replace replacements (for macros)."
) )
parser.add_argument("output", help="Single header file output") parser.add_argument("--output", default=None, help="Single header file output")
parser.add_argument( parser.add_argument(
"--main", "--main",
default="CLI/CLI.hpp", default="CLI11.hpp.in",
help="The main include file that defines the other files", help="The main include file that defines the other files",
) )
parser.add_argument("--include", default="../include", help="The include directory") parser.add_argument("files", nargs="*", help="The header files")
parser.add_argument("--namespace", help="Add an optional namespace") parser.add_argument("--namespace", default="CLI", help="Set the namespace")
parser.add_argument("--macro", help="Replaces CLI11_ with NEW_PREFIX_") parser.add_argument("--tag", default="CLI11", help="Tag to look up")
parser.add_argument(
"--macro", nargs=2, help="Replaces OLD_PREFIX_ with NEW_PREFIX_"
)
parser.add_argument("--version", help="Include this version in the generated file")
args = parser.parse_args() args = parser.parse_args()
MakeHeader(args.output, args.main, args.include, args.namespace, args.macro) MakeHeader(
args.output,
args.main,
args.files,
args.tag,
args.namespace,
args.macro,
args.version,
)