fmt icon indicating copy to clipboard operation
fmt copied to clipboard

What is the proper way to use fmt as a C++ 20 module?

Open yuannan opened this issue 1 year ago • 14 comments

I've seen loads of different examples online, but nothing seems to be in the official documents.

Some people have suggested to wrap fmt in a module and include that, while others have directly included it as a subdirectory in CMake.

I'm a little bit lost as what is the officially supported way to do this.

I'm building a new logging library specifically targeting cpp 23+ using fmt and I would really appreciate some input.

https://gitlab.com/Simple-Cpp/SplLog

Currently it compiles just fine on NixOS, but that is using the older 10.2.0 version that Nix ships. If I try to include as a git submodule and a cmake subdirectory it seems to explode while looking for __fwd/string.

Trying to use 10.2.0 from git but matching to the version Nix ships brings it's own compile issues.

If you can guide me on how to do this, I'd be happy to update the docs to make it a bit easier for new users.

yuannan avatar Nov 13 '24 00:11 yuannan

Thanks for the suggestion. We should add a section to the docs and in the meantime check out https://vitaut.net/posts/2023/simple-cxx20-modules/.

vitaut avatar Nov 14 '24 00:11 vitaut

The docs probably belong here: https://github.com/fmtlib/fmt/blob/master/doc/get-started.md.

vitaut avatar Nov 14 '24 00:11 vitaut

The docs probably belong here: https://github.com/fmtlib/fmt/blob/master/doc/get-started.md.

I've tried this exact tutorial but it only worked with make, but not ninja. My main project is built in ninja so I would prefer to keep it this way.

I'm using NixOS which might be part of the problem, how would I debug what is going wrong?

ninja
[1/6] Building CXX object CMakeFiles/hello.dir/hello.cc.o
FAILED: CMakeFiles/hello.dir/hello.cc.o 
/nix/store/irkh4bl62gwpbh8vsz9i2prlqxznlahw-clang-wrapper-18.1.8/bin/clang++  -I/tmp/fmt_mod/fmt/include -std=c++20 -MD -MT CMakeFiles/hello.dir/hello.cc.o -MF CMakeFiles/hello.dir/hello.cc.o.d -o CMakeFiles/hello.dir/hello.cc.o -c /tmp/fmt_mod/hello.cc
/tmp/fmt_mod/hello.cc:1:8: fatal error: module 'fmt' not found
    1 | import fmt;
      | ~~~~~~~^~~
1 error generated.
[2/6] Scanning /tmp/fmt_mod/fmt/src/fmt.cc for CXX dependencies
ninja: build stopped: subcommand failed.

yuannan avatar Nov 14 '24 16:11 yuannan

@yuannan I had a play with this and managed to get it working with a change:

Building with clang 18.1.8, cmake 3.31.0, c++ std c++20 or c++23 on Ubuntu, fmt trunk

Configure line: cmake -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=23 -DCMAKE_CXX_EXTENSIONS=OFF -Bbuild

However, I had to hack this if line: https://github.com/fmtlib/fmt/blob/9ced61bca440c0e214c2fb5f31d72ad6b855000a/CMakeLists.txt#L74-L77

To if (FMT_USE_CMAKE_MODULES AND 0) so force it to take the else. It works without changes for make for me too, just like you mentioned

I don't know if this is a known bug, with ninja, cmake, or how fmtlib is setup, or some combo. From a cursory scan, fmtlib seems to do it right but I'm no expert with modules

Arghnews avatar Nov 14 '24 20:11 Arghnews

Could be related to https://github.com/vitaut/modules/issues/16.

vitaut avatar Nov 15 '24 15:11 vitaut

Do you have a test for the fmt module?

see too https://github.com/ClausKlein/fmt-module?tab=readme-ov-file#readme

ClausKlein avatar Dec 04 '24 09:12 ClausKlein

NixOS recently updated to Clang 19, I'm now able to import the fmt module just fine, but I've got a different bug. https://github.com/NixOS/nixpkgs/issues/371540

Builds just fine on another machine with clang. Any ideas what is causing this?

yuannan avatar Jan 06 '25 21:01 yuannan

Looks like a workaround has been found in the linked issue.

vitaut avatar Jan 06 '25 22:01 vitaut

Looks like a workaround has been found in the linked issue.

Yeah I managed to figure it out after a LOT of hair pulling and literally looking thru binaries files 🫠.

Not great as it is a pretty dirty hack and causes incompatibilities. Meaning Everything depending on the workaround needs to also have the work around.

Looks like this issue is still here, but if its Nix, Clang, CMake, fmt, or me is still up in the air.

Good to see some progress none the less.

yuannan avatar Jan 07 '25 11:01 yuannan

CMake 3.31 seems to do a good job, and 4.x modules are even better. Using 3.31 I was able to surreptitiously transition a subset of our codebase's smaller libraries to modules.

Here's a small demo of using "import export" as a guerilla bootstrap with CMake

https://github.com/kfsone/cxxmod

kfsone avatar Jul 03 '25 19:07 kfsone

CMake 3.31 seems to do a good job, and 4.x modules are even better.

here is a full cxx_module example with multi platform CI, installation, test installed export packages, import std; using std::format, std::print, ... https://github.com/ClausKlein/cmake-init-modules/pull/6

ClausKlein avatar Jul 03 '25 20:07 ClausKlein

You guys can look at how I did ingestion for the forwarding. It works fine on my repo on this testing branch:

https://gitlab.com/splcpp/spllog/-/tree/header_preprocessing?ref_type=heads

