Commit d91d891f authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'issue/1065' into topic/novaquark

parents 6d463489 83c43935
......@@ -6,5 +6,9 @@ blog_release_note.md
build/*
github_release_note.md
html/*
manual
manual/examples
manual/html/*
manual/libcaf_core
manual/libcaf_io
manual/libcaf_openssl
release.txt
......@@ -130,14 +130,41 @@ function(pretty_yes var)
endif()
endfunction(pretty_yes)
add_executable(caf-generate-enum-strings cmake/caf-generate-enum-strings.cpp)
function(enum_to_string relative_input_file relative_output_file)
set(input "${CMAKE_CURRENT_SOURCE_DIR}/${relative_input_file}")
set(output "${CMAKE_CURRENT_BINARY_DIR}/${relative_output_file}")
add_executable(caf-generate-enum-strings
EXCLUDE_FROM_ALL
cmake/caf-generate-enum-strings.cpp)
add_custom_target(consistency-check)
add_custom_target(update-enum-strings)
# adds a consistency check that verifies that `cpp_file` is still valid by
# re-generating the file and comparing it to the existing file
function(add_enum_consistency_check hpp_file cpp_file)
set(input "${CMAKE_CURRENT_SOURCE_DIR}/${hpp_file}")
set(file_under_test "${CMAKE_CURRENT_SOURCE_DIR}/${cpp_file}")
set(output "${CMAKE_CURRENT_BINARY_DIR}/check/${cpp_file}")
get_filename_component(output_dir "${output}" DIRECTORY)
file(MAKE_DIRECTORY "${output_dir}")
add_custom_command(OUTPUT "${output}"
COMMAND caf-generate-enum-strings "${input}" "${output}"
DEPENDS caf-generate-enum-strings "${input}")
get_filename_component(target_name "${input}" NAME_WE)
add_custom_target("${target_name}"
COMMAND
"${CMAKE_COMMAND}"
"-Dfile_under_test=${file_under_test}"
"-Dgenerated_file=${output}"
-P "${PROJECT_SOURCE_DIR}/cmake/check-consistency.cmake"
DEPENDS "${output}")
add_dependencies(consistency-check "${target_name}")
add_custom_target("${target_name}-update"
COMMAND
caf-generate-enum-strings
"${input}"
"${file_under_test}"
DEPENDS caf-generate-enum-strings "${input}")
add_dependencies(update-enum-strings "${target_name}-update")
endfunction()
################################################################################
......@@ -258,14 +285,8 @@ if(CAF_ENABLE_GCOV)
else()
set(NO_INLINE "-fno-inline -fno-inline-small-functions -fno-default-inline")
endif()
build_string("EXTRA_FLAGS" "-fprofile-arcs -ftest-coverage ${NO_INLINE}")
endif()
# set -fno-exception if requested
if(CAF_FORCE_NO_EXCEPTIONS)
build_string("EXTRA_FLAGS" "-fno-exceptions")
endif()
# enable a ton of warnings if --more-clang-warnings is used
if(CAF_MORE_WARNINGS)
# enable a ton of warnings if --more-clang-warnings is used
if(CAF_MORE_WARNINGS)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(WFLAGS "-Weverything -Wno-c++98-compat -Wno-padded "
"-Wno-documentation-unknown-command -Wno-exit-time-destructors "
......@@ -298,17 +319,6 @@ if(CAF_MORE_WARNINGS)
# convert CMake list to a single string, erasing the ";" separators
string(REPLACE ";" "" WFLAGS_STR ${WFLAGS})
build_string("EXTRA_FLAGS" "${WFLAGS_STR}")
endif()
# allow enabling IPO on gcc/clang
if(POLICY CMP0069)
cmake_policy(SET CMP0069 NEW)
else()
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
build_string("EXTRA_FLAGS" "-flto")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
build_string("EXTRA_FLAGS" "-flto -fno-fat-lto-objects")
endif()
endif()
endif()
......@@ -322,13 +332,23 @@ endif()
if(NOT APPLE AND NOT WIN32)
build_string("EXTRA_FLAGS" "-pthread")
endif()
# -fPIC generates warnings on MinGW and Cygwin plus extra setup steps needed on MinGW
# Make sure to link to platform-specific threading dependency (e.g. pthread).
if (NOT TARGET Threads::Threads)
find_package(Threads REQUIRED)
endif ()
list(APPEND CAF_EXTRA_LDFLAGS Threads::Threads)
# Have CMake set -fPIC when necessary.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Adjust setup for MinGW and Cygwin.
if(MINGW)
add_definitions(-D_WIN32_WINNT=0x0600)
add_definitions(-DWIN32)
include(GenerateExportHeader)
list(APPEND CAF_EXTRA_LDFLAGS -lws2_32 -liphlpapi -lpsapi)
# build static to avoid runtime dependencies to GCC libraries
# Build static to avoid runtime dependencies to GCC libraries.
build_string("EXTRA_FLAGS" "-static")
elseif(CYGWIN)
build_string("EXTRA_FLAGS" "-U__STRICT_ANSI__")
......@@ -409,19 +429,22 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
if(NOT TARGET uninstall)
add_custom_target(uninstall)
endif()
add_custom_command(TARGET uninstall
add_custom_target(caf_uninstall)
add_custom_command(TARGET caf_uninstall
PRE_BUILD
COMMAND "${CMAKE_COMMAND}" -P
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
add_dependencies(uninstall caf_uninstall)
################################################################################
# set inclue paths for subprojects #
################################################################################
# path to caf core & io headers
set(LIBCAF_INCLUDE_DIRS
set(CAF_INCLUDE_DIRS
"${CMAKE_CURRENT_SOURCE_DIR}/libcaf_core"
"${CMAKE_CURRENT_SOURCE_DIR}/libcaf_io"
"${CMAKE_CURRENT_SOURCE_DIR}/libcaf_openssl"
"${CMAKE_CURRENT_SOURCE_DIR}/libcaf_test")
# enable tests if not disabled
if(NOT CAF_NO_UNIT_TESTS)
......@@ -436,7 +459,7 @@ else()
endmacro()
endif()
# all projects need the headers of the core components
include_directories("${LIBCAF_INCLUDE_DIRS}")
include_directories("${CAF_INCLUDE_DIRS}")
################################################################################
......@@ -506,8 +529,17 @@ endmacro()
add_caf_lib(core)
add_optional_caf_lib(io)
# build SSL module if OpenSSL library is available
if(NOT CAF_NO_OPENSSL)
# build SSL module if OpenSSL library is available or if a parent project
# defined OPENSSL_LIBRARIES for us
if(DEFINED OPENSSL_LIBRARIES)
if (NOT OPENSSL_INCLUDE_DIR)
message(FATAL_ERROR "got OPENSSL_LIBRARIES predefined but no OPENSSL_INCLUDE_DIR")
endif()
message(STATUS "use OPENSSL_LIBRARIES provided by parent: ${OPENSSL_LIBRARIES}")
message(STATUS "use OPENSSL_INCLUDE_DIR provided by parent: ${OPENSSL_INCLUDE_DIR}")
include_directories(BEFORE ${OPENSSL_INCLUDE_DIR})
add_optional_caf_lib(openssl)
elseif(NOT CAF_NO_OPENSSL)
find_package(OpenSSL)
if(OPENSSL_FOUND)
# Check OpenSSL version >= 1.0.1
......@@ -596,27 +628,28 @@ if(NOT CAF_NO_UNIT_TESTS)
endif()
# -- Fetch branch name and SHA if available ------------------------------------
# -- Setup for building manual and API documentation ---------------------------
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/release.txt")
if(NOT caf_is_subproject)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/release.txt")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/release.txt" CAF_RELEASE)
string(REGEX REPLACE "\n" "" CAF_RELEASE "${CAF_RELEASE}")
else()
else()
set(CAF_RELEASE "${CAF_VERSION}")
endif()
message(STATUS "Set release version for all documentation to ${CAF_RELEASE}.")
add_subdirectory(doc)
endif()
message(STATUS "Set release version for all documentation to ${CAF_RELEASE}.")
# -- Export important variables to the parent scope ----------------------------
# -- Setup for building manual and API documentation ---------------------------
add_subdirectory(doc)
################################################################################
# Add additional project files to GUI #
################################################################################
file(GLOB_RECURSE script_files "scripts/*")
add_custom_target(gui_dummy SOURCES configure ${script_files})
if(caf_is_subproject)
# Make sure parent projects can find build_config.hpp
list(APPEND CAF_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/libcaf_core")
set(CAF_VERSION "${CAF_VERSION}" CACHE INTERNAL "CAF release version")
set(CAF_LIBRARIES "${CAF_LIBRARIES}" CACHE INTERNAL "Library targets")
set(CAF_INCLUDE_DIRS "${CAF_INCLUDE_DIRS}" CACHE INTERNAL "Path to CAF headers")
endif()
################################################################################
# print summary #
......
......@@ -29,38 +29,19 @@ config = [
],
// Our build matrix. Keys are the operating system labels and values are build configurations.
buildMatrix: [
// Various Linux builds for debug and release.
['debian-8', [
builds: ['debug', 'release'],
tools: ['clang-4'],
]],
['centos-6', [
builds: ['debug', 'release'],
tools: ['gcc-7'],
]],
['centos-7', [
builds: ['debug', 'release'],
tools: ['gcc-7'],
]],
['ubuntu-16.04', [
builds: ['debug', 'release'],
tools: ['clang-4'],
]],
['ubuntu-18.04', [
builds: ['debug', 'release'],
tools: ['gcc-7'],
['Linux', [
builds: ['debug'],
tools: ['gcc7'],
]],
// On Fedora 28, our debug build also produces the coverage report.
['fedora-28', [
['Linux', [
builds: ['debug'],
tools: ['gcc-8'],
tools: ['gcc8'],
extraSteps: ['coverageReport'],
]],
['fedora-28', [
['Linux', [
builds: ['release'],
tools: ['gcc-8'],
tools: ['gcc8'],
]],
// Other UNIX systems.
['macOS', [
builds: ['debug', 'release'],
tools: ['clang'],
......@@ -69,7 +50,6 @@ config = [
builds: ['debug', 'release'],
tools: ['clang'],
]],
// Non-UNIX systems.
['Windows', [
// TODO: debug build currently broken
//builds: ['debug', 'release'],
......@@ -149,59 +129,58 @@ pipeline {
runClangFormat(config)
}
}
stage('Build') {
steps {
buildParallel(config, PrettyJobBaseName)
}
}
stage('Documentation') {
agent { label 'pandoc' }
stage('Check Consistency') {
agent { label 'unix' }
steps {
deleteDir()
unstash('sources')
dir('sources') {
// Configure and build.
cmakeBuild([
buildDir: 'build',
installation: 'cmake in search path',
sourceDir: '.',
cmakeArgs: '-DCAF_BUILD_TEX_MANUAL=yes',
steps: [[
args: '--target doc',
args: '--target consistency-check',
withCmake: true,
]],
])
sshagent(['84d71a75-cbb6-489a-8f4c-d0e2793201e9']) {
sh """
if [ "${env.GIT_BRANCH}" = "master" ]; then
rsync -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" -r -z --delete build/doc/html/ www.inet.haw-hamburg.de:/users/www/www.actor-framework.org/html/doc
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null build/doc/manual.pdf www.inet.haw-hamburg.de:/users/www/www.actor-framework.org/html/pdf/manual.pdf
fi
"""
}
}
dir('read-the-docs') {
git([
credentialsId: '9b054212-9bb4-41fd-ad8e-b7d47495303f',
url: 'git@github.com:actor-framework/read-the-docs.git',
])
sh """
if [ "${env.GIT_BRANCH}" = "master" ]; then
cp ../sources/build/doc/rst/* .
if [ -n "\$(git status --porcelain)" ]; then
git add .
git commit -m "Update Manual"
git push --set-upstream origin master
if [ -z "\$(grep 'exp.sha' ../sources/release.txt)" ] ; then
git tag \$(cat ../sources/release.txt)
git push origin \$(cat ../sources/release.txt)
fi
fi
fi
"""
}
}
}
stage('Build') {
steps {
buildParallel(config, PrettyJobBaseName)
}
}
// TODO: generate PDF from reStructuredText
// stage('Documentation') {
// agent { label 'pandoc' }
// steps {
// deleteDir()
// unstash('sources')
// dir('sources') {
// // Configure and build.
// cmakeBuild([
// buildDir: 'build',
// installation: 'cmake in search path',
// sourceDir: '.',
// cmakeArgs: '-DCAF_BUILD_TEX_MANUAL=yes',
// steps: [[
// args: '--target doc',
// withCmake: true,
// ]],
// ])
// sshagent(['84d71a75-cbb6-489a-8f4c-d0e2793201e9']) {
// sh """
// if [ "${env.GIT_BRANCH}" = "master" ]; then
// rsync -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" -r -z --delete build/doc/html/ www.inet.haw-hamburg.de:/users/www/www.actor-framework.org/html/doc
// scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null build/doc/manual.pdf www.inet.haw-hamburg.de:/users/www/www.actor-framework.org/html/pdf/manual.pdf
// fi
// """
// }
// }
// }
// }
stage('Notify') {
steps {
collectResults(config, PrettyJobName)
......
......@@ -128,6 +128,15 @@ A SNocs workspace is provided by GitHub user
* Doxygen (for the `doxygen` target)
* LaTeX (for the `manual` target)
## Build the Manual
CAF uses [Sphinx](https://www.sphinx-doc.org):
```sh
cd manual
sphinx-build . html
```
## Scientific Use
If you use CAF in a scientific context, please use one of the following citations:
......
......@@ -13,10 +13,9 @@
# Variables defined by this module:
#
# CAF_FOUND System has CAF headers and library
# CAF_VERSION Found CAF release number
# CAF_LIBRARIES List of library files for all components
# CAF_INCLUDE_DIRS List of include paths for all components
# CAF_LIBRARY_$C Library file for component $C
# CAF_INCLUDE_DIR_$C Include path for component $C
if(CAF_FIND_COMPONENTS STREQUAL "")
message(FATAL_ERROR "FindCAF requires at least one COMPONENT.")
......@@ -49,7 +48,8 @@ foreach (comp ${CAF_FIND_COMPONENTS})
/usr/local/include
/opt/local/include
/sw/include
${CMAKE_INSTALL_PREFIX}/include)
${CMAKE_INSTALL_PREFIX}/include
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
mark_as_advanced(CAF_INCLUDE_DIR_${UPPERCOMP})
if (NOT "${CAF_INCLUDE_DIR_${UPPERCOMP}}"
STREQUAL "CAF_INCLUDE_DIR_${UPPERCOMP}-NOTFOUND")
......@@ -66,6 +66,7 @@ foreach (comp ${CAF_FIND_COMPONENTS})
/usr/local/include
/opt/local/include
/sw/include
${CMAKE_INSTALL_PREFIX}/include
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
if ("${caf_build_header_path}" STREQUAL "caf_build_header_path-NOTFOUND")
message(WARNING "Found all.hpp for CAF core, but not build_config.hpp")
......@@ -91,6 +92,7 @@ foreach (comp ${CAF_FIND_COMPONENTS})
/usr/local/lib
/opt/local/lib
/sw/lib
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_BUILD_TYPE})
mark_as_advanced(CAF_LIBRARY_${UPPERCOMP})
......@@ -108,15 +110,35 @@ if (DEFINED CAF_INCLUDE_DIRS)
list(REMOVE_DUPLICATES CAF_INCLUDE_DIRS)
endif()
if (NOT CAF_INCLUDE_DIR_CORE STREQUAL "CAF_INCLUDE_DIR_CORE-NOTFOUND")
# read content of config.hpp
file(READ "${CAF_INCLUDE_DIR_CORE}/caf/config.hpp" CONFIG_HPP)
# get line containing the version
string(REGEX MATCH "#define CAF_VERSION [0-9]+" VERSION_LINE "${CONFIG_HPP}")
# extract version number from line
string(REGEX MATCH "[0-9]+" VERSION_INT "${VERSION_LINE}")
# calculate major, minor, and patch version
math(EXPR CAF_VERSION_MAJOR "${VERSION_INT} / 10000")
math(EXPR CAF_VERSION_MINOR "( ${VERSION_INT} / 100) % 100")
math(EXPR CAF_VERSION_PATCH "${VERSION_INT} % 100")
# create full version string
set(CAF_VERSION "${CAF_VERSION_MAJOR}.${CAF_VERSION_MINOR}.${CAF_VERSION_PATCH}")
if (NOT CAF_VERSION)
unset(CAF_VERSION)
message(WARNING "Unable to determine CAF version")
endif ()
endif ()
# let CMake check whether all requested components have been found
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CAF
FOUND_VAR CAF_FOUND
REQUIRED_VARS CAF_LIBRARIES CAF_INCLUDE_DIRS
REQUIRED_VARS CAF_VERSION CAF_LIBRARIES CAF_INCLUDE_DIRS
HANDLE_COMPONENTS)
# final step to tell CMake we're done
mark_as_advanced(CAF_ROOT_DIR
CAF_VERSION
CAF_LIBRARIES
CAF_INCLUDE_DIRS)
......@@ -36,7 +36,7 @@ void keep_alnum(string& str) {
int main(int argc, char** argv) {
if (argc != 3) {
cerr << "wrong number of arguments.\n"
<< "usage: " << argv[0] << "input-file output-file\n";
<< "usage: " << argv[0] << " input-file output-file\n";
return EXIT_FAILURE;
}
std::ifstream in{argv[1]};
......@@ -85,11 +85,15 @@ int main(int argc, char** argv) {
}
std::ofstream out{argv[2]};
if (!out) {
cerr << "unable to open output file: " << argv[1] << '\n';
cerr << "unable to open output file: " << argv[2] << '\n';
return EXIT_FAILURE;
}
// Print file header.
out << "#include \"" << namespaces[0];
out << "// clang-format off\n"
<< "// DO NOT EDIT: "
"this file is auto-generated by caf-generate-enum-strings.\n"
"// Run the target update-enum-strings if this file is out of sync.\n"
<< "#include \"" << namespaces[0];
for (size_t i = 1; i < namespaces.size(); ++i)
out << '/' << namespaces[i];
out << '/' << enum_name << ".hpp\"\n\n"
......
execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files
"${file_under_test}" "${generated_file}"
RESULT_VARIABLE result)
if(result EQUAL 0)
# files still in sync
else()
message(SEND_ERROR "${file_under_test} is out of sync! Run target "
"'update-enum-strings' to update automatically")
endif()
......@@ -48,7 +48,6 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
Optional Targets:
--with-qt-examples build Qt example(s)
--with-protobuf-examples build Google Protobuf example(s)
--with-tex-manual build the LaTeX manual
Installation Directories:
--prefix=PREFIX installation directory [/usr/local]
......@@ -327,9 +326,6 @@ while [ $# -ne 0 ]; do
--with-protobuf-examples)
append_cache_entry CAF_BUILD_PROTOBUF_EXAMPLES BOOL yes
;;
--with-tex-manual)
append_cache_entry CAF_BUILD_TEX_MANUAL BOOL yes
;;
--no-curl-examples)
append_cache_entry CAF_NO_CURL_EXAMPLES BOOL yes
;;
......
add_custom_target(doc)
# -- list all .tex source files ------------------------------------------------
set(sources
tex/Actors.tex
tex/Brokers.tex
tex/CommonPitfalls.tex
tex/ConfiguringActorApplications.tex
tex/Error.tex
tex/FAQ.tex
tex/FirstSteps.tex
tex/GroupCommunication.tex
tex/Introduction.tex
tex/ManagingGroupsOfWorkers.tex
tex/MessageHandlers.tex
tex/MessagePassing.tex
tex/Messages.tex
tex/MigrationGuides.tex
tex/NetworkTransparency.tex
tex/ReferenceCounting.tex
tex/Registry.tex
tex/RemoteSpawn.tex
tex/Scheduler.tex
tex/Streaming.tex
tex/TypeInspection.tex
tex/UsingAout.tex
tex/Utility.tex
tex/Testing.tex
)
# -- create target folders -----------------------------------------------------
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tex")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rst")
# -- process .in files ---------------------------------------------------------
configure_file("cmake/Doxyfile.in"
"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
@ONLY)
configure_file("cmake/variables.tex.in"
"${CMAKE_CURRENT_BINARY_DIR}/tex/variables.tex"
@ONLY)
configure_file("cmake/conf.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/rst/conf.py"
@ONLY)
configure_file("cmake/index_footer.rst.in"
"${CMAKE_CURRENT_BINARY_DIR}/rst/index_footer.rst"
@ONLY)
configure_file("cmake/index_header.rst.in"
"${CMAKE_CURRENT_BINARY_DIR}/rst/index_header.rst"
@ONLY)
# -- generate .rst files -------------------------------------------------------
add_executable(caf-generate-rst cmake/caf-generate-rst.cpp)
target_link_libraries(caf-generate-rst
${CAF_EXTRA_LDFLAGS}
${CAF_LIBRARIES}
${PTHREAD_LIBRARIES})
add_custom_target(rst)
add_dependencies(doc rst)
function(convert_to_rst tex_file)
get_filename_component(file_name "${tex_file}" NAME_WE)
set(input "${CMAKE_CURRENT_SOURCE_DIR}/tex/${tex_file}")
set(rst_file "${file_name}.rst")
set(output "${CMAKE_CURRENT_BINARY_DIR}/rst/${rst_file}")
add_custom_command(OUTPUT "${output}"
COMMAND
caf-generate-rst
-o "${output}"
-i "${input}"
-r "${PROJECT_SOURCE_DIR}"
DEPENDS caf-generate-rst "${input}")
add_custom_target("${rst_file}" DEPENDS "${output}")
add_dependencies(rst "${rst_file}")
endfunction()
foreach(filename ${sources})
get_filename_component(filename_no_dir "${filename}" NAME)
convert_to_rst("${filename_no_dir}")
endforeach()
# generate index.rst file from manual.tex
add_custom_target("index.rst"
DEPENDS "tex/manual.tex"
COMMAND "python"
"${CMAKE_SOURCE_DIR}/scripts/make_index_rst.py"
"${CMAKE_CURRENT_BINARY_DIR}/rst/index.rst"
"${CMAKE_SOURCE_DIR}/doc/tex/manual.tex"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rst")
add_dependencies(rst "index.rst")
# -- Doxygen setup -------------------------------------------------------------
find_package(Doxygen)
......@@ -116,21 +21,3 @@ else()
VERBATIM)
add_dependencies(doc doxygen)
endif()
# -- LaTeX setup ---------------------------------------------------------------
if (CAF_BUILD_TEX_MANUAL)
find_package(LATEX)
message(STATUS "Add optional target: manual.")
include("cmake/UseLATEX.cmake")
# enable synctex for convenient editing
set(LATEX_USE_SYNCTEX yes)
# add manual.pdf as target
add_latex_document(tex/manual.tex
INPUTS ${sources} "tex/variables.tex"
IMAGE_DIRS "pdf"
FORCE_PDF
TARGET_NAME manual)
add_dependencies(doc manual)
endif()
This diff is collapsed.
This diff is collapsed.
Version Information
===================
This version of the Manual was automatically generated from CAF commit
`@CAF_SHA@ <https://github.com/actor-framework/actor-framework/commit/@CAF_SHA@>`_.
CAF User Manual
===============
**C++ Actor Framework** version @CAF_RELEASE@.
Contents
========
This diff is collapsed.
\section{Network I/O with Brokers}
\label{broker}
When communicating to other services in the network, sometimes low-level socket
I/O is inevitable. For this reason, CAF provides \emph{brokers}. A broker is
an event-based actor running in the middleman that multiplexes socket I/O. It
can maintain any number of acceptors and connections. Since the broker runs in
the middleman, implementations should be careful to consume as little time as
possible in message handlers. Brokers should outsource any considerable amount
of work by spawning new actors or maintaining worker actors.
\textit{Note that all UDP-related functionality is still \experimental.}
\subsection{Spawning Brokers}
Brokers are implemented as functions and are spawned by calling on of the three
following member functions of the middleman.
\begin{lstlisting}
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts>
typename infer_handle_from_fun<F>::type
spawn_broker(F fun, Ts&&... xs);
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts>
expected<typename infer_handle_from_fun<F>::type>
spawn_client(F fun, const std::string& host, uint16_t port, Ts&&... xs);
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts>
expected<typename infer_handle_from_fun<F>::type>
spawn_server(F fun, uint16_t port, Ts&&... xs);
\end{lstlisting}
The function \lstinline^spawn_broker^ simply spawns a broker. The convenience
function \lstinline^spawn_client^ tries to connect to given host and port over
TCP and returns a broker managing this connection on success. Finally,
\lstinline^spawn_server^ opens a local TCP port and spawns a broker managing it
on success. There are no convenience functions spawn a UDP-based client or
server.
\subsection{Class \lstinline^broker^}
\label{broker-class}
\begin{lstlisting}
void configure_read(connection_handle hdl, receive_policy::config config);
\end{lstlisting}
Modifies the receive policy for the connection identified by \lstinline^hdl^.
This will cause the middleman to enqueue the next \lstinline^new_data_msg^
according to the given \lstinline^config^ created by
\lstinline^receive_policy::exactly(x)^, \lstinline^receive_policy::at_most(x)^,
or \lstinline^receive_policy::at_least(x)^ (with \lstinline^x^ denoting the
number of bytes).
\begin{lstlisting}
void write(connection_handle hdl, size_t num_bytes, const void* buf)
void write(datagram_handle hdl, size_t num_bytes, const void* buf)
\end{lstlisting}
Writes data to the output buffer.
\begin{lstlisting}
void enqueue_datagram(datagram_handle hdl, std::vector<char> buf);
\end{lstlisting}
Enqueues a buffer to be sent as a datagram. Use of this function is encouraged
over write as it allows reuse of the buffer which can be returned to the broker
in a \lstinline^datagram_sent_msg^.
\begin{lstlisting}
void flush(connection_handle hdl);
void flush(datagram_handle hdl);
\end{lstlisting}
Sends the data from the output buffer.
\begin{lstlisting}
template <class F, class... Ts>
actor fork(F fun, connection_handle hdl, Ts&&... xs);
\end{lstlisting}
Spawns a new broker that takes ownership of a given connection.
\begin{lstlisting}
size_t num_connections();
\end{lstlisting}
Returns the number of open connections.
\begin{lstlisting}
void close(connection_handle hdl);
void close(accept_handle hdl);
void close(datagram_handle hdl);
\end{lstlisting}
Closes the endpoint related to the handle.
\begin{lstlisting}
expected<std::pair<accept_handle, uint16_t>>
add_tcp_doorman(uint16_t port = 0, const char* in = nullptr,
bool reuse_addr = false);
\end{lstlisting}
Creates new doorman that accepts incoming connections on a given port and
returns the handle to the doorman and the port in use or an error.
\begin{lstlisting}
expected<connection_handle>
add_tcp_scribe(const std::string& host, uint16_t port);
\end{lstlisting}
Creates a new scribe to connect to host:port and returns handle to it or an
error.
\begin{lstlisting}
expected<std::pair<datagram_handle, uint16_t>>
add_udp_datagram_servant(uint16_t port = 0, const char* in = nullptr,
bool reuse_addr = false);
\end{lstlisting}
Creates a datagram servant to handle incoming datagrams on a given port.
Returns the handle to the servant and the port in use or an error.
\begin{lstlisting}
expected<datagram_handle>
add_udp_datagram_servant(const std::string& host, uint16_t port);
\end{lstlisting}
Creates a datagram servant to send datagrams to host:port and returns a handle
to it or an error.
\subsection{Broker-related Message Types}
Brokers receive system messages directly from the middleman for connection and
acceptor events.
\textbf{Note:} brokers are \emph{required} to handle these messages immediately
regardless of their current state. Not handling the system messages from the
broker results in loss of data, because system messages are \emph{not}
delivered through the mailbox and thus cannot be skipped.
\begin{lstlisting}
struct new_connection_msg {
accept_handle source;
connection_handle handle;
};
\end{lstlisting}
Indicates that \lstinline^source^ accepted a new TCP connection identified by
\lstinline^handle^.
\begin{lstlisting}
struct new_data_msg {
connection_handle handle;
std::vector<char> buf;
};
\end{lstlisting}
Contains raw bytes received from \lstinline^handle^. The amount of data
received per event is controlled with \lstinline^configure_read^ (see
\ref{broker-class}). It is worth mentioning that the buffer is re-used whenever
possible.
\begin{lstlisting}
struct data_transferred_msg {
connection_handle handle;
uint64_t written;
uint64_t remaining;
};
\end{lstlisting}
This message informs the broker that the \lstinline^handle^ sent
\lstinline^written^ bytes with \lstinline^remaining^ bytes in the buffer. Note,
that these messages are not sent per default but must be explicitly enabled via
the member function \lstinline^ack_writes^.
\begin{lstlisting}
struct connection_closed_msg {
connection_handle handle;
};
struct acceptor_closed_msg {
accept_handle handle;
};
\end{lstlisting}
A \lstinline^connection_closed_msg^ or \lstinline^acceptor_closed_msg^ informs
the broker that one of its handles is no longer valid.
\begin{lstlisting}
struct connection_passivated_msg {
connection_handle handle;
};
struct acceptor_passivated_msg {
accept_handle handle;
};
\end{lstlisting}
A \lstinline^connection_passivated_msg^ or \lstinline^acceptor_passivated_msg^
informs the broker that one of its handles entered passive mode and no longer
accepts new data or connections \see{trigger}.
The following messages are related to UDP communication
(see~\sref{transport-protocols}. Since UDP is not connection oriented, there is
no equivalent to the \lstinline^new_connection_msg^ of TCP.
\begin{lstlisting}
struct new_datagram_msg {
datagram_handle handle;
network::receive_buffer buf;
};
\end{lstlisting}
Contains the raw bytes from \lstinline^handle^. The buffer always has a maximum
size of 65k to receive all regular UDP messages. The amount of bytes can be
queried via the \lstinline^.size()^ member function. Similar to TCP, the buffer
is reused when possible---please do not resize it.
\begin{lstlisting}
struct datagram_sent_msg {
datagram_handle handle;
uint64_t written;
std::vector<char> buf;
};
\end{lstlisting}
This message informs the broker that the \lstinline^handle^ sent a datagram of
\lstinline^written^ bytes. It includes the buffer that held the sent message to
allow its reuse. Note, that these messages are not sent per default but must be
explicitly enabled via the member function \lstinline^ack_writes^.
\begin{lstlisting}
struct datagram_servant_closed_msg {
std::vector<datagram_handle> handles;
};
\end{lstlisting}
A \lstinline^datagram_servant_closed_msg^ informs the broker that one of its
handles is no longer valid.
\begin{lstlisting}
struct datagram_servant_passivated_msg {
datagram_handle handle;
};
\end{lstlisting}
A \lstinline^datagram_servant_closed_msg^ informs the broker that one of its
handles entered passive mode and no longer accepts new data \see{trigger}.
\subsection{Manually Triggering Events \experimental}
\label{trigger}
Brokers receive new events as \lstinline^new_connection_msg^ and
\lstinline^new_data_msg^ as soon and as often as they occur, per default. This
means a fast peer can overwhelm a broker by sending it data faster than the
broker can process it. In particular if the broker outsources work items to
other actors, because work items can accumulate in the mailboxes of the
workers.
Calling \lstinline^self->trigger(x,y)^, where \lstinline^x^ is a connection or
acceptor handle and \lstinline^y^ is a positive integer, allows brokers to halt
activities after \lstinline^y^ additional events. Once a connection or acceptor
stops accepting new data or connections, the broker receives a
\lstinline^connection_passivated_msg^ or \lstinline^acceptor_passivated_msg^.
Brokers can stop activities unconditionally with \lstinline^self->halt(x)^ and
resume activities unconditionally with \lstinline^self->trigger(x)^.
This diff is collapsed.
\section{Errors}
\label{error}
Errors in CAF have a code and a category, similar to
\lstinline^std::error_code^ and \lstinline^std::error_condition^. Unlike its
counterparts from the C++ standard library, \lstinline^error^ is
plattform-neutral and serializable. Instead of using category singletons, CAF
stores categories as atoms~\see{atom}. Errors can also include a message to
provide additional context information.
\subsection{Class Interface}
\begin{center}
\begin{tabular}{ll}
\textbf{Constructors} & ~ \\
\hline
\lstinline^(Enum x)^ & Construct error by calling \lstinline^make_error(x)^ \\
\hline
\lstinline^(uint8_t x, atom_value y)^ & Construct error with code \lstinline^x^ and category \lstinline^y^ \\
\hline
\lstinline^(uint8_t x, atom_value y, message z)^ & Construct error with code \lstinline^x^, category \lstinline^y^, and context \lstinline^z^ \\
\hline
~ & ~ \\ \textbf{Observers} & ~ \\
\hline
\lstinline^uint8_t code()^ & Returns the error code \\
\hline
\lstinline^atom_value category()^ & Returns the error category \\
\hline
\lstinline^message context()^ & Returns additional context information \\
\hline
\lstinline^explicit operator bool()^ & Returns \lstinline^code() != 0^ \\
\hline
\end{tabular}
\end{center}
\subsection{Add Custom Error Categories}
\label{custom-error}
Adding custom error categories requires three steps: (1)~declare an enum class
of type \lstinline^uint8_t^ with the first value starting at 1, (2)~implement a
free function \lstinline^make_error^ that converts the enum to an
\lstinline^error^ object, (3)~add the custom category to the actor system with
a render function. The last step is optional to allow users to retrieve a
better string representation from \lstinline^system.render(x)^ than
\lstinline^to_string(x)^ can offer. Note that any error code with value 0 is
interpreted as \emph{not-an-error}. The following example adds a custom error
category by performing the first two steps.
\cppexample[19-34]{message_passing/divider}
The implementation of \lstinline^to_string(error)^ is unable to call string
conversions for custom error categories. Hence,
\lstinline^to_string(make_error(math_error::division_by_zero))^ returns
\lstinline^"error(1, math)"^.
The following code adds a rendering function to the actor system to provide a
more satisfactory string conversion.
\cppexample[50-58]{message_passing/divider}
With the custom rendering function,
\lstinline^system.render(make_error(math_error::division_by_zero))^ returns
\lstinline^"math_error(division_by_zero)"^.
\clearpage
\subsection{System Error Codes}
\label{sec}
System Error Codes (SECs) use the error category \lstinline^"system"^. They
represent errors in the actor system or one of its modules and are defined as
follows.
\sourcefile[32-117]{libcaf_core/caf/sec.hpp}
%\clearpage
\subsection{Default Exit Reasons}
\label{exit-reason}
CAF uses the error category \lstinline^"exit"^ for default exit reasons. These
errors are usually fail states set by the actor system itself. The two
exceptions are \lstinline^exit_reason::user_shutdown^ and
\lstinline^exit_reason::kill^. The former is used in CAF to signalize orderly,
user-requested shutdown and can be used by programmers in the same way. The
latter terminates an actor unconditionally when used in \lstinline^send_exit^,
even if the default handler for exit messages~\see{exit-message} is overridden.
\sourcefile[29-49]{libcaf_core/caf/exit_reason.hpp}
\section{Frequently Asked Questions}
\label{faq}
This Section is a compilation of the most common questions via GitHub, chat,
and mailing list.
\subsection{Can I Encrypt CAF Communication?}
Yes, by using the OpenSSL module~\see{free-remoting-functions}.
\subsection{Can I Create Messages Dynamically?}
Yes.
Usually, messages are created implicitly when sending messages but can also be
created explicitly using \lstinline^make_message^. In both cases, types and
number of elements are known at compile time. To allow for fully dynamic
message generation, CAF also offers \lstinline^message_builder^:
\begin{lstlisting}
message_builder mb;
// prefix message with some atom
mb.append(strings_atom::value);
// fill message with some strings
std::vector<std::string> strings{/*...*/};
for (auto& str : strings)
mb.append(str);
// create the message
message msg = mb.to_message();
\end{lstlisting}
\subsection{What Debugging Tools Exist?}
The \lstinline^scripts/^ and \lstinline^tools/^ directories contain some useful
tools to aid in development and debugging.
\lstinline^scripts/atom.py^ converts integer atom values back into strings.
\lstinline^scripts/demystify.py^ replaces cryptic \lstinline^typed_mpi<...>^
templates with \lstinline^replies_to<...>::with<...>^ and
\lstinline^atom_constant<...>^ with a human-readable representation of the
actual atom.
\lstinline^scripts/caf-prof^ is an R script that generates plots from CAF
profiler output.
\lstinline^caf-vec^ is a (highly) experimental tool that annotates CAF logs
with vector timestamps. It gives you happens-before relations and a nice
visualization via \href{https://bestchai.bitbucket.io/shiviz/}{ShiViz}.
\section{Overview}
Compiling CAF requires CMake and a C++11-compatible compiler. To get and
compile the sources on UNIX-like systems, type the following in a terminal:
\begin{verbatim}
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
\end{verbatim}
We recommended to run the unit tests as well:
\begin{verbatim}
make test
\end{verbatim}
If the output indicates an error, please submit a bug report that includes (a)
your compiler version, (b) your OS, and (c) the content of the file
\texttt{build/Testing/Temporary/LastTest.log}.
\subsection{Features}
\begin{itemize}
\item Lightweight, fast and efficient actor implementations
\item Network transparent messaging
\item Error handling based on Erlang's failure model
\item Pattern matching for messages as internal DSL to ease development
\item Thread-mapped actors for soft migration of existing applications
\item Publish/subscribe group communication
\end{itemize}
\subsection{Minimal Compiler Versions}
\begin{itemize}
\item GCC 4.8
\item Clang 3.4
\item Visual Studio 2015, Update 3
\end{itemize}
\subsection{Supported Operating Systems}
\begin{itemize}
\item Linux
\item Mac OS X
\item Windows (static library only)
\end{itemize}
\clearpage
\subsection{Hello World Example}
\cppexample{hello_world}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
\section{Registry}
\label{registry}
The actor registry in CAF keeps track of the number of running actors and
allows to map actors to their ID or a custom atom~\see{atom} representing a
name. The registry does \emph{not} contain all actors. Actors have to be stored
in the registry explicitly. Users can access the registry through an actor
system by calling \lstinline^system.registry()^. The registry stores actors
using \lstinline^strong_actor_ptr^ \see{actor-pointer}.
Users can use the registry to make actors system-wide available by name. The
middleman~\see{middleman} uses the registry to keep track of all actors known
to remote nodes in order to serialize and deserialize them. Actors are removed
automatically when they terminate.
It is worth mentioning that the registry is not synchronized between connected
actor system. Each actor system has its own, local registry in a distributed
setting.
\begin{center}
\begin{tabular}{ll}
\textbf{Types} & ~ \\
\hline
\lstinline^name_map^ & \lstinline^unordered_map<atom_value, strong_actor_ptr>^ \\
\hline
~ & ~ \\ \textbf{Observers} & ~ \\
\hline
\lstinline^strong_actor_ptr get(actor_id)^ & Returns the actor associated to given ID. \\
\hline
\lstinline^strong_actor_ptr get(atom_value)^ & Returns the actor associated to given name. \\
\hline
\lstinline^name_map named_actors()^ & Returns all name mappings. \\
\hline
\lstinline^size_t running()^ & Returns the number of currently running actors. \\
\hline
~ & ~ \\ \textbf{Modifiers} & ~ \\
\hline
\lstinline^void put(actor_id, strong_actor_ptr)^ & Maps an actor to its ID. \\
\hline
\lstinline^void erase(actor_id)^ & Removes an ID mapping from the registry. \\
\hline
\lstinline^void put(atom_value, strong_actor_ptr)^ & Maps an actor to a name. \\
\hline
\lstinline^void erase(atom_value)^ & Removes a name mapping from the registry. \\
\hline
\end{tabular}
\end{center}
\section{Remote Spawning of Actors \experimental}
\label{remote-spawn}
Remote spawning is an extension of the dynamic spawn using run-time type names
\see{add-custom-actor-type}. The following example assumes a typed actor handle
named \lstinline^calculator^ with an actor implementing this messaging
interface named "calculator".
\cppexample[125-143]{remoting/remote_spawn}
We first connect to a CAF node with \lstinline^middleman().connect(...)^. On
success, \lstinline^connect^ returns the node ID we need for
\lstinline^remote_spawn^. This requires the server to open a port with
\lstinline^middleman().open(...)^ or \lstinline^middleman().publish(...)^.
Alternatively, we can obtain the node ID from an already existing remote actor
handle---returned from \lstinline^remote_actor^ for example---via
\lstinline^hdl->node()^. After connecting to the server, we can use
\lstinline^middleman().remote_spawn<...>(...)^ to create actors remotely.
This diff is collapsed.
This diff is collapsed.
\section{Using \texttt{aout} -- A Concurrency-safe Wrapper for \texttt{cout}}
When using \lstinline^cout^ from multiple actors, output often appears
interleaved. Moreover, using \lstinline^cout^ from multiple actors -- and thus
from multiple threads -- in parallel should be avoided regardless, since the
standard does not guarantee a thread-safe implementation.
By replacing \texttt{std::cout} with \texttt{caf::aout}, actors can achieve a
concurrency-safe text output. The header \lstinline^caf/all.hpp^ also defines
overloads for \texttt{std::endl} and \texttt{std::flush} for \lstinline^aout^,
but does not support the full range of ostream operations (yet). Each write
operation to \texttt{aout} sends a message to a `hidden' actor. This actor only
prints lines, unless output is forced using \lstinline^flush^. The example
below illustrates printing of lines of text from multiple actors (in random
order).
\cppexample{aout}
This diff is collapsed.
This diff is collapsed.
......@@ -2,7 +2,7 @@ project(caf_examples CXX)
add_custom_target(all_examples)
include_directories(${LIBCAF_INCLUDE_DIRS})
include_directories(${CAF_INCLUDE_DIRS})
macro(add folder name)
add_executable(${name} ${folder}/${name}.cpp ${ARGN})
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment