cmake: mark the compression tests as slow
[folly.git] / CMake / FollyFunctions.cmake
index ee538856b20fee6819fc70beb3002941d444be0b..1cfc60a3ed0868302aecc26cb4865401f524d500 100755 (executable)
-function(auto_sources RETURN_VALUE PATTERN SOURCE_SUBDIRS)\r
-  if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")\r
-    SET(PATH ".")\r
-    if (${ARGC} EQUAL 4)\r
-      list(GET ARGV 3 PATH)\r
-    endif ()\r
-  endif()\r
-\r
-  if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")\r
-    unset(${RETURN_VALUE})\r
-    file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")\r
-    list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})\r
-\r
-    file(GLOB subdirs RELATIVE ${PATH} ${PATH}/*)\r
-\r
-    foreach(DIR ${subdirs})\r
-      if (IS_DIRECTORY ${PATH}/${DIR})\r
-        if (NOT "${DIR}" STREQUAL "CMakeFiles")\r
-          file(GLOB_RECURSE SUBDIR_FILES "${PATH}/${DIR}/${PATTERN}")\r
-          list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})\r
-        endif()\r
-      endif()\r
-    endforeach()\r
-  else()\r
-    file(GLOB ${RETURN_VALUE} "${PATTERN}")\r
-\r
-    foreach (PATH ${SOURCE_SUBDIRS})\r
-      file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")\r
-      list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})\r
-    endforeach()\r
-  endif ()\r
-\r
-  set(${RETURN_VALUE} ${${RETURN_VALUE}} PARENT_SCOPE)\r
-endfunction(auto_sources)\r
-\r
-# Remove all files matching a set of patterns, and,\r
-# optionally, not matching a second set of patterns,\r
-# from a set of lists.\r
-#\r
-# Example:\r
-# This will remove all files in the CPP_SOURCES list\r
-# matching "/test/" or "Test.cpp$", but not matching\r
-# "BobTest.cpp$".\r
-# REMOVE_MATCHES_FROM_LISTS(CPP_SOURCES MATCHES "/test/" "Test.cpp$" IGNORE_MATCHES "BobTest.cpp$")\r
-#\r
-# Parameters:\r
-#\r
-# [...]:\r
-# The names of the lists to remove matches from.\r
-#\r
-# [MATCHES ...]:\r
-# The matches to remove from the lists.\r
-#\r
-# [IGNORE_MATCHES ...]:\r
-# The matches not to remove, even if they match\r
-# the main set of matches to remove.\r
-function(REMOVE_MATCHES_FROM_LISTS)\r
-  set(LISTS_TO_SEARCH)\r
-  set(MATCHES_TO_REMOVE)\r
-  set(MATCHES_TO_IGNORE)\r
-  set(argumentState 0)\r
-  foreach (arg ${ARGN})\r
-    if ("x${arg}" STREQUAL "xMATCHES")\r
-      set(argumentState 1)\r
-    elseif ("x${arg}" STREQUAL "xIGNORE_MATCHES")\r
-      set(argumentState 2)\r
-    elseif (argumentState EQUAL 0)\r
-      list(APPEND LISTS_TO_SEARCH ${arg})\r
-    elseif (argumentState EQUAL 1)\r
-      list(APPEND MATCHES_TO_REMOVE ${arg})\r
-    elseif (argumentState EQUAL 2)\r
-      list(APPEND MATCHES_TO_IGNORE ${arg})\r
-    else()\r
-      message(FATAL_ERROR "Unknown argument state!")\r
-    endif()\r
-  endforeach()\r
-\r
-  foreach (theList ${LISTS_TO_SEARCH})\r
-    foreach (entry ${${theList}})\r
-      foreach (match ${MATCHES_TO_REMOVE})\r
-        if (${entry} MATCHES ${match})\r
-          set(SHOULD_IGNORE OFF)\r
-          foreach (ign ${MATCHES_TO_IGNORE})\r
-            if (${entry} MATCHES ${ign})\r
-              set(SHOULD_IGNORE ON)\r
-              break()\r
-            endif()\r
-          endforeach()\r
-\r
-          if (NOT SHOULD_IGNORE)\r
-            list(REMOVE_ITEM ${theList} ${entry})\r
-          endif()\r
-        endif()\r
-      endforeach()\r
-    endforeach()\r
-    set(${theList} ${${theList}} PARENT_SCOPE)\r
-  endforeach()\r
-endfunction()\r
-\r
-# Automatically create source_group directives for the sources passed in.\r
-function(auto_source_group rootName rootDir)\r
-  file(TO_CMAKE_PATH "${rootDir}" rootDir)\r
-  string(LENGTH "${rootDir}" rootDirLength)\r
-  set(sourceGroups)\r
-  foreach (fil ${ARGN})\r
-    file(TO_CMAKE_PATH "${fil}" filePath)\r
-    string(FIND "${filePath}" "/" rIdx REVERSE)\r
-    if (rIdx EQUAL -1)\r
-      message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")\r
-    endif()\r
-    string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)\r
-\r
-    string(LENGTH "${filePath}" filePathLength)\r
-    string(FIND "${filePath}" "${rootDir}" rIdx)\r
-    if (rIdx EQUAL 0)\r
-      math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")\r
-      string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)\r
-\r
-      string(REPLACE "/" "\\" fileGroup "${fileGroup}")\r
-      set(fileGroup "\\${rootName}${fileGroup}")\r
-\r
-      list(FIND sourceGroups "${fileGroup}" rIdx)\r
-      if (rIdx EQUAL -1)\r
-        list(APPEND sourceGroups "${fileGroup}")\r
-        source_group("${fileGroup}" REGULAR_EXPRESSION "${filePath}/[^/.]+.(cpp|h)$")\r
-      endif()\r
-    endif()\r
-  endforeach()\r
-endfunction()\r
-\r
-# CMake is a pain and doesn't have an easy way to install only the files\r
-# we actually included in our build :(\r
-function(auto_install_files rootName rootDir)\r
-  file(TO_CMAKE_PATH "${rootDir}" rootDir)\r
-  string(LENGTH "${rootDir}" rootDirLength)\r
-  set(sourceGroups)\r
-  foreach (fil ${ARGN})\r
-    file(TO_CMAKE_PATH "${fil}" filePath)\r
-    string(FIND "${filePath}" "/" rIdx REVERSE)\r
-    if (rIdx EQUAL -1)\r
-      message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")\r
-    endif()\r
-    string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)\r
-\r
-    string(LENGTH "${filePath}" filePathLength)\r
-    string(FIND "${filePath}" "${rootDir}" rIdx)\r
-    if (rIdx EQUAL 0)\r
-      math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")\r
-      string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)\r
-      install(FILES ${fil} DESTINATION include/${rootName}${fileGroup})\r
-    endif()\r
-  endforeach()\r
-endfunction()\r
-\r
-function(folly_define_tests)\r
-  set(directory_count 0)\r
-  set(test_count 0)\r
-  set(currentArg 0)\r
-  while (currentArg LESS ${ARGC})\r
-    if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")\r
-      math(EXPR currentArg "${currentArg} + 1")\r
-      if (NOT currentArg LESS ${ARGC})\r
-        message(FATAL_ERROR "Expected base directory!")\r
-      endif()\r
-\r
-      set(cur_dir ${directory_count})\r
-      math(EXPR directory_count "${directory_count} + 1")\r
-      set(directory_${cur_dir}_name "${ARGV${currentArg}}")\r
-      # We need a single list of sources to get source_group to work nicely.\r
-      set(directory_${cur_dir}_source_list)\r
-\r
-      math(EXPR currentArg "${currentArg} + 1")\r
-      while (currentArg LESS ${ARGC})\r
-        if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")\r
-          break()\r
-        elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST")\r
-          math(EXPR currentArg "${currentArg} + 1")\r
-          if (NOT currentArg LESS ${ARGC})\r
-            message(FATAL_ERROR "Expected test name!")\r
-          endif()\r
-\r
-          set(cur_test ${test_count})\r
-          math(EXPR test_count "${test_count} + 1")\r
-          set(test_${cur_test}_name "${ARGV${currentArg}}")\r
-          math(EXPR currentArg "${currentArg} + 1")\r
-          set(test_${cur_test}_directory ${cur_dir})\r
-          set(test_${cur_test}_content_dir)\r
-          set(test_${cur_test}_headers)\r
-          set(test_${cur_test}_sources)\r
-          set(test_${cur_test}_tag "NONE")\r
-\r
-          set(argumentState 0)\r
-          while (currentArg LESS ${ARGC})\r
-            if ("x${ARGV${currentArg}}" STREQUAL "xHEADERS")\r
-              set(argumentState 1)\r
-            elseif ("x${ARGV${currentArg}}" STREQUAL "xSOURCES")\r
-              set(argumentState 2)\r
-            elseif ("x${ARGV${currentArg}}" STREQUAL "xCONTENT_DIR")\r
-              math(EXPR currentArg "${currentArg} + 1")\r
-              if (NOT currentArg LESS ${ARGC})\r
-                message(FATAL_ERROR "Expected content directory name!")\r
-              endif()\r
-              set(test_${cur_test}_content_dir "${ARGV${currentArg}}")\r
-            elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST" OR\r
-                    "x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")\r
-              break()\r
-            elseif (argumentState EQUAL 0)\r
-              if ("x${ARGV${currentArg}}" STREQUAL "xBROKEN")\r
-                set(test_${cur_test}_tag "BROKEN")\r
-              elseif ("x${ARGV${currentArg}}" STREQUAL "xHANGING")\r
-                set(test_${cur_test}_tag "HANGING")\r
-              elseif ("x${ARGV${currentArg}}" STREQUAL "xSLOW")\r
-                set(test_${cur_test}_tag "SLOW")\r
-              else()\r
-                message(FATAL_ERROR "Unknown test tag '${ARGV${currentArg}}'!")\r
-              endif()\r
-            elseif (argumentState EQUAL 1)\r
-              list(APPEND test_${cur_test}_headers\r
-                "${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"\r
-              )\r
-            elseif (argumentState EQUAL 2)\r
-              list(APPEND test_${cur_test}_sources\r
-                "${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"\r
-              )\r
-            else()\r
-              message(FATAL_ERROR "Unknown argument state!")\r
-            endif()\r
-            math(EXPR currentArg "${currentArg} + 1")\r
-          endwhile()\r
-\r
-          list(APPEND directory_${cur_dir}_source_list\r
-            ${test_${cur_test}_sources} ${test_${cur_test}_headers})\r
-        else()\r
-          message(FATAL_ERROR "Unknown argument inside directory '${ARGV${currentArg}}'!")\r
-        endif()\r
-      endwhile()\r
-    else()\r
-      message(FATAL_ERROR "Unknown argument '${ARGV${currentArg}}'!")\r
-    endif()\r
-  endwhile()\r
-\r
-  set(cur_dir 0)\r
-  while (cur_dir LESS directory_count)\r
-    source_group("" FILES ${directory_${cur_dir}_source_list})\r
-    math(EXPR cur_dir "${cur_dir} + 1")\r
-  endwhile()\r
-\r
-  set(cur_test 0)\r
-  while (cur_test LESS test_count)\r
-    if ("x${test_${cur_test}_tag}" STREQUAL "xNONE" OR\r
-        ("x${test_${cur_test}_tag}" STREQUAL "xBROKEN" AND BUILD_BROKEN_TESTS) OR\r
-        ("x${test_${cur_test}_tag}" STREQUAL "xSLOW" AND BUILD_SLOW_TESTS) OR\r
-        ("x${test_${cur_test}_tag}" STREQUAL "xHANGING" AND BUILD_HANGING_TESTS)\r
-    )\r
-      set(cur_test_name ${test_${cur_test}_name})\r
-      set(cur_dir_name ${directory_${test_${cur_test}_directory}_name})\r
-      add_executable(${cur_test_name}\r
-        ${test_${cur_test}_headers}\r
-        ${test_${cur_test}_sources}\r
-      )\r
-      if (NOT "x${test_${cur_test}_content_dir}" STREQUAL "x")\r
-        # Copy the content directory to the output directory tree so that\r
-        # tests can be run easily from Visual Studio without having to change\r
-        # the working directory for each test individually.\r
-        file(\r
-          COPY "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"\r
-          DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/folly/${cur_dir_name}${test_${cur_test}_content_dir}"\r
-        )\r
-        add_custom_command(TARGET ${cur_test_name} POST_BUILD COMMAND\r
-          ${CMAKE_COMMAND} ARGS -E copy_directory\r
-            "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"\r
-            "$<TARGET_FILE_DIR:${cur_test_name}>/folly/${cur_dir_name}${test_${cur_test}_content_dir}"\r
-          COMMENT "Copying test content for ${cur_test_name}" VERBATIM\r
-        )\r
-      endif()\r
-      # Strip the tailing test directory name for the folder name.\r
-      string(REPLACE "test/" "" test_dir_name "${cur_dir_name}")\r
-      set_property(TARGET ${cur_test_name} PROPERTY FOLDER "Tests/${test_dir_name}")\r
-      target_link_libraries(${cur_test_name} PRIVATE folly_test_support)\r
-      apply_folly_compile_options_to_target(${cur_test_name})\r
-    endif()\r
-    math(EXPR cur_test "${cur_test} + 1")\r
-  endwhile()\r
-endfunction()\r
+function(auto_sources RETURN_VALUE PATTERN SOURCE_SUBDIRS)
+  if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
+    SET(PATH ".")
+    if (${ARGC} EQUAL 4)
+      list(GET ARGV 3 PATH)
+    endif ()
+  endif()
+
+  if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
+    unset(${RETURN_VALUE})
+    file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
+    list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
+
+    file(GLOB subdirs RELATIVE ${PATH} ${PATH}/*)
+
+    foreach(DIR ${subdirs})
+      if (IS_DIRECTORY ${PATH}/${DIR})
+        if (NOT "${DIR}" STREQUAL "CMakeFiles")
+          file(GLOB_RECURSE SUBDIR_FILES "${PATH}/${DIR}/${PATTERN}")
+          list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
+        endif()
+      endif()
+    endforeach()
+  else()
+    file(GLOB ${RETURN_VALUE} "${PATTERN}")
+
+    foreach (PATH ${SOURCE_SUBDIRS})
+      file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
+      list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
+    endforeach()
+  endif ()
+
+  set(${RETURN_VALUE} ${${RETURN_VALUE}} PARENT_SCOPE)
+endfunction(auto_sources)
+
+# Remove all files matching a set of patterns, and,
+# optionally, not matching a second set of patterns,
+# from a set of lists.
+#
+# Example:
+# This will remove all files in the CPP_SOURCES list
+# matching "/test/" or "Test.cpp$", but not matching
+# "BobTest.cpp$".
+# REMOVE_MATCHES_FROM_LISTS(CPP_SOURCES MATCHES "/test/" "Test.cpp$" IGNORE_MATCHES "BobTest.cpp$")
+#
+# Parameters:
+#
+# [...]:
+# The names of the lists to remove matches from.
+#
+# [MATCHES ...]:
+# The matches to remove from the lists.
+#
+# [IGNORE_MATCHES ...]:
+# The matches not to remove, even if they match
+# the main set of matches to remove.
+function(REMOVE_MATCHES_FROM_LISTS)
+  set(LISTS_TO_SEARCH)
+  set(MATCHES_TO_REMOVE)
+  set(MATCHES_TO_IGNORE)
+  set(argumentState 0)
+  foreach (arg ${ARGN})
+    if ("x${arg}" STREQUAL "xMATCHES")
+      set(argumentState 1)
+    elseif ("x${arg}" STREQUAL "xIGNORE_MATCHES")
+      set(argumentState 2)
+    elseif (argumentState EQUAL 0)
+      list(APPEND LISTS_TO_SEARCH ${arg})
+    elseif (argumentState EQUAL 1)
+      list(APPEND MATCHES_TO_REMOVE ${arg})
+    elseif (argumentState EQUAL 2)
+      list(APPEND MATCHES_TO_IGNORE ${arg})
+    else()
+      message(FATAL_ERROR "Unknown argument state!")
+    endif()
+  endforeach()
+
+  foreach (theList ${LISTS_TO_SEARCH})
+    foreach (entry ${${theList}})
+      foreach (match ${MATCHES_TO_REMOVE})
+        if (${entry} MATCHES ${match})
+          set(SHOULD_IGNORE OFF)
+          foreach (ign ${MATCHES_TO_IGNORE})
+            if (${entry} MATCHES ${ign})
+              set(SHOULD_IGNORE ON)
+              break()
+            endif()
+          endforeach()
+
+          if (NOT SHOULD_IGNORE)
+            list(REMOVE_ITEM ${theList} ${entry})
+          endif()
+        endif()
+      endforeach()
+    endforeach()
+    set(${theList} ${${theList}} PARENT_SCOPE)
+  endforeach()
+endfunction()
+
+# Automatically create source_group directives for the sources passed in.
+function(auto_source_group rootName rootDir)
+  file(TO_CMAKE_PATH "${rootDir}" rootDir)
+  string(LENGTH "${rootDir}" rootDirLength)
+  set(sourceGroups)
+  foreach (fil ${ARGN})
+    file(TO_CMAKE_PATH "${fil}" filePath)
+    string(FIND "${filePath}" "/" rIdx REVERSE)
+    if (rIdx EQUAL -1)
+      message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")
+    endif()
+    string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
+
+    string(LENGTH "${filePath}" filePathLength)
+    string(FIND "${filePath}" "${rootDir}" rIdx)
+    if (rIdx EQUAL 0)
+      math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
+      string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
+
+      string(REPLACE "/" "\\" fileGroup "${fileGroup}")
+      set(fileGroup "\\${rootName}${fileGroup}")
+
+      list(FIND sourceGroups "${fileGroup}" rIdx)
+      if (rIdx EQUAL -1)
+        list(APPEND sourceGroups "${fileGroup}")
+        source_group("${fileGroup}" REGULAR_EXPRESSION "${filePath}/[^/.]+.(cpp|h)$")
+      endif()
+    endif()
+  endforeach()
+endfunction()
+
+# CMake is a pain and doesn't have an easy way to install only the files
+# we actually included in our build :(
+function(auto_install_files rootName rootDir)
+  file(TO_CMAKE_PATH "${rootDir}" rootDir)
+  string(LENGTH "${rootDir}" rootDirLength)
+  set(sourceGroups)
+  foreach (fil ${ARGN})
+    file(TO_CMAKE_PATH "${fil}" filePath)
+    string(FIND "${filePath}" "/" rIdx REVERSE)
+    if (rIdx EQUAL -1)
+      message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")
+    endif()
+    string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
+
+    string(LENGTH "${filePath}" filePathLength)
+    string(FIND "${filePath}" "${rootDir}" rIdx)
+    if (rIdx EQUAL 0)
+      math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
+      string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
+      install(FILES ${fil} DESTINATION include/${rootName}${fileGroup})
+    endif()
+  endforeach()
+endfunction()
+
+function(folly_define_tests)
+  set(directory_count 0)
+  set(test_count 0)
+  set(currentArg 0)
+  while (currentArg LESS ${ARGC})
+    if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
+      math(EXPR currentArg "${currentArg} + 1")
+      if (NOT currentArg LESS ${ARGC})
+        message(FATAL_ERROR "Expected base directory!")
+      endif()
+
+      set(cur_dir ${directory_count})
+      math(EXPR directory_count "${directory_count} + 1")
+      set(directory_${cur_dir}_name "${ARGV${currentArg}}")
+      # We need a single list of sources to get source_group to work nicely.
+      set(directory_${cur_dir}_source_list)
+
+      math(EXPR currentArg "${currentArg} + 1")
+      while (currentArg LESS ${ARGC})
+        if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
+          break()
+        elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST")
+          math(EXPR currentArg "${currentArg} + 1")
+          if (NOT currentArg LESS ${ARGC})
+            message(FATAL_ERROR "Expected test name!")
+          endif()
+
+          set(cur_test ${test_count})
+          math(EXPR test_count "${test_count} + 1")
+          set(test_${cur_test}_name "${ARGV${currentArg}}")
+          math(EXPR currentArg "${currentArg} + 1")
+          set(test_${cur_test}_directory ${cur_dir})
+          set(test_${cur_test}_content_dir)
+          set(test_${cur_test}_headers)
+          set(test_${cur_test}_sources)
+          set(test_${cur_test}_tag "NONE")
+
+          set(argumentState 0)
+          while (currentArg LESS ${ARGC})
+            if ("x${ARGV${currentArg}}" STREQUAL "xHEADERS")
+              set(argumentState 1)
+            elseif ("x${ARGV${currentArg}}" STREQUAL "xSOURCES")
+              set(argumentState 2)
+            elseif ("x${ARGV${currentArg}}" STREQUAL "xCONTENT_DIR")
+              math(EXPR currentArg "${currentArg} + 1")
+              if (NOT currentArg LESS ${ARGC})
+                message(FATAL_ERROR "Expected content directory name!")
+              endif()
+              set(test_${cur_test}_content_dir "${ARGV${currentArg}}")
+            elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST" OR
+                    "x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
+              break()
+            elseif (argumentState EQUAL 0)
+              if ("x${ARGV${currentArg}}" STREQUAL "xBROKEN")
+                set(test_${cur_test}_tag "BROKEN")
+              elseif ("x${ARGV${currentArg}}" STREQUAL "xHANGING")
+                set(test_${cur_test}_tag "HANGING")
+              elseif ("x${ARGV${currentArg}}" STREQUAL "xSLOW")
+                set(test_${cur_test}_tag "SLOW")
+              else()
+                message(FATAL_ERROR "Unknown test tag '${ARGV${currentArg}}'!")
+              endif()
+            elseif (argumentState EQUAL 1)
+              list(APPEND test_${cur_test}_headers
+                "${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"
+              )
+            elseif (argumentState EQUAL 2)
+              list(APPEND test_${cur_test}_sources
+                "${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"
+              )
+            else()
+              message(FATAL_ERROR "Unknown argument state!")
+            endif()
+            math(EXPR currentArg "${currentArg} + 1")
+          endwhile()
+
+          list(APPEND directory_${cur_dir}_source_list
+            ${test_${cur_test}_sources} ${test_${cur_test}_headers})
+        else()
+          message(FATAL_ERROR "Unknown argument inside directory '${ARGV${currentArg}}'!")
+        endif()
+      endwhile()
+    else()
+      message(FATAL_ERROR "Unknown argument '${ARGV${currentArg}}'!")
+    endif()
+  endwhile()
+
+  set(cur_dir 0)
+  while (cur_dir LESS directory_count)
+    source_group("" FILES ${directory_${cur_dir}_source_list})
+    math(EXPR cur_dir "${cur_dir} + 1")
+  endwhile()
+
+  set(cur_test 0)
+  while (cur_test LESS test_count)
+    if ("x${test_${cur_test}_tag}" STREQUAL "xNONE" OR
+        ("x${test_${cur_test}_tag}" STREQUAL "xBROKEN" AND BUILD_BROKEN_TESTS) OR
+        ("x${test_${cur_test}_tag}" STREQUAL "xSLOW" AND BUILD_SLOW_TESTS) OR
+        ("x${test_${cur_test}_tag}" STREQUAL "xHANGING" AND BUILD_HANGING_TESTS)
+    )
+      set(cur_test_name ${test_${cur_test}_name})
+      set(cur_dir_name ${directory_${test_${cur_test}_directory}_name})
+      add_executable(${cur_test_name}
+        ${test_${cur_test}_headers}
+        ${test_${cur_test}_sources}
+      )
+      if (HAVE_CMAKE_GTEST)
+        gtest_add_tests(TARGET ${cur_test_name}
+                        TEST_PREFIX "${cur_test_name}."
+                        TEST_LIST test_cases)
+        set_tests_properties(${test_cases} PROPERTIES TIMEOUT 120)
+      endif()
+      if (NOT "x${test_${cur_test}_content_dir}" STREQUAL "x")
+        # Copy the content directory to the output directory tree so that
+        # tests can be run easily from Visual Studio without having to change
+        # the working directory for each test individually.
+        file(
+          COPY "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"
+          DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/folly/${cur_dir_name}${test_${cur_test}_content_dir}"
+        )
+        add_custom_command(TARGET ${cur_test_name} POST_BUILD COMMAND
+          ${CMAKE_COMMAND} ARGS -E copy_directory
+            "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"
+            "$<TARGET_FILE_DIR:${cur_test_name}>/folly/${cur_dir_name}${test_${cur_test}_content_dir}"
+          COMMENT "Copying test content for ${cur_test_name}" VERBATIM
+        )
+      endif()
+      # Strip the tailing test directory name for the folder name.
+      string(REPLACE "test/" "" test_dir_name "${cur_dir_name}")
+      set_property(TARGET ${cur_test_name} PROPERTY FOLDER "Tests/${test_dir_name}")
+      target_link_libraries(${cur_test_name} PRIVATE folly_test_support)
+      apply_folly_compile_options_to_target(${cur_test_name})
+    endif()
+    math(EXPR cur_test "${cur_test} + 1")
+  endwhile()
+endfunction()