Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ packaging/windows/custom-actions/x64/*

# https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
CMakeUserPresets.json
# Rust builds
rxx/target
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,12 @@ include_directories(
add_subdirectory(data)
add_subdirectory(src)


if(MULTIPASS_ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
add_subdirectory(rxx)

include(packaging/cpack.cmake OPTIONAL)

Expand Down
65 changes: 65 additions & 0 deletions include/multipass/exceptions/rust_exceptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

/*
* Copyright (C) Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#pragma once

#include <string>

#include <multipass/format.h>

namespace multipass
{
class RustException : public std::exception
{
// This alternative implementation is so that both rust::Error and RustExceptions can be
// captured by the same catch statement.
public:
RustException(const std::string& error)
: exception{fmt::format("FFI boundary error: {}", error)}
{
}
const char* what() const noexcept override
{
return exception.c_str();
}
const std::string& what_std() const noexcept
{
return exception;
}

private:
const std::string exception;
};

class FaultyFFIArgument : public RustException
{
public:
FaultyFFIArgument(const std::string& arg_name)
: RustException(fmt::format("Faulty FFI boundary argument: {}", arg_name))
{
}
};
class UnknownRustError : public RustException
{
public:
UnknownRustError(const std::string& error)
: RustException(fmt::format("Unknown Rust error: {}", error))
{
}
};
} // namespace multipass
34 changes: 33 additions & 1 deletion include/multipass/logging/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
*/

#pragma once
#include <iostream>

#include <memory>
#include <multipass/logging/level.h>
#include <multipass/logging/logger.h>
#include <rust/cxx.h>

#include <fmt/std.h> // standard library formatters
#include <fmt/xchar.h> // char-type agnostic formatting
Expand All @@ -41,7 +44,36 @@ void log_message(Level level, std::string_view category, std::string_view messag
void set_logger(std::shared_ptr<Logger> logger);
Level get_logging_level();
Logger* get_logger(); // for tests, don't rely on it lasting

namespace rust
{
void log_message(Level level, ::rust::String category, ::rust::String message);
class Base
{
public:
virtual ~Base() = default;
virtual void virt_func()
{
std::cout << "\nBase\n";
}
void base_func()
{
std::cout << "\nBase func\n";
}
};
class Derived : public Base
{
public:
~Derived() override = default;
void virt_func() override
{
std::cout << "\nDerived\n";
}
};
inline std::unique_ptr<Base> get_base()
{
return std::make_unique<Derived>();
}
} // namespace rust
/**
* Log with formatting support
*
Expand Down
34 changes: 34 additions & 0 deletions include/multipass/name_generator_rs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

/*
* Copyright (C) Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include <petnamers/src/lib.rs.h>
#include <rust/cxx.h>

// The purpose of this header is to act as 1: a prettyfier of the included header in source and 2:
// to declare all wrappers for the Rust code, which should always include a try catch with rethrow
// plus argument checking with throw if incorrect. Exception type has to be rust::Error because that
// type is declared as final and the caller of the wrapped function will expect that exception but
// could forget about a non-rust::Error-derived exception.

namespace multipass
{
namespace petname
{
std::string generate_petname(petnamers::NumWords word_count, char sep);
}
} // namespace multipass
81 changes: 81 additions & 0 deletions rxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#Find cargo, which will be a hard dependency
find_program(CARGO_EXECUTABLE cargo)

#Set build variables
if (cmake_build_type_lower MATCHES "release")
set(rust_build "release")
set(rust_build_flag "--release")
else()
set(rust_build "debug")
set(rust_build_flag "")
endif()
if (WIN32)
set(lib_prefix "")
set(lib_suffix "lib")
else()
set(lib_prefix "lib")
set(lib_suffix "a")
endif()
# Build output path
set(RUST_BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/target/${rust_build}")
#Crates to build. Both consumers and providers of code to the C++ side
#have to be built, since the CXX glue code has to be generated and compiled
#for said usage
set(CRATES
rust_logger
petnamers
)

if(NOT CRATES STREQUAL "")
# cxx.cc is compiled and linked into the static libraries by cargo automatically
# There is no documentation about this but you can check the symbols by doing
# nm -g mylib.a | grep string
# This means that no need to handle cxx.cc
add_library(cxx INTERFACE)
target_include_directories(cxx INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/target/cxxbridge")
endif()
set(CARGO_PRODUCTS "")
foreach(crate_name ${CRATES})
set(crate_path "${CMAKE_CURRENT_SOURCE_DIR}/${crate_name}")
#Check that it is a crate (since target is not a crate)
if (IS_DIRECTORY "${crate_path}" AND EXISTS "${crate_path}/Cargo.toml")
message(STATUS "Configuring crate: ${crate_path}")
#Static library path for the rust source.
set(rust_out "${RUST_BINARY_DIR}/${lib_prefix}${crate_name}.${lib_suffix}")
#Target to depend on cargo build.
list(APPEND CARGO_PRODUCTS ${rust_out})
add_library(${crate_name}_rs STATIC IMPORTED)
set_target_properties(${crate_name}_rs PROPERTIES IMPORTED_LOCATION ${rust_out})
set(${crate_name}_link_libs "")
if (WIN32)
list(APPEND ${crate_name}_link_libs "$<LINK_ONLY:ntdll>")
endif()
set(bridge_source "${CMAKE_CURRENT_SOURCE_DIR}/target/cxxbridge/${crate_name}/src")
set(bridge_cc "${bridge_source}/lib.rs.cc")

list(APPEND CARGO_PRODUCTS ${bridge_cc})
#Library creation to compile the CXX glue source
add_library(${crate_name} STATIC ${bridge_cc})
#Here we can place the crate-wise dependencies
add_subdirectory(${crate_name})
#The CXX glue staticlib must link the rust staticlib
target_link_libraries(${crate_name} PUBLIC ${crate_name}_rs cxx)
target_link_libraries(${crate_name}_rs INTERFACE ${${crate_name}_link_libs})
add_dependencies(${crate_name} ${crate_name}_rs)
endif()
endforeach()

add_custom_command(
OUTPUT ${CARGO_PRODUCTS}
COMMAND ${CARGO_EXECUTABLE} build ${rust_build_flag} --lib
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building Rust crates and generating C++ glue..."
)
add_test(
NAME rust_cargo_tests
COMMAND cargo test --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml
--no-fail-fast

)

set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/target")
Loading
Loading