Build `folly::coro` with GCC

Build `folly::coro` with GCC

You have heard about Coroutine in C++, and you want to use it. There're two coroutine implementations that are considered most mature - cppcoro and folly::coro. They are written by the same guy - Lewis Baker. He's brilliant, and you shoud watch his cppcon talk on structured concurrency if you haven't already. folly::coro provides more features than cppcoro, so let's try it out.

First, let's have a toy program.

#include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/futures/Future.h>
#include <folly/executors/GlobalExecutor.h>
#include <folly/init/Init.h>
#include <iostream>

folly::coro::Task<int> slow() {
  std::cout << "before sleep" << std::endl;
  co_await folly::futures::sleep(std::chrono::seconds{1});
  std::cout << "after sleep" << std::endl;
  co_return 1;
}

int main(int argc, char** argv) {
  folly::init(&argc, &argv);
  folly::coro::blockingWait(
      slow().scheduleOn(folly::getGlobalCPUExecutor().get()));
  return 0;
}

Our goal is to compile this and run the program successfully.

Build folly with GCC and stdlibc++

First of all, you want to get folly the library and build it. Folly doesn't have a stable ABI, so you can just "install" it and among all of its dependencies into your project directory. Now after you've cloned the github repo, before you run the ./build.sh command (according to the README), we need to do a few more steps. That's why even if you already have folly built and installed, you would likely have to do it again with proper compiler flags.

First, switch your compiler to GCC, if you are using Clang. The steps with Clang are slightly different.

export CC=/usr/bin/gcc
export CXX=/usr/bin/g++

We also need to add the following flag to GCC so it will build folly with coroutine support.

export CXXFLAGS=-fcoroutine  # it would be -fcoroutine-ts for clang

Then run

./build.sh --install-dir /your/project/dir/folly/

The script will download all folly dependencies, e.g. boost, fmt, gflags, etc. and build folly with GCC.

Build our program with GCC and stdlibc++

g++ coro.cpp -std=c++17 -fcoroutines -I./lib/folly/include ./gcc-folly/folly/folly/lib/libfolly.a -lpthread -lm -ldl -lglog -lgflags -levent -ldouble-conversion ./gcc-folly/folly/fmt-jw5zFPx9bcovXWARXzsj4Dm5Slb5s8WHOv-hm0osfrA/lib64/libfmt.a -lunwind -liberty -lboost_context

I have gcc 10. And the above command compiles and successfully links against libstdc++ that comes with gcc 10. Notice that I staticly linked the fmt that was downloaded with the folly build script. Otherwise I got linker errors. I guess fmt doesn't have a stable ABI either.

And voila!

~/p/coro ❯❯❯ ./a.out
before sleep
after sleep

Failed attampt at building folly with Clang and libc++

First of all, you want to delete everything if your folly directory that's listed in .gitignore. Specifically, folly-config.h which during the build process sets many macros. You don't want your previous GCC build to set macros that won't work for your clang build.

export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
export CXXFLAGS="-stdlib=libc++ -fcoroutines-ts -I/usr/local/lib/c++/v1"
export LDFLAGS="-lc++abi"

The exact command might differ on your computer. The point is to construct the compiler and linker flags that folly can be properly compiled (e.g. use libc++ and its standard headers, and libc++abi, etc.)

Now folly is built with Clang. Let's build our program.

~/p/coro ❯❯❯ clang++ coro.cpp -std=libc++  -std=c++2a -fcoroutines-ts -I./clang-folly/folly/include -I/usr/local/include/c++/v1 -lpthread -lm -ldl -lglog -lgflags -levent -ldouble_conversion ./clang-folly/fmt-DykEYK9RumG6QnGTKWi-w5f6emSSD
hccE-zkSgDUDJI/lib64/libfmt.a -lunwind -liberty -lboost_context -lc++abi 2>&1                                                                                                                                                                 
In file included from coro.cpp:1:                                                                                                                                                                                                             
In file included from ./clang-folly/folly/include/folly/experimental/coro/Task.h:19:                                                                                                                                                          
In file included from /usr/local/include/c++/v1/exception:81:                                                                                                                                                                                 
In file included from /usr/local/include/c++/v1/cstdlib:85:                                                                                                                                                                                   
In file included from /usr/local/include/c++/v1/stdlib.h:93:                                                                                                                                                                                  
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0/stdlib.h:38:12: error: no member named 'abort' in namespace 'std'                                                                                             
using std::abort;                                                                                                                                                                                                                             
      ~~~~~^  

stdlib.h from libc++ has include_next <stdlib.h> which means it will try to include the next stdlib.h header from the system include path. Let's add -v flag to Clang.

