diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index a81d5a15dcb..9a3bf0c0ec8 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -222,20 +222,26 @@ jobs:
if: ${{ matrix.build-type == 'Debug' }}
timeout-minutes: 2
run: |
-
- trap 'echo "MULTIPASS_TESTS_EXIT_CODE=$?" >> $GITHUB_ENV' EXIT
- instance_name=`/snap/bin/lxc --project snapcraft --format=csv --columns=n list | grep multipass`
+ instance_name=$(/snap/bin/lxc --project snapcraft --format=csv --columns=n list | grep multipass)
+ trap '
+ EXIT_STATUS=$?
+ echo "MULTIPASS_TESTS_EXIT_CODE=$EXIT_STATUS" >> $GITHUB_ENV
+ # Check if any files were generated in /cores/
+ if /snap/bin/lxc --project snapcraft exec "$instance_name" -- bash -c "[ \"\$(ls -A /coredump 2>/dev/null)\" ]"; then
+ echo "CORE_DUMPS_FOUND=true" >> $GITHUB_ENV
+ fi
+ ' EXIT
/snap/bin/lxc --project snapcraft start $instance_name
# Let's print the core pattern so we can check if it's successfully propagated to the container.
/snap/bin/lxc --project snapcraft exec $instance_name -- bash -c 'cat /proc/sys/kernel/core_pattern'
- # Create the directory for the coredumps
- /snap/bin/lxc --project snapcraft exec $instance_name -- bash -c 'mkdir -p /coredump'
+ # Create the directory for the coredumps and ensure the directory is empty
+ /snap/bin/lxc --project snapcraft exec $instance_name -- bash -c 'mkdir -p /coredump && rm -f /coredump/*'
# Enable coredumps by setting the core dump size to "unlimited", and run the tests.
/snap/bin/lxc --project snapcraft exec $instance_name -- bash -c "\
+ cd /root/parts/multipass/build && \
ulimit -c unlimited && \
- env CTEST_OUTPUT_ON_FAILURE=1 \
LD_LIBRARY_PATH=/root/stage/usr/lib/x86_64-linux-gnu/:/root/stage/lib/:/root/parts/multipass/build/lib/ \
- /root/parts/multipass/build/bin/multipass_tests"
+ ctest -V"
- name: Measure coverage
id: measure-coverage
@@ -279,7 +285,7 @@ jobs:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Pull coredump and executable from LXC container
- if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0'}}
+ if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' && env.CORE_DUMPS_FOUND }}
# do not cause job to fail if there are no coredumps available.
continue-on-error: true
run: |
@@ -294,16 +300,26 @@ jobs:
instance_name=`/snap/bin/lxc --project snapcraft --format=csv --columns=n list | grep multipass`
# Pull the crashed executable from the container
/snap/bin/lxc --project snapcraft file pull \
- -p -r "$instance_name/root/parts/multipass/build/bin/multipass_tests" /tmp/coredump/multipass_tests
+ -p -r "$instance_name/root/parts/multipass/build/bin/multipass_cpp_tests" /tmp/coredump/multipass_cpp_tests
echo "Pulled the executable."
# Pull the coredump folder
/snap/bin/lxc --project snapcraft file pull -p -r "$instance_name/coredump" /tmp/coredump
echo "Pulled the coredumps folder."
+
+ mapfile -t BINS < <(/snap/bin/lxc --project snapcraft exec "$instance_name" -- bash -c "
+ cd /root/parts/multipass/src/rxx
+ cargo test --workspace --no-run --message-format=json --target-dir /root/parts/multipass/build/rxx | \
+ jq -r 'select(.reason == \"compiler-artifact\" and .executable != null) | .executable'
+ ")
+ for bin in "${BINS[@]}"; do
+ /snap/bin/lxc --project snapcraft file pull -p -r "$instance_name/$bin" "/tmp/coredump/$(basename "$bin")"
+ done
+ echo "Pulled the rust executables"
set +o xtrace
- name: Upload test coredump
uses: actions/upload-artifact@v7
- if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' }}
+ if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' && env.CORE_DUMPS_FOUND }}
with:
name: buildandtest-test-crash-${{ runner.os }}-${{ matrix.build-type }}
path: /tmp/coredump/**
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 2122b180dec..120914864c2 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -75,6 +75,7 @@ jobs:
env:
BUILD_DIR: ${{ github.workspace }}/build
SCCACHE_DIR: ${{ github.workspace }}/.sccache
+ RUST_DIR: ${{ github.workspace }}/rxx
RUSTC_WRAPPER: "sccache"
steps:
@@ -110,6 +111,9 @@ jobs:
mkdir -p ~/.pip
echo -e "[install]\nbreak-system-packages = true" > ~/.pip/pip.conf
+ - name: Set up Rust
+ uses: dtolnay/rust-toolchain@stable
+
- name: Install dependencies from brew
run: |
# Avoid reinstalling cmake
@@ -229,16 +233,42 @@ jobs:
- name: Build
run: cmake --build ${{ env.BUILD_DIR }}
+ - name: Collect rust binary paths
+ working-directory: ${{ env.RUST_DIR }}
+ run: |
+ {
+ echo 'RUST_EXEC_PATHS<<__EOF__'
+ cargo test --workspace --no-run --message-format=json --target-dir ${{ env.BUILD_DIR }}/rxx \
+ | jq -r 'select(.reason == "compiler-artifact" and .executable != null) | .executable' \
+ | while IFS= read -r exe; do
+ [ -n "$exe" ] && echo "$exe"
+ done
+ echo '__EOF__'
+ } >> "$GITHUB_ENV"
+
- name: Enable core dump generation
working-directory: ${{ env.BUILD_DIR }}
shell: bash
run : |
# Allow core dumps
sudo sysctl -w kern.coredump=1
+ # Force core dump location/pattern to match the upload step expectations
+ sudo sysctl -w kern.corefile=/cores/core.%P
# This entitlement is needed for a proc to produce a coredump
/usr/libexec/PlistBuddy -c "Add :com.apple.security.get-task-allow bool true" segv.entitlements
# Code-sign the test executable to grant core dump entitlement
- codesign -s - -f --entitlements segv.entitlements bin/multipass_tests
+ codesign -s - -f --entitlements segv.entitlements bin/multipass_cpp_tests
+
+ RUST_EXECUTABLES=()
+ while IFS= read -r line; do
+ [[ -n "$line" ]] || continue
+ RUST_EXECUTABLES+=("$line")
+ done <<< "$RUST_EXEC_PATHS"
+ for RUST_EXEC in "${RUST_EXECUTABLES[@]}";
+ do
+ codesign -s - -f --entitlements segv.entitlements "$RUST_EXEC"
+ done
+
# Ensure destination exists and is writable
sudo mkdir -p /cores || true
sudo chmod 1777 /cores
@@ -250,21 +280,31 @@ jobs:
- name: Test
working-directory: ${{ env.BUILD_DIR }}
run: |
- # Catch and save the test executable's exit code
- trap 'echo "MULTIPASS_TESTS_EXIT_CODE=$?" >> $GITHUB_ENV' EXIT
+ # Define the trap to catch the exit code AND check for core dumps
+ trap '
+ EXIT_STATUS=$?
+ echo "MULTIPASS_TESTS_EXIT_CODE=$EXIT_STATUS" >> $GITHUB_ENV
+ # Check if any files were generated in /cores/
+ if [ "$(ls -A /cores/ 2>/dev/null)" ]; then
+ echo "CORE_DUMPS_FOUND=true" >> $GITHUB_ENV
+ fi
+ ' EXIT
+ # Clean existing core dumps for coredump detection
+ sudo rm -f /cores/*
# Set soft limit for the core file size (512MiB)
ulimit -c 1048576
- bin/multipass_tests
+ ctest -V
- name: Upload test coredump
uses: actions/upload-artifact@v7
- if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' }}
+ if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' && env.CORE_DUMPS_FOUND }}
with:
name: buildandtest-test-crash-${{ matrix.runs-on }}-${{ matrix.build-type }}
retention-days: 7
path: |
/cores/**
- ${{ env.BUILD_DIR }}/bin/multipass_tests
+ ${{ env.BUILD_DIR }}/bin/multipass_cpp_tests
+ ${{ env.RUST_EXEC_PATHS }}
- name: Package
id: cmake-package
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 77287fc10a7..8bf6df8f0f2 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -86,6 +86,9 @@ jobs:
id: build-params
uses: ./.github/actions/build-params
+ - name: Set up Rust
+ uses: dtolnay/rust-toolchain@stable
+
- name: Install specific QEMU from Choco
uses: crazy-max/ghaction-chocolatey@v4
with:
@@ -176,7 +179,7 @@ jobs:
- name: Test
working-directory: ${{ env.BUILD_DIR }}
run: |
- bin/multipass_tests
+ ctest -V
- name: Package
id: cmake-package
diff --git a/BUILD.linux.md b/BUILD.linux.md
index 7206e89632b..f0053b8b012 100644
--- a/BUILD.linux.md
+++ b/BUILD.linux.md
@@ -10,6 +10,13 @@ sudo apt install devscripts equivs
mk-build-deps -s sudo -i
```
+### Install the Rust compiler
+
+Go [here](https://rust-lang.org/tools/install/) for the most recent method. The current method is:
+```
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
+```
+
## Building
First, go into the repository root and get all the submodules:
diff --git a/BUILD.macOS.md b/BUILD.macOS.md
index 884ee5dc984..55b85502093 100644
--- a/BUILD.macOS.md
+++ b/BUILD.macOS.md
@@ -40,6 +40,13 @@ means to obtain cmake is with Homebrew .
brew install cmake
+### Rust compiler
+
+To build multipass you will need the rust compiler. It can be installed using brew:
+```
+brew install rustup
+```
+
Building
---------------------------------------
diff --git a/BUILD.windows.md b/BUILD.windows.md
index 6221d22142f..06e45e1a459 100644
--- a/BUILD.windows.md
+++ b/BUILD.windows.md
@@ -15,7 +15,7 @@ After chocolatey is installed you can now install the rest of the dependencies f
Powershell(Admin). To get the best results, in the following order:
```[pwsh]
-choco install cmake ninja qemu-img git wget unzip -yfd
+choco install cmake ninja qemu-img git wget unzip rustup.install -yfd
```
```[pwsh]
@@ -140,7 +140,7 @@ Finally, to build the project, run:
cmake --build . --parallel
```
-This builds `multipass`, `multipassd`, and `multipass_tests`.
+This builds `multipass`, `multipassd`, and `multipass_cpp_tests`.
To create an installer, run `cmake --build . --target package`.
## Running `multipass`
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a3bf438293d..bb60c2a4ca1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -174,25 +174,25 @@ if(MULTIPASS_ENABLE_FLUTTER_GUI)
set(DART_PROTOC_PLUGIN ${CMAKE_SOURCE_DIR}/3rd-party/protobuf.dart/protoc_plugin/bin/protoc_plugin.exe)
endif(MULTIPASS_ENABLE_FLUTTER_GUI)
-string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_lower)
+string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_definitions(-DMULTIPASS_COMPILER_CLANG)
- if(cmake_build_type_lower MATCHES "asan")
+ if(CMAKE_BUILD_TYPE_LOWER MATCHES "asan")
add_compile_options(-fno-omit-frame-pointer -fsanitize=address)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address")
- elseif(cmake_build_type_lower MATCHES "ubsan")
+ elseif(CMAKE_BUILD_TYPE_LOWER MATCHES "ubsan")
add_compile_options(-fno-omit-frame-pointer -fsanitize=undefined)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
- elseif(cmake_build_type_lower MATCHES "tsan")
+ elseif(CMAKE_BUILD_TYPE_LOWER MATCHES "tsan")
add_compile_options(-fno-omit-frame-pointer -fsanitize=thread)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=thread")
endif()
endif()
# these we want to apply even to 3rd-party
-if(cmake_build_type_lower MATCHES "coverage")
+if(CMAKE_BUILD_TYPE_LOWER MATCHES "coverage")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(--coverage)
if(COMMAND add_link_options)
@@ -315,7 +315,7 @@ else()
endif()
endif()
-if(cmake_build_type_lower MATCHES "coverage")
+if(CMAKE_BUILD_TYPE_LOWER MATCHES "coverage")
find_program(GCOV gcov)
find_program(LCOV lcov)
find_program(GENHTML genhtml)
@@ -337,7 +337,7 @@ if(cmake_build_type_lower MATCHES "coverage")
endif()
add_custom_target(covreport
- DEPENDS multipass_tests
+ DEPENDS multipass_cpp_tests rust_coverage_tarpaulin
WORKING_DIRECTORY ${CMAKE_BUILD_DIR}
COMMAND ${LCOV} --directory . --zerocounters
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test
@@ -351,7 +351,9 @@ if(cmake_build_type_lower MATCHES "coverage")
${CMAKE_BINARY_DIR}'/*'
--output-file coverage.cleaned
COMMAND ${CMAKE_COMMAND} -E remove coverage.info
- COMMAND ${GENHTML} -o coverage coverage.cleaned
+ COMMAND ${CMAKE_COMMAND} -E remove coverage.info
+ COMMAND ${LCOV} -a coverage.cleaned -a lcov.info -o total_coverage.info
+ COMMAND ${GENHTML} -o coverage total_coverage.info
)
endif()
endif()
@@ -371,6 +373,9 @@ if(MULTIPASS_ENABLE_TESTS)
add_subdirectory(tests/unit)
endif()
+#Added later to be compatible with enable_testing() above
+add_subdirectory(rxx)
+
include(packaging/cpack.cmake OPTIONAL)
# Disable .clang-tidy in build folder
diff --git a/include/multipass/name_generator.h b/include/multipass/name_generator.h
index 7f607896e4c..48d8adf5232 100644
--- a/include/multipass/name_generator.h
+++ b/include/multipass/name_generator.h
@@ -37,5 +37,37 @@ class NameGenerator : private DisabledCopyMove
NameGenerator() = default;
};
-NameGenerator::UPtr make_default_name_generator();
+namespace petname
+{
+enum NumWords
+{
+ One,
+ Two,
+ Three,
+};
+template
+concept IsValidSeparator = (S == '-' || S == '_');
+
+namespace detail
+{
+NameGenerator::UPtr make_petname_provider_impl(NumWords num_words, char separator);
+} // namespace detail
+
+// Templated functions to avoid exposing the PetnameProvider type in the interface header with
+// the default arguments. The templates ensure the argument checks are done at compile-time.
+template
+ requires IsValidSeparator
+NameGenerator::UPtr make_petname_provider()
+{
+ return detail::make_petname_provider_impl(NW, S);
+};
+
+template
+ requires IsValidSeparator
+NameGenerator::UPtr make_petname_provider()
+{
+ return detail::make_petname_provider_impl(NumWords::Two, S);
+}
+} // namespace petname
+
} // namespace multipass
diff --git a/packaging/cpack.cmake b/packaging/cpack.cmake
index f34372ff8c5..d687438f9d7 100644
--- a/packaging/cpack.cmake
+++ b/packaging/cpack.cmake
@@ -74,7 +74,7 @@ if (MSVC)
# and creats an install(PROGRAMS ...) rule using the destination and component IDs setup below.
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION bin)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT multipassd)
- if(cmake_build_type_lower STREQUAL "debug")
+ if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
set(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE)
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
endif()
diff --git a/packaging/gui-less/snap/snapcraft.yaml b/packaging/gui-less/snap/snapcraft.yaml
index 0893497e88c..79f018205e2 100644
--- a/packaging/gui-less/snap/snapcraft.yaml
+++ b/packaging/gui-less/snap/snapcraft.yaml
@@ -195,6 +195,11 @@ parts:
override-pull: |
set -e
+ if ! snap list rustup >/dev/null 2>&1; then
+ snap install rustup --classic
+ rustup default stable
+ fi
+
# https://github.com/microsoft/vcpkg/issues/37279
# vcpkg-systemd: required for meson.build
python3 -m pip install --break-system-packages jinja2
diff --git a/rxx/CMakeLists.txt b/rxx/CMakeLists.txt
new file mode 100644
index 00000000000..77e0c0db446
--- /dev/null
+++ b/rxx/CMakeLists.txt
@@ -0,0 +1,150 @@
+# 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 version 3 as
+# published by the Free Software Foundation.
+#
+# 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 .
+
+#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_BUILD_DIR "${CMAKE_BINARY_DIR}/rxx")
+set(RUST_BIN_DIR "${RUST_BUILD_DIR}/${RUST_BUILD}")
+
+#Find cargo, which will be a hard dependency
+find_program(CARGO_EXECUTABLE cargo)
+#Set the build directory via env variable
+set(CARGO_CMD ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${RUST_BUILD_DIR}" ${CARGO_EXECUTABLE})
+#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
+ namegen
+)
+
+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 "${RUST_BUILD_DIR}/cxxbridge")
+endif()
+
+set(ACTIVE_FEATURES "default")
+set(CARGO_GENERATED_FILES "")
+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_BIN_DIR}/${LIB_PREFIX}${CRATE_NAME}.${LIB_SUFFIX}")
+ #Target to depend on cargo build.
+ 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 "$")
+ endif()
+ set(BRIDGE_SOURCE "${RUST_BUILD_DIR}/cxxbridge/${CRATE_NAME}/src")
+ set(BRIDGE_CC "${BRIDGE_SOURCE}/lib.rs.cc")
+
+ list(APPEND CARGO_GENERATED_FILES "${RUST_OUT}" "${BRIDGE_CC}")
+ set_source_files_properties(${BRIDGE_CC} PROPERTIES GENERATED TRUE)
+ set_source_files_properties(${RUST_OUT} PROPERTIES GENERATED TRUE)
+
+ #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})
+ endif()
+endforeach()
+
+list(JOIN ACTIVE_FEATURES "," FEATURES_CSV)
+add_custom_command(
+ OUTPUT ${CARGO_GENERATED_FILES}
+ COMMAND ${CARGO_CMD} build
+ --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml
+ --workspace
+ ${RUST_BUILD_FLAG}
+ --lib
+ --features "${FEATURES_CSV}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ COMMENT "Building Rust crates and generating C++ glue..."
+)
+
+add_custom_target(cargo_build DEPENDS ${CARGO_GENERATED_FILES})
+
+foreach(CRATE_NAME ${CRATES})
+ if(TARGET ${CRATE_NAME})
+ add_dependencies(${CRATE_NAME} cargo_build)
+ add_dependencies(${CRATE_NAME}_rs cargo_build)
+ endif()
+endforeach()
+#The macros crate cannot contain tests
+set(TESTING_EXCLUDE_CRATES_ARG --exclude macros)
+add_test(
+ NAME multipass_rust_tests
+ COMMAND ${CARGO_CMD} test
+ --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml
+ --no-fail-fast
+ --workspace
+ --lib
+ --tests
+ --features "${FEATURES_CSV}"
+ ${TESTING_EXCLUDE_CRATES_ARG}
+)
+set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${RUST_BIN_DIR}")
+
+if(CMAKE_BUILD_TYPE_LOWER MATCHES "coverage")
+ execute_process(
+ COMMAND ${CARGO_CMD} tarpaulin --version
+ RESULT_VARIABLE TARPAULIN_INSTALLED
+ OUTPUT_VARIABLE TARPAULIN_VERSION
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ if (NOT TARPAULIN_INSTALLED EQUAL 0)
+ execute_process(
+ COMMAND ${CARGO_CMD} install cargo-tarpaulin
+ )
+ endif()
+ #Engine is for non-x86_64 processors. Can only use if return codes from test are always 0 + no fork or similar syscalls
+ add_custom_target(rust_coverage_tarpaulin
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND ${CARGO_CMD} tarpaulin
+ --out Lcov
+ --output-dir ${CMAKE_BINARY_DIR}
+ --exclude-files '../*'
+ --engine llvm
+ --workspace
+ ${TESTING_EXCLUDE_CRATES_ARG}
+ COMMENT "Running Rust coverage with Tarpaulin"
+ )
+endif()
diff --git a/rxx/Cargo.lock b/rxx/Cargo.lock
new file mode 100644
index 00000000000..1ed313d36ce
--- /dev/null
+++ b/rxx/Cargo.lock
@@ -0,0 +1,412 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "anstyle"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
+
+[[package]]
+name = "cc"
+version = "1.2.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "clap"
+version = "4.5.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
+dependencies = [
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
+
+[[package]]
+name = "codespan-reporting"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681"
+dependencies = [
+ "serde",
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "cxx"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbda285ba6e5866529faf76352bdf73801d9b44a6308d7cd58ca2379f378e994"
+dependencies = [
+ "cc",
+ "cxx-build",
+ "cxxbridge-cmd",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "foldhash",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9efde466c5d532d57efd92f861da3bdb7f61e369128ce8b4c3fe0c9de4fa4d"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "indexmap",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-cmd"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3efb93799095bccd4f763ca07997dc39a69e5e61ab52d2c407d4988d21ce144d"
+dependencies = [
+ "clap",
+ "codespan-reporting",
+ "indexmap",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3092010228026e143b32a4463ed9fa8f86dca266af4bf5f3b2a26e113dbe4e45"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31d72ebfcd351ae404fb00ff378dfc9571827a00722c9e735c9181aec320ba0a"
+dependencies = [
+ "indexmap",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "indexmap"
+version = "2.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.178"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "namegen"
+version = "0.1.0"
+dependencies = [
+ "cxx",
+ "cxx-build",
+ "macros",
+ "rand",
+ "static_assertions",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "scratch"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+
+[[package]]
+name = "wasip2"
+version = "1.0.1+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/rxx/Cargo.toml b/rxx/Cargo.toml
new file mode 100644
index 00000000000..57087e5103a
--- /dev/null
+++ b/rxx/Cargo.toml
@@ -0,0 +1,45 @@
+# 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 version 3 as
+# published by the Free Software Foundation.
+#
+# 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 .
+
+[workspace]
+members = [
+ "macros",
+ "namegen",
+]
+resolver = "2"
+
+[workspace.package]
+version = "0.1.0"
+edition = "2024"
+rust-version = "1.91"
+
+[workspace.dependencies]
+cxx = "^1.0.0"
+cxx-build = "^1.0.0"
+static_assertions = "^1.1.0"
+syn = "^2.0.0"
+quote = "^1.0.0"
+proc-macro2 = "^1.0.0"
+rand = "^0.9.0"
+macros = { path = "macros" }
+namegen = { path = "namegen" }
+
+# This is debug
+[profile.dev]
+codegen-units = 1 #Less threads to compete with C++ compilation
+incremental = false #Removing incremental compilation allows lower codegen count
+
+[profile.release]
+lto = true #Removes unused code sections
+codegen-units = 1 #Less threads to compete with C++ compilation
diff --git a/rxx/macros/Cargo.toml b/rxx/macros/Cargo.toml
new file mode 100644
index 00000000000..fddf3cf1734
--- /dev/null
+++ b/rxx/macros/Cargo.toml
@@ -0,0 +1,27 @@
+# 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 version 3 as
+# published by the Free Software Foundation.
+#
+# 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 .
+
+[package]
+name = "macros"
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote.workspace = true
+syn.workspace = true
+proc-macro2.workspace = true
diff --git a/rxx/macros/src/lib.rs b/rxx/macros/src/lib.rs
new file mode 100644
index 00000000000..d9a8c5bd44b
--- /dev/null
+++ b/rxx/macros/src/lib.rs
@@ -0,0 +1,40 @@
+/*
+ * 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 .
+ *
+ */
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{LitStr, parse_macro_input};
+
+#[proc_macro]
+pub fn make_word_array(input: TokenStream) -> TokenStream {
+ // Parse filename as string literal
+ let filename = parse_macro_input!(input as LitStr).value();
+
+ // Read file contents
+ let content = std::fs::read_to_string(&filename).expect("Could not read file");
+
+ // Split & reformat as string literals for quote!
+ let words: Vec = content
+ .split_whitespace()
+ .map(|word| syn::LitStr::new(word, Span::call_site()))
+ .collect();
+
+ let output = quote! {
+ [ #( #words ),* ]
+ };
+ output.into()
+}
diff --git a/rxx/namegen/CMakeLists.txt b/rxx/namegen/CMakeLists.txt
new file mode 100644
index 00000000000..e1c15849fa7
--- /dev/null
+++ b/rxx/namegen/CMakeLists.txt
@@ -0,0 +1,28 @@
+# 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 version 3 as
+# published by the Free Software Foundation.
+#
+# 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 .
+
+# Here the rust build depends on the C++ side of the rust_logger bridge because
+# the rust_logger crate is compiled within this crate's static library.
+# Because we did not build it, we specify the dependency like:
+#list(APPEND ${crate_name}_link_libs "$")
+#Add necessary sources for wrappers (it is advised that the source is named after
+# the header in include/multipass/)
+#target_sources(${crate_name} PRIVATE "src/name_generator_rs.cpp")
+#Link necessary libraries for the exceptions header
+#target_link_libraries(${crate_name} PRIVATE fmt::fmt-header-only Qt::Core)
+
+target_sources(namegen PRIVATE
+ "src/name_generator.cpp"
+ "src/petname_generator.cpp"
+ )
diff --git a/rxx/namegen/Cargo.toml b/rxx/namegen/Cargo.toml
new file mode 100644
index 00000000000..6d45ebae236
--- /dev/null
+++ b/rxx/namegen/Cargo.toml
@@ -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 version 3 as
+# published by the Free Software Foundation.
+#
+# 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 .
+
+[package]
+name = "namegen"
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+
+[lib]
+crate-type = ["staticlib","rlib"]
+
+[features]
+default = []
+
+[dependencies]
+cxx.workspace = true
+macros.workspace = true
+static_assertions.workspace = true
+rand.workspace = true
+
+[build-dependencies]
+cxx-build.workspace = true
diff --git a/src/petname/make_name_generator.cpp b/rxx/namegen/build.rs
similarity index 68%
rename from src/petname/make_name_generator.cpp
rename to rxx/namegen/build.rs
index 9981db98692..9e0989f4783 100644
--- a/src/petname/make_name_generator.cpp
+++ b/rxx/namegen/build.rs
@@ -13,16 +13,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
- * Authored by: Alberto Aguirre
- *
*/
-#include "petname.h"
-#include
-
-namespace mp = multipass;
-
-mp::NameGenerator::UPtr mp::make_default_name_generator()
-{
- return std::make_unique(mp::Petname::NumWords::TWO, "-");
+fn main() {
+ // Generate the .h and .cc files (_do not_ compile them)
+ let _ = cxx_build::bridge("src/lib.rs"); // drops the builder, just codegen
+ println!("cargo:rerun-if-changed=src/lib.rs");
}
diff --git a/src/petname/adjectives.txt b/rxx/namegen/src/adjectives.txt
similarity index 100%
rename from src/petname/adjectives.txt
rename to rxx/namegen/src/adjectives.txt
diff --git a/src/petname/adverbs.txt b/rxx/namegen/src/adverbs.txt
similarity index 100%
rename from src/petname/adverbs.txt
rename to rxx/namegen/src/adverbs.txt
diff --git a/rxx/namegen/src/lib.rs b/rxx/namegen/src/lib.rs
new file mode 100644
index 00000000000..b772c9128d0
--- /dev/null
+++ b/rxx/namegen/src/lib.rs
@@ -0,0 +1,44 @@
+/*
+ * 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 .
+ *
+ */
+
+pub mod petname_error;
+pub mod petname_generator;
+use petname_generator::make_petname;
+
+#[cxx::bridge(namespace = "multipass::petname")]
+pub mod ffi {
+ //Both the stated here and in extern C++ tells the CXX bridge generator
+ //to use the C++-side definition of NumWords
+ #[repr(i32)]
+ pub enum NumWords {
+ One,
+ Two,
+ Three,
+ }
+
+ #[namespace = "rxx::petname"]
+ extern "Rust" {
+ //Result allows CXX to turn returned Rust Errors into cxx::RustError
+ //on the C++ side.
+ fn make_petname(num_words: NumWords, separator: c_char) -> Result;
+ }
+ #[namespace = "multipass::petname"]
+ extern "C++" {
+ include!("multipass/name_generator.h");
+ type NumWords;
+ }
+}
diff --git a/rxx/namegen/src/name_generator.cpp b/rxx/namegen/src/name_generator.cpp
new file mode 100644
index 00000000000..eb1687915c0
--- /dev/null
+++ b/rxx/namegen/src/name_generator.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "petname_generator.h"
+
+#include
+
+#include
+
+namespace mp = multipass;
+namespace mpp = multipass::petname;
+
+mp::NameGenerator::UPtr mpp::detail::make_petname_provider_impl(mpp::NumWords num_words,
+ char separator)
+{
+ return std::make_unique(num_words, separator);
+}
diff --git a/src/petname/names.txt b/rxx/namegen/src/names.txt
similarity index 100%
rename from src/petname/names.txt
rename to rxx/namegen/src/names.txt
diff --git a/rxx/namegen/src/petname_error.rs b/rxx/namegen/src/petname_error.rs
new file mode 100644
index 00000000000..864fa251ab3
--- /dev/null
+++ b/rxx/namegen/src/petname_error.rs
@@ -0,0 +1,35 @@
+/*
+ * 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 .
+ *
+ */
+
+use std::fmt;
+
+#[derive(Debug, PartialEq)]
+pub enum PetnameError {
+ InvalidWordNumber(i32),
+ InvalidSeparator(i8),
+}
+
+impl fmt::Display for PetnameError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ PetnameError::InvalidWordNumber(num) => write!(f, "Invalid word number: {}", num),
+ PetnameError::InvalidSeparator(sep) => {
+ write!(f, "Invalid separator, ASCII code: {}", sep)
+ }
+ }
+ }
+}
diff --git a/rxx/namegen/src/petname_generator.cpp b/rxx/namegen/src/petname_generator.cpp
new file mode 100644
index 00000000000..66948afc262
--- /dev/null
+++ b/rxx/namegen/src/petname_generator.cpp
@@ -0,0 +1,16 @@
+
+
+#include "petname_generator.h"
+#include
+
+namespace mp = multipass;
+
+mp::PetnameGenerator::PetnameGenerator(mp::petname::NumWords num_words, char separator)
+ : num_words{num_words}, separator{separator}
+{
+}
+
+std::string mp::PetnameGenerator::make_name()
+{
+ return std::string(rxx::petname::make_petname(num_words, separator));
+}
diff --git a/rxx/namegen/src/petname_generator.h b/rxx/namegen/src/petname_generator.h
new file mode 100644
index 00000000000..9dc556f452d
--- /dev/null
+++ b/rxx/namegen/src/petname_generator.h
@@ -0,0 +1,38 @@
+/*
+ * 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 .
+ *
+ */
+
+#pragma once
+
+#include
+
+#include
+
+namespace multipass
+{
+
+class PetnameGenerator final : public NameGenerator
+{
+public:
+ PetnameGenerator(petname::NumWords num_words, char separator);
+
+ std::string make_name() override;
+
+private:
+ petname::NumWords num_words;
+ char separator;
+};
+} // namespace multipass
diff --git a/rxx/namegen/src/petname_generator.rs b/rxx/namegen/src/petname_generator.rs
new file mode 100644
index 00000000000..d9238eea6d8
--- /dev/null
+++ b/rxx/namegen/src/petname_generator.rs
@@ -0,0 +1,63 @@
+/*
+ * 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 .
+ *
+ */
+
+use crate::ffi::NumWords;
+use crate::petname_error::PetnameError;
+
+use macros::make_word_array;
+use rand::{prelude::IndexedRandom, rngs::ThreadRng};
+use static_assertions as sa;
+
+use std::os::raw::c_char;
+//Paths are relative to the workspace root
+const ADJECTIVES: &[&str] = &make_word_array!("namegen/src/adjectives.txt");
+const ADVERBS: &[&str] = &make_word_array!("namegen/src/adverbs.txt");
+const NOUNS: &[&str] = &make_word_array!("namegen/src/names.txt");
+
+sa::const_assert!(!NOUNS.is_empty());
+sa::const_assert!(!ADJECTIVES.is_empty());
+sa::const_assert!(!ADVERBS.is_empty());
+
+pub fn make_petname(num_words: NumWords, separator_8: c_char) -> Result {
+ let separator_c = separator_8 as u8 as char;
+ if !matches!(separator_c, '-' | '_') {
+ return Err(PetnameError::InvalidSeparator(separator_8 as i8));
+ }
+
+ let sources: &[&[&str]] = match num_words {
+ NumWords::One => &[NOUNS],
+ NumWords::Two => &[ADJECTIVES, NOUNS],
+ NumWords::Three => &[ADVERBS, ADJECTIVES, NOUNS],
+ num => return Err(PetnameError::InvalidWordNumber(num.repr)),
+ };
+
+ let mut rng_engine = rand::rng();
+
+ let words: Vec<_> = sources
+ .iter()
+ .map(|&arr| choose_from_str_array(arr, &mut rng_engine))
+ .collect();
+
+ Ok(words.join(&separator_c.to_string()))
+}
+fn choose_from_str_array(word_array: &[&'static str], rng: &mut ThreadRng) -> &'static str {
+ match word_array.choose(rng).copied() {
+ Some(str) => str,
+ //If one of the sources of words was empty, compile-time failure
+ _ => unreachable!(),
+ }
+}
diff --git a/rxx/namegen/tests/test_petname_error.rs b/rxx/namegen/tests/test_petname_error.rs
new file mode 100644
index 00000000000..bd4d8cb4035
--- /dev/null
+++ b/rxx/namegen/tests/test_petname_error.rs
@@ -0,0 +1,12 @@
+use namegen::petname_error::PetnameError;
+#[test]
+fn test_error_display() {
+ assert_eq!(
+ PetnameError::InvalidWordNumber(5).to_string(),
+ "Invalid word number: 5"
+ );
+ assert_eq!(
+ PetnameError::InvalidSeparator(64).to_string(),
+ "Invalid separator, ASCII code: 64"
+ );
+}
diff --git a/rxx/namegen/tests/test_petname_generator.rs b/rxx/namegen/tests/test_petname_generator.rs
new file mode 100644
index 00000000000..7adbfdbe873
--- /dev/null
+++ b/rxx/namegen/tests/test_petname_generator.rs
@@ -0,0 +1,40 @@
+use namegen::{ffi::NumWords, petname_error::PetnameError, petname_generator::make_petname};
+use std::collections::HashSet;
+use std::ffi::c_char;
+
+#[test]
+fn generates_requested_word_number_with_separator() {
+ let petname_1 = make_petname(NumWords::One, '-' as c_char).unwrap();
+ let petname_2 = make_petname(NumWords::Two, '-' as c_char).unwrap();
+ let petname_3 = make_petname(NumWords::Three, '-' as c_char).unwrap();
+ assert_eq!(petname_1.split('-').count(), 1);
+ assert_eq!(petname_2.split('-').count(), 2);
+ assert_eq!(petname_3.split('-').count(), 3);
+}
+
+#[test]
+fn filters_out_bad_input() {
+ //First we test that failure is not due to CXX enum syntax
+ let result = make_petname(NumWords { repr: 0 }, '-' as c_char);
+ assert!(result.is_ok());
+ let result = make_petname(NumWords { repr: 4 }, '-' as c_char);
+ assert!(matches!(result, Err(PetnameError::InvalidWordNumber(4))));
+
+ let result = make_petname(NumWords::One, '(' as c_char);
+ assert!(matches!(result, Err(PetnameError::InvalidSeparator(_))));
+}
+
+#[test]
+fn can_generate_unique_names() {
+ let mut hashset: HashSet = HashSet::new();
+ const TOTAL_NAMES: usize = 1000;
+
+ for i in 0..TOTAL_NAMES {
+ let petname = make_petname(NumWords::Three, '-' as c_char).unwrap();
+ assert!(
+ hashset.insert(petname),
+ "Generated duplicate petname at iteration {}",
+ i
+ );
+ }
+}
diff --git a/snap/local/snapcraft-ci-override.yaml.in b/snap/local/snapcraft-ci-override.yaml.in
index 53f6ff75176..6fcc517c29e 100644
--- a/snap/local/snapcraft-ci-override.yaml.in
+++ b/snap/local/snapcraft-ci-override.yaml.in
@@ -67,3 +67,5 @@ parts:
- GITHUB_ACTIONS: '${GITHUB_ACTIONS}'
- CMAKE_PRESET: '${CMAKE_PRESET}'
- MP_ALLOW_OPTIONAL_FEATURES: '${MP_ALLOW_OPTIONAL_FEATURES}'
+ build-packages:
+ - jq
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index de4ae6ae56b..b33d3686146 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -211,6 +211,11 @@ parts:
override-pull: |
set -e
+ if ! snap list rustup >/dev/null 2>&1; then
+ snap install rustup --classic
+ rustup default stable
+ fi
+
# https://github.com/microsoft/vcpkg/issues/37279
# vcpkg-systemd: required for meson.build
python3 -m pip install --break-system-packages jinja2
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 221d2f083fc..ab60dee9eca 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -25,7 +25,6 @@ add_subdirectory(image_host)
add_subdirectory(iso)
add_subdirectory(logging)
add_subdirectory(network)
-add_subdirectory(petname)
add_subdirectory(platform)
add_subdirectory(process)
add_subdirectory(rpc)
diff --git a/src/client/gui/CMakeLists.txt b/src/client/gui/CMakeLists.txt
index 4cb78238493..4440f599513 100644
--- a/src/client/gui/CMakeLists.txt
+++ b/src/client/gui/CMakeLists.txt
@@ -19,7 +19,7 @@ message(STATUS "Setting build number to: ${MULTIPASS_BUILD_NUMBER}")
add_library(dart_ffi SHARED ffi/dart_ffi.cpp)
target_link_libraries(dart_ffi
- petname
+ namegen
client_common
client_platform
)
diff --git a/src/client/gui/ffi/dart_ffi.cpp b/src/client/gui/ffi/dart_ffi.cpp
index 7050de1ae0b..01ba280f6f3 100644
--- a/src/client/gui/ffi/dart_ffi.cpp
+++ b/src/client/gui/ffi/dart_ffi.cpp
@@ -32,7 +32,7 @@ char* generate_petname()
static constexpr auto error = "failed generating petname";
try
{
- static mp::NameGenerator::UPtr generator = mp::make_default_name_generator();
+ static mp::NameGenerator::UPtr generator = mp::petname::make_petname_provider();
const auto name = generator->make_name();
return strdup(name.c_str());
}
@@ -76,9 +76,7 @@ struct KeyCertificatePair get_cert_pair()
const auto provider = mpc::get_cert_provider();
const auto cert = provider->PEM_certificate();
const auto key = provider->PEM_signing_key();
- struct KeyCertificatePair pair
- {
- };
+ struct KeyCertificatePair pair{};
pair.pem_cert = strdup(cert.c_str());
pair.pem_priv_key = strdup(key.c_str());
return pair;
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index 14b26931226..24b0b143a63 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -34,7 +34,7 @@ target_link_libraries(daemon
fmt::fmt-header-only
image_host
logger
- petname
+ namegen
platform
rpc
settings
diff --git a/src/daemon/daemon_config.cpp b/src/daemon/daemon_config.cpp
index 12d08bbf34c..fecff90fa0f 100644
--- a/src/daemon/daemon_config.cpp
+++ b/src/daemon/daemon_config.cpp
@@ -193,7 +193,7 @@ std::unique_ptr mp::DaemonConfigBuilder::build()
days_to_expire);
}
if (name_generator == nullptr)
- name_generator = mp::make_default_name_generator();
+ name_generator = mp::petname::make_petname_provider();
if (server_address.empty())
server_address = platform::default_server_address();
if (ssh_key_provider == nullptr)
diff --git a/src/petname/CMakeLists.txt b/src/petname/CMakeLists.txt
deleted file mode 100644
index eb59e3e9d63..00000000000
--- a/src/petname/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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 version 3 as
-# published by the Free Software Foundation.
-#
-# 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 .
-#
-# Authored by: Alberto Aguirre
-
-set(PETNAME_GENERATED_SOURCE_DIR ${MULTIPASS_GENERATED_SOURCE_DIR}/multipass/petname)
-set(PETNAME_GENERATED_HEADER ${PETNAME_GENERATED_SOURCE_DIR}/names.h)
-file(MAKE_DIRECTORY ${PETNAME_GENERATED_SOURCE_DIR})
-
-add_executable(text_to_string_array
- text_to_string_array.cpp)
-
-add_custom_command(
- OUTPUT "${PETNAME_GENERATED_HEADER}"
- COMMAND $
- ARGS ${CMAKE_CURRENT_SOURCE_DIR}/adjectives.txt ${CMAKE_CURRENT_SOURCE_DIR}/adverbs.txt
- ${CMAKE_CURRENT_SOURCE_DIR}/names.txt ${PETNAME_GENERATED_HEADER}
- DEPENDS text_to_string_array
- COMMENT "Converting petnames to c++ header"
- VERBATIM)
-
-set_source_files_properties(${PETNAME_GENERATED_HEADER} PROPERTIES GENERATED TRUE)
-
-add_library(petname STATIC
- ${PETNAME_GENERATED_HEADER}
- petname.cpp
- make_name_generator.cpp)
diff --git a/src/petname/petname.cpp b/src/petname/petname.cpp
deleted file mode 100644
index 1079cd14ecb..00000000000
--- a/src/petname/petname.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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 .
- *
- * Authored by: Alberto Aguirre
- *
- */
-
-#include "petname.h"
-#include "multipass/petname/names.h"
-
-namespace mp = multipass;
-namespace
-{
-constexpr auto num_names = std::extent::value;
-constexpr auto num_adverbs = std::extent::value;
-constexpr auto num_adjectives = std::extent::value;
-
-// Arbitrary but arrays should have at least 100 entries each
-static_assert(num_names >= 100, "");
-static_assert(num_adverbs >= 100, "");
-static_assert(num_adjectives >= 100, "");
-
-std::mt19937 make_engine()
-{
- std::random_device device;
- return std::mt19937(device());
-}
-} // namespace
-
-mp::Petname::Petname(std::string separator) : Petname(NumWords::TWO, separator)
-{
-}
-
-mp::Petname::Petname(NumWords num_words) : Petname(num_words, "-")
-{
-}
-
-mp::Petname::Petname(NumWords num_words, std::string separator)
- : separator{separator},
- num_words{num_words},
- engine{make_engine()},
- name_dist{1, num_names - 1},
- adjective_dist{0, num_adjectives - 1},
- adverb_dist{0, num_adverbs - 1}
-{
-}
-
-std::string mp::Petname::make_name()
-{
- std::string name = multipass::petname::names[name_dist(engine)];
- std::string adjective = multipass::petname::adjectives[adjective_dist(engine)];
- std::string adverb = multipass::petname::adverbs[adverb_dist(engine)];
-
- switch (num_words)
- {
- case NumWords::ONE:
- return name;
- case NumWords::TWO:
- return adjective + separator + name;
- case NumWords::THREE:
- return adverb + separator + adjective + separator + name;
- default:
- throw std::invalid_argument("Invalid number of words chosen");
- }
-}
diff --git a/src/petname/petname.h b/src/petname/petname.h
deleted file mode 100644
index 1f44708c9fe..00000000000
--- a/src/petname/petname.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 .
- *
- * Authored by: Alberto Aguirre
- *
- */
-
-#pragma once
-
-#include
-
-#include
-#include
-
-namespace multipass
-{
-class Petname final : public NameGenerator
-{
-public:
- enum class NumWords
- {
- ONE,
- TWO,
- THREE
- };
-
- /// Constructs an instance that will generate names using
- /// the requested separator and the requested number of words
- Petname(NumWords num_words, std::string separator);
- /// Constructs an instance that will generate names using
- /// a default separator of "-" and the requested number of words
- explicit Petname(NumWords num_words);
- /// Constructs an instance that will generate names using
- /// the requested separator and two words
- explicit Petname(std::string separator);
-
- std::string make_name() override;
-
-private:
- std::string separator;
- NumWords num_words;
- std::mt19937 engine;
- std::uniform_int_distribution name_dist;
- std::uniform_int_distribution adjective_dist;
- std::uniform_int_distribution adverb_dist;
-};
-} // namespace multipass
diff --git a/src/petname/text_to_string_array.cpp b/src/petname/text_to_string_array.cpp
deleted file mode 100644
index dd296f3b6e2..00000000000
--- a/src/petname/text_to_string_array.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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 .
- *
- * Authored by: Alberto Aguirre
- *
- */
-
-#include
-#include
-#include
-#include
-
-namespace
-{
-void usage(char* argv[])
-{
- std::cout << "Usage:\n ";
- std::cout << argv[0] << "