I'm gonna clean up my code and properly merge it back into dev, then main soon. After I do that I'll whip up some docs for my lib and fmt.

I've been pretty busy with other work so I've not really had the time to clean up my code yet.

yuannan avatar Aug 20 '25 08:08 yuannan

@hedgehogform I am glad that you managed to fix your problem but it doesn't look related to this issue and probably belongs to spdlog, not here. So I am going to delete your comments to keep the discussion focused on modules.

vitaut avatar Aug 20 '25 20:08 vitaut

I wrote a demo showing how to use fmt as C++20 modules with Bazel: https://github.com/PikachuHyA/bazel_cxx20_modules_demo/blob/main/hello_fmt/README.md.

I get errors unless I define FMT_ATTACH_TO_GLOBAL_MODULE.

My environment:

  • OS: Ubuntu 24.04.1 LTS
  • Compiler: Clang 18.1.3

Do I always need to define FMT_ATTACH_TO_GLOBAL_MODULE?

Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
bazel-out/k8-fastbuild/bin/_objs/demo/main.pic.o:main.cc:function void fmt::v12::print@fmt<>(fmt::v12::fstring@fmt<>::t):(.text._ZN3fmt3v12W3fmt5printIJEEEvNS0_S1_7fstringIJDpT_EE1tEDpOS4_+0x8d): error: undefined reference to 'fmt::v12::vprint@fmt(fmt::v12::basic_string_view@fmt<char>, fmt::v12::basic_format_args@fmt<fmt::v12::context@fmt>)'
bazel-out/k8-fastbuild/bin/_objs/demo/main.pic.o:main.cc:function void fmt::v12::println@fmt<int>(_IO_FILE*, fmt::v12::fstring@fmt<int>::t, int&&):(.text._ZN3fmt3v12W3fmt7printlnIJiEEEvP8_IO_FILENS0_S1_7fstringIJDpT_EE1tEDpOS6_+0x6f): error: undefined reference to 'fmt::v12::vprintln@fmt(_IO_FILE*, fmt::v12::basic_string_view@fmt<char>, fmt::v12::basic_format_args@fmt<fmt::v12::context@fmt>)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::format_to_result@fmt::operator char*() const:(.text+0x746): error: undefined reference to 'fmt::v12::report_error@fmt(char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::text_style@fmt::operator|=(fmt::v12::text_style@fmt):(.text+0xb4e): error: undefined reference to 'fmt::v12::report_error@fmt(char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::text_style@fmt::get_foreground() const:(.text+0xcaf): error: undefined reference to 'fmt::v12::assert_fail@fmt(char const*, int, char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::text_style@fmt::get_background() const:(.text+0xd2f): error: undefined reference to 'fmt::v12::assert_fail@fmt(char const*, int, char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::text_style@fmt::get_emphasis() const:(.text+0xdaf): error: undefined reference to 'fmt::v12::assert_fail@fmt(char const*, int, char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::detail::bigint@fmt::subtract_aligned(fmt::v12::detail::bigint@fmt const&):(.text+0x10f7): error: undefined reference to 'fmt::v12::assert_fail@fmt(char const*, int, char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > fmt::v12::format@fmt<std::filesystem::__cxx11::path const&>(fmt::v12::fstring@fmt<std::filesystem::__cxx11::path const&>::t, std::filesystem::__cxx11::path const&):(.text._ZN3fmt3v12W3fmt6formatIJRKNSt10filesystem7__cxx114pathEEEENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS0_S1_7fstringIJDpT_EE1tEDpOSF_+0x78): error: undefined reference to 'fmt::v12::vformat@fmt[abi:cxx11](fmt::v12::basic_string_view@fmt<char>, fmt::v12::basic_format_args@fmt<fmt::v12::context@fmt>)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function char const* fmt::v12::detail::parse_format_specs@fmt<char>(char const*, char const*, fmt::v12::detail::dynamic_format_specs@fmt<char>&, fmt::v12::parse_context@fmt<char>&, fmt::v12::detail::type@fmt):(.text._ZN3fmt3v126detailW3fmt18parse_format_specsIcEEPKT_S6_S6_RNS1_S2_20dynamic_format_specsIS4_EERNS0_S2_13parse_contextIS4_EENS1_S2_4typeE+0x1f0): error: undefined reference to 'fmt::v12::report_error@fmt(char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function char const* fmt::v12::detail::parse_format_specs@fmt<char>(char const*, char const*, fmt::v12::detail::dynamic_format_specs@fmt<char>&, fmt::v12::parse_context@fmt<char>&, fmt::v12::detail::type@fmt):(.text._ZN3fmt3v126detailW3fmt18parse_format_specsIcEEPKT_S6_S6_RNS1_S2_20dynamic_format_specsIS4_EERNS0_S2_13parse_contextIS4_EENS1_S2_4typeE+0x411): error: undefined reference to 'fmt::v12::report_error@fmt(char const*)'
bazel-out/k8-fastbuild/bin/external/fmt+/_objs/fmt/fmt.pic.o:fmt.pic.pcm:function fmt::v12::detail::needs_escape@fmt(unsigned int):(.text._ZN3fmt3v126detailW3fmt12needs_escapeEj+0x40): error: undefined reference to 'fmt::v12::detail::is_printable@fmt(unsigned int)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Target //:demo failed to build
Use --verbose_failures to see the command lines of failed build steps.

PikachuHyA avatar Nov 04 '25 17:11 PikachuHyA