blob: ea06e07da544282c0243ef7787f017e4b74839b6 [file] [log] [blame]
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# This file allows cross-compiling of proton to JavaScript using emscripten
# (https://github.com/kripken/emscripten). As it is really a cross-compilation
# (as opposed to a binding like with swig) the approach is rather different and
# somewhat replicates the main build in the proton-c/CMakeLists.txt using quite
# a bit of "copy and paste reuse".
# TODO refactor this file (and proton-c/CMakeLists.txt) to keep the main
# compilation and cross-compilation consistent.
message(STATUS "Found emscripten, using that to build JavaScript binding")
# Find and install the node.js packages that we might need. We can assume that
# node.js itself is installed because Emscripten has a dependency on it.
find_package(NodePackages)
# Describe the target OS we are building to - Emscripten mimics the Linux platform.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_CROSSCOMPILING TRUE)
# Specify the compiler to use for cross-compilation.
set(CMAKE_C_COMPILER "${EMCC}")
# Don't do compiler autodetection, since we are cross-compiling.
include(CMakeForceCompiler)
CMAKE_FORCE_C_COMPILER("${CMAKE_C_COMPILER}" Clang)
# The Proton default build type is RelWithDebInfo, but for JavaScript the C debug
# mechanism is irrelevant. If Debug is explicitly set we turn off optimisations
# and don't run the closure compiler so any error/profiling messages are readable.
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "JavaScript build type is \"Debug\"")
else()
set(CMAKE_BUILD_TYPE Release)
message(STATUS "JavaScript build type is \"Release\"")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(EMSCRIPTEN_LINK_OPTIMISATIONS "-O2 --closure 1")
endif()
# From this point we should be using emscripten compilation tools.
message(STATUS "emscripten compilation environment:")
message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
# Set this to the proton-c directory, we're cross-compiling code from there.
set(PN_PATH ${CMAKE_SOURCE_DIR}/proton-c)
# TODO the OpenSSL stuff won't work for emscripten by default. It might well be
# possible to compile it from source using emscripten (that's the standard practice
# for libraries with emscripten) see https://github.com/kripken/emscripten/wiki/FAQ
# "Q. How do I link against system libraries like SDL, boost, etc.?"
# Though it might be more natural to simply use a TLS protected wss:// WebSocket URL.
# set(pn_ssl_impl src/ssl/openssl.c)
# set(SSL_LIB ${OPENSSL_LIBRARIES})
set(pn_ssl_impl ${PN_PATH}/src/ssl/ssl_stub.c)
# emscripten is Linux like so use the posix impls.
set(pn_io_impl ${PN_PATH}/src/posix/io.c)
set(pn_selector_impl ${PN_PATH}/src/posix/selector.c)
CHECK_LIBRARY_EXISTS (uuid uuid_generate "" UUID_GENERATE_IN_UUID)
if (UUID_GENERATE_IN_UUID)
set (UUID_LIB uuid)
endif (UUID_GENERATE_IN_UUID)
# Generate encodings.h and protocol.h
# It may be possible to use the ones generated for the main proton-c build but
# qpid-proton-core has a dependency on the generated files, so I'm not sure if
# it'll work without a command that can be run as a dependency. Probably best
# do it this way when cross-compiling - though more copy'n'paste
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/encodings.h
COMMAND python ${PN_PATH}/env.py PYTHONPATH=${PN_PATH} python ${PN_PATH}/src/codec/encodings.h.py > ${CMAKE_CURRENT_BINARY_DIR}/encodings.h
DEPENDS ${PN_PATH}/src/codec/encodings.h.py
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/protocol.h
COMMAND python ${PN_PATH}/env.py PYTHONPATH=${PN_PATH} python ${PN_PATH}/src/protocol.h.py > ${CMAKE_CURRENT_BINARY_DIR}/protocol.h
DEPENDS ${PN_PATH}/src/protocol.h.py
)
set(COMPILE_WARNING_FLAGS "-Werror -Wall -pedantic-errors -Wno-comment -Wno-warn-absolute-paths")
# Explicitly set PLATFORM_DEFINITIONS to Linux ones for emscripten as we don't
# want to inadvertently use Windows versions if we happen to be cross-compiling
# from a Windows platform
set(PLATFORM_DEFINITIONS "USE_CLOCK_GETTIME;USE_UUID_GENERATE;USE_STRERROR_R;USE_ATOLL")
# The following is largely a copy from the the main proton-c/CMakeLists.txt.
# The main difference is prefixing paths with ${PN_PATH}/ as we can't use a
# relative path from this CMakeLists.txt.
set(qpid-proton-platform
${pn_io_impl}
${pn_selector_impl}
${PN_PATH}/src/platform.c
${pn_ssl_impl}
)
set(qpid-proton-core
${PN_PATH}/src/object/object.c
${PN_PATH}/src/object/list.c
${PN_PATH}/src/object/map.c
${PN_PATH}/src/object/string.c
${PN_PATH}/src/object/iterator.c
${PN_PATH}/src/object/record.c
${PN_PATH}/src/log.c
${PN_PATH}/src/util.c
${PN_PATH}/src/url.c
${PN_PATH}/src/error.c
${PN_PATH}/src/buffer.c
${PN_PATH}/src/parser.c
${PN_PATH}/src/scanner.c
${PN_PATH}/src/types.c
${PN_PATH}/src/framing/framing.c
${PN_PATH}/src/codec/codec.c
${PN_PATH}/src/codec/decoder.c
${PN_PATH}/src/codec/encoder.c
${PN_PATH}/src/dispatcher/dispatcher.c
${PN_PATH}/src/engine/engine.c
${PN_PATH}/src/events/event.c
${PN_PATH}/src/transport/autodetect.c
${PN_PATH}/src/transport/transport.c
${PN_PATH}/src/message/message.c
${PN_PATH}/src/sasl/sasl.c
${PN_PATH}/src/sasl/none_sasl.c
${PN_PATH}/src/messenger/messenger.c
${PN_PATH}/src/messenger/subscription.c
${PN_PATH}/src/messenger/store.c
${PN_PATH}/src/messenger/transform.c
${PN_PATH}/src/selectable.c
${CMAKE_CURRENT_BINARY_DIR}/encodings.h
${CMAKE_CURRENT_BINARY_DIR}/protocol.h
)
set_source_files_properties(
${qpid-proton-core}
PROPERTIES
COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_LANGUAGE_FLAGS}"
)
set_source_files_properties(
${qpid-proton-platform}
PROPERTIES
COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}"
COMPILE_DEFINITIONS "${PLATFORM_DEFINITIONS}"
)
# Compile Proton into LLVM bitcode which will be used later in the linking stage
# of add_executable for compilation into Javascript.
add_library(
qpid-proton-bitcode SHARED
${qpid-proton-core}
${qpid-proton-platform}
)
set_target_properties(
qpid-proton-bitcode
PROPERTIES
VERSION "${PN_LIB_SOMAJOR}.${PN_LIB_SOMINOR}"
SOVERSION "${PN_LIB_SOMAJOR}"
LINK_FLAGS "${CATCH_UNDEFINED}"
)
target_link_libraries(qpid-proton-bitcode)
# Compile the send-async.c and recv-async.c examples into JavaScript
add_executable(send-async.js ${PN_PATH}/../examples/c/messenger/send-async.c)
target_link_libraries(send-async.js qpid-proton-bitcode)
add_executable(recv-async.js ${PN_PATH}/../examples/c/messenger/recv-async.c)
target_link_libraries(recv-async.js qpid-proton-bitcode)
set_target_properties(
send-async.js recv-async.js
PROPERTIES
COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}"
RUNTIME_OUTPUT_DIRECTORY examples
DEPENDS ws
LINK_FLAGS "-s \"WEBSOCKET_SUBPROTOCOL='AMQPWSB10'\" -${EMSCRIPTEN_LINK_OPTIMISATIONS}"
)
# Build the main JavaScript library called proton-messenger.js
add_executable(proton-messenger.js binding.c)
target_link_libraries(proton-messenger.js qpid-proton-bitcode ${UUID_LIB})
set_target_properties(
proton-messenger.js
PROPERTIES
COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}"
# The --memory-init-file 0 stops emscripten emitting a separate memory
# initialization file, if this was enabled it makes packaging harder as
# applications would expect proton-messenger.js.mem to be served too. It's even
# more fiddly with node.js packages. This behaviour might be reinstated if the
# packaging mechanism improves.
LINK_FLAGS "-s \"EXPORT_NAME='proton'\" -s \"WEBSOCKET_SUBPROTOCOL='AMQPWSB10'\" ${EMSCRIPTEN_LINK_OPTIMISATIONS} --memory-init-file 0 --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/binding-open.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/module.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/error.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/messenger.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/subscription.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/message.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-uuid.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-symbol.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-described.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-array.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-typed-number.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-long.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-binary.js --post-js ${CMAKE_CURRENT_SOURCE_DIR}/binding-close.js -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=\"[]\" -s EXPORTED_FUNCTIONS=\"['_pn_get_version_major', '_pn_get_version_minor', '_pn_uuid_generate', '_pn_bytes', '_pn_error_text', '_pn_code', '_pn_messenger', '_pn_messenger_name', '_pn_messenger_set_blocking', '_pn_messenger_free', '_pn_messenger_errno', '_pn_messenger_error', '_pn_messenger_get_outgoing_window', '_pn_messenger_set_outgoing_window', '_pn_messenger_get_incoming_window', '_pn_messenger_set_incoming_window', '_pn_messenger_start', '_pn_messenger_stop', '_pn_messenger_stopped', '_pn_messenger_subscribe', '_pn_messenger_put', '_pn_messenger_status', '_pn_messenger_buffered', '_pn_messenger_settle', '_pn_messenger_outgoing_tracker', '_pn_messenger_recv', '_pn_messenger_receiving', '_pn_messenger_get', '_pn_messenger_incoming_tracker', '_pn_messenger_incoming_subscription', '_pn_messenger_accept', '_pn_messenger_reject', '_pn_messenger_outgoing', '_pn_messenger_incoming', '_pn_messenger_route', '_pn_messenger_rewrite', '_pn_messenger_set_passive', '_pn_messenger_selectable', '_pn_subscription_get_context', '_pn_subscription_set_context', '_pn_subscription_address', '_pn_message', '_pn_message_id', '_pn_message_correlation_id', '_pn_message_free', '_pn_message_errno', '_pn_message_error', '_pn_message_clear', '_pn_message_is_inferred', '_pn_message_set_inferred', '_pn_message_is_durable', '_pn_message_set_durable', '_pn_message_get_priority', '_pn_message_set_priority', '_pn_message_get_ttl', '_pn_message_set_ttl', '_pn_message_is_first_acquirer', '_pn_message_set_first_acquirer', '_pn_message_get_delivery_count', '_pn_message_set_delivery_count', '_pn_message_get_user_id', '_pn_message_set_user_id', '_pn_message_get_address', '_pn_message_set_address', '_pn_message_get_subject', '_pn_message_set_subject', '_pn_message_get_reply_to', '_pn_message_set_reply_to', '_pn_message_get_content_type', '_pn_message_set_content_type', '_pn_message_get_content_encoding', '_pn_message_set_content_encoding', '_pn_message_get_expiry_time', '_pn_message_set_expiry_time', '_pn_message_get_creation_time', '_pn_message_set_creation_time', '_pn_message_get_group_id', '_pn_message_set_group_id', '_pn_message_get_group_sequence', '_pn_message_set_group_sequence', '_pn_message_get_reply_to_group_id', '_pn_message_set_reply_to_group_id', '_pn_message_encode', '_pn_message_decode', '_pn_message_instructions', '_pn_message_annotations', '_pn_message_properties', '_pn_message_body', '_pn_data', '_pn_data_free', '_pn_data_error', '_pn_data_errno', '_pn_data_clear', '_pn_data_rewind', '_pn_data_next', '_pn_data_prev', '_pn_data_enter', '_pn_data_exit', '_pn_data_lookup', '_pn_data_narrow', '_pn_data_widen', '_pn_data_type', '_pn_data_encode', '_pn_data_decode', '_pn_data_put_list', '_pn_data_put_map', '_pn_data_put_array', '_pn_data_put_described', '_pn_data_put_null', '_pn_data_put_bool', '_pn_data_put_ubyte', '_pn_data_put_byte', '_pn_data_put_ushort', '_pn_data_put_short', '_pn_data_put_uint', '_pn_data_put_int', '_pn_data_put_char', '_pn_data_put_ulong', '_pn_data_put_long', '_pn_data_put_timestamp', '_pn_data_put_float', '_pn_data_put_double', '_pn_data_put_decimal32', '_pn_data_put_decimal64', '_pn_data_put_decimal128', '_pn_data_put_uuid', '_pn_data_put_binary', '_pn_data_put_string', '_pn_data_put_symbol', '_pn_data_get_list', '_pn_data_get_map', '_pn_data_get_array', '_pn_data_is_array_described', '_pn_data_get_array_type', '_pn_data_is_described', '_pn_data_is_null', '_pn_data_get_bool', '_pn_data_get_ubyte', '_pn_data_get_byte', '_pn_data_get_ushort', '_pn_data_get_short', '_pn_data_get_uint', '_pn_data_get_int', '_pn_data_get_char', '_pn_data_get_ulong', '_pn_data_get_long', '_pn_data_get_timestamp', '_pn_data_get_float', '_pn_data_get_double', '_pn_data_get_decimal32', '_pn_data_get_decimal64', '_pn_data_get_decimal128', '_pn_data_get_uuid', '_pn_data_get_binary', '_pn_data_get_string', '_pn_data_get_symbol', '_pn_data_copy', '_pn_data_format', '_pn_data_dump', '_pn_selectable_readable', '_pn_selectable_is_reading', '_pn_selectable_writable', '_pn_selectable_is_writing', '_pn_selectable_is_terminal', '_pn_selectable_get_fd', '_pn_selectable_free']\""
)
# This command packages up the compiled proton-messenger.js into a node.js package
# called qpid-proton-messenger and copies it to the <proton>/node_modules directory.
# This allows the node.js test and example programs to do:
# var proton = require("qpid-proton-messenger");
add_custom_command(
TARGET proton-messenger.js
COMMAND ${CMAKE_COMMAND}
-E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/qpid-proton-messenger
${PROJECT_SOURCE_DIR}/node_modules/qpid-proton-messenger
COMMAND ${CMAKE_COMMAND}
-E copy
${CMAKE_CURRENT_BINARY_DIR}/proton-messenger.js
${PROJECT_SOURCE_DIR}/node_modules/qpid-proton-messenger/lib
COMMENT "Building qpid-proton-messenger package for node.js"
)
# The cleanall target removes the qpid-proton-messenger package from <proton>/node_modules
add_custom_target(
cleanall
COMMAND echo "CLEAN NODE MODULES"
COMMAND ${CMAKE_COMMAND}
-E remove_directory
${PROJECT_SOURCE_DIR}/node_modules/qpid-proton-messenger
)
# If the docs target is specified and the jsdoc3 package for node.js has been
# installed then build the JavaScript API documentation.
if (NODE_JSDOC_FOUND)
set(JSDOC_EXE ${PROJECT_SOURCE_DIR}/node_modules/.bin/jsdoc)
message(STATUS "Documentation Enabled. Using ${JSDOC_EXE} to build JavaScript docs")
add_custom_target(docs-js COMMAND ${JSDOC_EXE}
-d ${CMAKE_CURRENT_BINARY_DIR}/html
${CMAKE_CURRENT_SOURCE_DIR}/module.js
${CMAKE_CURRENT_SOURCE_DIR}/error.js
${CMAKE_CURRENT_SOURCE_DIR}/messenger.js
${CMAKE_CURRENT_SOURCE_DIR}/subscription.js
${CMAKE_CURRENT_SOURCE_DIR}/message.js
${CMAKE_CURRENT_SOURCE_DIR}/data.js
${CMAKE_CURRENT_SOURCE_DIR}/data-uuid.js
${CMAKE_CURRENT_SOURCE_DIR}/data-symbol.js
${CMAKE_CURRENT_SOURCE_DIR}/data-described.js
${CMAKE_CURRENT_SOURCE_DIR}/data-array.js
${CMAKE_CURRENT_SOURCE_DIR}/data-typed-number.js
${CMAKE_CURRENT_SOURCE_DIR}/data-long.js
${CMAKE_CURRENT_SOURCE_DIR}/data-binary.js)
add_dependencies(docs docs-js)
endif (NODE_JSDOC_FOUND)