diff --git a/.github/workflows/conan.yml b/.github/workflows/conan.yml index cd96e21c2..fb5009415 100644 --- a/.github/workflows/conan.yml +++ b/.github/workflows/conan.yml @@ -40,6 +40,8 @@ jobs: github.event_name == 'schedule' && steps.git_info.outputs.current_commit == steps.last_successful_commit.outputs.commit-hash + # TODO (RSDK-10666) add windows build testing + build_macos: if: github.repository_owner == 'viamrobotics' needs: [prepare] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db0728dbe..63b8d5549 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,6 +36,53 @@ jobs: uses: andymckay/cancel-action@0.2 if: steps.release_exists.outputs.id != '' + build_windows: + if: github.repository_owner == 'viamrobotics' + needs: [prepare] + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-windows + platform: windows_x86_64 + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.sha }} + + - name: Install dependencies + run: choco install -y conan git + + - name: Create package + shell: powershell + # TODO (RSDK-10666) Use conan invocations rather than cmake invocations here + run: | + Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 + refreshenv + conan profile detect + conan install . --output-folder=build-conan --build=missing -o "&:shared=False" + cmake . --preset conan-default + cmake --build --preset=conan-release --target ALL_BUILD install -j 8 + env: + CONAN_USER_HOME: c:/cache + CONAN_USER_HOME_SHORT: c:/cache/conan_shortpaths + + - name: Copy + run: | + cmake --install build-conan/build --prefix builds/viam-cpp-sdk-${{ matrix.platform }} + + - name: Create tar + run: | + tar -czvf builds/viam-cpp-sdk-${{ matrix.platform }}.tar.gz builds/viam-cpp-sdk-${{ matrix.platform }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: viam-cpp-sdk-${{ matrix.platform }}.tar.gz + path: builds/viam-cpp-sdk-${{ matrix.platform }}.tar.gz + build_macos: if: github.repository_owner == 'viamrobotics' needs: [prepare] diff --git a/.gitignore b/.gitignore index c37f076b3..584034097 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,9 @@ /build/* # For now, ignore any viam_rust_utils library in the root. +# The `lib` prefix is on unix-based systems only /libviam_rust_utils* +/viam_rust_utils* # Ignore clang cache. /.cache/ diff --git a/BUILDING.md b/BUILDING.md index 74f16b5d2..bf7d26613 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -260,6 +260,23 @@ have a different version of `protoc` available in your `PATH`, it will silently fail and later cause compilation failures due to protobuf version mismatches. +## Building for ARM Windows + +The C++ SDK works well on windows for both client and module code +provided there is internet connectivity. However, some manual work is +required to build for client code on ARM64 architecture. + +1. (client code only) clone [rust-utils](https://github.com/viamrobotics/rust-utils) >= v0.3.0 and +build locally with `cargo build --release`. Copy `target\release\viam_rust_utils.lib` +to the root of the C++ SDK directory. +2. Ensure `conan` is installed (see `Building with Conan` above). +3. Run the following: +``` +conan profile detect +cmake . --preset conan-default +cmake --build --preset=conan-release -j +``` + ## Options to Configure or Customize the Build ### Options for Package Search diff --git a/CMakeLists.txt b/CMakeLists.txt index 492c198ca..f7831ec56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,14 +266,20 @@ if (viam_rust_utils_files) ${viam_rust_utils_file} ONLY_IF_DIFFERENT ) -elseif(NOT WIN32) # TODO(RSDK-10366): Currently, rust_utils is not published for windows, so don't even try downloading +elseif(NOT WIN32 OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64") set(lvru_system_name ${CMAKE_SYSTEM_NAME}) if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(lvru_system_name "macosx") endif() + + set(lvru_system_processor ${CMAKE_SYSTEM_PROCESSOR}) + if(WIN32) + set(lvru_system_processor "x86_64") + endif() + file( DOWNLOAD - https://github.com/viamrobotics/rust-utils/releases/latest/download/${CMAKE_SHARED_LIBRARY_PREFIX}viam_rust_utils-${lvru_system_name}_${CMAKE_SYSTEM_PROCESSOR}${CMAKE_STATIC_LIBRARY_SUFFIX} + https://github.com/viamrobotics/rust-utils/releases/latest/download/${CMAKE_SHARED_LIBRARY_PREFIX}viam_rust_utils-${lvru_system_name}_${lvru_system_processor}${CMAKE_STATIC_LIBRARY_SUFFIX} ${viam_rust_utils_file} STATUS lvru_status ) @@ -283,24 +289,21 @@ elseif(NOT WIN32) # TODO(RSDK-10366): Currently, rust_utils is not published for if(NOT lvru_status_code EQUAL 0) message(FATAL_ERROR "No local viam_rust_utils found and failed to download: ${lvru_status_string}") endif() - +else() + message(WARNING "Currently running on Windows with no rust-utils file. Module code should work as expected, but client code may fail unexpectedly.") endif() -# TODO(RSDK-10366): Currently, rust_utils is not published for windows, so don't even declare the library -if (NOT WIN32) - add_library(viam_rust_utils SHARED IMPORTED) - +if (NOT WIN32 OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64") + add_library(viam_rust_utils STATIC IMPORTED) target_link_directories(viam_rust_utils INTERFACE "$" "$" ) - set_property(TARGET viam_rust_utils PROPERTY IMPORTED_LOCATION ${viam_rust_utils_file}) install( - IMPORTED_RUNTIME_ARTIFACTS viam_rust_utils - LIBRARY COMPONENT viam-cpp-sdk_runtime + FILES ${viam_rust_utils_file} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT viam-cpp-sdk_runtime ) endif() diff --git a/conanfile.py b/conanfile.py index 1fef0b57d..45eaad6b8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -91,10 +91,7 @@ def package(self): def package_info(self): - # TODO(RSDK-10366): Currently, rust_utils is not published for windows - # and the C++ SDK just doesn't include it as a dependency on that platform - if not self.settings.os == "Windows": - self.cpp_info.components["viam_rust_utils"].libs = ["viam_rust_utils"] + self.cpp_info.components["viam_rust_utils"].libs = ["viam_rust_utils"] self.cpp_info.components["viamsdk"].libs = ["viamsdk"] @@ -137,11 +134,8 @@ def package_info(self): "viamapi", ]) - # TODO(RSDK-10366): Currently, rust_utils is not published for windows - # and the C++ SDK just doesn't include it as a dependency on that platform - if self.settings.os != "Windows": - self.cpp_info.components["viamsdk"].requires.extend([ - "viam_rust_utils" - ]) + self.cpp_info.components["viamsdk"].requires.extend([ + "viam_rust_utils" + ]) self.cpp_info.components["viamsdk"].frameworks = ["Security"] diff --git a/src/viam/examples/README.md b/src/viam/examples/README.md index b80ee7781..ad29bb458 100644 --- a/src/viam/examples/README.md +++ b/src/viam/examples/README.md @@ -56,7 +56,7 @@ char *path = dial("", "", " struct API::traits { static ::viam::sdk::API api(); }; -} // namespace viam::sdk +} // namespace sdk +} // namespace viam // `GizmoClient` is the gRPC client implementation of a `Gizmo` component. class GizmoClient : public Gizmo { diff --git a/src/viam/examples/modules/complex/summation/api.hpp b/src/viam/examples/modules/complex/summation/api.hpp index 923b75a5a..3c4800899 100644 --- a/src/viam/examples/modules/complex/summation/api.hpp +++ b/src/viam/examples/modules/complex/summation/api.hpp @@ -29,12 +29,14 @@ class Summation : public Service { explicit Summation(std::string name); }; -namespace viam::sdk { +namespace viam { +namespace sdk { template <> struct API::traits { static API api(); }; -} // namespace viam::sdk +} // namespace sdk +} // namespace viam // `SummationClient` is the gRPC client implementation of a `Summation` // service. diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt index 637c8f933..5f7035b0a 100644 --- a/src/viam/sdk/CMakeLists.txt +++ b/src/viam/sdk/CMakeLists.txt @@ -276,10 +276,10 @@ target_link_libraries(viamsdk PRIVATE Threads::Threads ) -# TODO(RSDK-10366): Currently, rust_utils is not published for -# windows, so don't link to it. Instead, link a stub implementation -# that just calls `abort`. -if (NOT WIN32) +# if the `viam_rust_utils` target exists then we should use it. If not then +# we're probably on a non-x86_64 windows build or some other platform without +# automated `rust-utils` builds, so we should use the stubs instead. +if (TARGET viam_rust_utils) target_link_libraries(viamsdk PRIVATE viam_rust_utils ) @@ -287,11 +287,19 @@ else() target_sources(viamsdk PRIVATE rpc/private/viam_rust_utils_stubs.cpp) endif() + +# TODO several of these dependencies should properly be attached to `viam_rust_utils`, +# not `viamsdk`. However, we currently are unable to do so while maintaining compilation if (APPLE) target_link_libraries(viamsdk PUBLIC "-framework Security") -else() +elseif (NOT WIN32) target_link_libraries(viamsdk PRIVATE dl) target_link_libraries(viamsdk PRIVATE rt) +else() + target_link_libraries(viamsdk INTERFACE Ncrypt.lib) + target_link_libraries(viamsdk INTERFACE Secur32.lib) + target_link_libraries(viamsdk INTERFACE Ntdll.lib) + target_link_libraries(viamsdk INTERFACE Userenv.lib) endif() diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp index 740c50fff..6f4cd160f 100644 --- a/src/viam/sdk/robot/client.cpp +++ b/src/viam/sdk/robot/client.cpp @@ -294,7 +294,7 @@ std::shared_ptr RobotClient::at_address(const std::string& address, std::shared_ptr RobotClient::at_local_socket(const std::string& address, const Options& options) { - const std::string addr = "unix://" + address; + const std::string addr = "unix:" + address; auto robot = RobotClient::with_channel( ViamChannel(sdk::impl::create_viam_channel(addr, grpc::InsecureChannelCredentials())), options); diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp index 3c19a9edc..b9dceddc9 100644 --- a/src/viam/sdk/robot/client.hpp +++ b/src/viam/sdk/robot/client.hpp @@ -97,7 +97,7 @@ class RobotClient { /// @brief Creates a robot client connected to the robot at the provided local socket. /// @param address The local socket of the robot (a .sock file, etc.). /// @param options Options for connecting and refreshing. - /// Creates a direct connection to the robot using the `unix://` scheme. + /// Creates a direct connection to the robot using the `unix:` scheme. /// Only useful for connecting to robots across Unix sockets. static std::shared_ptr at_local_socket(const std::string& address, const Options& options); diff --git a/src/viam/sdk/rpc/dial.cpp b/src/viam/sdk/rpc/dial.cpp index 7425019f0..22c2c7645 100644 --- a/src/viam/sdk/rpc/dial.cpp +++ b/src/viam/sdk/rpc/dial.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -171,18 +172,23 @@ ViamChannel ViamChannel::dial(const char* uri, const boost::optionalc_str(); } - char* socket_path = ::dial( + char* proxy_path = ::dial( uri, entity, type, payload, opts.allows_insecure_downgrade(), float_timeout.count(), ptr); - if (socket_path == NULL) { + if (!proxy_path) { free_rust_runtime(ptr); throw Exception(ErrorCondition::k_connection, "Unable to establish connecting path"); } - std::string address("unix://"); - address += socket_path; + const std::string localhost_prefix("127.0.0.1"); + std::string address; + if (std::string(proxy_path).find(localhost_prefix) == std::string::npos) { + // proxy path is not a localhost address and is therefore a unix domain socket (UDS) + address += "unix:"; + } + address += proxy_path; return ViamChannel(sdk::impl::create_viam_channel(address, grpc::InsecureChannelCredentials()), - socket_path, + proxy_path, ptr); } diff --git a/src/viam/sdk/rpc/server.hpp b/src/viam/sdk/rpc/server.hpp index b938f7b1d..0615ce4e0 100644 --- a/src/viam/sdk/rpc/server.hpp +++ b/src/viam/sdk/rpc/server.hpp @@ -3,6 +3,8 @@ /// @brief Defines the `Server` class. #pragma once +#include + #include #include #include diff --git a/src/viam/sdk/tests/mocks/mock_robot.cpp b/src/viam/sdk/tests/mocks/mock_robot.cpp index ce9a62884..5522c91cf 100644 --- a/src/viam/sdk/tests/mocks/mock_robot.cpp +++ b/src/viam/sdk/tests/mocks/mock_robot.cpp @@ -299,7 +299,8 @@ ::grpc::Status MockRobotService::FrameSystemConfig( const ::viam::robot::v1::FrameSystemConfigRequest*, ::viam::robot::v1::FrameSystemConfigResponse* response) { auto client_md = context->client_metadata(); - if (auto client_info = client_md.find("viam_client"); client_info == client_md.end()) { + auto client_info = client_md.find("viam_client"); + if (client_info == client_md.end()) { return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "viam_client info not properly set in metadata"); } @@ -314,7 +315,8 @@ ::grpc::Status MockRobotService::TransformPose(::grpc::ServerContext* context, const ::viam::robot::v1::TransformPoseRequest*, ::viam::robot::v1::TransformPoseResponse* response) { auto client_md = context->client_metadata(); - if (auto client_info = client_md.find("viam_client"); client_info == client_md.end()) { + auto client_info = client_md.find("viam_client"); + if (client_info == client_md.end()) { return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "viam_client info not properly set in metadata"); } @@ -327,7 +329,8 @@ ::grpc::Status MockRobotService::GetMachineStatus( const ::viam::robot::v1::GetMachineStatusRequest*, ::viam::robot::v1::GetMachineStatusResponse* response) { auto client_md = context->client_metadata(); - if (auto client_info = client_md.find("viam_client"); client_info == client_md.end()) { + auto client_info = client_md.find("viam_client"); + if (client_info == client_md.end()) { return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "viam_client info not properly set in metadata"); } @@ -341,7 +344,8 @@ ::grpc::Status MockRobotService::GetOperations(::grpc::ServerContext* context, const ::viam::robot::v1::GetOperationsRequest*, ::viam::robot::v1::GetOperationsResponse* response) { auto client_md = context->client_metadata(); - if (auto client_info = client_md.find("viam_client"); client_info == client_md.end()) { + auto client_info = client_md.find("viam_client"); + if (client_info == client_md.end()) { return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "viam_client info not properly set in metadata"); }