Skip to content

[SYCL] Import/preprocess boost/mp11 at build time to use in SYCL headers. #5791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion sycl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ configure_file("${version_header}.in" "${version_header}")
set(feature_header "${sycl_inc_dir}/CL/sycl/feature_test.hpp")
configure_file("${feature_header}.in" "${feature_header}")

include(AddBoostMp11Headers)

# This is workaround to detect changes (add or modify) in subtree which
# are not detected by copy_directory command.
# TODO: detect and process remove header/directory case
Expand All @@ -159,7 +161,8 @@ string(REPLACE "${sycl_inc_dir}" "${SYCL_INCLUDE_BUILD_DIR}"
add_custom_target(sycl-headers
DEPENDS ${OUT_HEADERS_IN_SYCL_DIR}
${OUT_HEADERS_IN_CL_DIR}
${OUT_HEADERS_IN_STD_DIR})
${OUT_HEADERS_IN_STD_DIR}
boost_mp11-headers)

add_custom_command(
OUTPUT ${OUT_HEADERS_IN_SYCL_DIR}
Expand All @@ -177,6 +180,7 @@ add_custom_command(
install(DIRECTORY "${sycl_inc_dir}/sycl" DESTINATION ${SYCL_INCLUDE_DIR} COMPONENT sycl-headers)
install(DIRECTORY "${sycl_inc_dir}/CL" DESTINATION ${SYCL_INCLUDE_DIR}/sycl COMPONENT sycl-headers)
install(DIRECTORY "${sycl_inc_dir}/std" DESTINATION ${SYCL_INCLUDE_DIR} COMPONENT sycl-headers)
install(DIRECTORY ${BOOST_MP11_DESTINATION_DIR} DESTINATION ${SYCL_INCLUDE_DIR}/sycl COMPONENT boost_mp11-headers)

set(SYCL_RT_LIBS sycl)
if (MSVC)
Expand Down Expand Up @@ -315,6 +319,7 @@ endif()
# Listed here are component names contributing the package
set( SYCL_TOOLCHAIN_DEPLOY_COMPONENTS
append-file
boost_mp11-headers
clang
clang-offload-wrapper
clang-offload-bundler
Expand Down
55 changes: 55 additions & 0 deletions sycl/cmake/modules/AddBoostMp11Headers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# boost/mp11 headers import and preprocessing
# See more comments in cmake/modules/PreprocessBoostMp11Headers.cmake

include(FetchContent)

set(BOOST_MP11_GIT_REPO https://github.com/boostorg/mp11.git)
# Author: pdimov
# Date: Feb 16, 2022
# Comment:
# Merge pull request #71 from grisumbras/feature/mp_valid_and_true
set(BOOST_MP11_GIT_TAG 7bc4e1ae9b36ec8ee635c3629b59ec525bbe82b9)

# Either download from github or use existing if BOOST_MP11_SOURCE_DIR is set
if (NOT DEFINED BOOST_MP11_SOURCE_DIR)
message(STATUS "BOOST_MP11_SOURCE_DIR not set, downloading boost/mp11 headers from ${BOOST_MP11_GIT_REPO}")

FetchContent_Declare(boost_mp11
GIT_REPOSITORY ${BOOST_MP11_GIT_REPO}
GIT_TAG ${BOOST_MP11_GIT_TAG}
)
FetchContent_GetProperties(boost_mp11)
FetchContent_MakeAvailable(boost_mp11)

set(BOOST_MP11_SOURCE_DIR ${boost_mp11_SOURCE_DIR})
set(BOOST_MP11_SRC_PATH ${BOOST_MP11_GIT_REPO})
set(BOOST_MP11_SRC_ID "git commit hash: ${BOOST_MP11_GIT_TAG}")
else (NOT DEFINED BOOST_MP11_SOURCE_DIR)
message(STATUS "Using boost/mp11 headers from ${BOOST_MP11_SOURCE_DIR}")
set(BOOST_MP11_SRC_PATH ${BOOST_MP11_SOURCE_DIR})
set(BOOST_MP11_SRC_ID "ID not set")
endif(NOT DEFINED BOOST_MP11_SOURCE_DIR)

# Read all header file names into HEADERS_BOOST_MP11
file(GLOB_RECURSE HEADERS_BOOST_MP11 CONFIGURE_DEPENDS "${BOOST_MP11_SOURCE_DIR}/include/boost/*")

set(BOOST_MP11_DESTINATION_DIR ${SYCL_INCLUDE_BUILD_DIR}/sycl/detail/boost)
string(REPLACE "${BOOST_MP11_SOURCE_DIR}/include/boost" "${BOOST_MP11_DESTINATION_DIR}"
OUT_HEADERS_BOOST_MP11 "${HEADERS_BOOST_MP11}")

# The target which produces preprocessed boost/mp11 headers
add_custom_target(boost_mp11-headers
DEPENDS ${OUT_HEADERS_BOOST_MP11})

# Run preprocessing on each header, output result into
# ${BOOST_MP11_DESTINATION_DIR}
add_custom_command(
OUTPUT ${OUT_HEADERS_BOOST_MP11}
DEPENDS ${HEADERS_BOOST_MP11}
COMMAND ${CMAKE_COMMAND}
-DIN=${BOOST_MP11_SOURCE_DIR}/include/boost
-DOUT=${BOOST_MP11_DESTINATION_DIR}
-DSRC_PATH="${BOOST_MP11_SRC_PATH}"
-DSRC_ID="${BOOST_MP11_SRC_ID}"
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/PreprocessBoostMp11Headers.cmake
COMMENT "Preprocessing boost/mp11 headers ${BOOST_MP11_SOURCE_DIR}/include/boost -> ${BOOST_MP11_DESTINATION_DIR}...")
106 changes: 106 additions & 0 deletions sycl/cmake/modules/PreprocessBoostMp11Headers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Preprocess boost/mp11 headers to allow using specific version of mp11 (as
# defined in these project's cmake files) within SYCL library w/o risk of
# conflict with user program using another version of boost/mp11. Basically,
# this transformation moves all APIs from boost namespace to sycl::boost and
# adds SYCL_ prefix to boost macros names. See more specific comments in the
# code below.
# Variables which must be set by the caller to control behavior of this module:
# - IN
# The source directory with mp11 headers, must contain mp11.h.
# - OUT
# The destination directory where preprocessed source headers are put.
# - SRC_PATH
# An URL or directory name the source directory originates from. Used only in
# generated README text.
# - SRC_ID
# Git hash/tag or other ID identifying the original source. Used only in
# generated README text.
#
# Assumed to be invoked as a script:
# ${CMAKE_COMMAND} -DIN=... -DOUT=... -DSRC_PATH=... -DSRC_ID=... -P <this file>

function(preprocess_mp11_header)
cmake_parse_arguments(
MP11_HDR # prefix
"" # options
"SRC_NAME;DST_NAME;IN_DIR" # one value keywords
"" # multi-value keywords
${ARGN}) # arguments
file(READ ${MP11_HDR_SRC_NAME} FILE_CONTENTS)

# 1) replace `BOOST_*` macros with `SYCL_BOOST_*`.
string(REGEX REPLACE
"([ \t\n\r!])BOOST_"
"\\1SYCL_DETAIL_BOOST_"
FILE_CONTENTS "${FILE_CONTENTS}")
# 2) replace `namespace boost { ... }` with
# `namespace sycl { namespace detail { namespace boost { ... } } }`
string(REGEX REPLACE
"(\n[ \t]*namespace[ \t\n\r]+boost)"
"namespace sycl\n{\nnamespace detail\n{\\1"
FILE_CONTENTS "${FILE_CONTENTS}")
# ... use '} // namespace boost' as a marker for end-of-scope '}' replacement
string(REGEX REPLACE
"(\n[ \t]*}[ \t]*//[ \t]*namespace[ \t]+boost[ \t]*\n)"
"\\1} // namespace detail\n} // namespace sycl\n"
FILE_CONTENTS "${FILE_CONTENTS}")
# 3) replace `boost` in `#include <boost/...>` or `#include "boost/..."` with
# `sycl/detail/boost`
string(REGEX REPLACE
"(\n#include[ \t]*[<\"])boost"
"\\1sycl/detail/boost"
FILE_CONTENTS "${FILE_CONTENTS}")

set(SYCL_DERIVED_COPYRIGHT_NOTICE "\
// -*- C++ -*-\n\
//===----------------------------------------------------------------------===//\n\
// Modifications Copyright Intel Corporation 2022\n\
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\
//===----------------------------------------------------------------------===//\n\
// Auto-generated from boost/mp11 sources https://github.com/boostorg/mp11\n\n")

# 4) add proper copyright notice atop
string(PREPEND FILE_CONTENTS ${SYCL_DERIVED_COPYRIGHT_NOTICE})
file(WRITE ${MP11_HDR_DST_NAME} "${FILE_CONTENTS}")
endfunction(preprocess_mp11_header)

function(preprocess_mp11_headers)
cmake_parse_arguments(
MP11_HDRS # prefix
"" # options
"IN;OUT;SRC_PATH;SRC_ID" # one value keywords
"" # multi-value keywords
${ARGN}) # arguments

# 1) Perform necessary preprocessing of headers.
file(GLOB_RECURSE BOOST_MP11_SOURCES "${MP11_HDRS_IN}/*")

foreach(SRC ${BOOST_MP11_SOURCES})
string(REPLACE "${MP11_HDRS_IN}" "${MP11_HDRS_OUT}" DST "${SRC}")
preprocess_mp11_header(
SRC_NAME ${SRC}
DST_NAME ${DST}
IN_DIR ${MP11_HDRS_IN}
)
endforeach(SRC ${BOOST_MP11_SOURCES})

# 2) Add SYCL_README.txt to the output directory root
set(SYCL_README_TEXT "\
This directory contains boost/mp11 headers imported from\n\
${MP11_HDRS_SRC_PATH} (${MP11_HDRS_SRC_ID})\n\
and adapted for use in SYCL headers in a way that does not conflict with\n\
potential use of boost in user code. Particularly, `BOOST_*` macros are\n\
replaced with `SYCL_DETAIL_BOOST_*`, APIs are moved into the top-level
`sycl::detail` namespace. For example, `sycl::detail::boost::mp11::mp_list`.\n")

set(SYCL_README_FILE_NAME "${MP11_HDRS_OUT}/README.txt")

file(WRITE ${SYCL_README_FILE_NAME} "${SYCL_README_TEXT}")
endfunction(preprocess_mp11_headers)

preprocess_mp11_headers(
IN ${IN}
OUT ${OUT}
SRC_PATH ${SRC_PATH}
SRC_ID ${SRC_ID}
)
48 changes: 48 additions & 0 deletions sycl/test/basic_tests/boost_mp11_import_sanity_check.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
// Modifications Copyright Intel Corporation 2022
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//===----------------------------------------------------------------------===//
// Based on boost/mp11 tests obtained from
// https://github.com/boostorg/mp11/blob/develop/test/mp_fill.cpp
// (git commit a231733).

//===----------------------------------------------------------------------===//
// Copyright 2015 Peter Dimov.
//
// Distributed under the Boost Software License, Version 1.0.
//
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//===----------------------------------------------------------------------===//

// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple -c %s

// This is a sanity check test to verify that the automatic boost/mp11 import
// into SYCL is not badly broken.

#include <type_traits>

#include <sycl/detail/boost/mp11.hpp>

struct X1 {};

int main() {
using sycl::detail::boost::mp11::mp_fill;
using sycl::detail::boost::mp11::mp_list;

using L1 = mp_list<int, void(), float[]>;
static_assert(std::is_same_v<mp_fill<L1, X1>, mp_list<X1, X1, X1>>);

//

using L2 = std::tuple<int, char, float>;
static_assert(std::is_same_v<mp_fill<L2, X1>, std::tuple<X1, X1, X1>>);

//

using L3 = std::pair<char, double>;
static_assert(std::is_same_v<mp_fill<L3, X1>, std::pair<X1, X1>>);

return 0;
}