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