diff --git a/.gitattributes b/.gitattributes index 1abc10a..f1abe89 100644 --- a/.gitattributes +++ b/.gitattributes @@ -18,8 +18,9 @@ # #*.png binary +# See: https://build2.org/article/symlinks.xhtml#windows +# fmt/fmt/include symlink=dir fmt/fmt/src symlink=dir fmt/doc symlink=dir fmt-tests/basics/test symlink=dir -fmt-tests/test-main/test symlink=dir diff --git a/.gitignore b/.gitignore index 6d7a9f8..d7be5d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,51 @@ -.bdep/ -.vs/ - -# Local default options files. -# -.build2/local/ - -# Compiler/linker output. -# -*.d -*.t -*.i -*.ii -*.o -*.obj -*.so -*.dll -*.a -*.lib -*.exp -*.pdb -*.ilk -*.exe -*.exe.dlls/ -*.exe.manifest -*.pc +# Use whitelisting and ignore everything by default. +# +* + +# Don't ignore directories. +# +!*/ + +# Don't ignore Git-specific files. +# +!.git* + +# Allow all files from upstream sources. +# +!upstream + +# Don't ignore license and documentation files. +# +!*.md +!LICENSE + +# Don't ignore default C++ file extensions. +# +!*.h +!*.c +!*.cc +!*.inl + +# Don't ignore source generation files. +# +!*.in + +# Add original source files that might have been changed. +# +!*.orig + +# Don't ignore standard build2 files. +# +!buildfile +!bootstrap.build +!export.build +!root.build +!*manifest +!*testscript + +# Allow specific symbolic links to directories. +# +!fmt/fmt/include +!fmt/fmt/src +!fmt/doc +!fmt-tests/basics/test diff --git a/LICENSE b/LICENSE new file mode 120000 index 0000000..4d5ed76 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +upstream/LICENSE \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e4d1a58 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# build2 Package Repository for {fmt} + +This is a [build2](https://build2.org) package repository for [{fmt}](https://fmt.dev/), a modern C++ formatting library that provides a safe replacement for the `printf` family of functions. + +| Package | Summary | Status | +|---|---|---| +| **[`fmt`](fmt/PACKAGE-README.md)** | C++ Formatting Library | [![cppget.org](https://img.shields.io/website/https/cppget.org/fmt.svg?down_message=offline&label=cppget.org&style=for-the-badge&up_color=blue&up_message=online)](https://cppget.org/fmt) [![queue.cppget.org](https://img.shields.io/website/https/queue.cppget.org/fmt.svg?down_message=empty&down_color=blue&label=queue.cppget.org&style=for-the-badge&up_color=orange&up_message=running)](https://queue.cppget.org/fmt) | + +## Usage +If you want to use the `fmt` package in your `build2`-based project, add an appropriate repository manifest to your project's `repositories.manifest` and refer to [`fmt`'s PACKAGE README](fmt/PACKAGE-README.md). + +### `repositories.manifest` +To be able to fetch the package, add one of the following prerequisites to your project's `repositories.manifest`. + +**Option A: `cppget.org` (Recommended)** + +Based on your project's stability requirements, choose either the [`stable` section](https://cppget.org/?about#pkg%3Acppget.org%2Fstable) for thoroughly tested versions or the [`testing` section](https://cppget.org/?about#pkg%3Acppget.org%2Ftesting) for the latest releases before they are marked as stable. +For example: + + : + role: prerequisite + location: https://pkg.cppget.org/1/stable + # trust: ... + +**Option B: Git Repository** + + : + role: prerequisite + location: https://github.com/build2-packaging/fmt.git + +## Development Setup +The development setup for this repository uses the standard `bdep`-based workflow. +For general information and guidance on package maintenance, please see the [`build2` Documentation](https://build2.org/doc.xhtml). + +First, clone the repository via SSH or HTTPS. + + git clone --recurse https://github.com/build2-packaging/fmt.git # HTTPS + git clone --recurse git@github.com:build2-packaging/fmt.git # SSH + +Inside the repository's directory, initialize your build configuration. + + bdep init -C @gcc cc config.cxx=g++ config.cxx.features.modules=true config.install.root=../.install config.dist.root=../.dist + +Afterwards, use `b` or `bdep` to build, test, install, and distribute the packages. + +## Issues and Notes +- To generate a Binary Module Interface (BMI) from {fmt}'s C++ module after installation, the consumer must compile the module interface unit using the same macro configuration as the original library build. These macros are internal to the module and are therefore not exported via `cxx.export.poptions`. Instead, a generated `config.h` header encapsulates the required configuration through preprocessor macros. The module interface unit is patched to include this header immediately after `module;` in the global module fragment, ensuring the correct macros are available during BMI generation. +- In non-modular compiled builds, `fmt/format-inl.h` acts as a private implementation header for `src/format.cc`. While its installation in this specific configuration is technically unnecessary, we do not explicitly exclude it to reduce complexity. +- C++ module support in GCC versions prior to 16 is incomplete and unreliable for this library. As a result, such configurations are considered unsupported and are excluded from CI. +- On FreeBSD 15 with Clang 19, there is a discrepancy between compiler capabilities and system libraries. Although `__cpp_lib_modules` is defined (indicating that libc++ has module support), the base system does not ship the required module source or metadata files. Consequently, `import std;` is not functional in this environment which is reflected in the generated `config.h`. +- Upstream unit tests currently only support C++20 and fail when compiled with newer language standards. Therefore, the `fmt-tests` package is explicitly restricted to `cxx.std = 20`. +- Currently, unit tests are only run when the library headers are available and not when `config.fmt.module_only=true`. The unit tests use standard header includes rather than module imports. +- A known [issue in MSVC](https://developercommunity.visualstudio.com/t/Separate-preprocessing-with-P-fails-wit/10707183) prevents correct processing of {fmt}'s C++ module, producing error C7657 (“private module fragment cannot be declared before a module declaration”). The workaround (`cc.reprocess=true`) is only applicable when consuming the package directly via `build2`. As a result, unit tests fail on Windows when using the installed package. + +## Contributing +Contributions are welcome and greatly appreciated! +Please start by [opening an issue](https://github.com/build2-packaging/fmt/issues) to report a bug, suggest an improvement, or request a version update. +This helps us coordinate efforts and avoid duplicate work. +You are then welcome to submit a [pull request](https://github.com/build2-packaging/fmt/pulls) that references the issue. +For guidance on package maintenance, please see the [`build2` Packaging Guidelines](https://build2.org/build2-toolchain/doc/build2-toolchain-packaging.xhtml). diff --git a/buildfile b/buildfile index c3c8909..784ed49 100644 --- a/buildfile +++ b/buildfile @@ -1,6 +1,5 @@ -# Glue buildfile that "pulls" all the packages in the project. -# -import pkgs = [dir_paths] $process.run_regex(\ - cat $src_root/packages.manifest, '\s*location\s*:\s*(\S+)\s*', '\1') +import pkgs = fmt/ +import tests = fmt-tests/ ./: $pkgs +./: $tests: install = false diff --git a/fmt-tests/.gitignore b/fmt-tests/.gitignore deleted file mode 100644 index 1c363a0..0000000 --- a/fmt-tests/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Compiler/linker output. -# -*.d -*.t -*.i -*.i.* -*.ii -*.ii.* -*.o -*.obj -*.gcm -*.pcm -*.ifc -*.so -*.dylib -*.dll -*.a -*.lib -*.exp -*.pdb -*.ilk -*.exe -*.exe.dlls/ -*.exe.manifest -*.pc diff --git a/fmt-tests/ChangeLog.md b/fmt-tests/ChangeLog.md new file mode 120000 index 0000000..fb437de --- /dev/null +++ b/fmt-tests/ChangeLog.md @@ -0,0 +1 @@ +../upstream/ChangeLog.md \ No newline at end of file diff --git a/fmt-tests/LICENSE b/fmt-tests/LICENSE new file mode 120000 index 0000000..6246057 --- /dev/null +++ b/fmt-tests/LICENSE @@ -0,0 +1 @@ +../upstream/LICENSE \ No newline at end of file diff --git a/fmt-tests/PACKAGE-README.md b/fmt-tests/PACKAGE-README.md new file mode 120000 index 0000000..ae1a0ee --- /dev/null +++ b/fmt-tests/PACKAGE-README.md @@ -0,0 +1 @@ +../fmt/PACKAGE-README.md \ No newline at end of file diff --git a/fmt-tests/README.md b/fmt-tests/README.md new file mode 120000 index 0000000..a3cf035 --- /dev/null +++ b/fmt-tests/README.md @@ -0,0 +1 @@ +../upstream/README.md \ No newline at end of file diff --git a/fmt-tests/basics/.gitignore b/fmt-tests/basics/.gitignore deleted file mode 100644 index 8521b07..0000000 --- a/fmt-tests/basics/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -basics - -# Testscript output directory (can be symlink). -# -test-basics diff --git a/fmt-tests/basics/buildfile b/fmt-tests/basics/buildfile index 512c578..4cd87e6 100644 --- a/fmt-tests/basics/buildfile +++ b/fmt-tests/basics/buildfile @@ -1,4 +1,12 @@ -include ../test-main/ +import libs = \ + fmt%lib{fmt} \ + gtest%lib{gtest} \ + gmock%lib{gmock} + +libue{test-main}: \ + test/cxx{test-main gtest-extra util} \ + test/hxx{gtest-extra mock-allocator test-assert util} \ + $libs # NOTE: Maintaining explicit lists to match upstream, as it's not clear that there is intended to be a uniform pattern that can be reliably globbed. # See individual invocations of add_fmt_test() in upstream/test/CMakeLists.txt @@ -28,24 +36,15 @@ gtest_test_names = \ # TODO: scan-test - Excluded due to unresolved linker errors. for test_name : $gtest_test_names -{ - ./: exe{$test_name} : test/cxx{$test_name} - exe{$test_name}: ../test-main/liba{test-main}: - { - bin.whole = true - } -} + ./: exe{$test_name} : test/cxx{$test_name} libue{test-main} testscript # END [Tests that use gtest, and do not require any additional source/headers beyond a single cc file] # Tests using gtest but with additional prerequisites -./: exe{ranges-test} : test/cxx{ranges-test ranges-odr-test} -exe{ranges-test}: ../test-main/liba{test-main}: -{ - bin.whole = true -} +./: exe{ranges-test} : \ + test/cxx{ranges-test ranges-odr-test} libue{test-main} testscript # NOTE: format-impl-test- For whatever reason, format-impl-test is tied in upstream to header-only-test, which we do not support. Attempting to compile it alone yields errors, therefore omitted. # TODO: posix-mock-test - Excluded pending further work. Needs posix-mock.h, and has some msvc/runtime conditional logic that needs looking into. @@ -63,7 +62,7 @@ import fmt = fmt%lib{fmt} for test_name : $standalone_test_names { - ./: exe{$test_name} : test/cxx{$test_name} $fmt + ./: exe{$test_name} : test/cxx{$test_name} $fmt testscript } # END [Tests which do not use gtest and therefore should link directly to fmt only and not the test-main lib] diff --git a/fmt-tests/basics/testscript b/fmt-tests/basics/testscript new file mode 100644 index 0000000..afdedba --- /dev/null +++ b/fmt-tests/basics/testscript @@ -0,0 +1,4 @@ +# Redirect stdout to stderr (1>&2) and let stderr through (2>|). +# Cleanup anything the test might create, recursively (&***). +# +$* 1>&2 2>| &*** diff --git a/fmt-tests/build/.gitignore b/fmt-tests/build/.gitignore deleted file mode 100644 index 974e01d..0000000 --- a/fmt-tests/build/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/config.build -/root/ -/bootstrap/ -build/ diff --git a/fmt-tests/build/root.build b/fmt-tests/build/root.build index b269653..d7d8725 100644 --- a/fmt-tests/build/root.build +++ b/fmt-tests/build/root.build @@ -2,7 +2,7 @@ # #cxx.internal.scope = current -cxx.std = latest +cxx.std = 20 using cxx diff --git a/fmt-tests/buildfile b/fmt-tests/buildfile index f065128..cfda920 100644 --- a/fmt-tests/buildfile +++ b/fmt-tests/buildfile @@ -1,4 +1,4 @@ -./: manifest +./: manifest legal{LICENSE} doc{*.md} # NOTE: for the moment, we need to disable all the test targets in modules-only mode, since upstream fmt does not yet properly support it. import! [metadata, rule_hint=cxx.link] fmt_lib = fmt%lib{fmt} diff --git a/fmt-tests/manifest b/fmt-tests/manifest index 19a96de..1f2d9df 100644 --- a/fmt-tests/manifest +++ b/fmt-tests/manifest @@ -1,10 +1,21 @@ : 1 name: fmt-tests -version: 11.1.4 +version: 12.1.0-a.0.z +type: exe +language: c++ project: fmt -summary: Tests package for fmt upstream tests +summary: Formatting C++ library tests license: MIT -url: https://github.com/fmtlib/fmt/ + +description-file: README.md +changes-file: ChangeLog.md +url: https://fmt.dev/ +src-url: https://github.com/fmtlib/fmt/ +email: victor.zverovich@gmail.com + +package-description-file: PACKAGE-README.md +package-url: https://github.com/build2-packaging/fmt/ +package-email: packaging@build2.org depends: * build2 >= 0.17.0 depends: * bpkg >= 0.17.0 diff --git a/fmt-tests/test-main/buildfile b/fmt-tests/test-main/buildfile deleted file mode 100644 index 30b42fa..0000000 --- a/fmt-tests/test-main/buildfile +++ /dev/null @@ -1,14 +0,0 @@ - -libs = -import libs += fmt%lib{fmt} -import libs += gtest%lib{gtest} gmock%lib{gmock} - -liba{test-main}: test/cxx{test-main gtest-extra util} test/hxx{gtest-extra mock-allocator test-assert util} $libs - -# Export options. -# -liba{test-main}: -{ - cxx.export.poptions += "-I$src_base/test" - cxx.export.libs = $libs -} diff --git a/fmt-tests/test-main/test b/fmt-tests/test-main/test deleted file mode 120000 index 18e4790..0000000 --- a/fmt-tests/test-main/test +++ /dev/null @@ -1 +0,0 @@ -../../upstream/test \ No newline at end of file diff --git a/fmt/ChangeLog.md b/fmt/ChangeLog.md new file mode 120000 index 0000000..fb437de --- /dev/null +++ b/fmt/ChangeLog.md @@ -0,0 +1 @@ +../upstream/ChangeLog.md \ No newline at end of file diff --git a/fmt/LICENSE b/fmt/LICENSE new file mode 120000 index 0000000..6246057 --- /dev/null +++ b/fmt/LICENSE @@ -0,0 +1 @@ +../upstream/LICENSE \ No newline at end of file diff --git a/fmt/PACKAGE-README.md b/fmt/PACKAGE-README.md new file mode 100644 index 0000000..9c5d6b3 --- /dev/null +++ b/fmt/PACKAGE-README.md @@ -0,0 +1,55 @@ +# {fmt} - A Formatting C++ Library + +{fmt} is an open-source formatting library that provides a safe replacement for the `printf` family of functions. +Errors in format strings, which are a common source of vulnerabilities in C, are reported at compile time. +Formatting of most standard types, including all containers, dates, and times is supported out-of-the-box. +Also, {fmt} provides portable Unicode support on major operating systems with UTF-8 and char strings. + +# Usage +To use `fmt` in your project, add the following configurations to the respective files after you have gained access to a `build2` package repository that contains it. + +### `manifest` +To make `fmt` available for import, add the following dependency to the `manifest` of each package in your project that requires it, adjusting the version constraint as appropriate. + + depends: fmt ^12.1.0 + +### `buildfile` +To import the contained library, use the following declaration in your `buildfile`. + + import fmt = fmt%lib{fmt} + +Note also that `lib{fmt}` provides `build2` metadata that can be extracted with an immediate importation to describe its configuration. + + fmt.has_header = [bool] (!$config.fmt.module_only) + fmt.has_module = [bool] $cxx.features.modules + +### C++ Usage +If `$fmt.has_header == true`, {fmt}'s headers can be included as follows: + +```c++ +#include +``` + +If `$fmt.has_module == true`, {fmt}'s C++ module can imported via: + +```c++ +import fmt; +``` + +## Configuration + +### C++20 Modules Support +{fmt}'s C++ module support can be enabled by setting: + + config.cxx.features.modules = true + +By default, the library supports both module import through `import fmt;` and traditional header inclusion through, eg., `#include `. +To enable this dual usage, all entities are attached to the global module via global module linkage (`extern "C++"`). +This ensures compatibility between module and header-based consumption. + +To enforce strict module usage, use: + + config.fmt.module_only = true + +In this mode, header access is disabled and all entities are exclusively provided through the C++ module. +This results in full encapsulation within the module and avoids global module linkage. diff --git a/fmt/README.md b/fmt/README.md deleted file mode 100644 index f921b95..0000000 --- a/fmt/README.md +++ /dev/null @@ -1,26 +0,0 @@ -`{fmt}` library - Build2 package -================================ - -See [`{fmt}` documentation](https://fmt.dev/) for usage and details. - - - `{fmt}` : https://github.com/fmtlib/fmt/ - - Build2 : https://build2.org - -Note: This is the source code for the build2 package of the `{fmt}` C++ library, -the actual library sources snapshot can be found in the `./upstream/` submodule. - -## Configuration Options - -### Experimental C++20 modules support - -Modules support is WIP, both in the `build2` package and also in `fmt` upstream. Latest versions of MSVC or Clang are recommended, and the most up-to-date version of the package with regards to modules compatibility can be used via git and the [modules branch](https://github.com/build2-packaging/fmt/tree/modules). For example, in project `repositories.manifest`: -``` -: -role: prerequisite -location: https://github.com/build2-packaging/fmt.git#modules -``` - -Enable with `config.cxx.features.modules=true`. When enabled, by default dual mode is used meaning that the library can be consumed either through `import` or via `#include`. To enable this safely, all entities are attached to the global module (extern "C++"). See `modules_only` option for the alternative. - - `config.fmt.enable_import_std` : Set to `true` to consume the standard library as a module when building the `fmt` module. Support dependent on compiler and std lib. Defaults to `false`. - - `config.fmt.modules_only` : Set to `true` to enable modules-only mode for the package. In this mode, `fmt` entities are fully encapsulated in the `fmt` module meaning `#include`-based consumption cannot be mixed, and the package will not export headers. Defaults to `false`. - diff --git a/fmt/README.md b/fmt/README.md new file mode 120000 index 0000000..a3cf035 --- /dev/null +++ b/fmt/README.md @@ -0,0 +1 @@ +../upstream/README.md \ No newline at end of file diff --git a/fmt/build/.gitignore b/fmt/build/.gitignore deleted file mode 100644 index 4a730a3..0000000 --- a/fmt/build/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -config.build -root/ -bootstrap/ diff --git a/fmt/build/root.build b/fmt/build/root.build index 499fa36..6dee61b 100644 --- a/fmt/build/root.build +++ b/fmt/build/root.build @@ -1,18 +1,21 @@ +# Configuration Variables + +# Disable access to `lib{fmt}`'s headers to encapsulate +# all entities in {fmt}'s C++ module with module linkage. +# +config [bool] config.fmt.module_only ?= false + +# C++ Configuration +# cxx.std = latest using cxx hxx{*}: extension = h +ixx{*}: extension = cc cxx{*}: extension = cc mxx{*}: extension = cc # The test target for cross-testing (running tests under Wine, etc). # test.target = $cxx.target - - -############################## -# Project-specific options: - -config [bool] config.fmt.enable_import_std ?= false -config [bool] config.fmt.modules_only ?= false diff --git a/fmt/buildfile b/fmt/buildfile index 146d18d..732654d 100644 --- a/fmt/buildfile +++ b/fmt/buildfile @@ -1,5 +1,13 @@ -./: {*/ -build/ -upstream/ -doc/} manifest doc{README.md} doc/doc{**} +./: fmt/ manifest legal{LICENSE} doc{*.md} doc/doc{**} # Don't install tests. # -tests/: install = false +./: tests/: install = false + +# Install documentation to sub-directory. +# +doc/doc{*}: +{ + install = doc/doc/ + install.subdirs = true +} diff --git a/fmt/fmt/.gitignore b/fmt/fmt/.gitignore deleted file mode 100644 index 0c9102a..0000000 --- a/fmt/fmt/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Generated version header. -# -version.hxx diff --git a/fmt/fmt/buildfile b/fmt/fmt/buildfile index 56a6cbf..7e8b05d 100644 --- a/fmt/fmt/buildfile +++ b/fmt/fmt/buildfile @@ -1,70 +1,98 @@ -./ : lib{fmt} +# Using C++ modules, {fmt} incorporates all headers and sources into its +# module interface unit. We dynamically retype their target types as +# implementation fragments (`ixx{}`) to reflect their role as internal +# module details. While other strategies exist to achieve this, changing +# the target types provides the clearest communication of intent. -lib{fmt}: include/hxx{**} hxx{version} - -# Automatically build as module if the feature is enabled -build_fmt_module = $cxx.features.modules -allow_header_usage = ($build_fmt_module == false || $config.fmt.modules_only == false) - -if($build_fmt_module && $allow_header_usage) - info "Building fmt with dual module/header mode enabled" +# Headers: +# In module-only mode, headers are no longer public interface points; +# they become internal module details. Retyping to `ixx` enforces this +# encapsulation and signals that direct header access is discouraged. +# +# Note: +# In non-modular, compiled builds, `fmt/format-inl.h` acts as a private +# implementation header for `src/format.cc`. While its installation is +# technically unnecessary in this specific configuration, we do not +# explicitly exclude it to avoid complicating the installation logic. +# +hdr_type = ($config.fmt.module_only ? ixx : hxx) +lib{fmt}: include/$hdr_type{**.h} -# Workaround for MSVC bug: https://developercommunity.visualstudio.com/t/Separate-preprocessing-with-P-fails-wit/10707183 -if($build_fmt_module && $cxx.class == 'msvc') - cc.reprocess = true +# Sources: +# With {fmt}'s module, sources are no longer compiled as translation units. +# However, they must remain visible to the consumer's compiler to be read and +# processed during module importation. Using `ixx`, ensures these files are +# properly installed as source text rather than compiled into object files. +# +src_type = ($cxx.features.modules ? ixx : cxx) +lib{fmt}: src/$src_type{** -fmt} -lib{fmt}: src/mxx{fmt} : include = ($build_fmt_module) # `fmt` C++ module only -lib{fmt}: src/cxx{** -fmt} : include = (!$build_fmt_module) # no modules only +# Module +# +# To successfully generate a Binary Module Interface (BMI) after installation, +# the consumer must compile the module interface unit with the same macro +# configuration used during the library's original build. +# Since these macros are only required by the module itself, they should not be +# exported to the consumer's preprocessor scope via 'cxx.export.poptions'. +# To bridge this gap, we generate a persistent 'config.h' that encapsulates the +# package configuration. We then patch the module interface unit to include this +# header within the Global Module Fragment, ensuring the macros are available +# during BMI generation without leaking into the consumer's translation units. +# +lib{fmt}: src/hxx{config} src/mxx{fmt}: include = $cxx.features.modules -# Meta data for users -lib{fmt}: +src/ { - export.metadata = 1 fmt - fmt.has_header = [bool] $allow_header_usage - fmt.has_module = [bool] $build_fmt_module + # Place the header include immediately after 'module;' to ensure all + # relevant macros are available before any other macro preprocessing. + # + mxx{fmt}: file{fmt.cc} + {{ + diag patch $< -> $> + sed -e 's/^module;/module;\n#include "config.h"/' $path($<) >$path($>) + }} + + # The config header needs to define all macros that were required + # to compile the module interface unit during the installation. + # + hxx{config}: ../file{config.h.in} + {{ + diag config $< -> $> + cp $path($<) $path($>) + if! $config.fmt.module_only + echo '#define FMT_ATTACH_TO_GLOBAL_MODULE' >+$path($>) + end + }} } -# Include the generated version header into the distribution (so that we don't -# pick up an installed one) and don't remove it when cleaning in src (so that -# clean results in a state identical to distributed). +# Build and Export Options # -hxx{version} : in{version} $src_root/manifest -{ - dist = true - clean = ($src_root != $out_root) - install = include/fmt -} +cxx.poptions =+ "-I$out_base/src" "-I$src_base/src" "-I$src_base/include" +if! $config.fmt.module_only + lib{fmt}: cxx.export.poptions = "-I$src_base/include" -# Build options. -# -cxx.poptions =+ "-I$src_base/include" "-I$out_root" {objs bmis}{*}: cxx.poptions += -DFMT_LIB_EXPORT +libs{fmt}: cxx.export.poptions += -DFMT_SHARED -# If building fmt module, also enable attaching to the global module in order to allow concurrent #include and import. -if($build_fmt_module) +if ($cxx.class == 'msvc') { - if($allow_header_usage) - # Support mixing consuming as both modules and headers within a single build - cxx.poptions =+ -DFMT_ATTACH_TO_GLOBAL_MODULE - if($config.fmt.enable_import_std) - cxx.poptions =+ -DFMT_IMPORT_STD + # Workaround for MSVC Bug + # Otherwise, MSVC emits error C7657 (private module fragment cannot + # be declared before a module declaration) when processing the module. + # See: https://developercommunity.visualstudio.com/t/Separate-preprocessing-with-P-fails-wit/10707183 + # + if $cxx.features.modules + cc.reprocess = true } -# Export options. +# Metadata # -if($allow_header_usage) +lib{fmt}: { - # Note: these poptions only relevant for header consumption - lib{fmt}: cxx.export.poptions = "-I$src_base/include" "-I$out_root" - - if($build_fmt_module) - { - # For consumer-built BMI compatibility, this must be exported - lib{fmt}: cxx.export.poptions += -DFMT_ATTACH_TO_GLOBAL_MODULE - } - - libs{fmt}: cxx.export.poptions += -DFMT_SHARED + export.metadata = 1 fmt + fmt.has_header = [bool] (!$config.fmt.module_only) + fmt.has_module = [bool] $cxx.features.modules } # For pre-releases use the complete version to make sure they cannot be used @@ -76,14 +104,27 @@ if $version.pre_release else lib{fmt}: bin.lib.version = @"-$version.major.$version.minor" -# Install into the fmt/ subdirectory of, say, /usr/include/ -# recreating subdirectories. +# Installation # -include/ +# To support post-installation usage for the C++ modules, we isolate +# the library's components within a dedicated 'fmtlib/' subdirectory. +# Headers are installed into 'fmtlib/fmt/' to satisfy {fmt}'s internal +# inclusion scheme, e.g., #include "fmt/core.h". The module interface unit, +# its configuration headers, and the implementation sources reside at the +# 'fmtlib/' root. To ensure the compiler resolves header paths correctly, +# we point the pkg-config include path to 'include/fmtlib/'. +# +if! $config.fmt.module_only + lib{fmt}: cxx.pkgconfig.include = include/fmtlib/ + +include/{hxx ixx}{*}: +{ + install = include/fmtlib/ + install.subdirs = true +} + +src/{hxx mxx ixx}{*}: { - {hxx ixx txx}{*}: - { - install = include/ - install.subdirs = true - } + install = include/fmtlib/ + install.subdirs = true } diff --git a/fmt/fmt/config.h.in b/fmt/fmt/config.h.in new file mode 100644 index 0000000..1a73bbf --- /dev/null +++ b/fmt/fmt/config.h.in @@ -0,0 +1,14 @@ +#if __has_include() +# include +#endif + +// Use `import std;` if the compiler supports it. +#if defined(__cpp_lib_modules) && (__cpp_lib_modules >= 202207L) +# if !defined(__FreeBSD__) +# define FMT_IMPORT_STD +# endif +#endif + +#ifdef FMT_SHARED +# define FMT_LIB_EXPORT +#endif diff --git a/fmt/fmt/version.h.in b/fmt/fmt/version.h.in deleted file mode 100644 index 5d5e138..0000000 --- a/fmt/fmt/version.h.in +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -// The numeric version format is AAAAABBBBBCCCCCDDDE where: -// -// AAAAA - major version number -// BBBBB - minor version number -// CCCCC - bugfix version number -// DDD - alpha / beta (DDD + 500) version number -// E - final (0) / snapshot (1) -// -// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: -// -// Version AAAAABBBBBCCCCCDDDE -// -// 0.1.0 0000000001000000000 -// 0.1.2 0000000001000020000 -// 1.2.3 0000100002000030000 -// 2.2.0-a.1 0000200001999990010 -// 3.0.0-b.2 0000299999999995020 -// 2.2.0-a.1.z 0000200001999990011 -// -#define FMT_VERSION_BUILD2 $fmt.version.project_number$ULL -#define FMT_VERSION_STR "$fmt.version.project$" -#define FMT_VERSION_ID "$fmt.version.project_id$" - -#define FMT_VERSION_MAJOR $fmt.version.major$ -#define FMT_VERSION_MINOR $fmt.version.minor$ -#define FMT_VERSION_PATCH $fmt.version.patch$ - -#define FMT_PRE_RELEASE $fmt.version.pre_release$ - -#define FMT_SNAPSHOT_SN $fmt.version.snapshot_sn$ULL -#define FMT_SNAPSHOT_ID "$fmt.version.snapshot_id$" diff --git a/fmt/manifest b/fmt/manifest index 1306855..e708ba2 100644 --- a/fmt/manifest +++ b/fmt/manifest @@ -1,25 +1,42 @@ : 1 name: fmt -version: 11.1.4 -summary: "{fmt} is an open-source formatting library for C++. It can be used as a safe and fast alternative to (s)printf and iostreams." +version: 12.1.0-a.0.z +type: lib +language: c++ +project: fmt +summary: Formatting C++ library used as alternative to (s)printf and iostreams license: MIT + description-file: README.md -doc-url: https://fmt.dev/ -url: https://github.com/fmtlib/fmt/ +changes-file: ChangeLog.md +url: https://fmt.dev/ +src-url: https://github.com/fmtlib/fmt/ +email: victor.zverovich@gmail.com + +package-description-file: PACKAGE-README.md package-url: https://github.com/build2-packaging/fmt/ -package-email: mjklaim@gmail.com +package-email: packaging@build2.org tests: fmt-tests == $ depends: * build2 >= 0.17.0 depends: * bpkg >= 0.17.0 -builds: default -build-exclude: linux_debian_12-clang_17 ; clang-17 bug with libstdc++ std::tuple (https://github.com/llvm/llvm-project/issues/61415) -build-exclude: **/x86_64-w64-mingw32 ; unknown error building installed tests 'error: unable to stat path D:\a\msys64\mingw64\lib\x86_64-w64-mingw32\14.1.0\pkgconfig\: the device is not ready' +module-builds: default experimental : &( +gcc-16+ +clang-17+ +msvc ) +module-builds: -bindist +module-build-config: +\ +config.cxx.features.modules=true +; +Enable support for both the {fmt} headers and its C++ module. +\ -# Modules support still not sufficient to enable on CI -modules-builds: linux : &( +clang-18+ ) ; Modules builds only supported for latest Clang and MSVC -modules-build-config: config.cxx.features.modules=true ; Enable c++20 modules -modules-only-builds: linux : &( +clang-18+ ) ; Modules builds only supported for latest Clang and MSVC -modules-only-build-config: config.cxx.features.modules=true config.fmt.modules_only=true ; Enable c++20 modules and disable header usage +module-only-builds: default experimental : &( +gcc-16+ +clang-17+ +msvc ) +module-only-builds: -bindist +module-only-build-config: +\ +config.cxx.features.modules=true +config.fmt.module_only=true +; +Only use the {fmt} C++ module. +\ diff --git a/fmt/tests/.gitignore b/fmt/tests/.gitignore deleted file mode 100644 index 662178d..0000000 --- a/fmt/tests/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Test executables. -# -driver - -# Testscript output directories (can be symlinks). -# -test -test-* diff --git a/fmt/tests/basics/buildfile b/fmt/tests/basics/buildfile index 4c6f11d..9978a45 100644 --- a/fmt/tests/basics/buildfile +++ b/fmt/tests/basics/buildfile @@ -3,9 +3,5 @@ import! [metadata, rule_hint=cxx.link] libs = fmt%lib{fmt} ./ : exe{driver} : include = $($libs: fmt.has_header) ./ : exe{driver-modules} : include = $($libs: fmt.has_module) -exe{driver} : {cxx}{driver} hxx{tests.inl} $libs testscript{**} - -# For purposes of verifying that fmt headers are not made available for include when in modules-only mode -cxx.poptions =+ "-DFMT_BUILD2_HAS_HEADER=($($libs: fmt.has_header) ? 1 : 0)" - -exe{driver-modules} : {cxx}{driver-modules} hxx{tests.inl} $libs testscript{**} +exe{driver} : cxx{driver} ixx{tests} $libs +exe{driver-modules} : cxx{driver-modules} ixx{tests} $libs diff --git a/fmt/tests/basics/driver-modules.cc b/fmt/tests/basics/driver-modules.cc new file mode 100644 index 0000000..e478a6b --- /dev/null +++ b/fmt/tests/basics/driver-modules.cc @@ -0,0 +1,37 @@ +// Verify that fmt headers are not available if config.fmt.modules_only is true +// @NOTE: Disabled since this is rather hard to make robust. +// We need to install headers even in modules only mode in order to allow for +// BMI compilation. Even though we suppress exporting of the -I paths, in the +// case of installation to a common root prefix it will likely be the case that +// the headers will be accessible via the general include paths anyway. Only way +// we could reasonably prevent this would be to install to a different +// subfolder; avoiding adding such complexity for now since modules installation +// is far from finalized anyway. +// #if __has_include() != FMT_BUILD2_HAS_HEADER +// #error fmt headers should be available for include iff +// config.fmt.modules_only == false #endif + +#if __has_include() +#include +#endif + +#if defined(__cpp_lib_modules) && (__cpp_lib_modules >= 202207L) +#define HAS_IMPORT_STD +#endif + +#ifndef IMPORT_STD_SUPPORT +#include +#include +#include +#endif + +#undef NDEBUG +#include + +#ifdef IMPORT_STD_SUPPORT +import std; +#endif + +import fmt; + +#include "tests.inl" diff --git a/fmt/tests/basics/driver-modules.cxx b/fmt/tests/basics/driver-modules.cxx deleted file mode 100644 index 507633a..0000000 --- a/fmt/tests/basics/driver-modules.cxx +++ /dev/null @@ -1,20 +0,0 @@ - -// Verify that fmt headers are not available if config.fmt.modules_only is true -// @NOTE: Disabled since this is rather hard to make robust. -// We need to install headers even in modules only mode in order to allow for BMI compilation. -// Even though we suppress exporting of the -I paths, in the case of installation to a common root prefix it will likely be -// the case that the headers will be accessible via the general include paths anyway. Only way we could reasonably prevent this -// would be to install to a different subfolder; avoiding adding such complexity for now since modules installation is far -// from finalized anyway. -//#if __has_include() != FMT_BUILD2_HAS_HEADER -//#error fmt headers should be available for include iff config.fmt.modules_only == false -//#endif - -#include -#include -#include -#include - -import fmt; - -#include "tests.inl" diff --git a/fmt/tests/basics/driver.cxx b/fmt/tests/basics/driver.cc similarity index 90% rename from fmt/tests/basics/driver.cxx rename to fmt/tests/basics/driver.cc index cff192d..d4e7405 100644 --- a/fmt/tests/basics/driver.cxx +++ b/fmt/tests/basics/driver.cc @@ -1,15 +1,15 @@ -#include -#include #include +#include #include -#include -#include +#include +#include #include #include -#include -#include +#include #include -#include "tests.inl" +#undef NDEBUG +#include +#include "tests.inl" diff --git a/fmt/tests/basics/tests.inl b/fmt/tests/basics/tests.inl index b5de3e8..1950d5e 100644 --- a/fmt/tests/basics/tests.inl +++ b/fmt/tests/basics/tests.inl @@ -1,65 +1,57 @@ +struct date { + int year, month, day; +}; -int main () -{ - // THESE EXAMPLES ARE EXTRACTED FROM THE README AND SHOULD BE UPDATED EACH BREAKING VERSION +template <> struct fmt::formatter { + template + constexpr auto parse(ParseContext &ctx) const { + return ctx.begin(); + } - { - fmt::print("Hello, world!\n"); + template + constexpr auto format(const date &d, FormatContext &ctx) const { + // Namespace-qualify to avoid ambiguity with std::format_to. + return fmt::format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day); + } +}; - std::string s = fmt::format("The answer is {}.", 42); - assert(s == "The answer is 42."); +int main() { + // THESE EXAMPLES ARE EXTRACTED FROM THE README AND SHOULD BE UPDATED EACH + // BREAKING VERSION + { + fmt::print("Hello, world!\n"); - std::string s2 = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); - assert(s2 == "I'd rather be happy than right."); + std::string s = fmt::format("The answer is {}.", 42); + assert(s == "The answer is 42."); + std::string s2 = + fmt::format("I'd rather be {1} than {0}.", "right", "happy"); + assert(s2 == "I'd rather be happy than right."); } - { std::vector v = {1, 2, 3}; fmt::print("{}\n", v); } - { - fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, - "Hello, {}!\n", "world"); + fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, // + "Hello, {}!\n", "world"); fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | - fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); - fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, - "你好{}!\n", "世界"); + fmt::emphasis::underline, + "Olá, {}!\n", "Mundo"); + fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, // + "你好{}!\n", "世界"); } - { auto now = std::chrono::system_clock::now(); fmt::print("Date and time: {}\n", now); fmt::print("Time: {:%H:%M}\n", now); } - { fmt::print("Hello, {}!", "world"); // Python-like format string syntax fmt::printf("Hello, %s!", "world"); // printf format string syntax } - { - std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); + std::string s = fmt::format("The date is {}", date{2012, 12, 9}); + assert(s == "The date is 2012-12-9"); } - } - -struct date { - int year, month, day; -}; - -template <> -struct fmt::formatter { - template - constexpr auto parse(ParseContext& ctx) const { return ctx.begin(); } - - template - constexpr auto format(const date& d, FormatContext& ctx) const { - // Namespace-qualify to avoid ambiguity with std::format_to. - return fmt::format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day); - } -}; - -std::string s = fmt::format("The date is {}", date{2012, 12, 9}); - diff --git a/fmt/tests/build/.gitignore b/fmt/tests/build/.gitignore deleted file mode 100644 index 4a730a3..0000000 --- a/fmt/tests/build/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -config.build -root/ -bootstrap/ diff --git a/fmt/tests/build/root.build b/fmt/tests/build/root.build index 9c1f6bd..81bf1b0 100644 --- a/fmt/tests/build/root.build +++ b/fmt/tests/build/root.build @@ -2,11 +2,9 @@ cxx.std = latest using cxx -hxx{*}: extension = hxx -ixx{*}: extension = ixx -txx{*}: extension = txx -cxx{*}: extension = cxx -mxx{*}: extension = cc +hxx{*}: extension = h +ixx{*}: extension = inl +cxx{*}: extension = cc # Every exe{} in this subproject is by default a test. # diff --git a/upstream b/upstream index 1239137..407c905 160000 --- a/upstream +++ b/upstream @@ -1 +1 @@ -Subproject commit 123913715afeb8a437e6388b4473fcc4753e1c9a +Subproject commit 407c905e45ad75fc29bf0f9bb7c5c2fd3475976f