#include <...> search starts here:                                                                                                                                                                                                            
 ./clang-folly/folly/include                                                                                                                                                                                                                  
 /usr/local/include/c++/v1                                                                                                                                                                                                                    
 /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0                                                                                                                                                              
 /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0/x86_64-pc-linux-gnu                                                                                                                                          
 /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0/backward                                                                                                                                                     
 /usr/local/include                                                                                                                                                                                                                           
 /usr/lib/clang/11.1.0/include                                                                                                                                                                                                                
 /usr/include 

So the "next" stdlib.h comes from gcc here. Let's try remove that by setting -stdlib++-isystem flag of Clang to /usr/local/include/c++/v1, where it has the libc++ headers. Here's what I've got,

~/p/coro ❯❯❯ clang++ coro.cpp -std=libc++  -std=c++17 -fcoroutines-ts -I./clang-folly/folly/include -stdlib++-isystem /usr/local/include/c++/v1 -lpthread -lm -ldl ./clang-folly/glog-nY6-9ZKI5XRaJI_NUlE6f0fGTadcztXmsJ3jiy0ev3o/lib64/libglog.so ./clang-folly/gflags-VRES6w4l-hYsLHiL7oeMF11RzCanjyGDFLXG4bsP2v8/lib/libgflags.a -levent ./clang-folly/double-conversion-ofzjwl4IFoHrti1JqsbRSL-zDJzjsTRYzMzaXHpAbfY/lib/libdouble-conversion.a ./clang-folly/fmt-DykEYK9RumG6QnGTKWi-w5f6emSSDhccE-zkSgDUDJI/lib64/libfmt.a -lunwind -liberty -lboost_context -lc++abi ./clang-folly/folly/lib/libfolly.a /usr/local/lib/libc++.a 2>&1  | head
/usr/bin/ld: ./clang-folly/folly/lib/libfolly.a(Demangle.cpp.o): in function `folly::demangle(char const*, char*, unsigned long)':
/home/lupan/folly/folly/Demangle.cpp:165: undefined reference to `cplus_demangle_v3_callback'
/usr/bin/ld: ./clang-folly/folly/lib/libfolly.a(LogConfigParser.cpp.o): in function `double_conversion::DoubleToStringConverter::ToShortest(double, double_conversion::StringBuilder*) const':
/tmp/fbcode_builder_getdeps-ZhomeZlupanZfollyZbuildZfbcode_builder/installed/double-conversion-ofzjwl4IFoHrti1JqsbRSL-zDJzjsTRYzMzaXHpAbfY/include/double-conversion/double-conversion.h:158: undefined reference to `double_conversion::DoubleToStringConverter::ToShortestIeeeNumber(double, double_conversion::StringBuilder*, double_conversion::DoubleToStringConverter::DtoaMode) const'
/usr/bin/ld: ./clang-folly/folly/lib/libfolly.a(LogConfigParser.cpp.o): in function `double_conversion::DoubleToStringConverter::ToShortestSingle(float, double_conversion::StringBuilder*) const':
/tmp/fbcode_builder_getdeps-ZhomeZlupanZfollyZbuildZfbcode_builder/installed/double-conversion-ofzjwl4IFoHrti1JqsbRSL-zDJzjsTRYzMzaXHpAbfY/include/double-conversion/double-conversion.h:163: undefined reference to `double_conversion::DoubleToStringConverter::ToShortestIeeeNumber(double, double_conversion::StringBuilder*, double_conversion::DoubleToStringConverter::DtoaMode) const'
/usr/bin/ld: ./clang-folly/folly/lib/libfolly.a(LogConfigParser.cpp.o): in function `std::__1::enable_if<std::is_floating_point<double>::value&&IsSomeString<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::value, void>::type folly::toAppend<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, double>(double, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, double_conversion::DoubleToStringConverter::DtoaMode, unsigned int)':
/home/lupan/folly/folly/Conv.h:657: undefined reference to `double_conversion::DoubleToStringConverter::ToFixed(double, int, double_conversion::StringBuilder*) const'
/usr/bin/ld: /home/lupan/folly/folly/Conv.h:662: undefined reference to `double_conversion::DoubleToStringConverter::ToPrecision(double, int, double_conversion::StringBuilder*) const'
/usr/bin/ld: ./clang-folly/folly/lib/libfolly.a(Conv.cpp.o): in function `folly::Expected<float, folly::ConversionCode> folly::detail::str_to_floating<float>(folly::Range<char const*>*)':

It doesn't make much sense to me, given I am linking the same double_conversion that folly is built with. Anyway, maybe later.