CMake Lists
CMake Lists
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set(CMAKE_MODULE_PATH
# For in-fbsource builds on mac
"${CMAKE_CURRENT_SOURCE_DIR}/../opensource/fbcode_builder/CMake"
# For shipit-transformed builds
"${CMAKE_CURRENT_SOURCE_DIR}/build/fbcode_builder/CMake"
${CMAKE_MODULE_PATH})
set(CMAKE_CXX_STANDARD 17)
if (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_LEAN_AND_MEAN -DNOMINMAX -
DSTRICT")
endif()
option(BUILD_SHARED_LIBS
"If enabled, build libraries as a shared library. \
This is generally discouraged, since we do not commit to having \
a stable ABI."
OFF
)
# Mark BUILD_SHARED_LIBS as an "advanced" option, since enabling it
# is generally discouraged.
mark_as_advanced(BUILD_SHARED_LIBS)
option(USE_SYS_PYTHON
"If enabled, prefers to use the system installed python vs the user \
installed python. Flipping this switch will change which python is used \
during packaging and should be set to maximize portability."
ON
)
enable_testing()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/external/install/include")
# If we're building from inside the monorepo, make the local directory
# look like the shipit-transformed source in the git repo.
# On windows we do a dumb recursive copy of the files because we cannot
# guarantee that we'll be successful in setting up a symlink.
# On everything else we set up a simple symlink.
# In theory we can tell cmake to add a non-child subdir and avoid the
# copy/symlink thing, but we'd need to teach various targets how to resolve
# the path and that is rather a lot of work (I spent a couple of hours on this
# before throwing in the towel).
function(maybe_shipit_dir MONOREPO_RELATIVE_PATH)
get_filename_component(base "${MONOREPO_RELATIVE_PATH}" NAME)
if (NOT IS_SHIPPED_IT AND
NOT IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${base})
if (WIN32)
file(COPY
"${CMAKE_CURRENT_SOURCE_DIR}/${MONOREPO_RELATIVE_PATH}"
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
else()
execute_process(COMMAND
ln -s ${MONOREPO_RELATIVE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/${base})
endif()
endif()
endfunction()
if (ENABLE_EDEN_SUPPORT)
# We use shipit to mirror in these locations from the monorepo
maybe_shipit_dir("../eden")
endif()
if (WATCHMAN_VERSION_OVERRIDE)
set(PACKAGE_VERSION "${WATCHMAN_VERSION_OVERRIDE}")
elseif(DEFINED ENV{WATCHMAN_VERSION_OVERRIDE})
set(PACKAGE_VERSION "$ENV{WATCHMAN_VERSION_OVERRIDE}")
elseif(DEFINED ENV{FBSOURCE_DATE})
# If set, we expect FBSOURCE_DATE to have the form "20200324.113140"
set(PACKAGE_VERSION "$ENV{FBSOURCE_DATE}.0")
set(BUILD_INFO "$ENV{FBSOURCE_HASH}")
else()
find_program(GIT git)
if(GIT)
execute_process(
COMMAND "${GIT}" "show" "-s" "--format=%H;%cd" "--date=format:%Y%m%d.%H%M
%S.0"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE git_result
OUTPUT_VARIABLE git_data
ERROR_VARIABLE git_err
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(git_result EQUAL 0)
list(GET git_data 0 BUILD_INFO)
list(GET git_data 1 PACKAGE_VERSION)
endif()
endif()
endif()
message(STATUS "PACKAGE_VERSION=${PACKAGE_VERSION}, BUILD_INFO=${BUILD_INFO}")
set(PACKAGE_NAME "watchman")
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}")
set(PACKAGE_BUGREPORT "https://github.com/facebook/watchman/issues")
project(${PACKAGE_NAME} CXX C)
include(FBThriftCppLibrary)
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckStructHasMember)
include(CheckSymbolExists)
include(RustStaticLibrary)
if(NOT WIN32)
set(WATCHMAN_STATE_DIR "${CMAKE_INSTALL_PREFIX}/var/run/watchman" CACHE STRING
"Run-time path of the persistent state directory")
set(INSTALL_WATCHMAN_STATE_DIR OFF CACHE BOOL
"Whether WATCHMAN_STATE_DIR should be created by the cmake install
target. Disabling this is useful in the case where the CMAKE_INSTALL_PREFIX
is owned by a non-privileged user but where the WATCHMAN_STATE_DIR requires
administrative rights to create and set its permissions.")
else()
set(WATCHMAN_STATE_DIR)
set(INSTALL_WATCHMAN_STATE_DIR)
endif()
if(WATCHMAN_STATE_DIR)
config_h("#define WATCHMAN_STATE_DIR \"${WATCHMAN_STATE_DIR}\"")
endif()
config_h("#define PACKAGE_VERSION \"${PACKAGE_VERSION}\"")
if(BUILD_INFO)
config_h("#define WATCHMAN_BUILD_INFO \"${BUILD_INFO}\"")
endif ()
# While most of these tests are not strictly needed on windows, it is vital
# that we probe for and find strtoll in order for the jansson build to use
# a 64-bit integer type, otherwise the mtime_us field renders as garbage
# in the integration tests.
foreach(wat_func
accept4
backtrace
backtrace_symbols
backtrace_symbols_fd
fdopendir
getattrlistbulk
inotify_init
inotify_init1
kqueue
localeconv
memmem
mkostemp
openat
pipe2
port_create
statfs
strtoll
sys_siglist
)
CHECK_FUNCTION_EXISTS(${wat_func} have_${wat_func})
if (have_${wat_func})
string(TOUPPER have_${wat_func} sym)
config_h("#define ${sym} 1")
endif()
endforeach(wat_func)
foreach(wat_header
CoreServices/CoreServices.h
execinfo.h
fcntl.h
inttypes.h
locale.h
port.h
sys/event.h
sys/inotify.h
sys/mount.h
sys/param.h
sys/resource.h
sys/socket.h
sys/statfs.h
sys/statvfs.h
sys/types.h
sys/ucred.h
sys/vfs.h
valgrind/valgrind.h
)
string(TOUPPER have_${wat_header} sym)
string(REGEX REPLACE [./] _ sym ${sym})
CHECK_INCLUDE_FILES(${wat_header} ${sym})
if (${sym})
config_h("#define ${sym} 1")
endif()
endforeach(wat_header)
if(have_fcntl.h)
CHECK_SYMBOL_EXISTS(O_SYMLINK fcntl.h HAVE_DECL_O_SYMLINK)
if(HAVE_DECL_O_SYMLINK)
config_h("#define HAVE_DECL_O_SYMLINK 1")
endif()
endif()
find_package(PCRE2)
if(PCRE2_FOUND)
config_h("#define HAVE_PCRE_H 1")
endif()
# Now close out config.h. We only want to touch the file if the contents are
# different, so do a little dance to figure that out.
if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h")
file(MD5 "${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h" orig_hash)
file(MD5 "${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h.new" this_hash)
if(NOT orig_hash STREQUAL this_hash)
file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h.new"
"${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h")
endif()
else()
file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h.new"
"${CMAKE_CURRENT_BINARY_DIR}/watchman/config.h")
endif()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# This block is for cmake 3.0 which doesn't define the Threads::Threads
# interface section. Test for that and define it for ourselves.
if(THREADS_FOUND AND NOT TARGET Threads::Threads)
add_library(Threads::Threads INTERFACE IMPORTED)
if(THREADS_HAVE_PTHREAD_ARG)
set_property(TARGET Threads::Threads PROPERTY
INTERFACE_COMPILE_OPTIONS "-pthread")
endif()
if(CMAKE_THREAD_LIBS_INIT)
set_property(TARGET Threads::Threads PROPERTY
INTERFACE_LINK_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
endif()
endif()
find_package(OpenSSL)
# This block is for cmake 3.0 which doesn't define the OpenSSL::Crypto
# interface section. Test for that and define it for ourselves.
if(OPENSSL_FOUND AND NOT TARGET OpenSSL::Crypto)
add_library(OpenSSL::Crypto UNKNOWN IMPORTED)
set_target_properties(OpenSSL::Crypto PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
if(EXISTS "${OPENSSL_CRYPTO_LIBRARY}")
set_target_properties(OpenSSL::Crypto PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}")
endif()
endif()
find_package(Gflags REQUIRED)
include_directories(SYSTEM ${GFLAGS_INCLUDE_DIR})
find_package(Glog REQUIRED)
add_compile_definitions(GLOG_NO_ABBREVIATED_SEVERITIES)
if(NOT WIN32)
# Sometimes the environment has a Python installation earlier in the
# PATH that is not suitable for building extensions. Prefer the
# primary system installation.
set(Python3_FIND_STRATEGY LOCATION)
if(USE_SYS_PYTHON)
message(STATUS "Using sys python")
set(Python3_ROOT_DIR /usr/bin)
else()
message(STATUS "Using local python")
set(Python3_ROOT_DIR /usr/local/bin)
endif()
endif()
if(NOT Python3_Interpreter_FOUND)
message(STATUS "python not found, using default python")
unset(Python3_ROOT_DIR)
unset(Python3_FIND_STRATEGY)
find_package(Python3 COMPONENTS Interpreter Development)
endif()
if(Python3_Development_FOUND)
set(PYOUT "${CMAKE_CURRENT_BINARY_DIR}/pywatchman")
set(SETUP_PY "${CMAKE_CURRENT_SOURCE_DIR}/watchman/python/setup.py")
set(PYWATCHMAN_BASE ${CMAKE_CURRENT_SOURCE_DIR}/watchman/python)
file(GLOB PYWATCHMAN_PY_SRCS "watchman/python/pywatchman/*.py")
file(MAKE_DIRECTORY ${PYOUT})
add_custom_command(
COMMENT "Building pywatchman"
OUTPUT ${PYOUT}
DEPENDS ${PYWATCHMAN_PY_SRCS} "watchman/python/pywatchman/bser.c"
WORKING_DIRECTORY ${PYWATCHMAN_BASE}
COMMAND ${CMAKE_COMMAND} -E env
CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
${Python3_EXECUTABLE} ${SETUP_PY} build --build-base ${PYOUT}
COMMAND ${CMAKE_COMMAND} -E touch ${PYOUT}
)
add_custom_target(pybuild ALL DEPENDS ${PYOUT})
install(CODE "
execute_process(COMMAND
${CMAKE_COMMAND} -E env
CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
${Python3_EXECUTABLE} ${SETUP_PY} install
--root $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}
WORKING_DIRECTORY ${PYWATCHMAN_BASE}
RESULT_VARIABLE STATUS)
if (NOT STATUS STREQUAL 0)
message(FATAL_ERROR \"pywatchman install failed\")
endif()
")
include(FBPythonBinary)
add_subdirectory(watchman/python)
add_subdirectory(watchman/integration)
endif()
add_library(third_party_deps INTERFACE)
target_link_libraries(third_party_deps INTERFACE
Folly::folly
glog::glog
gflags
${Boost_LIBRARIES}
fb303::fb303
fmt::fmt
edencommon::edencommon_utils
edencommon::edencommon_telemetry
)
target_include_directories(third_party_deps INTERFACE
${FOLLY_INCLUDE_DIR}
${GLOG_INCLUDE_DIR}
${GFLAGS_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
)
if (ENABLE_EDEN_SUPPORT)
target_link_libraries(
third_party_deps
INTERFACE
${YARPL_LIBRARIES}
FBThrift::thriftcpp2
cpptoml
)
endif()
if(PCRE2_FOUND)
target_link_libraries(third_party_deps INTERFACE ${PCRE2_LIBRARY})
target_include_directories(third_party_deps INTERFACE ${PCRE2_INCLUDE_DIR})
target_compile_definitions(third_party_deps INTERFACE ${PCRE2_DEFINES})
if (WIN32)
# The pcre headers assume that the library is a dll by default
# but our preferred build environment only builds them as
# static, so be sure to ask for static pcre linkage
target_compile_definitions(third_party_deps INTERFACE PCRE2_STATIC)
endif()
endif()
target_link_libraries(third_party_deps INTERFACE Threads::Threads)
if(TARGET OpenSSL::Crypto)
target_link_libraries(third_party_deps INTERFACE OpenSSL::Crypto)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_libraries(third_party_deps INTERFACE "-framework CoreServices")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_link_libraries(third_party_deps INTERFACE
advapi32.lib
dbghelp.lib
shlwapi.lib
)
endif()
add_library(wildmatch STATIC
watchman/thirdparty/wildmatch/wildmatch.c
watchman/thirdparty/wildmatch/wildmatch.h
)
add_library(log STATIC
watchman/PubSub.cpp
watchman/LogConfig.cpp
watchman/Logging.cpp
watchman/portability/Backtrace.cpp
)
target_link_libraries(log third_party_deps)
add_library(err STATIC watchman/Poison.cpp watchman/root/warnerr.cpp)
target_link_libraries(err third_party_deps)
add_library(jansson_utf STATIC watchman/thirdparty/jansson/utf.cpp)
add_library(jansson STATIC
watchman/thirdparty/jansson/dump.cpp
watchman/thirdparty/jansson/error.cpp
watchman/thirdparty/jansson/load.cpp
watchman/thirdparty/jansson/strconv.cpp
watchman/thirdparty/jansson/value.cpp
)
target_link_libraries(jansson string third_party_deps)
list(APPEND testsupport_sources
watchman/ChildProcess.cpp
watchman/fs/FileDescriptor.cpp
watchman/fs/FileInformation.cpp
watchman/fs/FSDetect.cpp
watchman/FlagMap.cpp
watchman/IgnoreSet.cpp
watchman/PendingCollection.cpp
watchman/fs/Pipe.cpp
watchman/fs/WindowsTime.cpp
watchman/ThreadPool.cpp
watchman/WatchmanConfig.cpp
watchman/bser.cpp
watchman/fs/UnixDirHandle.cpp
watchman/fs/WinDirHandle.cpp
watchman/stream.cpp
watchman/stream_unix.cpp
watchman/stream_win.cpp
watchman/portability/PosixSpawn.cpp
watchman/portability/WinError.cpp
watchman/root/dir.cpp
watchman/root/file.cpp
)
if (ENABLE_EDEN_SUPPORT)
add_fbthrift_cpp_library(
eden_config_thrift
eden/fs/config/eden_config.thrift
)
add_fbthrift_cpp_library(
eden_service_thrift
eden/fs/service/eden.thrift
SERVICES
EdenService
DEPENDS
eden_config_thrift
fb303::fb303_thrift_cpp
)
add_fbthrift_cpp_library(
streamingeden_thrift
eden/fs/service/streamingeden.thrift
SERVICES
StreamingEdenService
DEPENDS
eden_service_thrift
fb303::fb303_thrift_cpp
)
endif()
list(APPEND watchman_sources
watchman/ChildProcess.cpp
watchman/Client.cpp
watchman/Clock.cpp
watchman/Command.cpp
watchman/CommandRegistry.cpp
watchman/Connect.cpp
watchman/ContentHash.cpp
watchman/CookieSync.cpp
watchman/Errors.cpp
watchman/fs/FileDescriptor.cpp
watchman/fs/FileInformation.cpp
watchman/fs/FileSystem.cpp
watchman/FlagMap.cpp
watchman/fs/FSDetect.cpp
watchman/GroupLookup.cpp
watchman/IgnoreSet.cpp
watchman/InMemoryView.cpp
watchman/Options.cpp
watchman/PDU.cpp
watchman/PendingCollection.cpp
watchman/PerfSample.cpp
watchman/fs/ParallelWalk.cpp
watchman/fs/Pipe.cpp
watchman/ProcessLock.cpp
watchman/ProcessUtil.cpp
# PubSub.cpp (in liblog)
watchman/QueryableView.cpp
watchman/SanityCheck.cpp
watchman/Shutdown.cpp
watchman/SignalHandler.cpp
watchman/SymlinkTargets.cpp
watchman/ThreadPool.cpp
watchman/TriggerCommand.cpp
watchman/fs/UnixDirHandle.cpp
watchman/fs/WindowsTime.cpp
watchman/UserDir.cpp
watchman/WatchmanConfig.cpp
watchman/fs/WinDirHandle.cpp
watchman/bser.cpp
watchman/listener-user.cpp
watchman/listener.cpp
watchman/main.cpp
watchman/sockname.cpp
watchman/state.cpp
watchman/stream.cpp
watchman/stream_unix.cpp
watchman/stream_stdout.cpp
watchman/stream_win.cpp
# string.cpp (in libstring)
watchman/portability/PosixSpawn.cpp
watchman/portability/WinError.cpp
watchman/query/FileResult.cpp
watchman/query/LocalFileResult.cpp
watchman/query/GlobEscaping.cpp
watchman/query/GlobTree.cpp
watchman/query/QueryContext.cpp
watchman/query/Query.cpp
watchman/query/QueryResult.cpp
watchman/query/TermRegistry.cpp
watchman/query/base.cpp
watchman/query/dirname.cpp
watchman/query/empty.cpp
watchman/query/eval.cpp
watchman/query/fieldlist.cpp
watchman/query/glob.cpp
watchman/query/intcompare.cpp
watchman/query/match.cpp
watchman/query/name.cpp
watchman/query/parse.cpp
watchman/query/pcre.cpp
watchman/query/since.cpp
watchman/query/suffix.cpp
watchman/query/type.cpp
watchman/cmds/debug.cpp
watchman/cmds/find.cpp
# cmds/heapprof.cpp
watchman/cmds/info.cpp
watchman/cmds/log.cpp
watchman/cmds/query.cpp
watchman/cmds/since.cpp
watchman/cmds/state.cpp
watchman/cmds/subscribe.cpp
watchman/cmds/trigger.cpp
watchman/cmds/watch.cpp
watchman/root/ageout.cpp
watchman/root/dir.cpp
watchman/root/file.cpp
watchman/root/init.cpp
watchman/root/iothread.cpp
watchman/root/notifythread.cpp
watchman/# root/poison.cpp (in liberr)
watchman/root/reap.cpp
watchman/root/resolve.cpp
watchman/root/sync.cpp
watchman/root/threading.cpp
# root/warnerr.cpp (in liberr)
watchman/root/watchlist.cpp
watchman/saved_state/LocalSavedStateInterface.cpp
watchman/saved_state/SavedStateFactory.cpp
watchman/saved_state/SavedStateInterface.cpp
watchman/scm/Git.cpp
watchman/scm/Mercurial.cpp
watchman/scm/SCM.cpp
watchman/telemetry/LogEvent.cpp
watchman/telemetry/WatchmanStats.cpp
watchman/telemetry/WatchmanStructuredLogger.cpp
watchman/thirdparty/getopt/GetOpt.cpp
watchman/watcher/Watcher.cpp
watchman/watcher/WatcherRegistry.cpp
watchman/watcher/fsevents.cpp
watchman/watcher/inotify.cpp
watchman/watcher/kqueue.cpp
watchman/watcher/portfs.cpp
watchman/watcher/kqueue_and_fsevents.cpp
watchman/watcher/win32.cpp
)
if (ENABLE_EDEN_SUPPORT)
# We currently only support talking to eden on posix systems
list(APPEND watchman_sources watchman/watcher/eden.cpp)
endif()
add_executable(watchman ${watchman_sources})
set_target_properties(watchman PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
target_link_libraries(
watchman
log
string
err
jansson
wildmatch
third_party_deps
)
if (WIN32)
add_subdirectory(watchman/thirdparty/deelevate_binding)
target_link_libraries(
watchman
libdeelevate
)
install_rust_executable(deelevate)
endif()
if (ENABLE_EDEN_SUPPORT)
target_link_libraries(
watchman
streamingeden_thrift
)
endif()
add_subdirectory(watchman/cli)
set(tests)
# Helper function to define a unit test executable
function(t_test NAME)
add_executable(${NAME}.t ${ARGN})
target_link_libraries(
${NAME}.t
testsupport wildmatch third_party_deps
${LIBGMOCK_LIBRARIES}
)
target_compile_definitions(${NAME}.t
PUBLIC WATCHMAN_TEST_SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\")
gtest_discover_tests(${NAME}.t)
list(APPEND tests ${NAME}.t)
endfunction()
# The `integration` target runs the unit tests and integration tests
add_custom_target(integration
DEPENDS pybuild check
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runtests.py
--watchman-path ${CMAKE_CURRENT_BINARY_DIR}/watchman
--pybuild-dir ${CMAKE_CURRENT_BINARY_DIR}/watchman/python
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
t_test(art watchman/test/ArtTest.cpp)
t_test(bser watchman/test/BserTest.cpp)
t_test(cache watchman/test/CacheTest.cpp)
t_test(childproc watchman/test/ChildProcTest.cpp)
t_test(fsdetect watchman/test/FSDetectTest.cpp)
t_test(ignore watchman/test/BserTest.cpp)
# Linking this test needs the targets graph to be cleaned up.
#t_test(inmemoryview watchman/test/InMemoryViewTest.cpp)
t_test(log watchman/test/LogTest.cpp)
t_test(maputil watchman/test/MapUtilTest.cpp)
t_test(pendingcollection watchman/test/PendingCollectionTest.cpp)
# Linking this test needs the targets graph to be cleaned up.
#t_test(perfsample watchman/test/PerfSampleTest.cpp)
t_test(result watchman/test/ResultTest.cpp)
t_test(ringbuffer watchman/test/RingBufferTest.cpp)
t_test(string watchman/test/StringTest.cpp)
t_test(wildmatch watchman/test/WildmatchTest.cpp)