1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5FindOpenACC
6-----------
7
8.. versionadded:: 3.10
9
10Detect OpenACC support by the compiler.
11
12This module can be used to detect OpenACC support in a compiler.
13If the compiler supports OpenACC, the flags required to compile with
14OpenACC support are returned in variables for the different languages.
15Currently, only NVHPC, PGI, GNU and Cray compilers are supported.
16
17Imported Targets
18^^^^^^^^^^^^^^^^
19
20.. versionadded:: 3.16
21
22The module provides :prop_tgt:`IMPORTED` targets:
23
24``OpenACC::OpenACC_<lang>``
25  Target for using OpenACC from ``<lang>``.
26
27Variables
28^^^^^^^^^
29
30This module will set the following variables per language in your
31project, where ``<lang>`` is one of C, CXX, or Fortran:
32
33``OpenACC_<lang>_FOUND``
34  Variable indicating if OpenACC support for ``<lang>`` was detected.
35``OpenACC_<lang>_FLAGS``
36  OpenACC compiler flags for ``<lang>``, separated by spaces.
37``OpenACC_<lang>_OPTIONS``
38  .. versionadded:: 3.16
39
40  OpenACC compiler flags for ``<lang>``, as a list. Suitable for usage
41  with target_compile_options or target_link_options.
42
43The module will also try to provide the OpenACC version variables:
44
45``OpenACC_<lang>_SPEC_DATE``
46  Date of the OpenACC specification implemented by the ``<lang>`` compiler.
47``OpenACC_<lang>_VERSION_MAJOR``
48  Major version of OpenACC implemented by the ``<lang>`` compiler.
49``OpenACC_<lang>_VERSION_MINOR``
50  Minor version of OpenACC implemented by the ``<lang>`` compiler.
51``OpenACC_<lang>_VERSION``
52  OpenACC version implemented by the ``<lang>`` compiler.
53
54The specification date is formatted as given in the OpenACC standard:
55``yyyymm`` where ``yyyy`` and ``mm`` represents the year and month of
56the OpenACC specification implemented by the ``<lang>`` compiler.
57
58Input Variables
59^^^^^^^^^^^^^^^
60
61``OpenACC_ACCEL_TARGET=<target>``
62If set, will the correct target accelerator flag set to the <target> will
63be returned with OpenACC_<lang>_FLAGS.
64#]=======================================================================]
65
66set(OpenACC_C_CXX_TEST_SOURCE
67"
68int main(){
69#ifdef _OPENACC
70  return 0;
71#else
72  breaks_on_purpose
73#endif
74}
75"
76)
77set(OpenACC_Fortran_TEST_SOURCE
78"
79program test
80#ifndef _OPENACC
81  breaks_on_purpose
82#endif
83endprogram test
84"
85)
86set(OpenACC_C_CXX_CHECK_VERSION_SOURCE
87"
88#include <stdio.h>
89const char accver_str[] = { 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'A',
90                            'C', 'C', '-', 'd', 'a', 't', 'e', '[',
91                            ('0' + ((_OPENACC/100000)%10)),
92                            ('0' + ((_OPENACC/10000)%10)),
93                            ('0' + ((_OPENACC/1000)%10)),
94                            ('0' + ((_OPENACC/100)%10)),
95                            ('0' + ((_OPENACC/10)%10)),
96                            ('0' + ((_OPENACC/1)%10)),
97                            ']', '\\0' };
98int main()
99{
100  puts(accver_str);
101  return 0;
102}
103")
104set(OpenACC_Fortran_CHECK_VERSION_SOURCE
105"
106      program acc_ver
107      implicit none
108      integer, parameter :: zero = ichar('0')
109      character, dimension(25), parameter :: accver_str =&
110      (/ 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'A', 'C', 'C', '-',&
111         'd', 'a', 't', 'e', '[',&
112         char(zero + mod(_OPENACC/100000, 10)),&
113         char(zero + mod(_OPENACC/10000, 10)),&
114         char(zero + mod(_OPENACC/1000, 10)),&
115         char(zero + mod(_OPENACC/100, 10)),&
116         char(zero + mod(_OPENACC/10, 10)),&
117         char(zero + mod(_OPENACC/1, 10)), ']' /)
118      print *, accver_str
119      end program acc_ver
120"
121)
122
123
124function(_OPENACC_WRITE_SOURCE_FILE LANG SRC_FILE_CONTENT_VAR SRC_FILE_NAME SRC_FILE_FULLPATH)
125  set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenACC)
126  if("${LANG}" STREQUAL "C")
127    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.c")
128    file(WRITE "${SRC_FILE}" "${OpenACC_C_CXX_${SRC_FILE_CONTENT_VAR}}")
129  elseif("${LANG}" STREQUAL "CXX")
130    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.cpp")
131    file(WRITE "${SRC_FILE}" "${OpenACC_C_CXX_${SRC_FILE_CONTENT_VAR}}")
132  elseif("${LANG}" STREQUAL "Fortran")
133    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.F90")
134    file(WRITE "${SRC_FILE}_in" "${OpenACC_Fortran_${SRC_FILE_CONTENT_VAR}}")
135    configure_file("${SRC_FILE}_in" "${SRC_FILE}" @ONLY)
136  endif()
137  set(${SRC_FILE_FULLPATH} "${SRC_FILE}" PARENT_SCOPE)
138endfunction()
139
140
141function(_OPENACC_GET_FLAGS_CANDIDATE LANG FLAG_VAR)
142  set(ACC_FLAG_NVHPC "-acc")
143  set(ACC_FLAG_PGI "-acc")
144  set(ACC_FLAG_GNU "-fopenacc")
145  set(ACC_FLAG_Cray "-h acc")
146
147  if(DEFINED ACC_FLAG_${CMAKE_${LANG}_COMPILER_ID})
148    set("${FLAG_VAR}" "${ACC_FLAG_${CMAKE_${LANG}_COMPILER_ID}}" PARENT_SCOPE)
149  else()
150    # Fall back to a few common flags.
151    set("${FLAG_VAR}" ${ACC_FLAG_GNU} ${ACC_FLAG_PGI})
152  endif()
153
154endfunction()
155
156
157function(_OPENACC_GET_ACCEL_TARGET_FLAG LANG TARGET FLAG_VAR)
158  # Find target accelerator flags.
159  set(ACC_TARGET_FLAG_NVHPC "-ta")
160  set(ACC_TARGET_FLAG_PGI "-ta")
161  if(DEFINED ACC_TARGET_FLAG_${CMAKE_${LANG}_COMPILER_ID})
162    set("${FLAG_VAR}" "${ACC_TARGET_FLAG_${CMAKE_${LANG}_COMPILER_ID}}=${TARGET}" PARENT_SCOPE)
163  endif()
164endfunction()
165
166
167function(_OPENACC_GET_VERBOSE_FLAG LANG FLAG_VAR)
168  # Find compiler's verbose flag for OpenACC.
169  set(ACC_VERBOSE_FLAG_NVHPC "-Minfo=accel")
170  set(ACC_VERBOSE_FLAG_PGI "-Minfo=accel")
171  if(DEFINED ACC_VERBOSE_FLAG_${CMAKE_${LANG}_COMPILER_ID})
172    set("${FLAG_VAR}" "${ACC_VERBOSE_FLAG_${CMAKE_${LANG}_COMPILER_ID}}" PARENT_SCOPE)
173  endif()
174endfunction()
175
176
177function(_OPENACC_GET_FLAGS LANG FLAG_VAR)
178  set(FLAG_CANDIDATES "")
179  _OPENACC_GET_FLAGS_CANDIDATE("${LANG}" FLAG_CANDIDATES)
180  _OPENACC_WRITE_SOURCE_FILE("${LANG}" "TEST_SOURCE" OpenACCTryFlag _OPENACC_TEST_SRC)
181
182  foreach(FLAG IN LISTS FLAG_CANDIDATES)
183    try_compile(OpenACC_FLAG_TEST_RESULT ${CMAKE_BINARY_DIR} ${_OPENACC_TEST_SRC}
184      CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${FLAG}"
185      OUTPUT_VARIABLE OpenACC_TRY_COMPILE_OUTPUT
186    )
187    if(OpenACC_FLAG_TEST_RESULT)
188      set("${FLAG_VAR}" "${FLAG}")
189      if(DEFINED OpenACC_ACCEL_TARGET)
190        _OPENACC_GET_ACCEL_TARGET_FLAG("${LANG}" "${OpenACC_ACCEL_TARGET}" TARGET_FLAG)
191        string(APPEND "${FLAG_VAR}" " ${TARGET_FLAG}")
192      endif()
193
194      if(CMAKE_VERBOSE_MAKEFILE)
195        # -Minfo=accel prints out OpenACC's messages on optimizations.
196        _OPENACC_GET_VERBOSE_FLAG("${LANG}" OpenACC_VERBOSE_FLAG)
197        string(APPEND "${FLAG_VAR}" " ${OpenACC_VERBOSE_FLAG}")
198      endif()
199      set("${FLAG_VAR}" "${${FLAG_VAR}}" PARENT_SCOPE)
200      break()
201    endif()
202  endforeach()
203
204endfunction()
205
206
207function(_OPENACC_GET_SPEC_DATE LANG SPEC_DATE)
208  _OPENACC_WRITE_SOURCE_FILE("${LANG}" "CHECK_VERSION_SOURCE" OpenACCCheckVersion _OPENACC_TEST_SRC)
209
210  set(BIN_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenACC/accver_${LANG}.bin")
211  try_compile(OpenACC_SPECTEST_${LANG} "${CMAKE_BINARY_DIR}" "${_OPENACC_TEST_SRC}"
212              CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenACC_${LANG}_FLAGS}"
213              COPY_FILE ${BIN_FILE}
214              OUTPUT_VARIABLE OUTPUT)
215
216  if(${OpenACC_SPECTEST_${LANG}})
217    file(STRINGS ${BIN_FILE} specstr LIMIT_COUNT 1 REGEX "INFO:OpenACC-date")
218    set(regex_spec_date ".*INFO:OpenACC-date\\[0*([^]]*)\\].*")
219    if("${specstr}" MATCHES "${regex_spec_date}")
220      set(${SPEC_DATE} "${CMAKE_MATCH_1}" PARENT_SCOPE)
221    endif()
222  endif()
223endfunction()
224
225
226macro(_OPENACC_SET_VERSION_BY_SPEC_DATE LANG)
227  set(OpenACC_SPEC_DATE_MAP
228    # Combined versions, 2.5 onwards
229    "201510=2.5"
230    # 2013 08 is the corrected version.
231    "201308=2.0"
232    "201306=2.0"
233    "201111=1.0"
234  )
235
236  string(REGEX MATCHALL "${OpenACC_${LANG}_SPEC_DATE}=([0-9]+)\\.([0-9]+)" _version_match "${OpenACC_SPEC_DATE_MAP}")
237  if(NOT _version_match STREQUAL "")
238    set(OpenACC_${LANG}_VERSION_MAJOR ${CMAKE_MATCH_1})
239    set(OpenACC_${LANG}_VERSION_MINOR ${CMAKE_MATCH_2})
240    set(OpenACC_${LANG}_VERSION "${OpenACC_${LANG}_VERSION_MAJOR}.${OpenACC_${LANG}_VERSION_MINOR}")
241  else()
242    unset(OpenACC_${LANG}_VERSION_MAJOR)
243    unset(OpenACC_${LANG}_VERSION_MINOR)
244    unset(OpenACC_${LANG}_VERSION)
245  endif()
246  unset(_version_match)
247  unset(OpenACC_SPEC_DATE_MAP)
248endmacro()
249
250
251include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
252foreach (LANG IN ITEMS C CXX Fortran)
253  if(CMAKE_${LANG}_COMPILER_LOADED)
254    set(OpenACC_${LANG}_FIND_QUIETLY ${OpenACC_FIND_QUIETLY})
255    set(OpenACC_${LANG}_FIND_REQUIRED ${OpenACC_FIND_REQUIRED})
256    set(OpenACC_${LANG}_FIND_VERSION ${OpenACC_FIND_VERSION})
257    set(OpenACC_${LANG}_FIND_VERSION_EXACT ${OpenACC_FIND_VERSION_EXACT})
258
259    if(NOT DEFINED OpenACC_${LANG}_FLAGS)
260      _OPENACC_GET_FLAGS("${LANG}" OpenACC_${LANG}_FLAGS)
261    endif()
262    if(NOT DEFINED OpenACC_${LANG}_OPTIONS)
263      separate_arguments(OpenACC_${LANG}_OPTIONS NATIVE_COMMAND "${OpenACC_${LANG}_FLAGS}")
264    endif()
265    _OPENACC_GET_SPEC_DATE("${LANG}" OpenACC_${LANG}_SPEC_DATE)
266    _OPENACC_SET_VERSION_BY_SPEC_DATE("${LANG}")
267
268    find_package_handle_standard_args(OpenACC_${LANG}
269      NAME_MISMATCHED
270      REQUIRED_VARS OpenACC_${LANG}_FLAGS
271      VERSION_VAR OpenACC_${LANG}_VERSION
272    )
273  endif()
274endforeach()
275
276foreach (LANG IN ITEMS C CXX Fortran)
277  if(OpenACC_${LANG}_FOUND AND NOT TARGET OpenACC::OpenACC_${LANG})
278    add_library(OpenACC::OpenACC_${LANG} INTERFACE IMPORTED)
279  endif()
280  if(OpenACC_${LANG}_LIBRARIES)
281    set_property(TARGET OpenACC::OpenACC_${LANG} PROPERTY
282      INTERFACE_LINK_LIBRARIES "${OpenACC_${LANG}_LIBRARIES}")
283  endif()
284  if(OpenACC_${LANG}_FLAGS)
285    set_property(TARGET OpenACC::OpenACC_${LANG} PROPERTY
286      INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenACC_${LANG}_OPTIONS}>")
287    set_property(TARGET OpenACC::OpenACC_${LANG} PROPERTY
288      INTERFACE_LINK_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenACC_${LANG}_OPTIONS}>")
289    unset(_OpenACC_${LANG}_OPTIONS)
290  endif()
291endforeach()
292
293unset(OpenACC_C_CXX_TEST_SOURCE)
294unset(OpenACC_Fortran_TEST_SOURCE)
295unset(OpenACC_C_CXX_CHECK_VERSION_SOURCE)
296unset(OpenACC_Fortran_CHECK_VERSION_SOURCE)
297