Fix copyright line in folly/synchronization/test/ParkingLotBenchmark.cpp
[folly.git] / CMake / FollyFunctions.cmake
1 function(auto_sources RETURN_VALUE PATTERN SOURCE_SUBDIRS)
2   if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
3     SET(PATH ".")
4     if (${ARGC} EQUAL 4)
5       list(GET ARGV 3 PATH)
6     endif ()
7   endif()
8
9   if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
10     unset(${RETURN_VALUE})
11     file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
12     list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
13
14     file(GLOB subdirs RELATIVE ${PATH} ${PATH}/*)
15
16     foreach(DIR ${subdirs})
17       if (IS_DIRECTORY ${PATH}/${DIR})
18         if (NOT "${DIR}" STREQUAL "CMakeFiles")
19           file(GLOB_RECURSE SUBDIR_FILES "${PATH}/${DIR}/${PATTERN}")
20           list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
21         endif()
22       endif()
23     endforeach()
24   else()
25     file(GLOB ${RETURN_VALUE} "${PATTERN}")
26
27     foreach (PATH ${SOURCE_SUBDIRS})
28       file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
29       list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
30     endforeach()
31   endif ()
32
33   set(${RETURN_VALUE} ${${RETURN_VALUE}} PARENT_SCOPE)
34 endfunction(auto_sources)
35
36 # Remove all files matching a set of patterns, and,
37 # optionally, not matching a second set of patterns,
38 # from a set of lists.
39 #
40 # Example:
41 # This will remove all files in the CPP_SOURCES list
42 # matching "/test/" or "Test.cpp$", but not matching
43 # "BobTest.cpp$".
44 # REMOVE_MATCHES_FROM_LISTS(CPP_SOURCES MATCHES "/test/" "Test.cpp$" IGNORE_MATCHES "BobTest.cpp$")
45 #
46 # Parameters:
47 #
48 # [...]:
49 # The names of the lists to remove matches from.
50 #
51 # [MATCHES ...]:
52 # The matches to remove from the lists.
53 #
54 # [IGNORE_MATCHES ...]:
55 # The matches not to remove, even if they match
56 # the main set of matches to remove.
57 function(REMOVE_MATCHES_FROM_LISTS)
58   set(LISTS_TO_SEARCH)
59   set(MATCHES_TO_REMOVE)
60   set(MATCHES_TO_IGNORE)
61   set(argumentState 0)
62   foreach (arg ${ARGN})
63     if ("x${arg}" STREQUAL "xMATCHES")
64       set(argumentState 1)
65     elseif ("x${arg}" STREQUAL "xIGNORE_MATCHES")
66       set(argumentState 2)
67     elseif (argumentState EQUAL 0)
68       list(APPEND LISTS_TO_SEARCH ${arg})
69     elseif (argumentState EQUAL 1)
70       list(APPEND MATCHES_TO_REMOVE ${arg})
71     elseif (argumentState EQUAL 2)
72       list(APPEND MATCHES_TO_IGNORE ${arg})
73     else()
74       message(FATAL_ERROR "Unknown argument state!")
75     endif()
76   endforeach()
77
78   foreach (theList ${LISTS_TO_SEARCH})
79     foreach (entry ${${theList}})
80       foreach (match ${MATCHES_TO_REMOVE})
81         if (${entry} MATCHES ${match})
82           set(SHOULD_IGNORE OFF)
83           foreach (ign ${MATCHES_TO_IGNORE})
84             if (${entry} MATCHES ${ign})
85               set(SHOULD_IGNORE ON)
86               break()
87             endif()
88           endforeach()
89
90           if (NOT SHOULD_IGNORE)
91             list(REMOVE_ITEM ${theList} ${entry})
92           endif()
93         endif()
94       endforeach()
95     endforeach()
96     set(${theList} ${${theList}} PARENT_SCOPE)
97   endforeach()
98 endfunction()
99
100 # Automatically create source_group directives for the sources passed in.
101 function(auto_source_group rootName rootDir)
102   file(TO_CMAKE_PATH "${rootDir}" rootDir)
103   string(LENGTH "${rootDir}" rootDirLength)
104   set(sourceGroups)
105   foreach (fil ${ARGN})
106     file(TO_CMAKE_PATH "${fil}" filePath)
107     string(FIND "${filePath}" "/" rIdx REVERSE)
108     if (rIdx EQUAL -1)
109       message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")
110     endif()
111     string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
112
113     string(LENGTH "${filePath}" filePathLength)
114     string(FIND "${filePath}" "${rootDir}" rIdx)
115     if (rIdx EQUAL 0)
116       math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
117       string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
118
119       string(REPLACE "/" "\\" fileGroup "${fileGroup}")
120       set(fileGroup "\\${rootName}${fileGroup}")
121
122       list(FIND sourceGroups "${fileGroup}" rIdx)
123       if (rIdx EQUAL -1)
124         list(APPEND sourceGroups "${fileGroup}")
125         source_group("${fileGroup}" REGULAR_EXPRESSION "${filePath}/[^/.]+.(cpp|h)$")
126       endif()
127     endif()
128   endforeach()
129 endfunction()
130
131 # CMake is a pain and doesn't have an easy way to install only the files
132 # we actually included in our build :(
133 function(auto_install_files rootName rootDir)
134   file(TO_CMAKE_PATH "${rootDir}" rootDir)
135   string(LENGTH "${rootDir}" rootDirLength)
136   set(sourceGroups)
137   foreach (fil ${ARGN})
138     file(TO_CMAKE_PATH "${fil}" filePath)
139     string(FIND "${filePath}" "/" rIdx REVERSE)
140     if (rIdx EQUAL -1)
141       message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")
142     endif()
143     string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
144
145     string(LENGTH "${filePath}" filePathLength)
146     string(FIND "${filePath}" "${rootDir}" rIdx)
147     if (rIdx EQUAL 0)
148       math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
149       string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
150       install(FILES ${fil} DESTINATION include/${rootName}${fileGroup})
151     endif()
152   endforeach()
153 endfunction()
154
155 function(folly_define_tests)
156   set(directory_count 0)
157   set(test_count 0)
158   set(currentArg 0)
159   while (currentArg LESS ${ARGC})
160     if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
161       math(EXPR currentArg "${currentArg} + 1")
162       if (NOT currentArg LESS ${ARGC})
163         message(FATAL_ERROR "Expected base directory!")
164       endif()
165
166       set(cur_dir ${directory_count})
167       math(EXPR directory_count "${directory_count} + 1")
168       set(directory_${cur_dir}_name "${ARGV${currentArg}}")
169       # We need a single list of sources to get source_group to work nicely.
170       set(directory_${cur_dir}_source_list)
171
172       math(EXPR currentArg "${currentArg} + 1")
173       while (currentArg LESS ${ARGC})
174         if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
175           break()
176         elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST")
177           math(EXPR currentArg "${currentArg} + 1")
178           if (NOT currentArg LESS ${ARGC})
179             message(FATAL_ERROR "Expected test name!")
180           endif()
181
182           set(cur_test ${test_count})
183           math(EXPR test_count "${test_count} + 1")
184           set(test_${cur_test}_name "${ARGV${currentArg}}")
185           math(EXPR currentArg "${currentArg} + 1")
186           set(test_${cur_test}_directory ${cur_dir})
187           set(test_${cur_test}_content_dir)
188           set(test_${cur_test}_headers)
189           set(test_${cur_test}_sources)
190           set(test_${cur_test}_tag "NONE")
191
192           set(argumentState 0)
193           while (currentArg LESS ${ARGC})
194             if ("x${ARGV${currentArg}}" STREQUAL "xHEADERS")
195               set(argumentState 1)
196             elseif ("x${ARGV${currentArg}}" STREQUAL "xSOURCES")
197               set(argumentState 2)
198             elseif ("x${ARGV${currentArg}}" STREQUAL "xCONTENT_DIR")
199               math(EXPR currentArg "${currentArg} + 1")
200               if (NOT currentArg LESS ${ARGC})
201                 message(FATAL_ERROR "Expected content directory name!")
202               endif()
203               set(test_${cur_test}_content_dir "${ARGV${currentArg}}")
204             elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST" OR
205                     "x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
206               break()
207             elseif (argumentState EQUAL 0)
208               if ("x${ARGV${currentArg}}" STREQUAL "xBROKEN")
209                 set(test_${cur_test}_tag "BROKEN")
210               elseif ("x${ARGV${currentArg}}" STREQUAL "xHANGING")
211                 set(test_${cur_test}_tag "HANGING")
212               elseif ("x${ARGV${currentArg}}" STREQUAL "xSLOW")
213                 set(test_${cur_test}_tag "SLOW")
214               else()
215                 message(FATAL_ERROR "Unknown test tag '${ARGV${currentArg}}'!")
216               endif()
217             elseif (argumentState EQUAL 1)
218               list(APPEND test_${cur_test}_headers
219                 "${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"
220               )
221             elseif (argumentState EQUAL 2)
222               list(APPEND test_${cur_test}_sources
223                 "${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"
224               )
225             else()
226               message(FATAL_ERROR "Unknown argument state!")
227             endif()
228             math(EXPR currentArg "${currentArg} + 1")
229           endwhile()
230
231           list(APPEND directory_${cur_dir}_source_list
232             ${test_${cur_test}_sources} ${test_${cur_test}_headers})
233         else()
234           message(FATAL_ERROR "Unknown argument inside directory '${ARGV${currentArg}}'!")
235         endif()
236       endwhile()
237     else()
238       message(FATAL_ERROR "Unknown argument '${ARGV${currentArg}}'!")
239     endif()
240   endwhile()
241
242   set(cur_dir 0)
243   while (cur_dir LESS directory_count)
244     source_group("" FILES ${directory_${cur_dir}_source_list})
245     math(EXPR cur_dir "${cur_dir} + 1")
246   endwhile()
247
248   set(cur_test 0)
249   while (cur_test LESS test_count)
250     if ("x${test_${cur_test}_tag}" STREQUAL "xNONE" OR
251         ("x${test_${cur_test}_tag}" STREQUAL "xBROKEN" AND BUILD_BROKEN_TESTS) OR
252         ("x${test_${cur_test}_tag}" STREQUAL "xSLOW" AND BUILD_SLOW_TESTS) OR
253         ("x${test_${cur_test}_tag}" STREQUAL "xHANGING" AND BUILD_HANGING_TESTS)
254     )
255       set(cur_test_name ${test_${cur_test}_name})
256       set(cur_dir_name ${directory_${test_${cur_test}_directory}_name})
257       add_executable(${cur_test_name}
258         ${test_${cur_test}_headers}
259         ${test_${cur_test}_sources}
260       )
261       if (HAVE_CMAKE_GTEST)
262         gtest_add_tests(TARGET ${cur_test_name}
263                         TEST_PREFIX "${cur_test_name}."
264                         TEST_LIST test_cases)
265         set_tests_properties(${test_cases} PROPERTIES TIMEOUT 120)
266       endif()
267       if (NOT "x${test_${cur_test}_content_dir}" STREQUAL "x")
268         # Copy the content directory to the output directory tree so that
269         # tests can be run easily from Visual Studio without having to change
270         # the working directory for each test individually.
271         file(
272           COPY "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"
273           DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/folly/${cur_dir_name}${test_${cur_test}_content_dir}"
274         )
275         add_custom_command(TARGET ${cur_test_name} POST_BUILD COMMAND
276           ${CMAKE_COMMAND} ARGS -E copy_directory
277             "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"
278             "$<TARGET_FILE_DIR:${cur_test_name}>/folly/${cur_dir_name}${test_${cur_test}_content_dir}"
279           COMMENT "Copying test content for ${cur_test_name}" VERBATIM
280         )
281       endif()
282       # Strip the tailing test directory name for the folder name.
283       string(REPLACE "test/" "" test_dir_name "${cur_dir_name}")
284       set_property(TARGET ${cur_test_name} PROPERTY FOLDER "Tests/${test_dir_name}")
285       target_link_libraries(${cur_test_name} PRIVATE folly_test_support)
286       apply_folly_compile_options_to_target(${cur_test_name})
287     endif()
288     math(EXPR cur_test "${cur_test} + 1")
289   endwhile()
290 endfunction()