Skip to content

Commit

Permalink
Csharp support (#25)
Browse files Browse the repository at this point in the history
* migrate cppcli support from https://github.com/iit-reacts/djinni/tree/iit
* migrate csharp unit tests to be configured by cmake entirely
* rename `--cpp-optional-header` from `<optional>` to `"optional"` because otherwise the command is broken on powershell
* replace file(READ ...) with file(STRINGS ...) in Djinni.cmake because it is shorter
* fix missing header <stdexcept> that the MSVC compiler complained about.

Co-authored-by: Leandro Freitas <freitass@gmail.com>
Co-authored-by: Harald <harald.achitz@gmail.com>
  • Loading branch information
3 people authored Apr 25, 2021
1 parent df858bd commit 35fdcac
Show file tree
Hide file tree
Showing 36 changed files with 1,548 additions and 61 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,32 @@ jobs:
- name: Test if expected files have been installed
working-directory: build/check_install_root
run: diff -u ../../test/jni_list.txt <(du -a | tac | awk -F ' ' '{print $2}' | sort)

build-on-windows-for-cppcli:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: install djinni
run: |
$VERSION = 'v' + [regex]::Match((Get-Content .tool-versions), '^djinni (\d+.\d+.\d+)').captures.groups[1].value
$URL = 'https://github.com/cross-language-cpp/djinni-generator/releases/download/' + $VERSION + '/djinni.bat'
Invoke-WebRequest -Uri $URL -OutFile djinni.bat
- name: Configure cmake
run: cmake -S . -B build -DDJINNI_WITH_CPPCLI=ON -DDJINNI_STATIC_LIB=ON -DCMAKE_INSTALL_PREFIX=build/check_install_root -DDJINNI_EXECUTABLE="$(((Get-Location).Path) -replace "\\","/")/djinni.bat" -G "Visual Studio 16 2019"
- name: Install nuget dependencies
working-directory: build/test-suite
run: dotnet restore DjinniCppCliTest.csproj --runtime win-x64
- name: Build release
run: cmake --build build --config Release
- name: Run tests
working-directory: build/test-suite
run: dotnet test Release/DjinniCppCliTest.dll
- name: Install files
run: cmake --build build --config Release --target install
- name: List installed files
working-directory: build/check_install_root
run: Resolve-Path -Path (Get-ChildItem -Recurse).FullName -Relative
- name: Test if expected files have been installed
working-directory: build/check_install_root
run: if((Compare-Object (Get-Content ..\..\test\cppcli_list.txt) (Resolve-Path -Path (Get-ChildItem -Recurse).FullName -Relative))) { Write-Error "file list not equal" }

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
build/*
.DS_Store
*.pyc
.idea/
cmake-build-debug/
check_install_root/
54 changes: 51 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
cmake_minimum_required(VERSION 3.6.0)

project(djinni_support_lib)
if(DJINNI_WITH_OBJC)
set(PROJECT_LANGUAGES OBJC OBJCXX)
elseif(DJINNI_WITH_CPPCLI)
set(PROJECT_LANGUAGES CSharp)
endif()

project(djinni_support_lib CXX ${PROJECT_LANGUAGES})

include(GNUInstallDirs)

Expand Down Expand Up @@ -33,6 +39,16 @@ set(SRC_C_WRAPPER
"djinni/cwrapper/wrapper_marshal.hpp"
)

set(SRC_CPPCLI
"djinni/cppcli/Assert.hpp"
"djinni/cppcli/AutoPtr.hpp"
"djinni/cppcli/Error.hpp"
"djinni/cppcli/Error.cpp"
"djinni/cppcli/Marshal.hpp"
"djinni/cppcli/WrapperCache.hpp"
"djinni/cppcli/WrapperCache.cpp"
)

option(DJINNI_STATIC_LIB "Build Djinni support library as a static library instead of dynamic (the default)." off)
if(DJINNI_STATIC_LIB)
add_library(djinni_support_lib STATIC ${SRC_SHARED})
Expand Down Expand Up @@ -89,6 +105,8 @@ if(DJINNI_WITH_OBJC)

endif()

target_include_directories(djinni_support_lib PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")

# JNI support
option(DJINNI_WITH_JNI "Include the JNI support code in Djinni support library." off)
option(DJINNI_JNI_WITH_MAIN "Include the default minimal JNI_OnLoad and JNI_OnUnload implementation in Djinni support library." on)
Expand Down Expand Up @@ -130,9 +148,39 @@ if(DJINNI_WITH_PYTHON)
)
endif()

option(DJINNI_WITH_CPPCLI "Include the C++/CLI support code in Djinni support library." off)
if(DJINNI_WITH_CPPCLI)

if(NOT MSVC)
message(FATAL_ERROR "Enabling DJINNI_WITH_CPPCLI without MSVC is not supported")
endif()

if(NOT DJINNI_STATIC_LIB)
message(FATAL_ERROR "DJINNI_WITH_CPPCLI requires DJINNI_STATIC_LIB to be true")
endif()

target_include_directories(djinni_support_lib PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/support-lib/cppcli/>")
target_sources(djinni_support_lib PRIVATE ${SRC_CPPCLI})
source_group("cppcli" FILES ${SRC_CPPCLI})
set_target_properties(djinni_support_lib PROPERTIES
VS_DOTNET_REFERENCES "System;System.Core"
COMMON_LANGUAGE_RUNTIME ""
)

install(
FILES
"djinni/cppcli/Assert.hpp"
"djinni/cppcli/AutoPtr.hpp"
"djinni/cppcli/Error.hpp"
"djinni/cppcli/Marshal.hpp"
"djinni/cppcli/WrapperCache.hpp"
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/djinni/cppcli
)
endif()

if(NOT (DJINNI_WITH_OBJC OR DJINNI_WITH_JNI OR DJINNI_WITH_PYTHON))
message(FATAL_ERROR "At least one of DJINNI_WITH_OBJC or DJINNI_WITH_JNI or DJINNI_WITH_PYTHON must be enabled.")
if(NOT (DJINNI_WITH_OBJC OR DJINNI_WITH_JNI OR DJINNI_WITH_PYTHON OR DJINNI_WITH_CPPCLI))
message(FATAL_ERROR "At least one of DJINNI_WITH_OBJC or DJINNI_WITH_JNI or DJINNI_WITH_PYTHON or DJINNI_WITH_CPPCLI must be enabled.")
endif()


Expand Down
23 changes: 23 additions & 0 deletions djinni/cppcli/Assert.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright 2021 cross-language-cpp
//
// Licensed 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.
//

#pragma once

#ifdef _DEBUG
#define ASSERT(condition, ...) ::System::Diagnostics::Debug::Assert(condition, ##__VA_ARGS__)
#else
#define ASSERT(condition, ...) // This macro will completely evaporate in Release.
#endif
70 changes: 70 additions & 0 deletions djinni/cppcli/AutoPtr.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Copyright 2021 cross-language-cpp
//
// Licensed 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.
//

#pragma once

#include "Assert.hpp"

template <typename T>
ref struct AutoPtr {
AutoPtr() : _ptr(0) {}
AutoPtr(T* ptr) : _ptr(ptr) {}
AutoPtr(AutoPtr<T>% right) : _ptr(right.Release()) {}

~AutoPtr() {
delete _ptr;
_ptr = 0;
}
!AutoPtr() {
//ASSERT(0 == _ptr);
delete _ptr;
}
T* operator->() {
if (0 == _ptr) {
throw gcnew System::ObjectDisposedException(System::String::Empty);
}

return _ptr;
}

T* GetPointer() {
return _ptr;
}
T& GetRef() {
if (0 == _ptr) {
throw gcnew System::ObjectDisposedException(System::String::Empty);
}

return *_ptr;
}
T* Release() {
T* released = _ptr;
_ptr = 0;
return released;
}
void Reset() {
Reset(0);
}
void Reset(T* ptr) {
if (ptr != _ptr) {
delete _ptr;
_ptr = ptr;
}
}

private:
T* _ptr;
};
33 changes: 33 additions & 0 deletions djinni/cppcli/Error.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright 2021 cross-language-cpp
//
// Licensed 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.
//

#include "Error.hpp"

namespace djinni {

void ThrowUnimplemented(const char *, const char * msg) {
throw gcnew System::NotImplementedException(msclr::interop::marshal_as<System::String^>(msg));
}

void ThrowNativeExceptionFromCurrent(const char *) {
try {
throw;
} catch (const std::exception & e) {
throw gcnew System::Exception(msclr::interop::marshal_as<System::String^>(e.what()));
}
}

}
38 changes: 38 additions & 0 deletions djinni/cppcli/Error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Copyright 2021 cross-language-cpp
//
// Licensed 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.
//

#pragma once

#include <msclr\marshal_cppstd.h>

namespace djinni {

// Throws an exception for an unimplemented method call.
void ThrowUnimplemented(const char * ctx, const char * msg);

// Helper function for exception translation. Do not call directly!
void ThrowNativeExceptionFromCurrent(const char * ctx);

}

#define DJINNI_UNIMPLEMENTED(msg) \
::djinni::ThrowUnimplemented(__FUNCTION__, msg); \
return nullptr; // Silence warning C4715: not all control paths return a value

#define DJINNI_TRANSLATE_EXCEPTIONS() \
catch (const std::exception&) { \
::djinni::ThrowNativeExceptionFromCurrent(__FUNCTION__); \
}
Loading

0 comments on commit 35fdcac

Please sign in to comment.