Skip to content

Commit cdf7622

Browse files
Tobias Loewandreasbuhr
authored andcommitted
added cmake coroutine detection for VS 2019 (coroutines without /await)
1 parent dfeca19 commit cdf7622

File tree

3 files changed

+253
-38
lines changed

3 files changed

+253
-38
lines changed

cmake/FindCoroutines.cmake

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# Copyright (c) 2019-present, Facebook, Inc.
2+
#
3+
# This source code is licensed under the Apache License found in the
4+
# LICENSE.txt file in the root directory of this source tree.
5+
6+
#[=======================================================================[.rst:
7+
8+
FindCoroutines
9+
##############
10+
11+
This module supports the C++ standard support for coroutines. Use
12+
the :imp-target:`std::coroutines` imported target to
13+
14+
Options
15+
*******
16+
17+
The ``COMPONENTS`` argument to this module supports the following values:
18+
19+
.. find-component:: Experimental
20+
:name: coro.Experimental
21+
22+
Allows the module to find the "experimental" Coroutines TS
23+
version of the coroutines library. This is the library that should be
24+
used with the ``std::experimental`` namespace.
25+
26+
.. find-component:: Final
27+
:name: coro.Final
28+
29+
Finds the final C++20 standard version of coroutines.
30+
31+
If no components are provided, behaves as if the
32+
:find-component:`coro.Final` component was specified.
33+
34+
If both :find-component:`coro.Experimental` and :find-component:`coro.Final` are
35+
provided, first looks for ``Final``, and falls back to ``Experimental`` in case
36+
of failure. If ``Final`` is found, :imp-target:`std::coroutines` and all
37+
:ref:`variables <coro.variables>` will refer to the ``Final`` version.
38+
39+
40+
Imported Targets
41+
****************
42+
43+
.. imp-target:: std::coroutines
44+
45+
The ``std::coroutines`` imported target is defined when any requested
46+
version of the C++ coroutines library has been found, whether it is
47+
*Experimental* or *Final*.
48+
49+
If no version of the coroutines library is available, this target will not
50+
be defined.
51+
52+
.. note::
53+
This target has ``cxx_std_17`` as an ``INTERFACE``
54+
:ref:`compile language standard feature <req-lang-standards>`. Linking
55+
to this target will automatically enable C++17 if no later standard
56+
version is already required on the linking target.
57+
58+
59+
.. _coro.variables:
60+
61+
Variables
62+
*********
63+
64+
.. variable:: CXX_COROUTINES_HAVE_COROUTINES
65+
66+
Set to ``TRUE`` when coroutines are supported in both the language and the
67+
library.
68+
69+
.. variable:: CXX_COROUTINES_HEADER
70+
71+
Set to either ``coroutine`` or ``experimental/coroutine`` depending on
72+
whether :find-component:`coro.Final` or :find-component:`coro.Experimental` was
73+
found.
74+
75+
.. variable:: CXX_COROUTINES_NAMESPACE
76+
77+
Set to either ``std`` or ``std::experimental``
78+
depending on whether :find-component:`coro.Final` or
79+
:find-component:`coro.Experimental` was found.
80+
81+
82+
Examples
83+
********
84+
85+
Using `find_package(Coroutines)` with no component arguments:
86+
87+
.. code-block:: cmake
88+
89+
find_package(Coroutines REQUIRED)
90+
91+
add_executable(my-program main.cpp)
92+
target_link_libraries(my-program PRIVATE std::coroutines)
93+
94+
95+
#]=======================================================================]
96+
97+
98+
if(TARGET std::coroutines)
99+
# This module has already been processed. Don't do it again.
100+
return()
101+
endif()
102+
103+
include(CMakePushCheckState)
104+
include(CheckIncludeFileCXX)
105+
include(CheckCXXSourceCompiles)
106+
107+
cmake_push_check_state()
108+
109+
set(CMAKE_REQUIRED_QUIET ${Coroutines_FIND_QUIETLY})
110+
111+
# All of our tests required C++17 or later
112+
if(("x${CMAKE_CXX_COMPILER_ID}" MATCHES "x.*Clang" AND "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
113+
set(_CXX_COROUTINES_STD17 "/std:c++17")
114+
set(_CXX_COROUTINES_AWAIT "/await")
115+
else()
116+
set(_CXX_COROUTINES_STD17 "-std=c++17")
117+
set(_CXX_COROUTINES_AWAIT "-fcoroutines-ts")
118+
endif()
119+
120+
# Normalize and check the component list we were given
121+
set(want_components ${Coroutines_FIND_COMPONENTS})
122+
if(Coroutines_FIND_COMPONENTS STREQUAL "")
123+
set(want_components Final)
124+
endif()
125+
126+
# Warn on any unrecognized components
127+
set(extra_components ${want_components})
128+
list(REMOVE_ITEM extra_components Final Experimental)
129+
foreach(component IN LISTS extra_components)
130+
message(WARNING "Extraneous find_package component for Coroutines: ${component}")
131+
endforeach()
132+
133+
# Detect which of Experimental and Final we should look for
134+
set(find_experimental TRUE)
135+
set(find_final TRUE)
136+
if(NOT "Final" IN_LIST want_components)
137+
set(find_final FALSE)
138+
endif()
139+
if(NOT "Experimental" IN_LIST want_components)
140+
set(find_experimental FALSE)
141+
endif()
142+
143+
if(find_final)
144+
check_include_file_cxx("coroutine" _CXX_COROUTINES_HAVE_HEADER)
145+
mark_as_advanced(_CXX_COROUTINES_HAVE_HEADER)
146+
if(_CXX_COROUTINES_HAVE_HEADER)
147+
# We found the non-experimental header. Don't bother looking for the
148+
# experimental one.
149+
set(find_experimental FALSE)
150+
endif()
151+
else()
152+
set(_CXX_COROUTINES_HAVE_HEADER FALSE)
153+
endif()
154+
155+
if(find_experimental)
156+
check_include_file_cxx("experimental/coroutine" _CXX_COROUTINES_HAVE_EXPERIMENTAL_HEADER)
157+
mark_as_advanced(_CXX_COROUTINES_HAVE_EXPERIMENTAL_HEADER)
158+
else()
159+
set(_CXX_COROUTINES_HAVE_EXPERIMENTAL_HEADER FALSE)
160+
endif()
161+
162+
if(_CXX_COROUTINES_HAVE_HEADER)
163+
set(_have_coro TRUE)
164+
set(_coro_header coroutine)
165+
set(_coro_namespace std)
166+
elseif(_CXX_COROUTINES_HAVE_EXPERIMENTAL_HEADER)
167+
set(_have_coro TRUE)
168+
set(_coro_header experimental/coroutine)
169+
set(_coro_namespace std::experimental)
170+
else()
171+
set(_have_coro FALSE)
172+
endif()
173+
174+
set(CXX_COROUTINES_HAVE_COROUTINES ${_have_coro} CACHE BOOL "TRUE if we have the C++ coroutines feature")
175+
set(CXX_COROUTINES_HEADER ${_coro_header} CACHE STRING "The header that should be included to obtain the coroutines APIs")
176+
set(CXX_COROUTINES_NAMESPACE ${_coro_namespace} CACHE STRING "The C++ namespace that contains the coroutines APIs")
177+
178+
set(_found FALSE)
179+
180+
if(CXX_COROUTINES_HAVE_COROUTINES)
181+
# We have some coroutines library available. Do link checks
182+
string(CONFIGURE [[
183+
#include <utility>
184+
#include <@CXX_COROUTINES_HEADER@>
185+
186+
struct present {
187+
struct promise_type {
188+
int result;
189+
present get_return_object() { return present{*this}; }
190+
@CXX_COROUTINES_NAMESPACE@::suspend_never initial_suspend() { return {}; }
191+
@CXX_COROUTINES_NAMESPACE@::suspend_always final_suspend() { return {}; }
192+
void return_value(int i) { result = i; }
193+
void unhandled_exception() {}
194+
};
195+
friend struct promise_type;
196+
present(present&& that) : coro_(std::exchange(that.coro_, {})) {}
197+
~present() { if(coro_) coro_.destroy(); }
198+
bool await_ready() const { return true; }
199+
void await_suspend(@CXX_COROUTINES_NAMESPACE@::coroutine_handle<>) const {}
200+
int await_resume() const { return coro_.promise().result; }
201+
private:
202+
present(promise_type& promise)
203+
: coro_(@CXX_COROUTINES_NAMESPACE@::coroutine_handle<promise_type>::from_promise(promise)) {}
204+
@CXX_COROUTINES_NAMESPACE@::coroutine_handle<promise_type> coro_;
205+
};
206+
207+
present f(int n) {
208+
if (n < 2)
209+
co_return 1;
210+
else
211+
co_return n * co_await f(n - 1);
212+
}
213+
214+
int main() {
215+
return f(5).await_resume() != 120;
216+
}
217+
]] code @ONLY)
218+
219+
# Try to compile a simple coroutines program without any compiler flags
220+
check_cxx_source_compiles("${code}" CXX_COROUTINES_NO_AWAIT_NEEDED)
221+
222+
set(can_link ${CXX_COROUTINES_NO_AWAIT_NEEDED})
223+
224+
if(NOT CXX_COROUTINES_NO_AWAIT_NEEDED)
225+
# Add the -fcoroutines-ts (or /await) flag
226+
set(CMAKE_REQUIRED_FLAGS "${_CXX_COROUTINES_STD17} ${_CXX_COROUTINES_AWAIT}")
227+
check_cxx_source_compiles("${code}" CXX_COROUTINES_AWAIT_NEEDED)
228+
set(can_link ${CXX_COROUTINES_AWAIT_NEEDED})
229+
endif()
230+
231+
if(can_link)
232+
add_library(std::coroutines INTERFACE IMPORTED)
233+
set(_found TRUE)
234+
235+
if(CXX_COROUTINES_NO_AWAIT_NEEDED)
236+
# Nothing to add...
237+
elseif(CXX_COROUTINES_AWAIT_NEEDED)
238+
target_compile_options(std::coroutines INTERFACE ${_CXX_COROUTINES_AWAIT})
239+
endif()
240+
else()
241+
set(CXX_COROUTINES_HAVE_COROUTINES FALSE)
242+
endif()
243+
endif()
244+
245+
cmake_pop_check_state()
246+
247+
set(Coroutines_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::coroutines" FORCE)
248+
249+
if(Coroutines_FIND_REQUIRED AND NOT Coroutines_FOUND)
250+
message(FATAL_ERROR "Cannot compile simple program using std::coroutines")
251+
endif()

cmake/FindCppcoroCoroutines.cmake

Lines changed: 0 additions & 36 deletions
This file was deleted.

lib/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ target_include_directories(cppcoro PUBLIC
159159
$<INSTALL_INTERFACE:include>)
160160
target_compile_features(cppcoro PUBLIC cxx_std_20)
161161

162-
find_package(CppcoroCoroutines REQUIRED)
163-
target_link_libraries(cppcoro PUBLIC cppcoro::coroutines)
162+
find_package(Coroutines REQUIRED)
163+
target_link_libraries(cppcoro PUBLIC std::coroutines)
164164

165165
install(TARGETS cppcoro EXPORT cppcoroTargets
166166
LIBRARY DESTINATION lib

0 commit comments

Comments
 (0)