diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cb93149b..85121d1f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,31 +9,43 @@ jobs:
fail-fast: false
matrix:
platform:
- # TODO: Build on more platforms
- os: macos-latest
cc: clang
cxx: clang++
type: static
+ backend: JavaScriptCore
- os: macos-latest
cc: clang
cxx: clang++
type: shared
+ backend: JavaScriptCore
+ # TODO(RaisinTen): Support other OSs and static configuration for
+ # the V8 backend.
+ - os: macos-latest
+ cc: clang
+ cxx: clang++
+ type: shared
+ backend: V8
- os: ubuntu-latest
cc: clang
cxx: clang++
type: static
+ backend: JavaScriptCore
- os: ubuntu-latest
cc: gcc
cxx: g++
type: static
+ backend: JavaScriptCore
- os: ubuntu-latest
cc: clang
cxx: clang++
type: shared
+ backend: JavaScriptCore
- os: ubuntu-latest
cc: gcc
cxx: g++
type: shared
+ backend: JavaScriptCore
# Sanitizers
- os: ubuntu-latest
@@ -41,21 +53,25 @@ jobs:
cxx: clang++
type: static
options: -DINCLUDEJS_ADDRESS_SANITIZER:BOOL=ON
+ backend: JavaScriptCore
- os: ubuntu-latest
cc: clang
cxx: clang++
type: static
options: -DINCLUDEJS_UNDEFINED_SANITIZER:BOOL=ON
+ backend: JavaScriptCore
- os: macos-latest
cc: clang
cxx: clang++
type: static
options: -DINCLUDEJS_ADDRESS_SANITIZER:BOOL=ON
+ backend: JavaScriptCore
- os: macos-latest
cc: clang
cxx: clang++
type: static
options: -DINCLUDEJS_UNDEFINED_SANITIZER:BOOL=ON
+ backend: JavaScriptCore
runs-on: ${{ matrix.platform.os }}
env:
@@ -88,7 +104,7 @@ jobs:
run: >
cmake -S . -B ./build
-DCMAKE_BUILD_TYPE:STRING=Release
- -DINCLUDE_BACKEND:STRING=JavaScriptCore
+ -DINCLUDEJS_BACKEND:STRING=${{ matrix.platform.backend }}
-DINCLUDEJS_TESTS:BOOL=ON
-DINCLUDEJS_DOCS:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
@@ -99,7 +115,7 @@ jobs:
run: >
cmake -S . -B ./build
-DCMAKE_BUILD_TYPE:STRING=Release
- -DINCLUDE_BACKEND:STRING=JavaScriptCore
+ -DINCLUDEJS_BACKEND:STRING=${{ matrix.platform.backend }}
-DINCLUDEJS_TESTS:BOOL=ON
-DINCLUDEJS_DOCS:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=ON
@@ -109,10 +125,10 @@ jobs:
- run: cmake --build ./build --config Release --parallel 4
- run: >
cmake --install ./build --prefix ./build/dist --config Release --verbose
- --component sourcemeta_includejs
+ --component includejs
- run: >
cmake --install ./build --prefix ./build/dist --config Release --verbose
- --component sourcemeta_includejs_dev
+ --component includejs_dev
# Not every CTest version supports the --test-dir option. If such option
# is not recognized, `ctest` will successfully exit finding no tests.
diff --git a/Brewfile b/Brewfile
index 97725f39..92ae3cd0 100644
--- a/Brewfile
+++ b/Brewfile
@@ -1,3 +1,4 @@
brew "cmake"
brew "clang-format"
brew "doxygen"
+brew "v8"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ffa8cdfa..297f612a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,7 +31,7 @@ install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
- COMPONENT sourcemeta_includejs_dev)
+ COMPONENT includejs_dev)
if(INCLUDEJS_ENGINE)
add_subdirectory(src/engine)
diff --git a/DEPENDENCIES b/DEPENDENCIES
index 59d4fdc7..b8e7575f 100644
--- a/DEPENDENCIES
+++ b/DEPENDENCIES
@@ -1,3 +1,3 @@
vendorpull https://github.com/sourcemeta/vendorpull 70342aaf458e6cb80baeb5b718901075fc42ede6
-noa https://github.com/sourcemeta/noa 653bda26413812241e503fd0b550a66f2df4700f
+noa https://github.com/sourcemeta/noa 9da0f1877859f60e439e31856cd4ef5fd4ca6063
googletest https://github.com/google/googletest 987e225614755fec7253aa95bf959c09e0d380d7
diff --git a/Makefile b/Makefile
index 5854a76e..27ee24da 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ CTEST = ctest
# Options
PRESET = Debug
SHARED = OFF
+BACKEND = JavaScriptCore
all: configure compile test
@@ -12,7 +13,7 @@ configure: .always
$(CMAKE) -S . -B ./build \
-DCMAKE_BUILD_TYPE:STRING=$(PRESET) \
-DCMAKE_COMPILE_WARNING_AS_ERROR:BOOL=ON \
- -DINCLUDE_BACKEND:STRING=JavaScriptCore \
+ -DINCLUDEJS_BACKEND:STRING=$(BACKEND) \
-DINCLUDEJS_ENGINE:BOOL=ON \
-DINCLUDEJS_TESTS:BOOL=ON \
-DINCLUDEJS_DOCS:BOOL=ON \
@@ -22,9 +23,9 @@ compile: .always
$(CMAKE) --build ./build --config $(PRESET) --target clang_format
$(CMAKE) --build ./build --config $(PRESET) --parallel 4
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
- --component sourcemeta_includejs
+ --component includejs
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
- --component sourcemeta_includejs_dev
+ --component includejs_dev
lint: .always
$(CMAKE) --build ./build --config $(PRESET) --target clang_tidy
diff --git a/README.markdown b/README.markdown
index 411284b5..a1db321a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1 +1,90 @@
-![IncludeJS](./assets/banner.png)
+
+includejs
+Build your own JavasScript runtime
+
+
+
+
+
+
+ 📖 Documentation
+
+
+`includejs` offers a unified API for building your own JavaScript runtime, bridging multiple engines and platforms with ease.
+
+## Example
+
+In our journey to enhance `includejs`, we've also developed small - a minimalist, experimental runtime
+aimed to provide ground for concepts and features we're considering for it.
+
+Hosted on GitHub at [`crossnx/small`](https://github.com/crossnx/small).
+
+## Features
+
+- Multiple runtime support: `JavascriptCore`, `v8` (more in the furture)
+- Cross platform support
+- Direclty consumable from `CMake`
+
+> 🤙 Stay tuned as we continue to expand our feature set.
+
+### JavasScript
+
+| Feature | JavaScriptCore | V8 |
+|----------------|-------------------|------------------|
+| Boolean | ✅ Supported | ❌ Not Supported |
+| Number | ✅ Supported | ❌ Not Supported |
+| String | ✅ Supported | ❌ Not Supported |
+| Object | ✅ Supported | ❌ Not Supported |
+| Array | ✅ Supported | ❌ Not Supported |
+| Promise | ✅ Supported | ❌ Not Supported |
+| Function | ✅ Supported | ❌ Not Supported |
+| Error | ✅ Supported | ❌ Not Supported |
+| Class | ❌ Not Supported | ❌ Not Supported |
+| Set | ❌ Not Supported | ❌ Not Supported |
+| Map | ❌ Not Supported | ❌ Not Supported |
+| BigInt | ❌ Not Supported | ❌ Not Supported |
+| Symbol | ❌ Not Supported | ❌ Not Supported |
+| ArrayBuffer | ❌ Not Supported | ❌ Not Supported |
+| TypedArray | ❌ Not Supported | ❌ Not Supported |
+
+### WHATWG APIs
+
+- [ ] Timers
+- [ ] Console
+
+### Platform support
+
+| Platform | JavaScriptCore | V8 |
+|-----------|-------------------|------------------|
+| macOS | ✅ Supported | ✅ Supported |
+| Linux | ✅ Supported | ✅ Supported |
+| Windows | ❌ Not Supported | ❌ Not Supported |
+
+## Dependencies
+
+- C++
+- CMake
+- JavascriptCore
+- Brew (macOS)
+
+## Usage
+
+You can consume `includejs` just with CMake:
+
+```cmake
+cmake_minimum_required(VERSION 3.18)
+
+project(your_runtime VERSION 0.0.1 LANGUAGES CXX)
+
+include(FetchContent)
+FetchContent_Declare(
+ includejs
+ GIT_REPOSITORY https://github.com/crossnx/includejs
+ GIT_TAG main
+ DOWNLOAD_EXTRACT_TIMESTAMP NO)
+FetchContent_MakeAvailable(includejs)
+
+add_executable(your_runtime main.cc)
+target_link_libraries(your_runtime PRIVATE includejs::engine)
+```
+
diff --git a/assets/banner.png b/assets/banner.png
old mode 100644
new mode 100755
index adca42b3..d7d8ff46
Binary files a/assets/banner.png and b/assets/banner.png differ
diff --git a/assets/logo.png b/assets/logo.png
old mode 100644
new mode 100755
index 5983dad6..faca564d
Binary files a/assets/logo.png and b/assets/logo.png differ
diff --git a/assets/logo.svg b/assets/logo.svg
old mode 100644
new mode 100755
index 4c162864..285f8676
--- a/assets/logo.svg
+++ b/assets/logo.svg
@@ -1,21 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/cmake/CompilerOptions.cmake b/cmake/CompilerOptions.cmake
index 918ec110..8db8a300 100644
--- a/cmake/CompilerOptions.cmake
+++ b/cmake/CompilerOptions.cmake
@@ -1,4 +1,4 @@
-function(sourcemeta_includejs_add_compile_options target)
+function(includejs_add_compile_options target)
if(NOA_COMPILER_MSVC)
# See https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category
target_compile_options("${target}" PRIVATE
diff --git a/cmake/FindV8.cmake b/cmake/FindV8.cmake
new file mode 100644
index 00000000..eb6cb15f
--- /dev/null
+++ b/cmake/FindV8.cmake
@@ -0,0 +1,27 @@
+if(NOT V8_FOUND)
+ if(APPLE)
+ add_library(v8 INTERFACE IMPORTED)
+ # Since the "includejs" library is compiled with the `-isysroot` flag of
+ # Clang, it is unable to find the V8 library and headers which have been
+ # downloaded from Homebrew.
+ # Refs: https://github.com/Homebrew/homebrew-core/issues/45061#issuecomment-541420664
+ set(V8_VERSION "12.1.285.24")
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+ set(HOMEBREW_CELLAR "/opt/homebrew/Cellar")
+ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ set(HOMEBREW_CELLAR "/usr/local/Cellar")
+ endif()
+ target_include_directories(v8
+ INTERFACE "${HOMEBREW_CELLAR}/v8/${V8_VERSION}/include")
+ target_link_directories(v8
+ INTERFACE "${HOMEBREW_CELLAR}/v8/${V8_VERSION}/lib")
+ target_link_libraries(v8 INTERFACE "-lv8")
+ target_link_libraries(v8 INTERFACE "-lv8_libplatform")
+ target_compile_definitions(v8
+ INTERFACE V8_COMPRESS_POINTERS)
+ target_compile_definitions(v8
+ INTERFACE V8_ENABLE_SANDBOX)
+ add_library(V8::V8 ALIAS v8)
+ set(V8_FOUND ON)
+ endif()
+endif()
diff --git a/config.cmake.in b/config.cmake.in
index 821612ad..17dce9c9 100644
--- a/config.cmake.in
+++ b/config.cmake.in
@@ -7,11 +7,13 @@ if(NOT INCLUDEJS_COMPONENTS)
list(APPEND INCLUDEJS_COMPONENTS engine)
endif()
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
+
foreach(component ${INCLUDEJS_COMPONENTS})
if(component STREQUAL "engine")
include(CMakeFindDependencyMacro)
find_dependency(@INCLUDEJS_BACKEND@)
- include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_includejs_engine.cmake")
+ include("${CMAKE_CURRENT_LIST_DIR}/includejs_engine.cmake")
else()
message(FATAL_ERROR "Unknown IncludeJS component: ${component}")
endif()
diff --git a/doxygen/logo.png b/doxygen/logo.png
old mode 100644
new mode 100755
index bfe3b9d9..a681d326
Binary files a/doxygen/logo.png and b/doxygen/logo.png differ
diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt
index 2cbb7cc5..e54272d3 100644
--- a/src/engine/CMakeLists.txt
+++ b/src/engine/CMakeLists.txt
@@ -8,25 +8,46 @@ if(INCLUDEJS_BACKEND STREQUAL "JavaScriptCore")
list(APPEND INCLUDEJS_ENGINE_SOURCES javascript_core/engine_context.cc)
list(APPEND INCLUDEJS_ENGINE_SOURCES javascript_core/engine_promise.cc)
list(APPEND INCLUDEJS_ENGINE_SOURCES javascript_core/engine_value.cc)
+elseif(INCLUDEJS_BACKEND STREQUAL "V8")
+ list(APPEND INCLUDEJS_ENGINE_SOURCES v8/engine.cc)
else()
message(FATAL_ERROR "Unknown IncludeJS backend: ${INCLUDEJS_BACKEND}")
endif()
-noa_library(NAMESPACE sourcemeta PROJECT includejs NAME engine
+noa_library(PROJECT includejs NAME engine
FOLDER "IncludeJS/Engine"
PRIVATE_HEADERS context.h function.h error.h promise.h value.h
SOURCES ${INCLUDEJS_ENGINE_SOURCES})
+
if(INCLUDEJS_INSTALL)
- noa_library_install(NAMESPACE sourcemeta PROJECT includejs NAME engine)
+ noa_library_install(PROJECT includejs NAME engine)
+ if(INCLUDEJS_BACKEND STREQUAL "JavaScriptCore")
+ install(FILES
+ "${PROJECT_SOURCE_DIR}/cmake/FindJavaScriptCore.cmake"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/includejs"
+ COMPONENT includejs_dev)
+ elseif(INCLUDEJS_BACKEND STREQUAL "V8")
+ install(FILES
+ "${PROJECT_SOURCE_DIR}/cmake/FindV8.cmake"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/includejs"
+ COMPONENT includejs_dev)
+ endif()
endif()
-sourcemeta_includejs_add_compile_options(sourcemeta_includejs_engine)
+
+includejs_add_compile_options(includejs_engine)
if(INCLUDEJS_BACKEND STREQUAL "JavaScriptCore")
find_package(JavaScriptCore REQUIRED)
- target_link_libraries(sourcemeta_includejs_engine
+ target_link_libraries(includejs_engine
PRIVATE JavaScriptCore::JavaScriptCore)
- target_compile_definitions(sourcemeta_includejs_engine
- PUBLIC SOURCEMETA_INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
+ target_compile_definitions(includejs_engine
+ PUBLIC INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
+elseif(INCLUDEJS_BACKEND STREQUAL "V8")
+ find_package(V8 REQUIRED)
+ target_link_libraries(includejs_engine
+ PRIVATE V8::V8)
+ target_compile_definitions(includejs_engine
+ PUBLIC INCLUDEJS_ENGINE_V8)
else()
message(FATAL_ERROR "Unknown IncludeJS backend: ${INCLUDEJS_BACKEND}")
endif()
diff --git a/src/engine/common/engine.cc b/src/engine/common/engine.cc
index 9cb91238..1ea5f3a5 100644
--- a/src/engine/common/engine.cc
+++ b/src/engine/common/engine.cc
@@ -1,4 +1,4 @@
-#include
+#include
#include // assert
#include // std::ifstream
@@ -8,7 +8,8 @@
#include // std::move
#include // std::vector
-namespace sourcemeta {
+#if !defined(INCLUDEJS_ENGINE_V8)
+
namespace includejs {
// For convenience
@@ -84,4 +85,5 @@ auto Engine::on_error(const Value &exception) -> void {
}
} // namespace includejs
-} // namespace sourcemeta
+
+#endif
diff --git a/src/engine/common/engine_context.cc b/src/engine/common/engine_context.cc
index 5f2e84a9..9875e302 100644
--- a/src/engine/common/engine_context.cc
+++ b/src/engine/common/engine_context.cc
@@ -1,6 +1,7 @@
-#include
+#include
+
+#if !defined(INCLUDEJS_ENGINE_V8)
-namespace sourcemeta {
namespace includejs {
auto Context::from(int value) const -> Value {
@@ -13,4 +14,5 @@ auto Context::from(const std::string &value) const -> Value {
}
} // namespace includejs
-} // namespace sourcemeta
+
+#endif
diff --git a/src/engine/common/engine_error.cc b/src/engine/common/engine_error.cc
index abc6f181..a7714423 100644
--- a/src/engine/common/engine_error.cc
+++ b/src/engine/common/engine_error.cc
@@ -1,9 +1,8 @@
-#include
+#include
#include // std::cbegin, std::cend
#include // std::move
-namespace sourcemeta {
namespace includejs {
Error::Error(std::string &&new_message, FrameContainer &&new_stacktrace)
@@ -22,4 +21,3 @@ auto Error::end() const -> FrameContainer::const_iterator {
}
} // namespace includejs
-} // namespace sourcemeta
diff --git a/src/engine/include/sourcemeta/includejs/engine.h b/src/engine/include/includejs/engine.h
similarity index 80%
rename from src/engine/include/sourcemeta/includejs/engine.h
rename to src/engine/include/includejs/engine.h
index 6afd911f..e2d1c3f8 100644
--- a/src/engine/include/sourcemeta/includejs/engine.h
+++ b/src/engine/include/includejs/engine.h
@@ -1,15 +1,15 @@
-#ifndef SOURCEMETA_INCLUDEJS_ENGINE_H_
-#define SOURCEMETA_INCLUDEJS_ENGINE_H_
+#ifndef INCLUDEJS_ENGINE_H_
+#define INCLUDEJS_ENGINE_H_
/// @defgroup engine Engine
/// @brief The IncludeJS higher-level abstraction of a JavaScript engine
#include "engine_export.h"
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
#include // std::filesystem::path
#include // std::initializer_list
@@ -20,11 +20,10 @@
#include // std::string
#include // std::vector
-namespace sourcemeta {
namespace includejs {
/// @ingroup engine
-class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Engine {
+class INCLUDEJS_ENGINE_EXPORT Engine {
public:
Engine();
~Engine();
@@ -35,8 +34,11 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Engine {
auto evaluate(std::ifstream &stream, const std::filesystem::path &path)
-> Value;
+ // TODO(RaisinTen): Add support for bind_function() to the V8 backend.
+#if !defined(INCLUDEJS_ENGINE_V8)
auto bind_function(std::initializer_list location,
Function function) -> void;
+#endif
auto bind_global(std::initializer_list location, Value value)
-> void;
auto bind_global(std::initializer_list location, bool value)
@@ -55,6 +57,5 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Engine {
};
} // namespace includejs
-} // namespace sourcemeta
#endif
diff --git a/src/engine/include/sourcemeta/includejs/engine_context.h b/src/engine/include/includejs/engine_context.h
similarity index 74%
rename from src/engine/include/sourcemeta/includejs/engine_context.h
rename to src/engine/include/includejs/engine_context.h
index 640950d5..80d6c93a 100644
--- a/src/engine/include/sourcemeta/includejs/engine_context.h
+++ b/src/engine/include/includejs/engine_context.h
@@ -1,19 +1,18 @@
-#ifndef SOURCEMETA_INCLUDEJS_ENGINE_CONTEXT_H_
-#define SOURCEMETA_INCLUDEJS_ENGINE_CONTEXT_H_
+#ifndef INCLUDEJS_ENGINE_CONTEXT_H_
+#define INCLUDEJS_ENGINE_CONTEXT_H_
#include "engine_export.h"
-#include
-#include
+#include
+#include
#include // std::unique_ptr
#include // std::string
-namespace sourcemeta {
namespace includejs {
/// @ingroup engine
-class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Context {
+class INCLUDEJS_ENGINE_EXPORT Context {
public:
// Consumers are not meant to create this class directly
#ifndef DOXYGEN
@@ -24,6 +23,7 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Context {
auto make_error(const std::string &message) const -> Value;
auto make_object() const -> Value;
auto make_promise() const -> Promise;
+ auto make_array() const -> Value;
auto from(const std::string &value) const -> Value;
auto from(const char *) const -> Value;
auto from(int value) const -> Value;
@@ -38,6 +38,5 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Context {
};
} // namespace includejs
-} // namespace sourcemeta
#endif
diff --git a/src/engine/include/sourcemeta/includejs/engine_error.h b/src/engine/include/includejs/engine_error.h
similarity index 76%
rename from src/engine/include/sourcemeta/includejs/engine_error.h
rename to src/engine/include/includejs/engine_error.h
index f1053dfd..8e0b27e6 100644
--- a/src/engine/include/sourcemeta/includejs/engine_error.h
+++ b/src/engine/include/includejs/engine_error.h
@@ -1,5 +1,5 @@
-#ifndef SOURCEMETA_INCLUDEJS_ENGINE_ERROR_H_
-#define SOURCEMETA_INCLUDEJS_ENGINE_ERROR_H_
+#ifndef INCLUDEJS_ENGINE_ERROR_H_
+#define INCLUDEJS_ENGINE_ERROR_H_
#include "engine_export.h"
@@ -9,11 +9,9 @@
#include // std::string
#include // std::vector
-namespace sourcemeta {
namespace includejs {
-
/// @ingroup engine
-struct SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Frame {
+struct INCLUDEJS_ENGINE_EXPORT Frame {
const std::optional scope;
const std::optional path;
const unsigned long long line;
@@ -21,7 +19,7 @@ struct SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Frame {
};
/// @ingroup engine
-class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Error : public std::exception {
+class INCLUDEJS_ENGINE_EXPORT Error : public std::exception {
private:
using FrameContainer = std::vector;
@@ -35,8 +33,6 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Error : public std::exception {
const std::string message;
const FrameContainer stacktrace;
};
-
} // namespace includejs
-} // namespace sourcemeta
#endif
diff --git a/src/engine/include/sourcemeta/includejs/engine_function.h b/src/engine/include/includejs/engine_function.h
similarity index 59%
rename from src/engine/include/sourcemeta/includejs/engine_function.h
rename to src/engine/include/includejs/engine_function.h
index 6b0f51d9..e6d4129f 100644
--- a/src/engine/include/sourcemeta/includejs/engine_function.h
+++ b/src/engine/include/includejs/engine_function.h
@@ -1,31 +1,29 @@
-#ifndef SOURCEMETA_INCLUDEJS_ENGINE_FUNCTION_H_
-#define SOURCEMETA_INCLUDEJS_ENGINE_FUNCTION_H_
+#ifndef INCLUDEJS_ENGINE_FUNCTION_H_
+#define INCLUDEJS_ENGINE_FUNCTION_H_
#include "engine_export.h"
-#include
-#include
+#include
+#include
-#if defined(SOURCEMETA_INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
+#if defined(INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
#include // std::exception
#include // std::vector
#endif
#ifdef DOXYGEN
/// @ingroup engine
-#define SOURCEMETA_INCLUDEJS_ARGS
+#define INCLUDEJS_ARGS
#else
#if __cplusplus >= 202002L
#include // std::span
-#define SOURCEMETA_INCLUDEJS_ARGS std::span
+#define INCLUDEJS_ARGS std::span
#else
-#define SOURCEMETA_INCLUDEJS_ARGS \
- const std::vector &
+#define INCLUDEJS_ARGS const std::vector &
#endif
#endif
-#if defined(SOURCEMETA_INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
-namespace sourcemeta {
+#if defined(INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
namespace includejs {
// This is a opaque function signature that can be force-casted into
// JSObjectCallAsFunctionCallback
@@ -33,15 +31,14 @@ namespace includejs {
using Function = const void *(*)(const void *, const void *, const void *,
const size_t, const void *[], const void **);
} // namespace includejs
-} // namespace sourcemeta
#endif
-#if defined(SOURCEMETA_INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
-#define __SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION_INTERNAL(function, call_as) \
+#if defined(INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
+#define _INCLUDEJS_EXPOSE_FUNCTION_INTERNAL(function, call_as) \
static const void *function(const void *context, const void *, const void *, \
const size_t argc, const void *raw_arguments[], \
const void **exception) { \
- std::vector<::sourcemeta::includejs::Value> arguments; \
+ std::vector<::includejs::Value> arguments; \
arguments.reserve(argc); \
for (std::size_t index = 0; index < argc; index++) { \
arguments.emplace_back(context, raw_arguments[index]); \
@@ -49,7 +46,7 @@ using Function = const void *(*)(const void *, const void *, const void *,
try { \
return call_as({context}, arguments).native(); \
} catch (const std::exception &error) { \
- const ::sourcemeta::includejs::Context ignition_context{context}; \
+ const ::includejs::Context ignition_context{context}; \
*exception = ignition_context.make_error(error.what()).native(); \
return ignition_context.make_undefined().native(); \
} \
@@ -58,15 +55,15 @@ using Function = const void *(*)(const void *, const void *, const void *,
#ifdef DOXYGEN
/// @ingroup engine
-#define SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION(function)
+#define INCLUDEJS_EXPOSE_FUNCTION(function)
/// @ingroup engine
-#define SOURCEMETA_INCLUDEJS_EXPOSE_TEMPLATE_FUNCTION(function)
+#define INCLUDEJS_EXPOSE_TEMPLATE_FUNCTION(function)
#else
-#define SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION(function) \
- __SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION_INTERNAL(function, function)
-#define SOURCEMETA_INCLUDEJS_EXPOSE_TEMPLATE_FUNCTION(function) \
+#define INCLUDEJS_EXPOSE_FUNCTION(function) \
+ _INCLUDEJS_EXPOSE_FUNCTION_INTERNAL(function, function)
+#define INCLUDEJS_EXPOSE_TEMPLATE_FUNCTION(function) \
template \
- __SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION_INTERNAL(function, function)
+ _INCLUDEJS_EXPOSE_FUNCTION_INTERNAL(function, function)
#endif
#endif
diff --git a/src/engine/include/sourcemeta/includejs/engine_promise.h b/src/engine/include/includejs/engine_promise.h
similarity index 66%
rename from src/engine/include/sourcemeta/includejs/engine_promise.h
rename to src/engine/include/includejs/engine_promise.h
index 7b0d69c8..b7b1ec08 100644
--- a/src/engine/include/sourcemeta/includejs/engine_promise.h
+++ b/src/engine/include/includejs/engine_promise.h
@@ -1,17 +1,16 @@
-#ifndef SOURCEMETA_INCLUDEJS_ENGINE_PROMISE_H_
-#define SOURCEMETA_INCLUDEJS_ENGINE_PROMISE_H_
+#ifndef INCLUDEJS_ENGINE_PROMISE_H_
+#define INCLUDEJS_ENGINE_PROMISE_H_
#include "engine_export.h"
-#include
+#include
#include // std::unique_ptr
-namespace sourcemeta {
namespace includejs {
/// @ingroup engine
-class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Promise {
+class INCLUDEJS_ENGINE_EXPORT Promise {
public:
// Consumers are not meant to create this class directly
#ifndef DOXYGEN
@@ -27,8 +26,6 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Promise {
std::unique_ptr internal;
bool completed{false};
};
-
} // namespace includejs
-} // namespace sourcemeta
#endif
diff --git a/src/engine/include/sourcemeta/includejs/engine_value.h b/src/engine/include/includejs/engine_value.h
similarity index 70%
rename from src/engine/include/sourcemeta/includejs/engine_value.h
rename to src/engine/include/includejs/engine_value.h
index f5e08c6b..907debd3 100644
--- a/src/engine/include/sourcemeta/includejs/engine_value.h
+++ b/src/engine/include/includejs/engine_value.h
@@ -1,5 +1,5 @@
-#ifndef SOURCEMETA_INCLUDEJS_ENGINE_VALUE_H_
-#define SOURCEMETA_INCLUDEJS_ENGINE_VALUE_H_
+#ifndef INCLUDEJS_ENGINE_VALUE_H_
+#define INCLUDEJS_ENGINE_VALUE_H_
#include "engine_export.h"
@@ -10,12 +10,24 @@
#include // std::string
#include // std::vector
-namespace sourcemeta {
namespace includejs {
+enum class ValueType {
+ Number,
+ String,
+ Error,
+ Object,
+ Boolean,
+ Undefined,
+ Null,
+ Array,
+ Function,
+ Unknown
+};
+
// Inspired by https://github.com/sourcemeta/jsontoolkit
/// @ingroup engine
-class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Value {
+class INCLUDEJS_ENGINE_EXPORT Value {
public:
// Consumers are not meant to create this class directly
#ifndef DOXYGEN
@@ -34,13 +46,22 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Value {
auto is_error() const -> bool;
auto is_object() const -> bool;
auto is_boolean() const -> bool;
+ auto is_undefined() const -> bool;
+ auto is_null() const -> bool;
+ auto is_array() const -> bool;
+ auto is_function() const -> bool;
auto to_number() const -> double;
auto to_string() const -> std::string;
auto to_boolean() const -> bool;
+ auto to_function() const -> Function;
+ auto type() const -> ValueType;
auto at(const std::string &property) const -> std::optional;
+ auto at(const unsigned int &position) const -> std::optional;
auto set(const std::string &property, Value value) -> void;
auto set(const std::string &property, Function function) -> void;
+ auto push(Value value) -> void;
auto to_map() const -> std::map;
+ auto to_vector() const -> std::vector;
// For internal use only
#ifndef DOXYGEN
@@ -53,6 +74,5 @@ class SOURCEMETA_INCLUDEJS_ENGINE_EXPORT Value {
};
} // namespace includejs
-} // namespace sourcemeta
#endif
diff --git a/src/engine/javascript_core/engine.cc b/src/engine/javascript_core/engine.cc
index da3936a5..d548b408 100644
--- a/src/engine/javascript_core/engine.cc
+++ b/src/engine/javascript_core/engine.cc
@@ -1,4 +1,4 @@
-#include
+#include
extern "C" {
#include
@@ -12,7 +12,6 @@ extern "C" {
#include // std::move
#include // std::vector
-namespace sourcemeta {
namespace includejs {
struct Engine::Internal {
@@ -197,4 +196,3 @@ auto Engine::bind_global(std::initializer_list location,
}
} // namespace includejs
-} // namespace sourcemeta
diff --git a/src/engine/javascript_core/engine_context.cc b/src/engine/javascript_core/engine_context.cc
index 650ee970..374c3461 100644
--- a/src/engine/javascript_core/engine_context.cc
+++ b/src/engine/javascript_core/engine_context.cc
@@ -1,4 +1,4 @@
-#include
+#include
extern "C" {
#include
@@ -6,7 +6,6 @@ extern "C" {
#include // assert
-namespace sourcemeta {
namespace includejs {
struct Context::Internal {
@@ -59,6 +58,11 @@ auto Context::make_object() const -> Value {
return {this->internal->context, res};
}
+auto Context::make_array() const -> Value {
+ return {this->internal->context,
+ JSObjectMakeArray(this->internal->context, 0, nullptr, nullptr)};
+}
+
auto Context::make_promise() const -> Promise {
return {static_cast(this->internal->context)};
}
@@ -80,9 +84,12 @@ auto Context::from(const char *value) const -> Value {
return {this->internal->context, result};
}
+auto Context::from(std::nullptr_t) const -> Value {
+ return {this->internal->context, JSValueMakeNull(this->internal->context)};
+}
+
auto Context::global() const -> Value {
return {this->internal->context, this->internal->global};
}
} // namespace includejs
-} // namespace sourcemeta
diff --git a/src/engine/javascript_core/engine_promise.cc b/src/engine/javascript_core/engine_promise.cc
index a3289077..e60e4e87 100644
--- a/src/engine/javascript_core/engine_promise.cc
+++ b/src/engine/javascript_core/engine_promise.cc
@@ -1,4 +1,4 @@
-#include
+#include
extern "C" {
#include
@@ -6,7 +6,6 @@ extern "C" {
#include // assert
-namespace sourcemeta {
namespace includejs {
struct Promise::Internal {
@@ -54,4 +53,3 @@ auto Promise::value() -> Value {
}
} // namespace includejs
-} // namespace sourcemeta
diff --git a/src/engine/javascript_core/engine_value.cc b/src/engine/javascript_core/engine_value.cc
index dc05eab9..e29fdf90 100644
--- a/src/engine/javascript_core/engine_value.cc
+++ b/src/engine/javascript_core/engine_value.cc
@@ -1,4 +1,4 @@
-#include
+#include
extern "C" {
#include
@@ -10,7 +10,6 @@ extern "C" {
#include // std::runtime_error
#include // std::vector
-namespace sourcemeta {
namespace includejs {
struct Value::Internal {
@@ -18,6 +17,59 @@ struct Value::Internal {
JSValueRef value;
};
+static auto js_string_to_std_string(JSStringRef value) -> std::string {
+ // JavaScriptCore doesn't have a function to fetch the UTF-8 byte size
+ // of a given string. It can only give us the amount of Unicode code-points
+ // in the string, which may be more than the bytes required to store it.
+ // As a consequence, we can't do much than allocating the maximum possible
+ // buffer to hold the string.
+ const size_t max_size = JSStringGetMaximumUTF8CStringSize(value);
+ std::vector buffer(max_size);
+ // Converts a JavaScript string into a null-terminated UTF-8 string,
+ // and copies the result into an external byte buffer.
+ JSStringGetUTF8CString(value, buffer.data(), max_size);
+ return {buffer.data()};
+}
+
+// Converting a value into a string first requires copying
+// the value reference into a string reference.
+static auto js_value_to_std_string(JSContextRef context, JSValueRef value)
+ -> std::string {
+ JSValueRef exception = nullptr;
+ JSStringRef copy = JSValueToStringCopy(context, value, &exception);
+ assert(!exception);
+
+ try {
+ std::string result{js_string_to_std_string(copy)};
+ JSStringRelease(copy);
+ return result;
+ } catch (const std::exception &) {
+ JSStringRelease(copy);
+ throw;
+ }
+}
+
+static auto get_current_object(JSContextRef context, JSValueRef value)
+ -> JSObjectRef {
+ JSValueRef exception = nullptr;
+ JSObjectRef object = JSValueToObject(context, value, &exception);
+ assert(!exception);
+ assert(object == value);
+ return object;
+}
+
+static auto get_object_length(JSContextRef context, JSObjectRef object)
+ -> std::size_t {
+ JSValueRef exception = nullptr;
+ JSStringRef length_string = JSStringCreateWithUTF8CString("length");
+ const double length = JSValueToNumber(
+ context, JSObjectGetProperty(context, object, length_string, &exception),
+ &exception);
+ assert(!exception);
+ JSStringRelease(length_string);
+ return static_cast(length);
+}
+
Value::Value(const void *context, const void *value)
: internal{std::make_unique()} {
this->internal->context = static_cast(context);
@@ -30,6 +82,32 @@ Value::~Value() {}
Value::Value(Value &&other) noexcept : internal{std::move(other.internal)} {}
+auto Value::type() const -> ValueType {
+ if (is_number()) {
+ return ValueType::Number;
+ } else if (is_string()) {
+ return ValueType::String;
+ } else if (is_error()) {
+ return ValueType::Error;
+ } else if (is_function()) {
+ return ValueType::Function;
+ } else if (is_array()) {
+ return ValueType::Array;
+ } else if (is_object()) {
+ return ValueType::Object;
+ } else if (is_boolean()) {
+ return ValueType::Boolean;
+ } else if (is_undefined()) {
+ return ValueType::Undefined;
+ } else if (is_null()) {
+ return ValueType::Null;
+ } else if (is_function()) {
+ return ValueType::Function;
+ } else {
+ return ValueType::Unknown;
+ }
+}
+
auto Value::is_number() const -> bool {
return JSValueIsNumber(this->internal->context, this->internal->value);
}
@@ -38,6 +116,14 @@ auto Value::is_string() const -> bool {
return JSValueIsString(this->internal->context, this->internal->value);
}
+auto Value::is_undefined() const -> bool {
+ return JSValueIsUndefined(this->internal->context, this->internal->value);
+}
+
+auto Value::is_array() const -> bool {
+ return JSValueIsArray(this->internal->context, this->internal->value);
+}
+
auto Value::is_error() const -> bool {
JSObjectRef global = JSContextGetGlobalObject(this->internal->context);
JSStringRef class_name = JSStringCreateWithUTF8CString("Error");
@@ -66,6 +152,21 @@ auto Value::is_boolean() const -> bool {
return JSValueIsBoolean(this->internal->context, this->internal->value);
}
+auto Value::is_null() const -> bool {
+ return JSValueIsNull(this->internal->context, this->internal->value);
+}
+
+auto Value::is_function() const -> bool {
+ if (!is_object()) {
+ return false;
+ }
+
+ JSObjectRef object =
+ get_current_object(this->internal->context, this->internal->value);
+
+ return JSObjectIsFunction(this->internal->context, object);
+}
+
auto Value::to_number() const -> double {
assert(is_number());
JSValueRef exception = nullptr;
@@ -75,38 +176,6 @@ auto Value::to_number() const -> double {
return result;
}
-static auto js_string_to_std_string(JSStringRef value) -> std::string {
- // JavaScriptCore doesn't have a function to fetch the UTF-8 byte size
- // of a given string. It can only give us the amount of Unicode code-points
- // in the string, which may be more than the bytes required to store it.
- // As a consequence, we can't do much than allocating the maximum possible
- // buffer to hold the string.
- const size_t max_size = JSStringGetMaximumUTF8CStringSize(value);
- std::vector buffer(max_size);
- // Converts a JavaScript string into a null-terminated UTF-8 string,
- // and copies the result into an external byte buffer.
- JSStringGetUTF8CString(value, buffer.data(), max_size);
- return {buffer.data()};
-}
-
-// Converting a value into a string first requires copying
-// the value reference into a string reference.
-static auto js_value_to_std_string(JSContextRef context, JSValueRef value)
- -> std::string {
- JSValueRef exception = nullptr;
- JSStringRef copy = JSValueToStringCopy(context, value, &exception);
- assert(!exception);
-
- try {
- std::string result{js_string_to_std_string(copy)};
- JSStringRelease(copy);
- return result;
- } catch (const std::exception &) {
- JSStringRelease(copy);
- throw;
- }
-}
-
auto Value::to_string() const -> std::string {
assert(is_string());
return js_value_to_std_string(this->internal->context, this->internal->value);
@@ -117,6 +186,31 @@ auto Value::to_boolean() const -> bool {
return JSValueToBoolean(this->internal->context, this->internal->value);
}
+auto Value::to_function() const -> Function {
+ assert(is_function());
+
+ return [context = this->internal->context,
+ value = this->internal->value](std::vector args) {
+ JSValueRef exception = nullptr;
+ JSObjectRef func = JSValueToObject(context, value, &exception);
+ assert(!exception);
+ assert(func != nullptr);
+
+ std::vector js_args;
+ js_args.reserve(args.size());
+ for (Value &arg : args) {
+ js_args.push_back(static_cast(arg.native()));
+ }
+
+ // Now we can call the function, get the result and return through
+ // the Value class.
+ JSValueRef result = JSObjectCallAsFunction(
+ context, func, nullptr, js_args.size(), js_args.data(), nullptr);
+ assert(result != nullptr);
+ return Value{context, result};
+ };
+}
+
auto Value::at(const std::string &property) const -> std::optional {
assert(is_object());
JSValueRef exception = nullptr;
@@ -135,6 +229,15 @@ auto Value::at(const std::string &property) const -> std::optional {
property_string, &exception);
assert(!exception);
JSStringRelease(property_string);
+
+ // The function could not be called outside of this object context
+ // as the C++ function store is tied to the object.
+ // This is why we don't support function properties.
+ if (JSObjectIsFunction(
+ this->internal->context,
+ JSValueToObject(this->internal->context, result, &exception))) {
+ throw std::runtime_error{"Function properties are not supported"};
+ }
return std::make_optional(this->internal->context, result);
}
@@ -248,9 +351,57 @@ auto Value::to_map() const -> std::map {
return map;
}
+auto Value::to_vector() const -> std::vector {
+ assert(is_array());
+ JSObjectRef object =
+ get_current_object(this->internal->context, this->internal->value);
+ std::size_t length = get_object_length(this->internal->context, object);
+
+ JSValueRef exception = nullptr;
+ std::vector vector;
+ for (unsigned int index = 0; index < static_cast(length);
+ index++) {
+ JSValueRef value = JSObjectGetPropertyAtIndex(this->internal->context,
+ object, index, &exception);
+ assert(!exception);
+ vector.emplace_back(static_cast(this->internal->context),
+ value);
+ }
+ return vector;
+}
+
+auto Value::at(const unsigned int &position) const -> std::optional {
+ assert(is_array());
+ JSObjectRef object =
+ get_current_object(this->internal->context, this->internal->value);
+ std::size_t length = get_object_length(this->internal->context, object);
+
+ if (position >= static_cast(length)) {
+ return std::nullopt;
+ }
+
+ JSValueRef exception = nullptr;
+ JSValueRef result = JSObjectGetPropertyAtIndex(this->internal->context,
+ object, position, &exception);
+ assert(!exception);
+ return std::make_optional(this->internal->context, result);
+}
+
+auto Value::push(Value value) -> void {
+ assert(is_array());
+ JSObjectRef object =
+ get_current_object(this->internal->context, this->internal->value);
+ std::size_t length = get_object_length(this->internal->context, object);
+
+ JSValueRef exception = nullptr;
+ JSObjectSetPropertyAtIndex(
+ this->internal->context, object, static_cast(length),
+ static_cast(value.native()), &exception);
+ assert(!exception);
+}
+
auto Value::native() const -> const void * {
return static_cast(this->internal->value);
}
} // namespace includejs
-} // namespace sourcemeta
diff --git a/src/engine/v8/engine.cc b/src/engine/v8/engine.cc
new file mode 100644
index 00000000..d6a2721b
--- /dev/null
+++ b/src/engine/v8/engine.cc
@@ -0,0 +1,93 @@
+#include
+
+#include
+
+#include
+#include
+
+namespace includejs {
+
+struct Engine::Internal {};
+
+Engine::Engine() : internal{std::make_unique()} {
+ // TODO(RaisinTen): This code is here to make it easy to verify if the V8
+ // integration is working.
+
+ // This example code has been taken from:
+ // https://chromium.googlesource.com/v8/v8/+/branch-heads/11.9/samples/hello-world.cc
+
+ // Initialize V8.
+ std::unique_ptr platform = v8::platform::NewDefaultPlatform();
+ v8::V8::InitializePlatform(platform.get());
+ v8::V8::Initialize();
+
+ // Create a new Isolate and make it the current one.
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator =
+ v8::ArrayBuffer::Allocator::NewDefaultAllocator();
+ v8::Isolate *isolate = v8::Isolate::New(create_params);
+ {
+ v8::Isolate::Scope isolate_scope(isolate);
+ // Create a stack-allocated handle scope.
+ v8::HandleScope handle_scope(isolate);
+ // Create a new context.
+ v8::Local context = v8::Context::New(isolate);
+ // Enter the context for compiling and running the hello world script.
+ v8::Context::Scope context_scope(context);
+ {
+ // Create a string containing the JavaScript source code.
+ v8::Local source =
+ v8::String::NewFromUtf8Literal(isolate, "'Hello' + ', World!'");
+ // Compile the source code.
+ v8::Local script =
+ v8::Script::Compile(context, source).ToLocalChecked();
+ // Run the script to get the result.
+ v8::Local result = script->Run(context).ToLocalChecked();
+ // Convert the result to an UTF8 string and print it.
+ v8::String::Utf8Value utf8(isolate, result);
+ printf("%s\n", *utf8);
+ }
+ {
+ // Use the JavaScript API to generate a WebAssembly module.
+ //
+ // |bytes| contains the binary format for the following module:
+ //
+ // (func (export "add") (param i32 i32) (result i32)
+ // get_local 0
+ // get_local 1
+ // i32.add)
+ //
+ const char csource[] = R"(
+ let bytes = new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01,
+ 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07,
+ 0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01,
+ 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b
+ ]);
+ let module = new WebAssembly.Module(bytes);
+ let instance = new WebAssembly.Instance(module);
+ instance.exports.add(3, 4);
+ )";
+ // Create a string containing the JavaScript source code.
+ v8::Local source =
+ v8::String::NewFromUtf8Literal(isolate, csource);
+ // Compile the source code.
+ v8::Local script =
+ v8::Script::Compile(context, source).ToLocalChecked();
+ // Run the script to get the result.
+ v8::Local result = script->Run(context).ToLocalChecked();
+ // Convert the result to a uint32 and print it.
+ uint32_t number = result->Uint32Value(context).ToChecked();
+ printf("3 + 4 = %u\n", number);
+ }
+ }
+ // Dispose the isolate and tear down V8.
+ isolate->Dispose();
+ v8::V8::Dispose();
+ v8::V8::DisposePlatform();
+ delete create_params.array_buffer_allocator;
+}
+
+Engine::~Engine() {}
+
+} // namespace includejs
diff --git a/test/engine/CMakeLists.txt b/test/engine/CMakeLists.txt
index 7f621a10..5d5aaefc 100644
--- a/test/engine/CMakeLists.txt
+++ b/test/engine/CMakeLists.txt
@@ -1,15 +1,25 @@
-add_executable(sourcemeta_includejs_engine_unit
+if(INCLUDEJS_BACKEND STREQUAL "V8")
+ # TODO(RaisinTen): Start testing the V8 backend.
+ return()
+endif()
+
+add_executable(includejs_engine_unit
engine_binding_test.cc
engine_evaluate_test.cc
engine_stacktraces_test.cc
- engine_value_object_test.cc)
+ engine_value_object_test.cc
+ engine_value_array_test.cc
+ engine_value_function_test.cc
+ engine_value_undefined_test.cc
+ engine_value_type_test.cc
+ engine_value_null_test.cc)
-sourcemeta_includejs_add_compile_options(sourcemeta_includejs_engine_unit)
+includejs_add_compile_options(includejs_engine_unit)
-target_link_libraries(sourcemeta_includejs_engine_unit
+target_link_libraries(includejs_engine_unit
PRIVATE GTest::gtest GTest::gtest_main)
-target_link_libraries(sourcemeta_includejs_engine_unit
- PRIVATE sourcemeta::includejs::engine)
-gtest_discover_tests(sourcemeta_includejs_engine_unit)
-set_target_properties(sourcemeta_includejs_engine_unit
+target_link_libraries(includejs_engine_unit
+ PRIVATE includejs::engine)
+gtest_discover_tests(includejs_engine_unit)
+set_target_properties(includejs_engine_unit
PROPERTIES FOLDER "IncludeJS/Engine")
diff --git a/test/engine/engine_binding_test.cc b/test/engine/engine_binding_test.cc
index 5cc7c688..3e20de74 100644
--- a/test/engine/engine_binding_test.cc
+++ b/test/engine/engine_binding_test.cc
@@ -1,135 +1,120 @@
#include
-#include
+#include
-static auto is_string(const sourcemeta::includejs::Context &context,
- SOURCEMETA_INCLUDEJS_ARGS arguments)
- -> sourcemeta::includejs::Value {
+static auto is_string(const includejs::Context &context,
+ INCLUDEJS_ARGS arguments) -> includejs::Value {
return context.from(!arguments.empty() && arguments.front().is_string());
}
// TODO: Add overload that doesn't take arguments
-static auto get_details(const sourcemeta::includejs::Context &context,
- SOURCEMETA_INCLUDEJS_ARGS)
- -> sourcemeta::includejs::Value {
- sourcemeta::includejs::Value result{context.make_object()};
+static auto get_details(const includejs::Context &context, INCLUDEJS_ARGS)
+ -> includejs::Value {
+ includejs::Value result{context.make_object()};
result.set("version", context.from("1.0.0"));
return result;
}
-static auto promise_test(const sourcemeta::includejs::Context &context,
- SOURCEMETA_INCLUDEJS_ARGS)
- -> sourcemeta::includejs::Value {
- sourcemeta::includejs::Promise promise{context.make_promise()};
+static auto promise_test(const includejs::Context &context, INCLUDEJS_ARGS)
+ -> includejs::Value {
+ includejs::Promise promise{context.make_promise()};
promise.resolve(context.from(true));
return promise.value();
}
-SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION(is_string);
-SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION(get_details);
-SOURCEMETA_INCLUDEJS_EXPOSE_FUNCTION(promise_test);
+INCLUDEJS_EXPOSE_FUNCTION(is_string);
+INCLUDEJS_EXPOSE_FUNCTION(get_details);
+INCLUDEJS_EXPOSE_FUNCTION(promise_test);
TEST(IncludeJS_Engine, bind_global_root_integer) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_global({"my_global"}, 2);
- sourcemeta::includejs::Value result{engine.evaluate("my_global", "index.js")};
+ includejs::Value result{engine.evaluate("my_global", "index.js")};
EXPECT_TRUE(result.is_number());
EXPECT_EQ(result.to_number(), 2);
}
TEST(IncludeJS_Engine, bind_global_nested_integer) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_global({"foo", "bar", "baz"}, 8);
- sourcemeta::includejs::Value result{
- engine.evaluate("foo.bar.baz", "index.js")};
+ includejs::Value result{engine.evaluate("foo.bar.baz", "index.js")};
EXPECT_TRUE(result.is_number());
EXPECT_EQ(result.to_number(), 8);
}
TEST(IncludeJS_Engine, bind_global_nested_boolean_true) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_global({"foo", "bar", "baz"}, true);
- sourcemeta::includejs::Value result{
- engine.evaluate("foo.bar.baz", "index.js")};
+ includejs::Value result{engine.evaluate("foo.bar.baz", "index.js")};
EXPECT_TRUE(result.is_boolean());
EXPECT_EQ(result.to_boolean(), true);
}
TEST(IncludeJS_Engine, bind_global_nested_boolean_false) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_global({"foo", "bar", "baz"}, false);
- sourcemeta::includejs::Value result{
- engine.evaluate("foo.bar.baz", "index.js")};
+ includejs::Value result{engine.evaluate("foo.bar.baz", "index.js")};
EXPECT_TRUE(result.is_boolean());
EXPECT_EQ(result.to_boolean(), false);
}
TEST(IncludeJS_Engine, bind_global_array) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_global({"my", "array"}, {engine.context().from("foo"),
engine.context().from("bar"),
engine.context().from("baz")});
// Length
- sourcemeta::includejs::Value length{
- engine.evaluate("my.array.length", "index.js")};
+ includejs::Value length{engine.evaluate("my.array.length", "index.js")};
EXPECT_TRUE(length.is_number());
EXPECT_EQ(length.to_number(), 3);
// Value at index 0
- sourcemeta::includejs::Value result1{
- engine.evaluate("my.array[0]", "index.js")};
+ includejs::Value result1{engine.evaluate("my.array[0]", "index.js")};
EXPECT_TRUE(result1.is_string());
EXPECT_EQ(result1.to_string(), "foo");
// Value at index 1
- sourcemeta::includejs::Value result2{
- engine.evaluate("my.array[1]", "index.js")};
+ includejs::Value result2{engine.evaluate("my.array[1]", "index.js")};
EXPECT_TRUE(result2.is_string());
EXPECT_EQ(result2.to_string(), "bar");
// Value at index 2
- sourcemeta::includejs::Value result3{
- engine.evaluate("my.array[2]", "index.js")};
+ includejs::Value result3{engine.evaluate("my.array[2]", "index.js")};
EXPECT_TRUE(result3.is_string());
EXPECT_EQ(result3.to_string(), "baz");
}
TEST(IncludeJS_Engine, bind_function_return_boolean_false) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_function({"isString"}, &is_string);
- sourcemeta::includejs::Value result{
- engine.evaluate("isString(1)", "index.js")};
+ includejs::Value result{engine.evaluate("isString(1)", "index.js")};
EXPECT_TRUE(result.is_boolean());
EXPECT_EQ(result.to_boolean(), false);
}
TEST(IncludeJS_Engine, bind_function_return_boolean_true) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_function({"isString"}, &is_string);
- sourcemeta::includejs::Value result{
- engine.evaluate("isString('foo')", "index.js")};
+ includejs::Value result{engine.evaluate("isString('foo')", "index.js")};
EXPECT_TRUE(result.is_boolean());
EXPECT_EQ(result.to_boolean(), true);
}
TEST(IncludeJS_Engine, bind_function_details_object) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_function({"getDetails"}, &get_details);
- sourcemeta::includejs::Value result{
- engine.evaluate("getDetails()", "index.js")};
+ includejs::Value result{engine.evaluate("getDetails()", "index.js")};
EXPECT_TRUE(result.is_object());
- const std::optional version{
- result.at("version")};
+ const std::optional version{result.at("version")};
EXPECT_TRUE(version.has_value());
EXPECT_TRUE(version->is_string());
EXPECT_EQ(version->to_string(), "1.0.0");
}
TEST(IncludeJS_Engine, bind_function_promise) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
engine.bind_function({"myPromise"}, &promise_test);
- sourcemeta::includejs::Value result{
- engine.evaluate("myPromise()", "index.js")};
+ includejs::Value result{engine.evaluate("myPromise()", "index.js")};
EXPECT_TRUE(result.is_object());
}
diff --git a/test/engine/engine_evaluate_test.cc b/test/engine/engine_evaluate_test.cc
index 734d9ec5..1ddf673c 100644
--- a/test/engine/engine_evaluate_test.cc
+++ b/test/engine/engine_evaluate_test.cc
@@ -1,29 +1,27 @@
#include
-#include
+#include
TEST(IncludeJS_Engine, evaluate_result_integer) {
- sourcemeta::includejs::Engine engine;
- sourcemeta::includejs::Value result{engine.evaluate("1 + 3", "index.js")};
+ includejs::Engine engine;
+ includejs::Value result{engine.evaluate("1 + 3", "index.js")};
EXPECT_TRUE(result.is_number());
EXPECT_EQ(result.to_number(), 4);
}
TEST(IncludeJS_Engine, evaluate_result_string) {
- sourcemeta::includejs::Engine engine;
- sourcemeta::includejs::Value result{engine.evaluate("\"foo\"", "index.js")};
+ includejs::Engine engine;
+ includejs::Value result{engine.evaluate("\"foo\"", "index.js")};
EXPECT_TRUE(result.is_string());
EXPECT_EQ(result.to_string(), "foo");
}
TEST(IncludeJS_Engine, invalid_function_call) {
- sourcemeta::includejs::Engine engine;
- EXPECT_THROW(engine.evaluate("xyz()", "index.js"),
- sourcemeta::includejs::Error);
+ includejs::Engine engine;
+ EXPECT_THROW(engine.evaluate("xyz()", "index.js"), includejs::Error);
}
TEST(IncludeJS_Engine, evaluate_syntax_error) {
- sourcemeta::includejs::Engine engine;
- EXPECT_THROW(engine.evaluate("1 +", "index.js"),
- sourcemeta::includejs::Error);
+ includejs::Engine engine;
+ EXPECT_THROW(engine.evaluate("1 +", "index.js"), includejs::Error);
}
diff --git a/test/engine/engine_stacktraces_test.cc b/test/engine/engine_stacktraces_test.cc
index 597c3cb4..668e9a7f 100644
--- a/test/engine/engine_stacktraces_test.cc
+++ b/test/engine/engine_stacktraces_test.cc
@@ -1,20 +1,19 @@
#include
-#include
+#include
#include
#include
TEST(IncludeJS_Engine, unknown_global_function) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
try {
engine.evaluate("this_does_not_exist()", "index.js");
FAIL();
- } catch (const sourcemeta::includejs::Error &error) {
+ } catch (const includejs::Error &error) {
EXPECT_EQ(std::string{error.what()},
"Can't find variable: this_does_not_exist");
- const std::vector stacktrace{error.begin(),
- error.end()};
+ const std::vector stacktrace{error.begin(), error.end()};
EXPECT_EQ(stacktrace.size(), 1);
EXPECT_FALSE(stacktrace.at(0).scope.has_value());
EXPECT_TRUE(stacktrace.at(0).path.has_value());
diff --git a/test/engine/engine_value_array_test.cc b/test/engine/engine_value_array_test.cc
new file mode 100644
index 00000000..e3dfeaa1
--- /dev/null
+++ b/test/engine/engine_value_array_test.cc
@@ -0,0 +1,44 @@
+#include
+
+#include
+
+TEST(IncludeJS_Engine, create_array) {
+ includejs::Engine engine;
+
+ auto arr = engine.context().make_array();
+
+ EXPECT_TRUE(arr.is_array());
+}
+
+TEST(IncludeJS_Engine, transform_array_to_vector) {
+ includejs::Engine engine;
+
+ auto arr = engine.context().make_array();
+ arr.push(engine.context().from(42));
+
+ auto vec = arr.to_vector();
+ EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec.at(0).to_number(), 42);
+}
+
+TEST(IncludeJS_Engine, evaluate_array) {
+ includejs::Engine engine;
+ includejs::Value result{engine.evaluate("([])", "index.js")};
+ EXPECT_TRUE(result.is_array());
+}
+
+TEST(IncludeJS_Engine, push_and_get_array_element) {
+ includejs::Engine engine;
+
+ auto arr = engine.context().make_array();
+ arr.push(engine.context().from(42));
+ arr.push(engine.context().from("baz"));
+
+ includejs::Value result = arr.at(0).value();
+ EXPECT_TRUE(result.is_number());
+ EXPECT_EQ(result.to_number(), 42);
+
+ includejs::Value result2 = arr.at(1).value();
+ EXPECT_TRUE(result2.is_string());
+ EXPECT_EQ(result2.to_string(), "baz");
+}
diff --git a/test/engine/engine_value_function_test.cc b/test/engine/engine_value_function_test.cc
new file mode 100644
index 00000000..6aba1cd0
--- /dev/null
+++ b/test/engine/engine_value_function_test.cc
@@ -0,0 +1,23 @@
+#include
+
+#include
+
+TEST(IncludeJS_Engine, is_function) {
+ includejs::Engine engine;
+
+ auto result = engine.evaluate("(function a() {})", "index.js");
+
+ EXPECT_TRUE(result.is_function());
+}
+
+TEST(IncludeJS_Engine, to_function) {
+ includejs::Engine engine;
+
+ auto result = engine.evaluate("(function a() { return 42; })", "index.js");
+
+ EXPECT_TRUE(result.is_function());
+
+ auto value = result.to_function()({});
+ EXPECT_TRUE(value.is_number());
+ EXPECT_EQ(value.to_number(), 42);
+}
diff --git a/test/engine/engine_value_null_test.cc b/test/engine/engine_value_null_test.cc
new file mode 100644
index 00000000..4697afe7
--- /dev/null
+++ b/test/engine/engine_value_null_test.cc
@@ -0,0 +1,16 @@
+#include
+
+#include
+
+TEST(IncludeJS_Engine, create_null) {
+ includejs::Engine engine;
+
+ auto null = engine.context().from(nullptr);
+ EXPECT_TRUE(null.is_null());
+}
+
+TEST(IncludeJS_Engine, evaluate_null) {
+ includejs::Engine engine;
+ includejs::Value result{engine.evaluate("null", "index.js")};
+ EXPECT_TRUE(result.is_null());
+}
diff --git a/test/engine/engine_value_object_test.cc b/test/engine/engine_value_object_test.cc
index 3582f28c..6c4da93b 100644
--- a/test/engine/engine_value_object_test.cc
+++ b/test/engine/engine_value_object_test.cc
@@ -1,37 +1,74 @@
#include
-#include
+#include
-// TODO(tonygo): Add tests for each kind of type
-TEST(IncludeJS_Engine, set_object_property) {
- sourcemeta::includejs::Engine engine;
+TEST(IncludeJS_Engine, create_object) {
+ includejs::Engine engine;
+
+ auto object = engine.context().make_object();
+
+ EXPECT_TRUE(object.is_object());
+}
+
+TEST(IncludeJS_Engine, transform_object_to_map) {
+ includejs::Engine engine;
auto object = engine.context().make_object();
object.set("foo", engine.context().from(42));
- engine.bind_global({"obj"}, std::move(object));
- sourcemeta::includejs::Value result{engine.evaluate("obj.foo", "index.js")};
+ auto map = object.to_map();
+ EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.at("foo").to_number(), 42);
+}
+
+TEST(IncludeJS_Engine, evaluate_object) {
+ includejs::Engine engine;
+ includejs::Value result{engine.evaluate("({})", "index.js")};
+ EXPECT_TRUE(result.is_object());
+}
+
+TEST(IncludeJS_Engine, set_and_get_object_property) {
+ includejs::Engine engine;
+
+ auto object = engine.context().make_object();
+ object.set("foo", engine.context().from(42));
+ object.set("bar", engine.context().from("baz"));
+
+ includejs::Value result = object.at("foo").value();
EXPECT_TRUE(result.is_number());
EXPECT_EQ(result.to_number(), 42);
+
+ includejs::Value result2 = object.at("bar").value();
+ EXPECT_TRUE(result2.is_string());
+ EXPECT_EQ(result2.to_string(), "baz");
}
TEST(IncludeJS_Engine, set_object_function) {
- sourcemeta::includejs::Engine engine;
+ includejs::Engine engine;
auto object = engine.context().make_object();
- object.set(
- "foo",
- [](std::vector arguments)
- -> sourcemeta::includejs::Value { return std::move(arguments[0]); });
+ object.set("foo",
+ [](std::vector arguments) -> includejs::Value {
+ return std::move(arguments[0]);
+ });
engine.bind_global({"obj"}, std::move(object));
- sourcemeta::includejs::Value result{
- engine.evaluate("obj.foo(42)", "index.js")};
+ includejs::Value result{engine.evaluate("obj.foo(42)", "index.js")};
EXPECT_TRUE(result.is_number());
EXPECT_EQ(result.to_number(), 42);
- sourcemeta::includejs::Value result2{
- engine.evaluate("obj.foo('bar')", "index.js")};
+ includejs::Value result2{engine.evaluate("obj.foo('bar')", "index.js")};
EXPECT_TRUE(result2.is_string());
EXPECT_EQ(result2.to_string(), "bar");
}
+
+TEST(IncludeJS_Engine, at_throw_for_functions) {
+ includejs::Engine engine;
+ includejs::Context &context = engine.context();
+
+ auto obj = context.make_object();
+ obj.set("woo", [](std::vector args) -> includejs::Value {
+ return std::move(args[0]);
+ });
+ EXPECT_THROW(obj.at("woo"), std::runtime_error);
+}
diff --git a/test/engine/engine_value_type_test.cc b/test/engine/engine_value_type_test.cc
new file mode 100644
index 00000000..b8ed02ac
--- /dev/null
+++ b/test/engine/engine_value_type_test.cc
@@ -0,0 +1,66 @@
+#include
+
+#include
+
+TEST(IncludeJS_Engine, null_type) {
+ includejs::Engine engine;
+
+ auto null = engine.context().from(nullptr);
+ EXPECT_TRUE(null.type() == includejs::ValueType::Null);
+}
+
+TEST(IncludeJS_Engine, undefined_type) {
+ includejs::Engine engine;
+
+ auto undefined = engine.context().make_undefined();
+ EXPECT_TRUE(undefined.type() == includejs::ValueType::Undefined);
+}
+
+TEST(IncludeJS_Engine, error_type) {
+ includejs::Engine engine;
+
+ auto error = engine.context().make_error("error");
+ EXPECT_TRUE(error.type() == includejs::ValueType::Error);
+}
+
+TEST(IncludeJS_Engine, object_type) {
+ includejs::Engine engine;
+
+ auto object = engine.context().make_object();
+ EXPECT_TRUE(object.type() == includejs::ValueType::Object);
+}
+
+TEST(IncludeJS_Engine, array_type) {
+ includejs::Engine engine;
+
+ auto array = engine.context().make_array();
+ EXPECT_TRUE(array.type() == includejs::ValueType::Array);
+}
+
+TEST(IncludeJS_Engine, number_type) {
+ includejs::Engine engine;
+
+ auto number = engine.context().from(42);
+ EXPECT_TRUE(number.type() == includejs::ValueType::Number);
+}
+
+TEST(IncludeJS_Engine, string_type) {
+ includejs::Engine engine;
+
+ auto string = engine.context().from("foo");
+ EXPECT_TRUE(string.type() == includejs::ValueType::String);
+}
+
+TEST(IncludeJS_Engine, boolean_type) {
+ includejs::Engine engine;
+
+ auto boolean = engine.context().from(true);
+ EXPECT_TRUE(boolean.type() == includejs::ValueType::Boolean);
+}
+
+TEST(IncludeJS_Engine, function_type) {
+ includejs::Engine engine;
+
+ auto result = engine.evaluate("(function foo() {})", "index.js");
+ EXPECT_TRUE(result.type() == includejs::ValueType::Function);
+}
diff --git a/test/engine/engine_value_undefined_test.cc b/test/engine/engine_value_undefined_test.cc
new file mode 100644
index 00000000..3b885eca
--- /dev/null
+++ b/test/engine/engine_value_undefined_test.cc
@@ -0,0 +1,16 @@
+#include
+
+#include
+
+TEST(IncludeJS_Engine, create_undefined) {
+ includejs::Engine engine;
+
+ auto object = engine.context().make_undefined();
+ EXPECT_TRUE(object.is_undefined());
+}
+
+TEST(IncludeJS_Engine, evaluate_undefined) {
+ includejs::Engine engine;
+ includejs::Value result{engine.evaluate("undefined", "index.js")};
+ EXPECT_TRUE(result.is_undefined());
+}
diff --git a/test/packaging/find_package/CMakeLists.txt b/test/packaging/find_package/CMakeLists.txt
index 8ceef97d..a80d027e 100644
--- a/test/packaging/find_package/CMakeLists.txt
+++ b/test/packaging/find_package/CMakeLists.txt
@@ -3,7 +3,6 @@ project(includejs_hello VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
-list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../../../cmake")
find_package(IncludeJS REQUIRED)
add_executable(includejs_hello hello.cc)
-target_link_libraries(includejs_hello PRIVATE sourcemeta::includejs::engine)
+target_link_libraries(includejs_hello PRIVATE includejs::engine)
diff --git a/test/packaging/find_package/hello.cc b/test/packaging/find_package/hello.cc
index d6c0807b..b218544e 100644
--- a/test/packaging/find_package/hello.cc
+++ b/test/packaging/find_package/hello.cc
@@ -1,13 +1,16 @@
-#include
+#include
#include // EXIT_SUCCESS
#include // EXIT_SUCCESS
#include // std::cout
auto main() -> int {
- sourcemeta::includejs::Engine engine;
- sourcemeta::includejs::Value result{engine.evaluate("1 + 3", "index.js")};
+ includejs::Engine engine;
+ // TODO(RaisinTen): Remove this once V8 supports this.
+#if !defined(INCLUDEJS_ENGINE_V8)
+ includejs::Value result{engine.evaluate("1 + 3", "index.js")};
assert(result.is_number());
std::cout << result.to_number() << "\n";
+#endif
return EXIT_SUCCESS;
}
diff --git a/vendor/noa/cmake/noa/library.cmake b/vendor/noa/cmake/noa/library.cmake
index 527bbfe5..dd7019a5 100644
--- a/vendor/noa/cmake/noa/library.cmake
+++ b/vendor/noa/cmake/noa/library.cmake
@@ -2,9 +2,6 @@ function(noa_library)
cmake_parse_arguments(NOA_LIBRARY ""
"NAMESPACE;PROJECT;NAME;FOLDER" "PRIVATE_HEADERS;SOURCES" ${ARGN})
- if(NOT NOA_LIBRARY_NAMESPACE)
- message(FATAL_ERROR "You must pass the namespace name using the NAMESPACE option")
- endif()
if(NOT NOA_LIBRARY_PROJECT)
message(FATAL_ERROR "You must pass the project name using the PROJECT option")
endif()
@@ -15,7 +12,12 @@ function(noa_library)
message(FATAL_ERROR "You must pass the folder name using the FOLDER option")
endif()
- set(INCLUDE_PREFIX "include/${NOA_LIBRARY_NAMESPACE}/${NOA_LIBRARY_PROJECT}")
+ if(NOA_LIBRARY_NAMESPACE)
+ set(INCLUDE_PREFIX "include/${NOA_LIBRARY_NAMESPACE}/${NOA_LIBRARY_PROJECT}")
+ else()
+ set(INCLUDE_PREFIX "include/${NOA_LIBRARY_PROJECT}")
+ endif()
+
set(PUBLIC_HEADER "${INCLUDE_PREFIX}/${NOA_LIBRARY_NAME}.h")
if(NOA_LIBRARY_SOURCES)
@@ -27,41 +29,46 @@ function(noa_library)
set(ABSOLUTE_PRIVATE_HEADERS)
endif()
+ if(NOA_LIBRARY_NAMESPACE)
+ set(TARGET_NAME "${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}")
+ set(ALIAS_NAME "${NOA_LIBRARY_NAMESPACE}::${NOA_LIBRARY_PROJECT}::${NOA_LIBRARY_NAME}")
+ else()
+ set(TARGET_NAME "${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}")
+ set(ALIAS_NAME "${NOA_LIBRARY_PROJECT}::${NOA_LIBRARY_NAME}")
+ endif()
+
if(NOA_LIBRARY_SOURCES)
- add_library(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ add_library(${TARGET_NAME}
${PUBLIC_HEADER} ${ABSOLUTE_PRIVATE_HEADERS} ${NOA_LIBRARY_SOURCES})
else()
- add_library(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME} INTERFACE
+ add_library(${TARGET_NAME} INTERFACE
${PUBLIC_HEADER} ${ABSOLUTE_PRIVATE_HEADERS})
endif()
- add_library(${NOA_LIBRARY_NAMESPACE}::${NOA_LIBRARY_PROJECT}::${NOA_LIBRARY_NAME}
- ALIAS ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME})
+ add_library(${ALIAS_NAME} ALIAS ${TARGET_NAME})
if(NOA_LIBRARY_SOURCES)
- target_include_directories(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
- PUBLIC
- "$"
- "$")
+ target_include_directories(${TARGET_NAME} PUBLIC
+ "$"
+ "$")
else()
- target_include_directories(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
- INTERFACE
- "$"
- "$")
+ target_include_directories(${TARGET_NAME} INTERFACE
+ "$"
+ "$")
endif()
if(NOA_LIBRARY_SOURCES)
- set_target_properties(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ set_target_properties(${TARGET_NAME}
PROPERTIES
- OUTPUT_NAME ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ OUTPUT_NAME ${TARGET_NAME}
PUBLIC_HEADER "${PUBLIC_HEADER}"
PRIVATE_HEADER "${ABSOLUTE_PRIVATE_HEADERS}"
EXPORT_NAME "${NOA_LIBRARY_PROJECT}::${NOA_LIBRARY_NAME}"
FOLDER "${NOA_LIBRARY_FOLDER}")
else()
- set_target_properties(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ set_target_properties(${TARGET_NAME}
PROPERTIES
- OUTPUT_NAME ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ OUTPUT_NAME ${TARGET_NAME}
PUBLIC_HEADER "${PUBLIC_HEADER}"
PRIVATE_HEADER "${ABSOLUTE_PRIVATE_HEADERS}"
FOLDER "${NOA_LIBRARY_FOLDER}")
@@ -69,15 +76,15 @@ function(noa_library)
if(NOA_LIBRARY_SOURCES)
include(GenerateExportHeader)
- generate_export_header(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ generate_export_header(${TARGET_NAME}
EXPORT_FILE_NAME ${NOA_LIBRARY_NAME}_export.h)
- set_target_properties(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ set_target_properties(${TARGET_NAME}
PROPERTIES
SOVERSION "${PROJECT_VERSION_MAJOR}"
VERSION "${PROJECT_VERSION}")
# To find the generated files
- target_include_directories(${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ target_include_directories(${TARGET_NAME}
PUBLIC "$")
endif()
endfunction()
@@ -85,9 +92,6 @@ endfunction()
function(noa_library_install)
cmake_parse_arguments(NOA_LIBRARY "" "NAMESPACE;PROJECT;NAME" "" ${ARGN})
- if(NOT NOA_LIBRARY_NAMESPACE)
- message(FATAL_ERROR "You must pass the namespace name using the NAMESPACE option")
- endif()
if(NOT NOA_LIBRARY_PROJECT)
message(FATAL_ERROR "You must pass the project name using the PROJECT option")
endif()
@@ -95,22 +99,34 @@ function(noa_library_install)
message(FATAL_ERROR "You must pass the library name using the NAME option")
endif()
+ if(NOA_LIBRARY_NAMESPACE)
+ set(COMPONENT_NAME "${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}")
+ set(TARGET_NAME "${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}")
+ set(INCLUDE_PATH "${CMAKE_INSTALL_INCLUDEDIR}/${NOA_LIBRARY_NAMESPACE}/${NOA_LIBRARY_PROJECT}")
+ set(NAMESPACE_PREFIX "${NOA_LIBRARY_NAMESPACE}::")
+ else()
+ set(COMPONENT_NAME "${NOA_LIBRARY_PROJECT}")
+ set(TARGET_NAME "${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}")
+ set(INCLUDE_PATH "${CMAKE_INSTALL_INCLUDEDIR}/${NOA_LIBRARY_PROJECT}")
+ set(NAMESPACE_PREFIX "")
+ endif()
+
include(GNUInstallDirs)
- install(TARGETS ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
- EXPORT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
- PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${NOA_LIBRARY_NAMESPACE}/${NOA_LIBRARY_PROJECT}"
- COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_dev
- PRIVATE_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${NOA_LIBRARY_NAMESPACE}/${NOA_LIBRARY_PROJECT}"
- COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_dev
+ install(TARGETS ${TARGET_NAME}
+ EXPORT ${TARGET_NAME}
+ PUBLIC_HEADER DESTINATION "${INCLUDE_PATH}"
+ COMPONENT ${COMPONENT_NAME}_dev
+ PRIVATE_HEADER DESTINATION "${INCLUDE_PATH}"
+ COMPONENT ${COMPONENT_NAME}_dev
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
- COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}
+ COMPONENT ${COMPONENT_NAME}
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
- COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}
- NAMELINK_COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_dev
+ COMPONENT ${COMPONENT_NAME}
+ NAMELINK_COMPONENT ${COMPONENT_NAME}_dev
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
- COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_dev)
- install(EXPORT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_${NOA_LIBRARY_NAME}
+ COMPONENT ${COMPONENT_NAME}_dev)
+ install(EXPORT ${TARGET_NAME}
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${NOA_LIBRARY_PROJECT}"
- NAMESPACE ${NOA_LIBRARY_NAMESPACE}::
- COMPONENT ${NOA_LIBRARY_NAMESPACE}_${NOA_LIBRARY_PROJECT}_dev)
+ NAMESPACE ${NAMESPACE_PREFIX}
+ COMPONENT ${COMPONENT_NAME}_dev)
endfunction()