1#[[Cmake helper function to parse source files from make files
2this is to avoid breaking existing make and auto make support
3but still have the option to use CMake with only lists at one place]]
4
5cmake_minimum_required(VERSION 3.1)
6
7function(get_library_version OPUS_LIBRARY_VERSION OPUS_LIBRARY_VERSION_MAJOR)
8  file(STRINGS configure.ac opus_lt_current_string
9       LIMIT_COUNT 1
10       REGEX "OPUS_LT_CURRENT=")
11  string(REGEX MATCH
12               "OPUS_LT_CURRENT=([0-9]*)"
13               _
14               ${opus_lt_current_string})
15  set(OPUS_LT_CURRENT ${CMAKE_MATCH_1})
16
17  file(STRINGS configure.ac opus_lt_revision_string
18       LIMIT_COUNT 1
19       REGEX "OPUS_LT_REVISION=")
20  string(REGEX MATCH
21               "OPUS_LT_REVISION=([0-9]*)"
22               _
23               ${opus_lt_revision_string})
24  set(OPUS_LT_REVISION ${CMAKE_MATCH_1})
25
26  file(STRINGS configure.ac opus_lt_age_string
27       LIMIT_COUNT 1
28       REGEX "OPUS_LT_AGE=")
29  string(REGEX MATCH
30               "OPUS_LT_AGE=([0-9]*)"
31               _
32               ${opus_lt_age_string})
33  set(OPUS_LT_AGE ${CMAKE_MATCH_1})
34
35  math(EXPR OPUS_LIBRARY_VERSION_MAJOR "${OPUS_LT_CURRENT} - ${OPUS_LT_AGE}")
36  set(OPUS_LIBRARY_VERSION_MINOR ${OPUS_LT_AGE})
37  set(OPUS_LIBRARY_VERSION_PATCH ${OPUS_LT_REVISION})
38  set(
39    OPUS_LIBRARY_VERSION
40    "${OPUS_LIBRARY_VERSION_MAJOR}.${OPUS_LIBRARY_VERSION_MINOR}.${OPUS_LIBRARY_VERSION_PATCH}"
41    PARENT_SCOPE)
42  set(OPUS_LIBRARY_VERSION_MAJOR ${OPUS_LIBRARY_VERSION_MAJOR} PARENT_SCOPE)
43endfunction()
44
45function(get_package_version PACKAGE_VERSION)
46  find_package(Git)
47  if(GIT_FOUND)
48    execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --match "v*"
49                    OUTPUT_VARIABLE OPUS_PACKAGE_VERSION)
50    if(OPUS_PACKAGE_VERSION)
51      string(STRIP ${OPUS_PACKAGE_VERSION}, OPUS_PACKAGE_VERSION)
52      string(REPLACE \n
53                     ""
54                     OPUS_PACKAGE_VERSION
55                     ${OPUS_PACKAGE_VERSION})
56      string(REPLACE ,
57                     ""
58                     OPUS_PACKAGE_VERSION
59                     ${OPUS_PACKAGE_VERSION})
60
61      string(SUBSTRING ${OPUS_PACKAGE_VERSION}
62                       1
63                       -1
64                       OPUS_PACKAGE_VERSION)
65      set(PACKAGE_VERSION ${OPUS_PACKAGE_VERSION} PARENT_SCOPE)
66      return()
67    endif()
68  endif()
69
70  if(EXISTS "${CMAKE_SOURCE_DIR}/package_version")
71    # Not a git repo, lets' try to parse it from package_version file if exists
72    file(STRINGS package_version opus_package_version_string
73         LIMIT_COUNT 1
74         REGEX "PACKAGE_VERSION=")
75    string(REPLACE "PACKAGE_VERSION="
76                   ""
77                   opus_package_version_string
78                   ${opus_package_version_string})
79    string(REPLACE "\""
80                   ""
81                   opus_package_version_string
82                   ${opus_package_version_string})
83    set(PACKAGE_VERSION ${opus_package_version_string} PARENT_SCOPE)
84    return()
85  endif()
86
87  # if all else fails set to 0
88  set(PACKAGE_VERSION 0 PARENT_SCOPE)
89endfunction()
90
91function(check_and_set_flag NAME FLAG)
92  include(CheckCCompilerFlag)
93  check_c_compiler_flag(${FLAG} ${NAME}_SUPPORTED)
94  if(${NAME}_SUPPORTED)
95    add_definitions(${FLAG})
96  endif()
97endfunction()
98
99function(check_flag NAME FLAG)
100  include(CheckCCompilerFlag)
101  check_c_compiler_flag(${FLAG} ${NAME}_SUPPORTED)
102endfunction()
103
104include(CheckIncludeFile)
105# function to check if compiler supports SSE, SSE2, SSE4.1 and AVX if target
106# systems may not have SSE support then use OPUS_MAY_HAVE_SSE option if target
107# system is guaranteed to have SSE support then OPUS_PRESUME_SSE can be used to
108# skip SSE runtime check
109function(opus_detect_sse COMPILER_SUPPORT_SIMD)
110  message(STATUS "Check SIMD support by compiler")
111  check_include_file(xmmintrin.h HAVE_XMMINTRIN_H) # SSE1
112  if(HAVE_XMMINTRIN_H)
113    if(MSVC)
114      # different arch options for 32 and 64 bit target for MSVC
115      if(CMAKE_SIZEOF_VOID_P EQUAL 4)
116        check_flag(SSE1 /arch:SSE)
117      else()
118        set(SSE1_SUPPORTED 1 PARENT_SCOPE)
119      endif()
120    else()
121      check_and_set_flag(SSE1 -msse)
122    endif()
123  else()
124    set(SSE1_SUPPORTED 0 PARENT_SCOPE)
125  endif()
126
127  check_include_file(emmintrin.h HAVE_EMMINTRIN_H) # SSE2
128  if(HAVE_EMMINTRIN_H)
129    if(MSVC)
130      if(CMAKE_SIZEOF_VOID_P EQUAL 4)
131        check_flag(SSE2 /arch:SSE2)
132      else()
133        set(SSE2_SUPPORTED 1 PARENT_SCOPE)
134      endif()
135    else()
136      check_and_set_flag(SSE2 -msse2)
137    endif()
138  else()
139    set(SSE2_SUPPORTED 0 PARENT_SCOPE)
140  endif()
141
142  check_include_file(smmintrin.h HAVE_SMMINTRIN_H) # SSE4.1
143  if(HAVE_SMMINTRIN_H)
144    if(MSVC)
145      if(CMAKE_SIZEOF_VOID_P EQUAL 4)
146        check_flag(SSE4_1 /arch:SSE2) # SSE2 and above
147      else()
148        set(SSE4_1_SUPPORTED 1 PARENT_SCOPE)
149      endif()
150    else()
151      check_and_set_flag(SSE4_1 -msse4.1)
152    endif()
153  else()
154    set(SSE4_1_SUPPORTED 0 PARENT_SCOPE)
155  endif()
156
157  check_include_file(immintrin.h HAVE_IMMINTRIN_H) # AVX
158  if(HAVE_IMMINTRIN_H)
159    if(MSVC)
160      check_flag(AVX /arch:AVX)
161    else()
162      check_and_set_flag(AVX -mavx)
163    endif()
164  else()
165    set(AVX_SUPPORTED 0 PARENT_SCOPE)
166  endif()
167
168  if(MSVC) # To avoid warning D9025 of overriding compiler options
169    if(AVX_SUPPORTED) # on 64 bit and 32 bits
170      add_definitions(/arch:AVX)
171    elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) # if AVX not supported then set SSE flag
172      if(SSE4_1_SUPPORTED OR SSE2_SUPPORTED)
173        add_definitions(/arch:SSE2)
174      elseif(SSE1_SUPPORTED)
175        add_definitions(/arch:SSE)
176      endif()
177    endif()
178  endif()
179
180  if(SSE1_SUPPORTED OR SSE2_SUPPORTED OR SSE4_1_SUPPORTED OR AVX_SUPPORTED)
181    set(COMPILER_SUPPORT_SIMD 1 PARENT_SCOPE)
182  else()
183    message(STATUS "No SIMD support in compiler")
184  endif()
185endfunction()
186
187function(opus_detect_neon COMPILER_SUPPORT_NEON)
188  if(CMAKE_SYSTEM_PROCESSOR MATCHES "(armv7-a|aarch64)")
189    message(STATUS "Check NEON support by compiler")
190    check_include_file(arm_neon.h HAVE_ARM_NEON_H)
191    if(HAVE_ARM_NEON_H)
192      set(COMPILER_SUPPORT_NEON ${HAVE_ARM_NEON_H} PARENT_SCOPE)
193    endif()
194  endif()
195endfunction()
196
197function(opus_supports_cpu_detection RUNTIME_CPU_CAPABILITY_DETECTION)
198  if(MSVC)
199    check_include_file(intrin.h HAVE_INTRIN_H)
200  else()
201    check_include_file(cpuid.h HAVE_CPUID_H)
202  endif()
203  if(HAVE_INTRIN_H OR HAVE_CPUID_H)
204    set(RUNTIME_CPU_CAPABILITY_DETECTION 1 PARENT_SCOPE)
205  else()
206    set(RUNTIME_CPU_CAPABILITY_DETECTION 0 PARENT_SCOPE)
207  endif()
208endfunction()
209
210function(add_sources_group target group)
211  target_sources(${target} PRIVATE ${ARGN})
212  source_group(${group} FILES ${ARGN})
213endfunction()
214
215function(get_opus_sources SOURCE_GROUP MAKE_FILE SOURCES)
216  # read file, each item in list is one group
217  file(STRINGS ${MAKE_FILE} opus_sources)
218
219  # add wildcard for regex match
220  string(CONCAT SOURCE_GROUP ${SOURCE_GROUP} ".*$")
221
222  # find group
223  foreach(val IN LISTS opus_sources)
224    if(val MATCHES ${SOURCE_GROUP})
225      list(LENGTH val list_length)
226      if(${list_length} EQUAL 1)
227        # for tests split by '=' and clean up the rest into a list
228        string(FIND ${val} "=" index)
229        math(EXPR index "${index} + 1")
230        string(SUBSTRING ${val}
231                         ${index}
232                         -1
233                         sources)
234        string(REPLACE " "
235                       ";"
236                       sources
237                       ${sources})
238      else()
239        # discard the group
240        list(REMOVE_AT val 0)
241        set(sources ${val})
242      endif()
243      break()
244    endif()
245  endforeach()
246
247  list(LENGTH sources list_length)
248  if(${list_length} LESS 1)
249    message(
250      FATAL_ERROR
251        "No files parsed succesfully from ${SOURCE_GROUP} in ${MAKE_FILE}")
252  endif()
253
254  # remove trailing whitespaces
255  set(list_var "")
256  foreach(source ${sources})
257    string(STRIP "${source}" source)
258    list(APPEND list_var "${source}")
259  endforeach()
260
261  set(${SOURCES} ${list_var} PARENT_SCOPE)
262endfunction()
263