diff --git a/CMakeLists.txt b/CMakeLists.txt
index be5d3928..41cd0da0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -50,6 +50,8 @@ message(STATUS "Building xeus-cpp v${${PROJECT_NAME}_VERSION}")
# Build options
# =============
+# Note: XEUS_SEARCH_PATH is now handled at runtime through environment variables
+
option(XEUS_CPP_BUILD_STATIC "Build xeus-cpp static library" ON)
option(XEUS_CPP_BUILD_SHARED "Split xcpp build into executable and library" ON)
option(XEUS_CPP_BUILD_EXECUTABLE "Build the xcpp executable" ON)
@@ -404,6 +406,7 @@ endif()
# =====
if(XEUS_CPP_BUILD_TESTS)
+ enable_testing()
add_subdirectory(test)
endif()
diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp
index f099b688..93aee3a0 100644
--- a/src/xinterpreter.cpp
+++ b/src/xinterpreter.cpp
@@ -15,6 +15,10 @@
#include "xeus-cpp/xinterpreter.hpp"
#include "xeus-cpp/xmagics.hpp"
+#include // for std::strlen
+#include // for std::istringstream
+#include // for std::getline
+
#include "xinput.hpp"
#include "xinspect.hpp"
#ifndef EMSCRIPTEN
@@ -26,43 +30,62 @@
using Args = std::vector;
-void* createInterpreter(const Args &ExtraArgs = {}) {
- Args ClangArgs = {/*"-xc++"*/"-v"}; // ? {"-Xclang", "-emit-llvm-only", "-Xclang", "-diagnostic-log-file", "-Xclang", "-", "-xc++"};
- if (std::find_if(ExtraArgs.begin(), ExtraArgs.end(), [](const std::string& s) {
- return s == "-resource-dir";}) == ExtraArgs.end()) {
- std::string resource_dir = Cpp::DetectResourceDir();
- if (resource_dir.empty())
- std::cerr << "Failed to detect the resource-dir\n";
- ClangArgs.push_back("-resource-dir");
- ClangArgs.push_back(resource_dir.c_str());
- }
- std::vector CxxSystemIncludes;
- Cpp::DetectSystemCompilerIncludePaths(CxxSystemIncludes);
- for (const std::string& CxxInclude : CxxSystemIncludes) {
- ClangArgs.push_back("-isystem");
- ClangArgs.push_back(CxxInclude.c_str());
- }
- ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end());
- // FIXME: We should process the kernel input options and conditionally pass
- // the gpu args here.
- return Cpp::CreateInterpreter(ClangArgs/*, {"-cuda"}*/);
+void* createInterpreter(const Args& ExtraArgs = {})
+{
+ Args ClangArgs = {/*"-xc++"*/ "-v"}; // ? {"-Xclang", "-emit-llvm-only", "-Xclang",
+ // "-diagnostic-log-file", "-Xclang", "-", "-xc++"};
+ if (std::find_if(
+ ExtraArgs.begin(),
+ ExtraArgs.end(),
+ [](const std::string& s)
+ {
+ return s == "-resource-dir";
+ }
+ )
+ == ExtraArgs.end())
+ {
+ std::string resource_dir = Cpp::DetectResourceDir();
+ if (resource_dir.empty())
+ {
+ std::cerr << "Failed to detect the resource-dir\n";
+ }
+ ClangArgs.push_back("-resource-dir");
+ ClangArgs.push_back(resource_dir.c_str());
+ }
+ std::vector CxxSystemIncludes;
+ Cpp::DetectSystemCompilerIncludePaths(CxxSystemIncludes);
+ for (const std::string& CxxInclude : CxxSystemIncludes)
+ {
+ ClangArgs.push_back("-isystem");
+ ClangArgs.push_back(CxxInclude.c_str());
+ }
+ ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end());
+ // FIXME: We should process the kernel input options and conditionally pass
+ // the gpu args here.
+ return Cpp::CreateInterpreter(ClangArgs /*, {"-cuda"}*/);
}
using namespace std::placeholders;
namespace xcpp
{
- struct StreamRedirectRAII {
- std::string &err;
- StreamRedirectRAII(std::string &e) : err(e) {
- Cpp::BeginStdStreamCapture(Cpp::kStdErr);
- Cpp::BeginStdStreamCapture(Cpp::kStdOut);
- }
- ~StreamRedirectRAII() {
- std::string out = Cpp::EndStdStreamCapture();
- err = Cpp::EndStdStreamCapture();
- std::cout << out;
- }
+ struct StreamRedirectRAII
+ {
+ std::string& err;
+
+ StreamRedirectRAII(std::string& e)
+ : err(e)
+ {
+ Cpp::BeginStdStreamCapture(Cpp::kStdErr);
+ Cpp::BeginStdStreamCapture(Cpp::kStdOut);
+ }
+
+ ~StreamRedirectRAII()
+ {
+ std::string out = Cpp::EndStdStreamCapture();
+ err = Cpp::EndStdStreamCapture();
+ std::cout << out;
+ }
};
void interpreter::configure_impl()
@@ -98,15 +121,14 @@ __get_cxx_version ()
return std::to_string(cxx_version);
}
-
- interpreter::interpreter(int argc, const char* const* argv) :
- xmagics()
+ interpreter::interpreter(int argc, const char* const* argv)
+ : xmagics()
, p_cout_strbuf(nullptr)
, p_cerr_strbuf(nullptr)
, m_cout_buffer(std::bind(&interpreter::publish_stdout, this, _1))
, m_cerr_buffer(std::bind(&interpreter::publish_stderr, this, _1))
{
- //NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic)
createInterpreter(Args(argv ? argv + 1 : argv, argv + argc));
m_version = get_stdopt();
redirect_output();
@@ -210,10 +232,11 @@ __get_cxx_version ()
//
// JupyterLab displays the "{ename}: {evalue}" if the traceback is
// empty.
- if (evalue.size() < 4) {
+ if (evalue.size() < 4)
+ {
ename = " ";
}
- std::vector traceback({ename + evalue});
+ std::vector traceback({ename + evalue});
if (!config.silent)
{
publish_execution_error(ename, evalue, traceback);
@@ -256,7 +279,8 @@ __get_cxx_version ()
Cpp::CodeComplete(results, code.c_str(), 1, _cursor_pos + 1);
- return xeus::create_complete_reply(results /*matches*/,
+ return xeus::create_complete_reply(
+ results /*matches*/,
cursor_pos - to_complete.length() /*cursor_start*/,
cursor_pos /*cursor_end*/
);
@@ -277,13 +301,17 @@ __get_cxx_version ()
nl::json interpreter::is_complete_request_impl(const std::string& code)
{
- if (!code.empty() && code[code.size() - 1] == '\\') {
+ if (!code.empty() && code[code.size() - 1] == '\\')
+ {
auto found = code.rfind('\n');
if (found == std::string::npos)
+ {
found = -1;
+ }
auto found1 = found++;
- while (isspace(code[++found1])) ;
- return xeus::create_is_complete_reply("incomplete", code.substr(found, found1-found));
+ while (isspace(code[++found1]))
+ ;
+ return xeus::create_is_complete_reply("incomplete", code.substr(found, found1 - found));
}
return xeus::create_is_complete_reply("complete");
@@ -357,16 +385,38 @@ __get_cxx_version ()
void interpreter::init_includes()
{
+ // Add the standard include path
Cpp::AddIncludePath((xeus::prefix_path() + "/include/").c_str());
+
+ // Get include paths from environment variable
+ const char* non_standard_paths = std::getenv("XEUS_SEARCH_PATH");
+ if (!non_standard_paths)
+ {
+ non_standard_paths = "";
+ }
+
+ if (std::strlen(non_standard_paths) > 0)
+ {
+ // Split the paths by colon ':' and add each one
+ std::istringstream stream(non_standard_paths);
+ std::string path;
+ while (std::getline(stream, path, ':'))
+ {
+ if (!path.empty())
+ {
+ Cpp::AddIncludePath(path.c_str());
+ }
+ }
+ }
}
void interpreter::init_preamble()
{
- //NOLINTBEGIN(cppcoreguidelines-owning-memory)
+ // NOLINTBEGIN(cppcoreguidelines-owning-memory)
preamble_manager.register_preamble("introspection", std::make_unique());
preamble_manager.register_preamble("magics", std::make_unique());
preamble_manager.register_preamble("shell", std::make_unique());
- //NOLINTEND(cppcoreguidelines-owning-memory)
+ // NOLINTEND(cppcoreguidelines-owning-memory)
}
void interpreter::init_magic()
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8ef68992..e323a468 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -67,3 +67,21 @@ target_link_libraries(test_xeus_cpp xeus-cpp doctest::doctest ${CMAKE_THREAD_LIB
target_include_directories(test_xeus_cpp PRIVATE ${XEUS_CPP_INCLUDE_DIR})
add_custom_target(xtest COMMAND test_xeus_cpp DEPENDS test_xeus_cpp)
+
+# Test for non-standard include paths
+add_executable(test_include_paths test_include_paths.cpp)
+target_link_libraries(test_include_paths PRIVATE doctest::doctest)
+target_include_directories(test_include_paths PRIVATE
+ ${CMAKE_SOURCE_DIR}/test/custom_includes
+ ${DOCTEST_INCLUDE_DIR}
+)
+target_compile_definitions(test_include_paths PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN)
+add_test(NAME test_include_paths COMMAND test_include_paths)
+
+set(XEUS_SEARCH_PATH $,:>)
+
+if (NOT EMSCRIPTEN)
+ set(XEUS_SEARCH_PATH "${XEUS_SEARCH_PATH}:${CMAKE_CURRENT_SOURCE_DIR}/src")
+endif()
+
+target_compile_definitions(xeus-cpp PRIVATE "XEUS_SEARCH_PATH=\"${XEUS_SEARCH_PATH}\"")
\ No newline at end of file
diff --git a/test/custom_includes/test_header.hpp b/test/custom_includes/test_header.hpp
new file mode 100644
index 00000000..ad3354b1
--- /dev/null
+++ b/test/custom_includes/test_header.hpp
@@ -0,0 +1,8 @@
+#ifndef TEST_HEADER_HPP
+#define TEST_HEADER_HPP
+
+namespace test_ns {
+ constexpr int test_value = 42;
+}
+
+#endif
diff --git a/test/test_include_paths.cpp b/test/test_include_paths.cpp
new file mode 100644
index 00000000..b27bf670
--- /dev/null
+++ b/test/test_include_paths.cpp
@@ -0,0 +1,8 @@
+#include
+#include
+#include "test_header.hpp"
+
+TEST_CASE("Test non-standard include paths")
+{
+ CHECK(test_ns::test_value == 42);
+}