| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| Copyright (c) 2012 - 2016, Victor Zverovich | ||
|
|
||
| All rights reserved. | ||
|
|
||
| Redistribution and use in source and binary forms, 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. | ||
|
|
||
| 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 OWNER 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| // Formatting library for C++ - std::locale support | ||
| // | ||
| // Copyright (c) 2012 - present, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #ifndef FMT_LOCALE_H_ | ||
| #define FMT_LOCALE_H_ | ||
|
|
||
| #include "format.h" | ||
| #include <locale> | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
|
|
||
| namespace internal { | ||
| template <typename Char> | ||
| typename buffer_context<Char>::type::iterator vformat_to( | ||
| const std::locale &loc, basic_buffer<Char> &buf, | ||
| basic_string_view<Char> format_str, | ||
| basic_format_args<typename buffer_context<Char>::type> args) { | ||
| typedef back_insert_range<basic_buffer<Char> > range; | ||
| return vformat_to<arg_formatter<range>>( | ||
| buf, to_string_view(format_str), args, internal::locale_ref(loc)); | ||
| } | ||
|
|
||
| template <typename Char> | ||
| std::basic_string<Char> vformat( | ||
| const std::locale &loc, basic_string_view<Char> format_str, | ||
| basic_format_args<typename buffer_context<Char>::type> args) { | ||
| basic_memory_buffer<Char> buffer; | ||
| internal::vformat_to(loc, buffer, format_str, args); | ||
| return fmt::to_string(buffer); | ||
| } | ||
| } | ||
|
|
||
| template <typename S, typename Char = FMT_CHAR(S)> | ||
| inline std::basic_string<Char> vformat( | ||
| const std::locale &loc, const S &format_str, | ||
| basic_format_args<typename buffer_context<Char>::type> args) { | ||
| return internal::vformat(loc, to_string_view(format_str), args); | ||
| } | ||
|
|
||
| template <typename S, typename... Args> | ||
| inline std::basic_string<FMT_CHAR(S)> format( | ||
| const std::locale &loc, const S &format_str, const Args &... args) { | ||
| return internal::vformat( | ||
| loc, to_string_view(format_str), | ||
| *internal::checked_args<S, Args...>(format_str, args...)); | ||
| } | ||
|
|
||
| template <typename String, typename OutputIt, typename... Args> | ||
| inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value, | ||
| OutputIt>::type | ||
| vformat_to(OutputIt out, const std::locale &loc, const String &format_str, | ||
| typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) { | ||
| typedef output_range<OutputIt, FMT_CHAR(String)> range; | ||
| return vformat_to<arg_formatter<range>>( | ||
| range(out), to_string_view(format_str), args, internal::locale_ref(loc)); | ||
| } | ||
|
|
||
| template <typename OutputIt, typename S, typename... Args> | ||
| inline typename std::enable_if< | ||
| internal::is_string<S>::value && | ||
| internal::is_output_iterator<OutputIt>::value, OutputIt>::type | ||
| format_to(OutputIt out, const std::locale &loc, const S &format_str, | ||
| const Args &... args) { | ||
| internal::check_format_string<Args...>(format_str); | ||
| typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context; | ||
| format_arg_store<context, Args...> as{args...}; | ||
| return vformat_to(out, loc, to_string_view(format_str), | ||
| basic_format_args<context>(as)); | ||
| } | ||
|
|
||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_LOCALE_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| // Formatting library for C++ - std::ostream support | ||
| // | ||
| // Copyright (c) 2012 - present, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #ifndef FMT_OSTREAM_H_ | ||
| #define FMT_OSTREAM_H_ | ||
|
|
||
| #include "format.h" | ||
| #include <ostream> | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
| namespace internal { | ||
|
|
||
| template <class Char> | ||
| class formatbuf : public std::basic_streambuf<Char> { | ||
| private: | ||
| typedef typename std::basic_streambuf<Char>::int_type int_type; | ||
| typedef typename std::basic_streambuf<Char>::traits_type traits_type; | ||
|
|
||
| basic_buffer<Char> &buffer_; | ||
|
|
||
| public: | ||
| formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {} | ||
|
|
||
| protected: | ||
| // The put-area is actually always empty. This makes the implementation | ||
| // simpler and has the advantage that the streambuf and the buffer are always | ||
| // in sync and sputc never writes into uninitialized memory. The obvious | ||
| // disadvantage is that each call to sputc always results in a (virtual) call | ||
| // to overflow. There is no disadvantage here for sputn since this always | ||
| // results in a call to xsputn. | ||
|
|
||
| int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { | ||
| if (!traits_type::eq_int_type(ch, traits_type::eof())) | ||
| buffer_.push_back(static_cast<Char>(ch)); | ||
| return ch; | ||
| } | ||
|
|
||
| std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { | ||
| buffer_.append(s, s + count); | ||
| return count; | ||
| } | ||
| }; | ||
|
|
||
| template <typename Char> | ||
| struct test_stream : std::basic_ostream<Char> { | ||
| private: | ||
| struct null; | ||
| // Hide all operator<< from std::basic_ostream<Char>. | ||
| void operator<<(null); | ||
| }; | ||
|
|
||
| // Checks if T has a user-defined operator<< (e.g. not a member of std::ostream). | ||
| template <typename T, typename Char> | ||
| class is_streamable { | ||
| private: | ||
| template <typename U> | ||
| static decltype( | ||
| internal::declval<test_stream<Char>&>() | ||
| << internal::declval<U>(), std::true_type()) test(int); | ||
|
|
||
| template <typename> | ||
| static std::false_type test(...); | ||
|
|
||
| typedef decltype(test<T>(0)) result; | ||
|
|
||
| public: | ||
| static const bool value = result::value; | ||
| }; | ||
|
|
||
| // Write the content of buf to os. | ||
| template <typename Char> | ||
| void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) { | ||
| const Char *data = buf.data(); | ||
| typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize; | ||
| UnsignedStreamSize size = buf.size(); | ||
| UnsignedStreamSize max_size = | ||
| internal::to_unsigned((std::numeric_limits<std::streamsize>::max)()); | ||
| do { | ||
| UnsignedStreamSize n = size <= max_size ? size : max_size; | ||
| os.write(data, static_cast<std::streamsize>(n)); | ||
| data += n; | ||
| size -= n; | ||
| } while (size != 0); | ||
| } | ||
|
|
||
| template <typename Char, typename T> | ||
| void format_value(basic_buffer<Char> &buffer, const T &value) { | ||
| internal::formatbuf<Char> format_buf(buffer); | ||
| std::basic_ostream<Char> output(&format_buf); | ||
| output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | ||
| output << value; | ||
| buffer.resize(buffer.size()); | ||
| } | ||
| } // namespace internal | ||
|
|
||
| // Disable conversion to int if T has an overloaded operator<< which is a free | ||
| // function (not a member of std::ostream). | ||
| template <typename T, typename Char> | ||
| struct convert_to_int<T, Char, void> { | ||
| static const bool value = | ||
| convert_to_int<T, Char, int>::value && | ||
| !internal::is_streamable<T, Char>::value; | ||
| }; | ||
|
|
||
| // Formats an object of type T that has an overloaded ostream operator<<. | ||
| template <typename T, typename Char> | ||
| struct formatter<T, Char, | ||
| typename std::enable_if< | ||
| internal::is_streamable<T, Char>::value && | ||
| !internal::format_type< | ||
| typename buffer_context<Char>::type, T>::value>::type> | ||
| : formatter<basic_string_view<Char>, Char> { | ||
|
|
||
| template <typename Context> | ||
| auto format(const T &value, Context &ctx) -> decltype(ctx.out()) { | ||
| basic_memory_buffer<Char> buffer; | ||
| internal::format_value(buffer, value); | ||
| basic_string_view<Char> str(buffer.data(), buffer.size()); | ||
| return formatter<basic_string_view<Char>, Char>::format(str, ctx); | ||
| } | ||
| }; | ||
|
|
||
| template <typename Char> | ||
| inline void vprint(std::basic_ostream<Char> &os, | ||
| basic_string_view<Char> format_str, | ||
| basic_format_args<typename buffer_context<Char>::type> args) { | ||
| basic_memory_buffer<Char> buffer; | ||
| internal::vformat_to(buffer, format_str, args); | ||
| internal::write(os, buffer); | ||
| } | ||
| /** | ||
| \rst | ||
| Prints formatted data to the stream *os*. | ||
| **Example**:: | ||
| fmt::print(cerr, "Don't {}!", "panic"); | ||
| \endrst | ||
| */ | ||
| template <typename S, typename... Args> | ||
| inline typename std::enable_if<internal::is_string<S>::value>::type | ||
| print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str, | ||
| const Args & ... args) { | ||
| internal::checked_args<S, Args...> ca(format_str, args...); | ||
| vprint(os, to_string_view(format_str), *ca); | ||
| } | ||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_OSTREAM_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,324 @@ | ||
| // A C++ interface to POSIX functions. | ||
| // | ||
| // Copyright (c) 2012 - 2016, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #ifndef FMT_POSIX_H_ | ||
| #define FMT_POSIX_H_ | ||
|
|
||
| #if defined(__MINGW32__) || defined(__CYGWIN__) | ||
| // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. | ||
| # undef __STRICT_ANSI__ | ||
| #endif | ||
|
|
||
| #include <errno.h> | ||
| #include <fcntl.h> // for O_RDONLY | ||
| #include <locale.h> // for locale_t | ||
| #include <stdio.h> | ||
| #include <stdlib.h> // for strtod_l | ||
|
|
||
| #include <cstddef> | ||
|
|
||
| #if defined __APPLE__ || defined(__FreeBSD__) | ||
| # include <xlocale.h> // for LC_NUMERIC_MASK on OS X | ||
| #endif | ||
|
|
||
| #include "format.h" | ||
|
|
||
| #ifndef FMT_POSIX | ||
| # if defined(_WIN32) && !defined(__MINGW32__) | ||
| // Fix warnings about deprecated symbols. | ||
| # define FMT_POSIX(call) _##call | ||
| # else | ||
| # define FMT_POSIX(call) call | ||
| # endif | ||
| #endif | ||
|
|
||
| // Calls to system functions are wrapped in FMT_SYSTEM for testability. | ||
| #ifdef FMT_SYSTEM | ||
| # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) | ||
| #else | ||
| # define FMT_SYSTEM(call) call | ||
| # ifdef _WIN32 | ||
| // Fix warnings about deprecated symbols. | ||
| # define FMT_POSIX_CALL(call) ::_##call | ||
| # else | ||
| # define FMT_POSIX_CALL(call) ::call | ||
| # endif | ||
| #endif | ||
|
|
||
| // Retries the expression while it evaluates to error_result and errno | ||
| // equals to EINTR. | ||
| #ifndef _WIN32 | ||
| # define FMT_RETRY_VAL(result, expression, error_result) \ | ||
| do { \ | ||
| result = (expression); \ | ||
| } while (result == error_result && errno == EINTR) | ||
| #else | ||
| # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) | ||
| #endif | ||
|
|
||
| #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
|
|
||
| /** | ||
| \rst | ||
| A reference to a null-terminated string. It can be constructed from a C | ||
| string or ``std::string``. | ||
| You can use one of the following typedefs for common character types: | ||
| +---------------+-----------------------------+ | ||
| | Type | Definition | | ||
| +===============+=============================+ | ||
| | cstring_view | basic_cstring_view<char> | | ||
| +---------------+-----------------------------+ | ||
| | wcstring_view | basic_cstring_view<wchar_t> | | ||
| +---------------+-----------------------------+ | ||
| This class is most useful as a parameter type to allow passing | ||
| different types of strings to a function, for example:: | ||
| template <typename... Args> | ||
| std::string format(cstring_view format_str, const Args & ... args); | ||
| format("{}", 42); | ||
| format(std::string("{}"), 42); | ||
| \endrst | ||
| */ | ||
| template <typename Char> | ||
| class basic_cstring_view { | ||
| private: | ||
| const Char *data_; | ||
|
|
||
| public: | ||
| /** Constructs a string reference object from a C string. */ | ||
| basic_cstring_view(const Char *s) : data_(s) {} | ||
|
|
||
| /** | ||
| \rst | ||
| Constructs a string reference from an ``std::string`` object. | ||
| \endrst | ||
| */ | ||
| basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {} | ||
|
|
||
| /** Returns the pointer to a C string. */ | ||
| const Char *c_str() const { return data_; } | ||
| }; | ||
|
|
||
| typedef basic_cstring_view<char> cstring_view; | ||
| typedef basic_cstring_view<wchar_t> wcstring_view; | ||
|
|
||
| // An error code. | ||
| class error_code { | ||
| private: | ||
| int value_; | ||
|
|
||
| public: | ||
| explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} | ||
|
|
||
| int get() const FMT_NOEXCEPT { return value_; } | ||
| }; | ||
|
|
||
| // A buffered file. | ||
| class buffered_file { | ||
| private: | ||
| FILE *file_; | ||
|
|
||
| friend class file; | ||
|
|
||
| explicit buffered_file(FILE *f) : file_(f) {} | ||
|
|
||
| public: | ||
| // Constructs a buffered_file object which doesn't represent any file. | ||
| buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {} | ||
|
|
||
| // Destroys the object closing the file it represents if any. | ||
| FMT_API ~buffered_file() FMT_NOEXCEPT; | ||
|
|
||
| private: | ||
| buffered_file(const buffered_file &) = delete; | ||
| void operator=(const buffered_file &) = delete; | ||
|
|
||
|
|
||
| public: | ||
| buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) { | ||
| other.file_ = FMT_NULL; | ||
| } | ||
|
|
||
| buffered_file& operator=(buffered_file &&other) { | ||
| close(); | ||
| file_ = other.file_; | ||
| other.file_ = FMT_NULL; | ||
| return *this; | ||
| } | ||
|
|
||
| // Opens a file. | ||
| FMT_API buffered_file(cstring_view filename, cstring_view mode); | ||
|
|
||
| // Closes the file. | ||
| FMT_API void close(); | ||
|
|
||
| // Returns the pointer to a FILE object representing this file. | ||
| FILE *get() const FMT_NOEXCEPT { return file_; } | ||
|
|
||
| // We place parentheses around fileno to workaround a bug in some versions | ||
| // of MinGW that define fileno as a macro. | ||
| FMT_API int (fileno)() const; | ||
|
|
||
| void vprint(string_view format_str, format_args args) { | ||
| fmt::vprint(file_, format_str, args); | ||
| } | ||
|
|
||
| template <typename... Args> | ||
| inline void print(string_view format_str, const Args & ... args) { | ||
| vprint(format_str, make_format_args(args...)); | ||
| } | ||
| }; | ||
|
|
||
| // A file. Closed file is represented by a file object with descriptor -1. | ||
| // Methods that are not declared with FMT_NOEXCEPT may throw | ||
| // fmt::system_error in case of failure. Note that some errors such as | ||
| // closing the file multiple times will cause a crash on Windows rather | ||
| // than an exception. You can get standard behavior by overriding the | ||
| // invalid parameter handler with _set_invalid_parameter_handler. | ||
| class file { | ||
| private: | ||
| int fd_; // File descriptor. | ||
|
|
||
| // Constructs a file object with a given descriptor. | ||
| explicit file(int fd) : fd_(fd) {} | ||
|
|
||
| public: | ||
| // Possible values for the oflag argument to the constructor. | ||
| enum { | ||
| RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. | ||
| WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. | ||
| RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. | ||
| }; | ||
|
|
||
| // Constructs a file object which doesn't represent any file. | ||
| file() FMT_NOEXCEPT : fd_(-1) {} | ||
|
|
||
| // Opens a file and constructs a file object representing this file. | ||
| FMT_API file(cstring_view path, int oflag); | ||
|
|
||
| private: | ||
| file(const file &) = delete; | ||
| void operator=(const file &) = delete; | ||
|
|
||
| public: | ||
| file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) { | ||
| other.fd_ = -1; | ||
| } | ||
|
|
||
| file& operator=(file &&other) { | ||
| close(); | ||
| fd_ = other.fd_; | ||
| other.fd_ = -1; | ||
| return *this; | ||
| } | ||
|
|
||
| // Destroys the object closing the file it represents if any. | ||
| FMT_API ~file() FMT_NOEXCEPT; | ||
|
|
||
| // Returns the file descriptor. | ||
| int descriptor() const FMT_NOEXCEPT { return fd_; } | ||
|
|
||
| // Closes the file. | ||
| FMT_API void close(); | ||
|
|
||
| // Returns the file size. The size has signed type for consistency with | ||
| // stat::st_size. | ||
| FMT_API long long size() const; | ||
|
|
||
| // Attempts to read count bytes from the file into the specified buffer. | ||
| FMT_API std::size_t read(void *buffer, std::size_t count); | ||
|
|
||
| // Attempts to write count bytes from the specified buffer to the file. | ||
| FMT_API std::size_t write(const void *buffer, std::size_t count); | ||
|
|
||
| // Duplicates a file descriptor with the dup function and returns | ||
| // the duplicate as a file object. | ||
| FMT_API static file dup(int fd); | ||
|
|
||
| // Makes fd be the copy of this file descriptor, closing fd first if | ||
| // necessary. | ||
| FMT_API void dup2(int fd); | ||
|
|
||
| // Makes fd be the copy of this file descriptor, closing fd first if | ||
| // necessary. | ||
| FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT; | ||
|
|
||
| // Creates a pipe setting up read_end and write_end file objects for reading | ||
| // and writing respectively. | ||
| FMT_API static void pipe(file &read_end, file &write_end); | ||
|
|
||
| // Creates a buffered_file object associated with this file and detaches | ||
| // this file object from the file. | ||
| FMT_API buffered_file fdopen(const char *mode); | ||
| }; | ||
|
|
||
| // Returns the memory page size. | ||
| long getpagesize(); | ||
|
|
||
| #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \ | ||
| !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \ | ||
| !defined(__NEWLIB_H__) | ||
| # define FMT_LOCALE | ||
| #endif | ||
|
|
||
| #ifdef FMT_LOCALE | ||
| // A "C" numeric locale. | ||
| class Locale { | ||
| private: | ||
| # ifdef _MSC_VER | ||
| typedef _locale_t locale_t; | ||
|
|
||
| enum { LC_NUMERIC_MASK = LC_NUMERIC }; | ||
|
|
||
| static locale_t newlocale(int category_mask, const char *locale, locale_t) { | ||
| return _create_locale(category_mask, locale); | ||
| } | ||
|
|
||
| static void freelocale(locale_t locale) { | ||
| _free_locale(locale); | ||
| } | ||
|
|
||
| static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { | ||
| return _strtod_l(nptr, endptr, locale); | ||
| } | ||
| # endif | ||
|
|
||
| locale_t locale_; | ||
|
|
||
| Locale(const Locale &) = delete; | ||
| void operator=(const Locale &) = delete; | ||
|
|
||
| public: | ||
| typedef locale_t Type; | ||
|
|
||
| Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { | ||
| if (!locale_) | ||
| FMT_THROW(system_error(errno, "cannot create locale")); | ||
| } | ||
| ~Locale() { freelocale(locale_); } | ||
|
|
||
| Type get() const { return locale_; } | ||
|
|
||
| // Converts string to floating-point number and advances str past the end | ||
| // of the parsed input. | ||
| double strtod(const char *&str) const { | ||
| char *end = FMT_NULL; | ||
| double result = strtod_l(str, &end, locale_); | ||
| str = end; | ||
| return result; | ||
| } | ||
| }; | ||
| #endif // FMT_LOCALE | ||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_POSIX_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,308 @@ | ||
| // Formatting library for C++ - the core API | ||
| // | ||
| // Copyright (c) 2012 - present, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
| // | ||
| // Copyright (c) 2018 - present, Remotion (Igor Schulz) | ||
| // All Rights Reserved | ||
| // {fmt} support for ranges, containers and types tuple interface. | ||
|
|
||
| #ifndef FMT_RANGES_H_ | ||
| #define FMT_RANGES_H_ | ||
|
|
||
| #include "format.h" | ||
| #include <type_traits> | ||
|
|
||
| // output only up to N items from the range. | ||
| #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT | ||
| # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 | ||
| #endif | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
|
|
||
| template <typename Char> | ||
| struct formatting_base { | ||
| template <typename ParseContext> | ||
| FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { | ||
| return ctx.begin(); | ||
| } | ||
| }; | ||
|
|
||
| template <typename Char, typename Enable = void> | ||
| struct formatting_range : formatting_base<Char> { | ||
| static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = | ||
| FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range. | ||
| Char prefix; | ||
| Char delimiter; | ||
| Char postfix; | ||
| formatting_range() : prefix('{'), delimiter(','), postfix('}') {} | ||
| static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; | ||
| static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; | ||
| }; | ||
|
|
||
| template <typename Char, typename Enable = void> | ||
| struct formatting_tuple : formatting_base<Char> { | ||
| Char prefix; | ||
| Char delimiter; | ||
| Char postfix; | ||
| formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} | ||
| static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; | ||
| static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; | ||
| }; | ||
|
|
||
| namespace internal { | ||
|
|
||
| template <typename RangeT, typename OutputIterator> | ||
| void copy(const RangeT &range, OutputIterator out) { | ||
| for (auto it = range.begin(), end = range.end(); it != end; ++it) | ||
| *out++ = *it; | ||
| } | ||
|
|
||
| template <typename OutputIterator> | ||
| void copy(const char *str, OutputIterator out) { | ||
| const char *p_curr = str; | ||
| while (*p_curr) { | ||
| *out++ = *p_curr++; | ||
| } | ||
| } | ||
|
|
||
| template <typename OutputIterator> | ||
| void copy(char ch, OutputIterator out) { | ||
| *out++ = ch; | ||
| } | ||
|
|
||
| /// Return true value if T has std::string interface, like std::string_view. | ||
| template <typename T> | ||
| class is_like_std_string { | ||
| template <typename U> | ||
| static auto check(U *p) -> | ||
| decltype(p->find('a'), p->length(), p->data(), int()); | ||
| template <typename> | ||
| static void check(...); | ||
|
|
||
| public: | ||
| static FMT_CONSTEXPR_DECL const bool value = | ||
| !std::is_void<decltype(check<T>(FMT_NULL))>::value; | ||
| }; | ||
|
|
||
| template <typename Char> | ||
| struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; | ||
|
|
||
| template <typename... Ts> | ||
| struct conditional_helper {}; | ||
|
|
||
| template <typename T, typename _ = void> | ||
| struct is_range_ : std::false_type {}; | ||
|
|
||
| #if !FMT_MSC_VER || FMT_MSC_VER > 1800 | ||
| template <typename T> | ||
| struct is_range_<T, typename std::conditional< | ||
| false, | ||
| conditional_helper<decltype(internal::declval<T>().begin()), | ||
| decltype(internal::declval<T>().end())>, | ||
| void>::type> : std::true_type {}; | ||
| #endif | ||
|
|
||
| /// tuple_size and tuple_element check. | ||
| template <typename T> | ||
| class is_tuple_like_ { | ||
| template <typename U> | ||
| static auto check(U *p) -> | ||
| decltype(std::tuple_size<U>::value, | ||
| internal::declval<typename std::tuple_element<0, U>::type>(), int()); | ||
| template <typename> | ||
| static void check(...); | ||
|
|
||
| public: | ||
| static FMT_CONSTEXPR_DECL const bool value = | ||
| !std::is_void<decltype(check<T>(FMT_NULL))>::value; | ||
| }; | ||
|
|
||
| // Check for integer_sequence | ||
| #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 | ||
| template <typename T, T... N> | ||
| using integer_sequence = std::integer_sequence<T, N...>; | ||
| template <std::size_t... N> | ||
| using index_sequence = std::index_sequence<N...>; | ||
| template <std::size_t N> | ||
| using make_index_sequence = std::make_index_sequence<N>; | ||
| #else | ||
| template <typename T, T... N> | ||
| struct integer_sequence { | ||
| typedef T value_type; | ||
|
|
||
| static FMT_CONSTEXPR std::size_t size() { | ||
| return sizeof...(N); | ||
| } | ||
| }; | ||
|
|
||
| template <std::size_t... N> | ||
| using index_sequence = integer_sequence<std::size_t, N...>; | ||
|
|
||
| template <typename T, std::size_t N, T... Ns> | ||
| struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; | ||
| template <typename T, T... Ns> | ||
| struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; | ||
|
|
||
| template <std::size_t N> | ||
| using make_index_sequence = make_integer_sequence<std::size_t, N>; | ||
| #endif | ||
|
|
||
| template <class Tuple, class F, size_t... Is> | ||
| void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT { | ||
| using std::get; | ||
| // using free function get<I>(T) now. | ||
| const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; | ||
| (void)_; // blocks warnings | ||
| } | ||
|
|
||
| template <class T> | ||
| FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> | ||
| get_indexes(T const &) { return {}; } | ||
|
|
||
| template <class Tuple, class F> | ||
| void for_each(Tuple &&tup, F &&f) { | ||
| const auto indexes = get_indexes(tup); | ||
| for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); | ||
| } | ||
|
|
||
| template<typename Arg> | ||
| FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, | ||
| typename std::enable_if< | ||
| !is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) { | ||
| return add_space ? " {}" : "{}"; | ||
| } | ||
|
|
||
| template<typename Arg> | ||
| FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, | ||
| typename std::enable_if< | ||
| is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) { | ||
| return add_space ? " \"{}\"" : "\"{}\""; | ||
| } | ||
|
|
||
| FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { | ||
| return add_space ? " \"{}\"" : "\"{}\""; | ||
| } | ||
| FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { | ||
| return add_space ? L" \"{}\"" : L"\"{}\""; | ||
| } | ||
|
|
||
| FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { | ||
| return add_space ? " '{}'" : "'{}'"; | ||
| } | ||
| FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { | ||
| return add_space ? L" '{}'" : L"'{}'"; | ||
| } | ||
|
|
||
| } // namespace internal | ||
|
|
||
| template <typename T> | ||
| struct is_tuple_like { | ||
| static FMT_CONSTEXPR_DECL const bool value = | ||
| internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; | ||
| }; | ||
|
|
||
| template <typename TupleT, typename Char> | ||
| struct formatter<TupleT, Char, | ||
| typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> { | ||
| private: | ||
| // C++11 generic lambda for format() | ||
| template <typename FormatContext> | ||
| struct format_each { | ||
| template <typename T> | ||
| void operator()(const T& v) { | ||
| if (i > 0) { | ||
| if (formatting.add_prepostfix_space) { | ||
| *out++ = ' '; | ||
| } | ||
| internal::copy(formatting.delimiter, out); | ||
| } | ||
| format_to(out, | ||
| internal::format_str_quoted( | ||
| (formatting.add_delimiter_spaces && i > 0), v), | ||
| v); | ||
| ++i; | ||
| } | ||
|
|
||
| formatting_tuple<Char>& formatting; | ||
| std::size_t& i; | ||
| typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out; | ||
| }; | ||
|
|
||
| public: | ||
| formatting_tuple<Char> formatting; | ||
|
|
||
| template <typename ParseContext> | ||
| FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { | ||
| return formatting.parse(ctx); | ||
| } | ||
|
|
||
| template <typename FormatContext = format_context> | ||
| auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) { | ||
| auto out = ctx.out(); | ||
| std::size_t i = 0; | ||
| internal::copy(formatting.prefix, out); | ||
|
|
||
| internal::for_each(values, format_each<FormatContext>{formatting, i, out}); | ||
| if (formatting.add_prepostfix_space) { | ||
| *out++ = ' '; | ||
| } | ||
| internal::copy(formatting.postfix, out); | ||
|
|
||
| return ctx.out(); | ||
| } | ||
| }; | ||
|
|
||
| template <typename T> | ||
| struct is_range { | ||
| static FMT_CONSTEXPR_DECL const bool value = | ||
| internal::is_range_<T>::value && !internal::is_like_std_string<T>::value; | ||
| }; | ||
|
|
||
| template <typename RangeT, typename Char> | ||
| struct formatter<RangeT, Char, | ||
| typename std::enable_if<fmt::is_range<RangeT>::value>::type> { | ||
|
|
||
| formatting_range<Char> formatting; | ||
|
|
||
| template <typename ParseContext> | ||
| FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { | ||
| return formatting.parse(ctx); | ||
| } | ||
|
|
||
| template <typename FormatContext> | ||
| typename FormatContext::iterator format( | ||
| const RangeT &values, FormatContext &ctx) { | ||
| auto out = ctx.out(); | ||
| internal::copy(formatting.prefix, out); | ||
| std::size_t i = 0; | ||
| for (auto it = values.begin(), end = values.end(); it != end; ++it) { | ||
| if (i > 0) { | ||
| if (formatting.add_prepostfix_space) { | ||
| *out++ = ' '; | ||
| } | ||
| internal::copy(formatting.delimiter, out); | ||
| } | ||
| format_to(out, | ||
| internal::format_str_quoted( | ||
| (formatting.add_delimiter_spaces && i > 0), *it), | ||
| *it); | ||
| if (++i > formatting.range_length_limit) { | ||
| format_to(out, " ... <other elements>"); | ||
| break; | ||
| } | ||
| } | ||
| if (formatting.add_prepostfix_space) { | ||
| *out++ = ' '; | ||
| } | ||
| internal::copy(formatting.postfix, out); | ||
| return ctx.out(); | ||
| } | ||
| }; | ||
|
|
||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_RANGES_H_ | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| // Formatting library for C++ - time formatting | ||
| // | ||
| // Copyright (c) 2012 - present, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #ifndef FMT_TIME_H_ | ||
| #define FMT_TIME_H_ | ||
|
|
||
| #include "format.h" | ||
| #include <ctime> | ||
| #include <locale> | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
|
|
||
| // Prevents expansion of a preceding token as a function-style macro. | ||
| // Usage: f FMT_NOMACRO() | ||
| #define FMT_NOMACRO | ||
|
|
||
| namespace internal{ | ||
| inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } | ||
| inline null<> localtime_s(...) { return null<>(); } | ||
| inline null<> gmtime_r(...) { return null<>(); } | ||
| inline null<> gmtime_s(...) { return null<>(); } | ||
| } // namespace internal | ||
|
|
||
| // Thread-safe replacement for std::localtime | ||
| inline std::tm localtime(std::time_t time) { | ||
| struct dispatcher { | ||
| std::time_t time_; | ||
| std::tm tm_; | ||
|
|
||
| dispatcher(std::time_t t): time_(t) {} | ||
|
|
||
| bool run() { | ||
| using namespace fmt::internal; | ||
| return handle(localtime_r(&time_, &tm_)); | ||
| } | ||
|
|
||
| bool handle(std::tm *tm) { return tm != FMT_NULL; } | ||
|
|
||
| bool handle(internal::null<>) { | ||
| using namespace fmt::internal; | ||
| return fallback(localtime_s(&tm_, &time_)); | ||
| } | ||
|
|
||
| bool fallback(int res) { return res == 0; } | ||
|
|
||
| #if !FMT_MSC_VER | ||
| bool fallback(internal::null<>) { | ||
| using namespace fmt::internal; | ||
| std::tm *tm = std::localtime(&time_); | ||
| if (tm) tm_ = *tm; | ||
| return tm != FMT_NULL; | ||
| } | ||
| #endif | ||
| }; | ||
| dispatcher lt(time); | ||
| // Too big time values may be unsupported. | ||
| if (!lt.run()) | ||
| FMT_THROW(format_error("time_t value out of range")); | ||
| return lt.tm_; | ||
| } | ||
|
|
||
| // Thread-safe replacement for std::gmtime | ||
| inline std::tm gmtime(std::time_t time) { | ||
| struct dispatcher { | ||
| std::time_t time_; | ||
| std::tm tm_; | ||
|
|
||
| dispatcher(std::time_t t): time_(t) {} | ||
|
|
||
| bool run() { | ||
| using namespace fmt::internal; | ||
| return handle(gmtime_r(&time_, &tm_)); | ||
| } | ||
|
|
||
| bool handle(std::tm *tm) { return tm != FMT_NULL; } | ||
|
|
||
| bool handle(internal::null<>) { | ||
| using namespace fmt::internal; | ||
| return fallback(gmtime_s(&tm_, &time_)); | ||
| } | ||
|
|
||
| bool fallback(int res) { return res == 0; } | ||
|
|
||
| #if !FMT_MSC_VER | ||
| bool fallback(internal::null<>) { | ||
| std::tm *tm = std::gmtime(&time_); | ||
| if (tm) tm_ = *tm; | ||
| return tm != FMT_NULL; | ||
| } | ||
| #endif | ||
| }; | ||
| dispatcher gt(time); | ||
| // Too big time values may be unsupported. | ||
| if (!gt.run()) | ||
| FMT_THROW(format_error("time_t value out of range")); | ||
| return gt.tm_; | ||
| } | ||
|
|
||
| namespace internal { | ||
| inline std::size_t strftime(char *str, std::size_t count, const char *format, | ||
| const std::tm *time) { | ||
| return std::strftime(str, count, format, time); | ||
| } | ||
|
|
||
| inline std::size_t strftime(wchar_t *str, std::size_t count, | ||
| const wchar_t *format, const std::tm *time) { | ||
| return std::wcsftime(str, count, format, time); | ||
| } | ||
| } | ||
|
|
||
| template <typename Char> | ||
| struct formatter<std::tm, Char> { | ||
| template <typename ParseContext> | ||
| auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { | ||
| auto it = ctx.begin(); | ||
| if (it != ctx.end() && *it == ':') | ||
| ++it; | ||
| auto end = it; | ||
| while (end != ctx.end() && *end != '}') | ||
| ++end; | ||
| tm_format.reserve(internal::to_unsigned(end - it + 1)); | ||
| tm_format.append(it, end); | ||
| tm_format.push_back('\0'); | ||
| return end; | ||
| } | ||
|
|
||
| template <typename FormatContext> | ||
| auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { | ||
| basic_memory_buffer<Char> buf; | ||
| std::size_t start = buf.size(); | ||
| for (;;) { | ||
| std::size_t size = buf.capacity() - start; | ||
| std::size_t count = | ||
| internal::strftime(&buf[start], size, &tm_format[0], &tm); | ||
| if (count != 0) { | ||
| buf.resize(start + count); | ||
| break; | ||
| } | ||
| if (size >= tm_format.size() * 256) { | ||
| // If the buffer is 256 times larger than the format string, assume | ||
| // that `strftime` gives an empty result. There doesn't seem to be a | ||
| // better way to distinguish the two cases: | ||
| // https://github.com/fmtlib/fmt/issues/367 | ||
| break; | ||
| } | ||
| const std::size_t MIN_GROWTH = 10; | ||
| buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); | ||
| } | ||
| return std::copy(buf.begin(), buf.end(), ctx.out()); | ||
| } | ||
|
|
||
| basic_memory_buffer<Char> tm_format; | ||
| }; | ||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_TIME_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Formatting library for C++ | ||
| // | ||
| // Copyright (c) 2012 - 2016, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #include "fmt/format-inl.h" | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
| template struct internal::basic_data<void>; | ||
| template FMT_API internal::locale_ref::locale_ref(const std::locale &loc); | ||
| template FMT_API std::locale internal::locale_ref::get<std::locale>() const; | ||
|
|
||
| // Explicit instantiations for char. | ||
|
|
||
| template FMT_API char internal::thousands_sep_impl(locale_ref); | ||
|
|
||
| template FMT_API void internal::basic_buffer<char>::append(const char *, const char *); | ||
|
|
||
| template FMT_API void internal::arg_map<format_context>::init( | ||
| const basic_format_args<format_context> &args); | ||
|
|
||
| template FMT_API int internal::char_traits<char>::format_float( | ||
| char *, std::size_t, const char *, int, double); | ||
|
|
||
| template FMT_API int internal::char_traits<char>::format_float( | ||
| char *, std::size_t, const char *, int, long double); | ||
|
|
||
| template FMT_API std::string internal::vformat<char>( | ||
| string_view, basic_format_args<format_context>); | ||
|
|
||
| template FMT_API format_context::iterator internal::vformat_to( | ||
| internal::buffer &, string_view, basic_format_args<format_context>); | ||
|
|
||
| template FMT_API void internal::sprintf_format( | ||
| double, internal::buffer &, core_format_specs); | ||
| template FMT_API void internal::sprintf_format( | ||
| long double, internal::buffer &, core_format_specs); | ||
|
|
||
| // Explicit instantiations for wchar_t. | ||
|
|
||
| template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); | ||
|
|
||
| template FMT_API void internal::basic_buffer<wchar_t>::append( | ||
| const wchar_t *, const wchar_t *); | ||
|
|
||
| template FMT_API void internal::arg_map<wformat_context>::init( | ||
| const basic_format_args<wformat_context> &); | ||
|
|
||
| template FMT_API int internal::char_traits<wchar_t>::format_float( | ||
| wchar_t *, std::size_t, const wchar_t *, int, double); | ||
|
|
||
| template FMT_API int internal::char_traits<wchar_t>::format_float( | ||
| wchar_t *, std::size_t, const wchar_t *, int, long double); | ||
|
|
||
| template FMT_API std::wstring internal::vformat<wchar_t>( | ||
| wstring_view, basic_format_args<wformat_context>); | ||
| FMT_END_NAMESPACE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,244 @@ | ||
| // A C++ interface to POSIX functions. | ||
| // | ||
| // Copyright (c) 2012 - 2016, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| // Disable bogus MSVC warnings. | ||
| #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) | ||
| # define _CRT_SECURE_NO_WARNINGS | ||
| #endif | ||
|
|
||
| #include "fmt/posix.h" | ||
|
|
||
| #include <limits.h> | ||
| #include <sys/types.h> | ||
| #include <sys/stat.h> | ||
|
|
||
| #ifndef _WIN32 | ||
| # include <unistd.h> | ||
| #else | ||
| # ifndef WIN32_LEAN_AND_MEAN | ||
| # define WIN32_LEAN_AND_MEAN | ||
| # endif | ||
| # include <windows.h> | ||
| # include <io.h> | ||
|
|
||
| # define O_CREAT _O_CREAT | ||
| # define O_TRUNC _O_TRUNC | ||
|
|
||
| # ifndef S_IRUSR | ||
| # define S_IRUSR _S_IREAD | ||
| # endif | ||
|
|
||
| # ifndef S_IWUSR | ||
| # define S_IWUSR _S_IWRITE | ||
| # endif | ||
|
|
||
| # ifdef __MINGW32__ | ||
| # define _SH_DENYNO 0x40 | ||
| # endif | ||
|
|
||
| #endif // _WIN32 | ||
|
|
||
| #ifdef fileno | ||
| # undef fileno | ||
| #endif | ||
|
|
||
| namespace { | ||
| #ifdef _WIN32 | ||
| // Return type of read and write functions. | ||
| typedef int RWResult; | ||
|
|
||
| // On Windows the count argument to read and write is unsigned, so convert | ||
| // it from size_t preventing integer overflow. | ||
| inline unsigned convert_rwcount(std::size_t count) { | ||
| return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; | ||
| } | ||
| #else | ||
| // Return type of read and write functions. | ||
| typedef ssize_t RWResult; | ||
|
|
||
| inline std::size_t convert_rwcount(std::size_t count) { return count; } | ||
| #endif | ||
| } | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
|
|
||
| buffered_file::~buffered_file() FMT_NOEXCEPT { | ||
| if (file_ && FMT_SYSTEM(fclose(file_)) != 0) | ||
| report_system_error(errno, "cannot close file"); | ||
| } | ||
|
|
||
| buffered_file::buffered_file(cstring_view filename, cstring_view mode) { | ||
| FMT_RETRY_VAL(file_, | ||
| FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL); | ||
| if (!file_) | ||
| FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); | ||
| } | ||
|
|
||
| void buffered_file::close() { | ||
| if (!file_) | ||
| return; | ||
| int result = FMT_SYSTEM(fclose(file_)); | ||
| file_ = FMT_NULL; | ||
| if (result != 0) | ||
| FMT_THROW(system_error(errno, "cannot close file")); | ||
| } | ||
|
|
||
| // A macro used to prevent expansion of fileno on broken versions of MinGW. | ||
| #define FMT_ARGS | ||
|
|
||
| int buffered_file::fileno() const { | ||
| int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); | ||
| if (fd == -1) | ||
| FMT_THROW(system_error(errno, "cannot get file descriptor")); | ||
| return fd; | ||
| } | ||
|
|
||
| file::file(cstring_view path, int oflag) { | ||
| int mode = S_IRUSR | S_IWUSR; | ||
| #if defined(_WIN32) && !defined(__MINGW32__) | ||
| fd_ = -1; | ||
| FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); | ||
| #else | ||
| FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); | ||
| #endif | ||
| if (fd_ == -1) | ||
| FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); | ||
| } | ||
|
|
||
| file::~file() FMT_NOEXCEPT { | ||
| // Don't retry close in case of EINTR! | ||
| // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | ||
| if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) | ||
| report_system_error(errno, "cannot close file"); | ||
| } | ||
|
|
||
| void file::close() { | ||
| if (fd_ == -1) | ||
| return; | ||
| // Don't retry close in case of EINTR! | ||
| // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | ||
| int result = FMT_POSIX_CALL(close(fd_)); | ||
| fd_ = -1; | ||
| if (result != 0) | ||
| FMT_THROW(system_error(errno, "cannot close file")); | ||
| } | ||
|
|
||
| long long file::size() const { | ||
| #ifdef _WIN32 | ||
| // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT | ||
| // is less than 0x0500 as is the case with some default MinGW builds. | ||
| // Both functions support large file sizes. | ||
| DWORD size_upper = 0; | ||
| HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); | ||
| DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); | ||
| if (size_lower == INVALID_FILE_SIZE) { | ||
| DWORD error = GetLastError(); | ||
| if (error != NO_ERROR) | ||
| FMT_THROW(windows_error(GetLastError(), "cannot get file size")); | ||
| } | ||
| unsigned long long long_size = size_upper; | ||
| return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; | ||
| #else | ||
| typedef struct stat Stat; | ||
| Stat file_stat = Stat(); | ||
| if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) | ||
| FMT_THROW(system_error(errno, "cannot get file attributes")); | ||
| static_assert(sizeof(long long) >= sizeof(file_stat.st_size), | ||
| "return type of file::size is not large enough"); | ||
| return file_stat.st_size; | ||
| #endif | ||
| } | ||
|
|
||
| std::size_t file::read(void *buffer, std::size_t count) { | ||
| RWResult result = 0; | ||
| FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); | ||
| if (result < 0) | ||
| FMT_THROW(system_error(errno, "cannot read from file")); | ||
| return internal::to_unsigned(result); | ||
| } | ||
|
|
||
| std::size_t file::write(const void *buffer, std::size_t count) { | ||
| RWResult result = 0; | ||
| FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); | ||
| if (result < 0) | ||
| FMT_THROW(system_error(errno, "cannot write to file")); | ||
| return internal::to_unsigned(result); | ||
| } | ||
|
|
||
| file file::dup(int fd) { | ||
| // Don't retry as dup doesn't return EINTR. | ||
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html | ||
| int new_fd = FMT_POSIX_CALL(dup(fd)); | ||
| if (new_fd == -1) | ||
| FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); | ||
| return file(new_fd); | ||
| } | ||
|
|
||
| void file::dup2(int fd) { | ||
| int result = 0; | ||
| FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | ||
| if (result == -1) { | ||
| FMT_THROW(system_error(errno, | ||
| "cannot duplicate file descriptor {} to {}", fd_, fd)); | ||
| } | ||
| } | ||
|
|
||
| void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT { | ||
| int result = 0; | ||
| FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | ||
| if (result == -1) | ||
| ec = error_code(errno); | ||
| } | ||
|
|
||
| void file::pipe(file &read_end, file &write_end) { | ||
| // Close the descriptors first to make sure that assignments don't throw | ||
| // and there are no leaks. | ||
| read_end.close(); | ||
| write_end.close(); | ||
| int fds[2] = {}; | ||
| #ifdef _WIN32 | ||
| // Make the default pipe capacity same as on Linux 2.6.11+. | ||
| enum { DEFAULT_CAPACITY = 65536 }; | ||
| int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); | ||
| #else | ||
| // Don't retry as the pipe function doesn't return EINTR. | ||
| // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html | ||
| int result = FMT_POSIX_CALL(pipe(fds)); | ||
| #endif | ||
| if (result != 0) | ||
| FMT_THROW(system_error(errno, "cannot create pipe")); | ||
| // The following assignments don't throw because read_fd and write_fd | ||
| // are closed. | ||
| read_end = file(fds[0]); | ||
| write_end = file(fds[1]); | ||
| } | ||
|
|
||
| buffered_file file::fdopen(const char *mode) { | ||
| // Don't retry as fdopen doesn't return EINTR. | ||
| FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); | ||
| if (!f) | ||
| FMT_THROW(system_error(errno, | ||
| "cannot associate stream with file descriptor")); | ||
| buffered_file bf(f); | ||
| fd_ = -1; | ||
| return bf; | ||
| } | ||
|
|
||
| long getpagesize() { | ||
| #ifdef _WIN32 | ||
| SYSTEM_INFO si; | ||
| GetSystemInfo(&si); | ||
| return si.dwPageSize; | ||
| #else | ||
| long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); | ||
| if (size < 0) | ||
| FMT_THROW(system_error(errno, "cannot get memory page size")); | ||
| return size; | ||
| #endif | ||
| } | ||
| FMT_END_NAMESPACE | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| LOCAL_PATH := $(call my-dir) | ||
| include $(CLEAR_VARS) | ||
|
|
||
| LOCAL_MODULE := fmt_static | ||
| LOCAL_MODULE_FILENAME := libfmt | ||
|
|
||
| LOCAL_SRC_FILES := ../src/format.cc | ||
|
|
||
| LOCAL_C_INCLUDES := $(LOCAL_PATH) | ||
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) | ||
|
|
||
| LOCAL_CFLAGS += -std=c++11 -fexceptions | ||
|
|
||
| include $(BUILD_STATIC_LIBRARY) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| <manifest package="net.fmtlib" /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| This directory contains build support files such as | ||
|
|
||
| * CMake modules | ||
| * Build scripts | ||
| * qmake (static build with dynamic libc only) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #!/usr/bin/env python | ||
| # Build the project on AppVeyor. | ||
|
|
||
| import os | ||
| from subprocess import check_call | ||
|
|
||
| build = os.environ['BUILD'] | ||
| config = os.environ['CONFIGURATION'] | ||
| platform = os.environ['PLATFORM'] | ||
| path = os.environ['PATH'] | ||
| image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE'] | ||
| jobid = os.environ['APPVEYOR_JOB_ID'] | ||
| cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..'] | ||
| if build == 'mingw': | ||
| cmake_command.append('-GMinGW Makefiles') | ||
| build_command = ['mingw32-make', '-j4'] | ||
| test_command = ['mingw32-make', 'test'] | ||
| # Remove the path to Git bin directory from $PATH because it breaks | ||
| # MinGW config. | ||
| path = path.replace(r'C:\Program Files (x86)\Git\bin', '') | ||
| os.environ['PATH'] = r'C:\MinGW\bin;' + path | ||
| else: | ||
| # Add MSBuild 14.0 to PATH as described in | ||
| # http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc. | ||
| os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path | ||
| if image == 'Visual Studio 2013': | ||
| generator = 'Visual Studio 12 2013' | ||
| elif image == 'Visual Studio 2015': | ||
| generator = 'Visual Studio 14 2015' | ||
| elif image == 'Visual Studio 2017': | ||
| generator = 'Visual Studio 15 2017' | ||
| if platform == 'x64': | ||
| generator += ' Win64' | ||
| cmake_command.append('-G' + generator) | ||
| build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4'] | ||
| test_command = ['ctest', '-C', config] | ||
|
|
||
| check_call(cmake_command) | ||
| check_call(build_command) | ||
| check_call(test_command) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| configuration: | ||
| - Debug | ||
| - Release | ||
|
|
||
| clone_depth: 1 | ||
|
|
||
| platform: | ||
| - Win32 | ||
| - x64 | ||
|
|
||
| image: | ||
| - Visual Studio 2013 | ||
| - Visual Studio 2015 | ||
| - Visual Studio 2017 | ||
|
|
||
| environment: | ||
| CTEST_OUTPUT_ON_FAILURE: 1 | ||
| MSVC_DEFAULT_OPTIONS: ON | ||
| BUILD: msvc | ||
|
|
||
| before_build: | ||
| - mkdir build | ||
| - cd build | ||
|
|
||
| build_script: | ||
| - python ../support/appveyor-build.py | ||
|
|
||
| on_failure: | ||
| - appveyor PushArtifact Testing/Temporary/LastTest.log | ||
| - appveyor AddTest test | ||
|
|
||
| # Uncomment this to debug AppVeyor failures. | ||
| #on_finish: | ||
| # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
|
|
||
| // General gradle arguments for root project | ||
| buildscript { | ||
| repositories { | ||
| google() | ||
| jcenter() | ||
| } | ||
| dependencies { | ||
| // | ||
| // https://developer.android.com/studio/releases/gradle-plugin | ||
| // | ||
| // Notice that 3.1.3 here is the version of [Android Gradle Plugin] | ||
| // Accroding to URL above you will need Gradle 4.4 or higher | ||
| // | ||
| classpath 'com.android.tools.build:gradle:3.1.3' | ||
| } | ||
| } | ||
| repositories { | ||
| google() | ||
| jcenter() | ||
| } | ||
|
|
||
| // Output: Shared library (.so) for Android | ||
| apply plugin: 'com.android.library' | ||
|
|
||
| android { | ||
| compileSdkVersion 25 // Android 7.0 | ||
|
|
||
| // Target ABI | ||
| // - This option controls target platform of module | ||
| // - The platform might be limited by compiler's support | ||
| // some can work with Clang(default), but some can work only with GCC... | ||
| // if bad, both toolchains might not support it | ||
| splits { | ||
| abi { | ||
| enable true | ||
| // Specify platforms for Application | ||
| reset() | ||
| include "arm64-v8a", "armeabi-v7a", "x86_64" | ||
| } | ||
| } | ||
|
|
||
| defaultConfig { | ||
| minSdkVersion 21 // Android 5.0+ | ||
| targetSdkVersion 25 // Follow Compile SDK | ||
| versionCode 20 // Follow release count | ||
| versionName "5.2.1" // Follow Official version | ||
| testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | ||
|
|
||
| externalNativeBuild { | ||
| cmake { | ||
| arguments "-DANDROID_STL=c++_shared" // Specify Android STL | ||
| arguments "-DBUILD_SHARED_LIBS=true" // Build shared object | ||
| arguments "-DFMT_TEST=false" // Skip test | ||
| arguments "-DFMT_DOC=false" // Skip document | ||
| cppFlags "-std=c++17" | ||
| } | ||
| } | ||
| println("Gradle CMake Plugin: ") | ||
| println(externalNativeBuild.cmake.cppFlags) | ||
| println(externalNativeBuild.cmake.arguments) | ||
| } | ||
|
|
||
| // External Native build | ||
| // - Use existing CMakeList.txt | ||
| // - Give path to CMake. This gradle file should be | ||
| // neighbor of the top level cmake | ||
| externalNativeBuild { | ||
| cmake { | ||
| path "../CMakeLists.txt" | ||
| // buildStagingDirectory "./build" // Custom path for cmake output | ||
| } | ||
| //println(cmake.path) | ||
| } | ||
|
|
||
| sourceSets{ | ||
| // Android Manifest for Gradle | ||
| main { | ||
| manifest.srcFile 'AndroidManifest.xml' | ||
| } | ||
| } | ||
| } | ||
|
|
||
| assemble.doLast | ||
| { | ||
| // Instead of `ninja install`, Gradle will deploy the files. | ||
| // We are doing this since FMT is dependent to the ANDROID_STL after build | ||
| copy { | ||
| from 'build/intermediates/cmake' | ||
| into '../libs' | ||
| } | ||
| // Copy debug binaries | ||
| copy { | ||
| from '../libs/debug/obj' | ||
| into '../libs/debug' | ||
| } | ||
| // Copy Release binaries | ||
| copy { | ||
| from '../libs/release/obj' | ||
| into '../libs/release' | ||
| } | ||
| // Remove empty directory | ||
| delete '../libs/debug/obj' | ||
| delete '../libs/release/obj' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # A CMake script to find SetEnv.cmd. | ||
|
|
||
| find_program(WINSDK_SETENV NAMES SetEnv.cmd | ||
| PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin") | ||
| if (WINSDK_SETENV AND PRINT_PATH) | ||
| execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}") | ||
| endif () |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # C++14 feature support detection | ||
|
|
||
| include(CheckCXXSourceCompiles) | ||
| include(CheckCXXCompilerFlag) | ||
|
|
||
| if (NOT CMAKE_CXX_STANDARD) | ||
| set(CMAKE_CXX_STANDARD 11) | ||
| endif() | ||
| message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") | ||
|
|
||
| if (CMAKE_CXX_STANDARD EQUAL 20) | ||
| check_cxx_compiler_flag(-std=c++20 has_std_20_flag) | ||
| check_cxx_compiler_flag(-std=c++2a has_std_2a_flag) | ||
|
|
||
| if (has_std_20_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++20) | ||
| elseif (has_std_2a_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++2a) | ||
| endif () | ||
| elseif (CMAKE_CXX_STANDARD EQUAL 17) | ||
| check_cxx_compiler_flag(-std=c++17 has_std_17_flag) | ||
| check_cxx_compiler_flag(-std=c++1z has_std_1z_flag) | ||
|
|
||
| if (has_std_17_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++17) | ||
| elseif (has_std_1z_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++1z) | ||
| endif () | ||
| elseif (CMAKE_CXX_STANDARD EQUAL 14) | ||
| check_cxx_compiler_flag(-std=c++14 has_std_14_flag) | ||
| check_cxx_compiler_flag(-std=c++1y has_std_1y_flag) | ||
|
|
||
| if (has_std_14_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++14) | ||
| elseif (has_std_1y_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++1y) | ||
| endif () | ||
| elseif (CMAKE_CXX_STANDARD EQUAL 11) | ||
| check_cxx_compiler_flag(-std=c++11 has_std_11_flag) | ||
| check_cxx_compiler_flag(-std=c++0x has_std_0x_flag) | ||
|
|
||
| if (has_std_11_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++11) | ||
| elseif (has_std_0x_flag) | ||
| set(CXX_STANDARD_FLAG -std=c++0x) | ||
| endif () | ||
| endif () | ||
|
|
||
| set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG}) | ||
|
|
||
| # Check if variadic templates are working and not affected by GCC bug 39653: | ||
| # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653 | ||
| check_cxx_source_compiles(" | ||
| template <class T, class ...Types> | ||
| struct S { typedef typename S<Types...>::type type; }; | ||
| int main() {}" SUPPORTS_VARIADIC_TEMPLATES) | ||
| if (NOT SUPPORTS_VARIADIC_TEMPLATES) | ||
| set (SUPPORTS_VARIADIC_TEMPLATES OFF) | ||
| endif () | ||
|
|
||
| # Check if initializer lists are supported. | ||
| check_cxx_source_compiles(" | ||
| #include <initializer_list> | ||
| int main() {}" SUPPORTS_INITIALIZER_LIST) | ||
| if (NOT SUPPORTS_INITIALIZER_LIST) | ||
| set (SUPPORTS_INITIALIZER_LIST OFF) | ||
| endif () | ||
|
|
||
| # Check if enum bases are available | ||
| check_cxx_source_compiles(" | ||
| enum C : char {A}; | ||
| int main() {}" | ||
| SUPPORTS_ENUM_BASE) | ||
| if (NOT SUPPORTS_ENUM_BASE) | ||
| set (SUPPORTS_ENUM_BASE OFF) | ||
| endif () | ||
|
|
||
| # Check if type traits are available | ||
| check_cxx_source_compiles(" | ||
| #include <type_traits> | ||
| class C { void operator=(const C&); }; | ||
| int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }" | ||
| SUPPORTS_TYPE_TRAITS) | ||
| if (NOT SUPPORTS_TYPE_TRAITS) | ||
| set (SUPPORTS_TYPE_TRAITS OFF) | ||
| endif () | ||
|
|
||
| # Check if user-defined literals are available | ||
| check_cxx_source_compiles(" | ||
| void operator\"\" _udl(long double); | ||
| int main() {}" | ||
| SUPPORTS_USER_DEFINED_LITERALS) | ||
| if (NOT SUPPORTS_USER_DEFINED_LITERALS) | ||
| set (SUPPORTS_USER_DEFINED_LITERALS OFF) | ||
| endif () | ||
|
|
||
| set(CMAKE_REQUIRED_FLAGS ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @PACKAGE_INIT@ | ||
|
|
||
| include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) | ||
| check_required_components(fmt) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| prefix=@CMAKE_INSTALL_PREFIX@ | ||
| exec_prefix=@CMAKE_INSTALL_PREFIX@ | ||
| libdir=@CMAKE_INSTALL_FULL_LIBDIR@ | ||
| includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ | ||
|
|
||
| Name: fmt | ||
| Description: A modern formatting library | ||
| Version: @FMT_VERSION@ | ||
| Libs: -L${libdir} -lfmt | ||
| Cflags: -I${includedir} | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| @echo on | ||
| rem This scripts configures build environment and runs CMake. | ||
| rem Use it instead of running CMake directly when building with | ||
| rem the Microsoft SDK toolchain rather than Visual Studio. | ||
| rem It is used in the same way as cmake, for example: | ||
| rem | ||
| rem run-cmake -G "Visual Studio 10 Win64" . | ||
|
|
||
| for /F "delims=" %%i IN ('cmake "-DPRINT_PATH=1" -P %~dp0/FindSetEnv.cmake') DO set setenv=%%i | ||
| if NOT "%setenv%" == "" call "%setenv%" | ||
| cmake %* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #!/usr/bin/env python | ||
| # Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print | ||
| # normalized (with most-significant bit equal to 1) significands in hexadecimal. | ||
|
|
||
| from __future__ import print_function | ||
|
|
||
| min_exponent = -348 | ||
| max_exponent = 340 | ||
| step = 8 | ||
| significand_size = 64 | ||
| exp_offset = 2000 | ||
|
|
||
| class fp: | ||
| pass | ||
|
|
||
| powers = [] | ||
| for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)): | ||
| result = fp() | ||
| n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp | ||
| k = significand_size + 1 | ||
| # Convert to binary and round. | ||
| binary = '{:b}'.format(n) | ||
| result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2 | ||
| result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size | ||
| powers.append(result) | ||
| # Sanity check. | ||
| exp_offset10 = 400 | ||
| actual = result.f * 10 ** exp_offset10 | ||
| if result.e > 0: | ||
| actual *= 2 ** result.e | ||
| else: | ||
| for j in range(-result.e): | ||
| actual /= 2 | ||
| expected = 10 ** (exp_offset10 + exp) | ||
| precision = len('{}'.format(expected)) - len('{}'.format(actual - expected)) | ||
| if precision < 19: | ||
| print('low precision:', precision) | ||
| exit(1) | ||
|
|
||
| print('Significands:', end='') | ||
| for i, fp in enumerate(powers): | ||
| if i % 3 == 0: | ||
| print(end='\n ') | ||
| print(' {:0<#16x}'.format(fp.f, ), end=',') | ||
|
|
||
| print('\n\nExponents:', end='') | ||
| for i, fp in enumerate(powers): | ||
| if i % 11 == 0: | ||
| print(end='\n ') | ||
| print(' {:5}'.format(fp.e), end=',') | ||
|
|
||
| print('\n\nMax exponent difference:', | ||
| max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:])) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Staticlib configuration for qmake builds | ||
| # For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc | ||
| # from compilation so it _MUST_ be called as qmake -nodepend | ||
| # A workaround is implemented below: a custom compiler is defined which does not track dependencies | ||
|
|
||
| TEMPLATE = lib | ||
|
|
||
| TARGET = fmt | ||
|
|
||
| QMAKE_EXT_CPP = .cc | ||
|
|
||
| CONFIG = staticlib warn_on c++11 | ||
|
|
||
| FMT_SOURCES = \ | ||
| ../src/format.cc \ | ||
| ../src/posix.cc | ||
|
|
||
| fmt.name = libfmt | ||
| fmt.input = FMT_SOURCES | ||
| fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ | ||
| fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ | ||
| fmt.depends = ${QMAKE_FILE_IN} | ||
| # QMAKE_RUN_CXX will not be expanded | ||
| fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN} | ||
| fmt.variable_out = OBJECTS | ||
| fmt.CONFIG = no_dependencies no_link | ||
| QMAKE_EXTRA_COMPILERS += fmt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,261 @@ | ||
| #!/usr/bin/env python | ||
|
|
||
| """Manage site and releases. | ||
| Usage: | ||
| manage.py release [<branch>] | ||
| manage.py site | ||
| """ | ||
|
|
||
| from __future__ import print_function | ||
| import datetime, docopt, errno, fileinput, json, os | ||
| import re, requests, shutil, sys, tempfile | ||
| from contextlib import contextmanager | ||
| from distutils.version import LooseVersion | ||
| from subprocess import check_call | ||
|
|
||
|
|
||
| class Git: | ||
| def __init__(self, dir): | ||
| self.dir = dir | ||
|
|
||
| def call(self, method, args, **kwargs): | ||
| return check_call(['git', method] + list(args), **kwargs) | ||
|
|
||
| def add(self, *args): | ||
| return self.call('add', args, cwd=self.dir) | ||
|
|
||
| def checkout(self, *args): | ||
| return self.call('checkout', args, cwd=self.dir) | ||
|
|
||
| def clean(self, *args): | ||
| return self.call('clean', args, cwd=self.dir) | ||
|
|
||
| def clone(self, *args): | ||
| return self.call('clone', list(args) + [self.dir]) | ||
|
|
||
| def commit(self, *args): | ||
| return self.call('commit', args, cwd=self.dir) | ||
|
|
||
| def pull(self, *args): | ||
| return self.call('pull', args, cwd=self.dir) | ||
|
|
||
| def push(self, *args): | ||
| return self.call('push', args, cwd=self.dir) | ||
|
|
||
| def reset(self, *args): | ||
| return self.call('reset', args, cwd=self.dir) | ||
|
|
||
| def update(self, *args): | ||
| clone = not os.path.exists(self.dir) | ||
| if clone: | ||
| self.clone(*args) | ||
| return clone | ||
|
|
||
|
|
||
| def clean_checkout(repo, branch): | ||
| repo.clean('-f', '-d') | ||
| repo.reset('--hard') | ||
| repo.checkout(branch) | ||
|
|
||
|
|
||
| class Runner: | ||
| def __init__(self, cwd): | ||
| self.cwd = cwd | ||
|
|
||
| def __call__(self, *args, **kwargs): | ||
| kwargs['cwd'] = kwargs.get('cwd', self.cwd) | ||
| check_call(args, **kwargs) | ||
|
|
||
|
|
||
| def create_build_env(): | ||
| """Create a build environment.""" | ||
| class Env: | ||
| pass | ||
| env = Env() | ||
|
|
||
| # Import the documentation build module. | ||
| env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
| sys.path.insert(0, os.path.join(env.fmt_dir, 'doc')) | ||
| import build | ||
|
|
||
| env.build_dir = 'build' | ||
| env.versions = build.versions | ||
|
|
||
| # Virtualenv and repos are cached to speed up builds. | ||
| build.create_build_env(os.path.join(env.build_dir, 'virtualenv')) | ||
|
|
||
| env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt')) | ||
| return env | ||
|
|
||
|
|
||
| @contextmanager | ||
| def rewrite(filename): | ||
| class Buffer: | ||
| pass | ||
| buffer = Buffer() | ||
| if not os.path.exists(filename): | ||
| buffer.data = '' | ||
| yield buffer | ||
| return | ||
| with open(filename) as f: | ||
| buffer.data = f.read() | ||
| yield buffer | ||
| with open(filename, 'w') as f: | ||
| f.write(buffer.data) | ||
|
|
||
|
|
||
| fmt_repo_url = 'git@github.com:fmtlib/fmt' | ||
|
|
||
|
|
||
| def update_site(env): | ||
| env.fmt_repo.update(fmt_repo_url) | ||
|
|
||
| doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io')) | ||
| doc_repo.update('git@github.com:fmtlib/fmtlib.github.io') | ||
|
|
||
| for version in env.versions: | ||
| clean_checkout(env.fmt_repo, version) | ||
| target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc') | ||
| # Remove the old theme. | ||
| for entry in os.listdir(target_doc_dir): | ||
| path = os.path.join(target_doc_dir, entry) | ||
| if os.path.isdir(path): | ||
| shutil.rmtree(path) | ||
| # Copy the new theme. | ||
| for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap', | ||
| 'conf.py', 'fmt.less']: | ||
| src = os.path.join(env.fmt_dir, 'doc', entry) | ||
| dst = os.path.join(target_doc_dir, entry) | ||
| copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile | ||
| copy(src, dst) | ||
| # Rename index to contents. | ||
| contents = os.path.join(target_doc_dir, 'contents.rst') | ||
| if not os.path.exists(contents): | ||
| os.rename(os.path.join(target_doc_dir, 'index.rst'), contents) | ||
| # Fix issues in reference.rst/api.rst. | ||
| for filename in ['reference.rst', 'api.rst']: | ||
| pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M) | ||
| with rewrite(os.path.join(target_doc_dir, filename)) as b: | ||
| b.data = b.data.replace('std::ostream &', 'std::ostream&') | ||
| b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data) | ||
| b.data = b.data.replace('std::FILE*', 'std::FILE *') | ||
| b.data = b.data.replace('unsigned int', 'unsigned') | ||
| b.data = b.data.replace('operator""_', 'operator"" _') | ||
| # Fix a broken link in index.rst. | ||
| index = os.path.join(target_doc_dir, 'index.rst') | ||
| with rewrite(index) as b: | ||
| b.data = b.data.replace( | ||
| 'doc/latest/index.html#format-string-syntax', 'syntax.html') | ||
| # Build the docs. | ||
| html_dir = os.path.join(env.build_dir, 'html') | ||
| if os.path.exists(html_dir): | ||
| shutil.rmtree(html_dir) | ||
| include_dir = env.fmt_repo.dir | ||
| if LooseVersion(version) >= LooseVersion('5.0.0'): | ||
| include_dir = os.path.join(include_dir, 'include', 'fmt') | ||
| elif LooseVersion(version) >= LooseVersion('3.0.0'): | ||
| include_dir = os.path.join(include_dir, 'fmt') | ||
| import build | ||
| build.build_docs(version, doc_dir=target_doc_dir, | ||
| include_dir=include_dir, work_dir=env.build_dir) | ||
| shutil.rmtree(os.path.join(html_dir, '.doctrees')) | ||
| # Create symlinks for older versions. | ||
| for link, target in {'index': 'contents', 'api': 'reference'}.items(): | ||
| link = os.path.join(html_dir, link) + '.html' | ||
| target += '.html' | ||
| if os.path.exists(os.path.join(html_dir, target)) and \ | ||
| not os.path.exists(link): | ||
| os.symlink(target, link) | ||
| # Copy docs to the website. | ||
| version_doc_dir = os.path.join(doc_repo.dir, version) | ||
| try: | ||
| shutil.rmtree(version_doc_dir) | ||
| except OSError as e: | ||
| if e.errno != errno.ENOENT: | ||
| raise | ||
| shutil.move(html_dir, version_doc_dir) | ||
|
|
||
|
|
||
| def release(args): | ||
| env = create_build_env() | ||
| fmt_repo = env.fmt_repo | ||
|
|
||
| branch = args.get('<branch>') | ||
| if branch is None: | ||
| branch = 'master' | ||
| if not fmt_repo.update('-b', branch, fmt_repo_url): | ||
| clean_checkout(fmt_repo, branch) | ||
|
|
||
| # Convert changelog from RST to GitHub-flavored Markdown and get the | ||
| # version. | ||
| changelog = 'ChangeLog.rst' | ||
| changelog_path = os.path.join(fmt_repo.dir, changelog) | ||
| import rst2md | ||
| changes, version = rst2md.convert(changelog_path) | ||
| cmakelists = 'CMakeLists.txt' | ||
| for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists), | ||
| inplace=True): | ||
| prefix = 'set(FMT_VERSION ' | ||
| if line.startswith(prefix): | ||
| line = prefix + version + ')\n' | ||
| sys.stdout.write(line) | ||
|
|
||
| # Update the version in the changelog. | ||
| title_len = 0 | ||
| for line in fileinput.input(changelog_path, inplace=True): | ||
| if line.decode('utf-8').startswith(version + ' - TBD'): | ||
| line = version + ' - ' + datetime.date.today().isoformat() | ||
| title_len = len(line) | ||
| line += '\n' | ||
| elif title_len: | ||
| line = '-' * title_len + '\n' | ||
| title_len = 0 | ||
| sys.stdout.write(line) | ||
|
|
||
| # Add the version to the build script. | ||
| script = os.path.join('doc', 'build.py') | ||
| script_path = os.path.join(fmt_repo.dir, script) | ||
| for line in fileinput.input(script_path, inplace=True): | ||
| m = re.match(r'( *versions = )\[(.+)\]', line) | ||
| if m: | ||
| line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version) | ||
| sys.stdout.write(line) | ||
|
|
||
| fmt_repo.checkout('-B', 'release') | ||
| fmt_repo.add(changelog, cmakelists, script) | ||
| fmt_repo.commit('-m', 'Update version') | ||
|
|
||
| # Build the docs and package. | ||
| run = Runner(fmt_repo.dir) | ||
| run('cmake', '.') | ||
| run('make', 'doc', 'package_source') | ||
| update_site(env) | ||
|
|
||
| # Create a release on GitHub. | ||
| fmt_repo.push('origin', 'release') | ||
| params = {'access_token': os.getenv('FMT_TOKEN')} | ||
| r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases', | ||
| params=params, | ||
| data=json.dumps({'tag_name': version, | ||
| 'target_commitish': 'release', | ||
| 'body': changes, 'draft': True})) | ||
| if r.status_code != 201: | ||
| raise Exception('Failed to create a release ' + str(r)) | ||
| id = r.json()['id'] | ||
| uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases' | ||
| package = 'fmt-{}.zip'.format(version) | ||
| r = requests.post( | ||
| '{}/{}/assets?name={}'.format(uploads_url, id, package), | ||
| headers={'Content-Type': 'application/zip'}, | ||
| params=params, data=open('build/fmt/' + package, 'rb')) | ||
| if r.status_code != 201: | ||
| raise Exception('Failed to upload an asset ' + str(r)) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| args = docopt.docopt(__doc__) | ||
| if args.get('release'): | ||
| release(args) | ||
| elif args.get('site'): | ||
| update_site(create_build_env()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| #!/usr/bin/env python | ||
| # reStructuredText (RST) to GitHub-flavored Markdown converter | ||
|
|
||
| import re, sys | ||
| from docutils import core, nodes, writers | ||
|
|
||
|
|
||
| def is_github_ref(node): | ||
| return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri']) | ||
|
|
||
|
|
||
| class Translator(nodes.NodeVisitor): | ||
| def __init__(self, document): | ||
| nodes.NodeVisitor.__init__(self, document) | ||
| self.output = '' | ||
| self.indent = 0 | ||
| self.preserve_newlines = False | ||
|
|
||
| def write(self, text): | ||
| self.output += text.replace('\n', '\n' + ' ' * self.indent) | ||
|
|
||
| def visit_document(self, node): | ||
| pass | ||
|
|
||
| def depart_document(self, node): | ||
| pass | ||
|
|
||
| def visit_section(self, node): | ||
| pass | ||
|
|
||
| def depart_section(self, node): | ||
| # Skip all sections except the first one. | ||
| raise nodes.StopTraversal | ||
|
|
||
| def visit_title(self, node): | ||
| self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1) | ||
| raise nodes.SkipChildren | ||
|
|
||
| def visit_title_reference(self, node): | ||
| raise Exception(node) | ||
|
|
||
| def depart_title(self, node): | ||
| pass | ||
|
|
||
| def visit_Text(self, node): | ||
| if not self.preserve_newlines: | ||
| node = node.replace('\n', ' ') | ||
| self.write(node) | ||
|
|
||
| def depart_Text(self, node): | ||
| pass | ||
|
|
||
| def visit_bullet_list(self, node): | ||
| pass | ||
|
|
||
| def depart_bullet_list(self, node): | ||
| pass | ||
|
|
||
| def visit_list_item(self, node): | ||
| self.write('* ') | ||
| self.indent += 2 | ||
|
|
||
| def depart_list_item(self, node): | ||
| self.indent -= 2 | ||
| self.write('\n\n') | ||
|
|
||
| def visit_paragraph(self, node): | ||
| pass | ||
|
|
||
| def depart_paragraph(self, node): | ||
| pass | ||
|
|
||
| def visit_reference(self, node): | ||
| if not is_github_ref(node): | ||
| self.write('[') | ||
|
|
||
| def depart_reference(self, node): | ||
| if not is_github_ref(node): | ||
| self.write('](' + node['refuri'] + ')') | ||
|
|
||
| def visit_target(self, node): | ||
| pass | ||
|
|
||
| def depart_target(self, node): | ||
| pass | ||
|
|
||
| def visit_literal(self, node): | ||
| self.write('`') | ||
|
|
||
| def depart_literal(self, node): | ||
| self.write('`') | ||
|
|
||
| def visit_literal_block(self, node): | ||
| self.write('\n\n```') | ||
| if 'c++' in node['classes']: | ||
| self.write('c++') | ||
| self.write('\n') | ||
| self.preserve_newlines = True | ||
|
|
||
| def depart_literal_block(self, node): | ||
| self.write('\n```\n') | ||
| self.preserve_newlines = False | ||
|
|
||
| def visit_inline(self, node): | ||
| pass | ||
|
|
||
| def depart_inline(self, node): | ||
| pass | ||
|
|
||
| def visit_image(self, node): | ||
| self.write('') | ||
|
|
||
| def depart_image(self, node): | ||
| pass | ||
|
|
||
| def write_row(self, row, widths): | ||
| for i, entry in enumerate(row): | ||
| text = entry[0][0] if len(entry) > 0 else '' | ||
| if i != 0: | ||
| self.write('|') | ||
| self.write('{:{}}'.format(text, widths[i])) | ||
| self.write('\n') | ||
|
|
||
| def visit_table(self, node): | ||
| table = node.children[0] | ||
| colspecs = table[:-2] | ||
| thead = table[-2] | ||
| tbody = table[-1] | ||
| widths = [int(cs['colwidth']) for cs in colspecs] | ||
| sep = '|'.join(['-' * w for w in widths]) + '\n' | ||
| self.write('\n\n') | ||
| self.write_row(thead[0], widths) | ||
| self.write(sep) | ||
| for row in tbody: | ||
| self.write_row(row, widths) | ||
| raise nodes.SkipChildren | ||
|
|
||
| def depart_table(self, node): | ||
| pass | ||
|
|
||
| class MDWriter(writers.Writer): | ||
| """GitHub-flavored markdown writer""" | ||
|
|
||
| supported = ('md',) | ||
| """Formats this writer supports.""" | ||
|
|
||
| def translate(self): | ||
| translator = Translator(self.document) | ||
| self.document.walkabout(translator) | ||
| self.output = (translator.output, translator.version) | ||
|
|
||
|
|
||
| def convert(rst_path): | ||
| """Converts RST file to Markdown.""" | ||
| return core.publish_file(source_path=rst_path, writer=MDWriter()) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| convert(sys.argv[1]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Sphinx configuration for readthedocs. | ||
|
|
||
| import os, sys | ||
|
|
||
| master_doc = 'index' | ||
| html_theme = 'theme' | ||
| html_theme_path = ["."] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| If you are not redirected automatically, follow the | ||
| `link to the fmt documentation <http://fmtlib.net/latest/>`_. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| {% extends "basic/layout.html" %} | ||
|
|
||
| {% block extrahead %} | ||
| <meta charset="UTF-8"> | ||
| <meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/"> | ||
| <script type="text/javascript"> | ||
| window.location.href = "http://fmtlib.net/latest/" | ||
| </script> | ||
| <title>Page Redirection</title> | ||
| {% endblock %} | ||
|
|
||
| {% block document %} | ||
| If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>. | ||
| {% endblock %} | ||
|
|
||
| {% block footer %} | ||
| {% endblock %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [theme] | ||
| inherit = basic |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| #!/usr/bin/env python | ||
| # Build the project on Travis CI. | ||
|
|
||
| from __future__ import print_function | ||
| import errno, os, shutil, subprocess, sys, urllib | ||
| from subprocess import call, check_call, Popen, PIPE, STDOUT | ||
|
|
||
| def rmtree_if_exists(dir): | ||
| try: | ||
| shutil.rmtree(dir) | ||
| except OSError as e: | ||
| if e.errno == errno.ENOENT: | ||
| pass | ||
|
|
||
| def makedirs_if_not_exist(dir): | ||
| try: | ||
| os.makedirs(dir) | ||
| except OSError as e: | ||
| if e.errno != errno.EEXIST: | ||
| raise | ||
|
|
||
| def install_dependencies(): | ||
| branch = os.environ['TRAVIS_BRANCH'] | ||
| if branch != 'master': | ||
| print('Branch: ' + branch) | ||
| exit(0) # Ignore non-master branches | ||
| check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' + | ||
| '| sudo apt-key add -', shell=True) | ||
| check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' + | ||
| '| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True) | ||
| check_call(['sudo', 'apt-get', 'update']) | ||
| check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs']) | ||
| check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css']) | ||
| deb_file = 'doxygen_1.8.6-2_amd64.deb' | ||
| urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' + | ||
| deb_file, deb_file) | ||
| check_call(['sudo', 'dpkg', '-i', deb_file]) | ||
|
|
||
| fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | ||
|
|
||
| build = os.environ['BUILD'] | ||
| if build == 'Doc': | ||
| travis = 'TRAVIS' in os.environ | ||
| if travis: | ||
| install_dependencies() | ||
| sys.path.insert(0, os.path.join(fmt_dir, 'doc')) | ||
| import build | ||
| build.create_build_env() | ||
| html_dir = build.build_docs() | ||
| repo = 'fmtlib.github.io' | ||
| if travis and 'KEY' not in os.environ: | ||
| # Don't update the repo if building on Travis from an account that | ||
| # doesn't have push access. | ||
| print('Skipping update of ' + repo) | ||
| exit(0) | ||
| # Clone the fmtlib.github.io repo. | ||
| rmtree_if_exists(repo) | ||
| git_url = 'https://github.com/' if travis else 'git@github.com:' | ||
| check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)]) | ||
| # Copy docs to the repo. | ||
| target_dir = os.path.join(repo, 'dev') | ||
| rmtree_if_exists(target_dir) | ||
| shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*')) | ||
| if travis: | ||
| check_call(['git', 'config', '--global', 'user.name', 'amplbot']) | ||
| check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com']) | ||
| # Push docs to GitHub pages. | ||
| check_call(['git', 'add', '--all'], cwd=repo) | ||
| if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo): | ||
| check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo) | ||
| cmd = 'git push' | ||
| if travis: | ||
| cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master' | ||
| p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo) | ||
| # Print the output without the key. | ||
| print(p.communicate()[0].replace(os.environ['KEY'], '$KEY')) | ||
| if p.returncode != 0: | ||
| raise subprocess.CalledProcessError(p.returncode, cmd) | ||
| exit(0) | ||
|
|
||
| standard = os.environ['STANDARD'] | ||
| install_dir = os.path.join(fmt_dir, "_install") | ||
| build_dir = os.path.join(fmt_dir, "_build") | ||
| test_build_dir = os.path.join(fmt_dir, "_build_test") | ||
|
|
||
| # Configure library. | ||
| makedirs_if_not_exist(build_dir) | ||
| cmake_flags = [ | ||
| '-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build, | ||
| '-DCMAKE_CXX_STANDARD=' + standard | ||
| ] | ||
| check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] + | ||
| cmake_flags, cwd=build_dir) | ||
|
|
||
| # Build library. | ||
| check_call(['make', '-j4'], cwd=build_dir) | ||
|
|
||
| # Test library. | ||
| env = os.environ.copy() | ||
| env['CTEST_OUTPUT_ON_FAILURE'] = '1' | ||
| if call(['make', 'test'], env=env, cwd=build_dir): | ||
| with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f: | ||
| print(f.read()) | ||
| sys.exit(-1) | ||
|
|
||
| # Install library. | ||
| check_call(['make', 'install'], cwd=build_dir) | ||
|
|
||
| # Test installation. | ||
| makedirs_if_not_exist(test_build_dir) | ||
| check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] + | ||
| cmake_flags, cwd=test_build_dir) | ||
| check_call(['make', '-j4'], cwd=test_build_dir) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #!/usr/bin/env python | ||
| # Update the coverity branch from the master branch. | ||
| # It is not done automatically because Coverity Scan limits | ||
| # the number of submissions per day. | ||
|
|
||
| from __future__ import print_function | ||
| import shutil, tempfile | ||
| from subprocess import check_output, STDOUT | ||
|
|
||
| class Git: | ||
| def __init__(self, dir): | ||
| self.dir = dir | ||
|
|
||
| def __call__(self, *args): | ||
| output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT) | ||
| print(output) | ||
| return output | ||
|
|
||
| dir = tempfile.mkdtemp() | ||
| try: | ||
| git = Git(dir) | ||
| git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir) | ||
| output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master') | ||
| if 'Fast-forward' not in output: | ||
| git('reset', 'HEAD', '.travis.yml') | ||
| git('checkout', '--', '.travis.yml') | ||
| git('commit', '-m', 'Update coverity branch') | ||
| git('push') | ||
| finally: | ||
| shutil.rmtree(dir) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -132,6 +132,7 @@ target_link_libraries(common | ||
| PUBLIC | ||
| ${CMAKE_THREAD_LIBS_INIT} | ||
| enet | ||
| fmt::fmt | ||
| ${MBEDTLS_LIBRARIES} | ||
|
|
||
| PRIVATE | ||