diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8921bb6..24b7553 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -12,6 +12,9 @@ on: jobs: build-and-push: runs-on: ubuntu-latest + strategy: + matrix: + component: [server, client] steps: - name: Checkout code @@ -27,11 +30,9 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and tag the Docker image - run: make build-images - - - name: tag images - run: make tag-images - - - name: Push Docker images to GitHub Packages - run: make push-images + - name: Build container image + run: make build-${{ matrix.component }}-image + - name: Tag container image + run: make tag-${{ matrix.component }}-image + - name: Push container image + run: make push-${{ matrix.component }}-image diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0e3b3c..c08058d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release +name: Cross-Platform and Cross-Architecture Release Build on: push: @@ -7,27 +7,66 @@ on: jobs: build-and-release: - runs-on: ubuntu-latest + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + # Define combinations of operating systems and architectures + - {os: ubuntu-latest, arch: x64, server-artifact: serial_server_linux_x64, client-artifact: serial_client_linux_x64} + - {os: ubuntu-latest, arch: arm64, server-artifact: serial_server_linux_arm64, client-artifact: serial_client_linux_arm64} + - {os: windows-latest, arch: x64, server-artifact: serial_server_windows_x64.exe, client-artifact: serial_client_windows_x64.exe} + - {os: windows-latest, arch: arm64, server-artifact: serial_server_windows_arm64.exe, client-artifact: serial_client_windows_arm64.exe} + - {os: macos-latest, arch: x64, server-artifact: serial_server_mac_x64, client-artifact: serial_client_mac_x64} + - {os: macos-latest, arch: arm64, server-artifact: serial_server_mac_arm64, client-artifact: serial_client_mac_arm64} steps: - uses: actions/checkout@v3 + - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.x' - - name: Install dependencies + + - name: Install dependencies (Ubuntu) + if: startsWith(matrix.config.os, 'ubuntu') run: | sudo apt-get update sudo apt-get install -y libboost-all-dev + + - name: Install dependencies (macOS) + if: startsWith(matrix.config.os, 'macos') + run: | + brew install boost + + - name: Install dependencies (Windows) + if: startsWith(matrix.config.os, 'windows') + run: | + choco install boost-msvc-14.2 + + - name: Set up CMake (All platforms) + uses: lukka/get-cmake@latest + + - name: Configure CMake + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_ARCHITECTURE_ID=${{ matrix.config.arch }} + - name: Build run: | - cmake -S . -B build - cmake --build build - - name: Archive Production Artifacts + cmake --build build --config Release + + - name: Archive Server Production Artifacts uses: actions/upload-artifact@v3 with: - name: binaries - path: build/ + name: ${{ matrix.config.server-artifact }} + path: build/${{ matrix.config.server-artifact }} + + - name: Archive Client Production Artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.config.client-artifact }} + path: build/${{ matrix.config.client-artifact }} + - name: Create Release id: create_release uses: actions/create-release@v1 @@ -38,12 +77,54 @@ jobs: release_name: Release ${{ github.ref }} draft: false prerelease: false - - name: Upload Release Asset + + - name: Upload Server Release Asset + uses: actions/upload-release-asset@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/${{ matrix.config.server-artifact }} + asset_name: ${{ matrix.config.server-artifact }} + asset_content_type: application/octet-stream + + - name: Upload Client Release Asset uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./build/serial_server - asset_name: serial_server + asset_path: ./build/${{ matrix.config.client-artifact }} + asset_name: ${{ matrix.config.client-artifact }} asset_content_type: application/octet-stream + + container-image: + runs-on: ubuntu-latest + strategy: + matrix: + component: [server, client] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build container image + run: make build-${{ matrix.component }}-image + + - name: Tag containner image + run: make tag-${{ matrix.component }}-image + + - name: Push containner images + run: make push-${{ matrix.component }}-images + + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4803d8..c51e389 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,19 +8,55 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + # Define combinations of operating systems and architectures + - {os: ubuntu-latest, arch: x64, server-artifact: serial_server_linux_x64, client-artifact: serial_client_linux_x64} + - {os: ubuntu-latest, arch: arm64, server-artifact: serial_server_linux_arm64, client-artifact: serial_client_linux_arm64} + - {os: windows-latest, arch: x64, server-artifact: serial_server_windows_x64.exe, client-artifact: serial_client_windows_x64.exe} + - {os: windows-latest, arch: arm64, server-artifact: serial_server_windows_arm64.exe, client-artifact: serial_client_windows_arm64.exe} + - {os: macos-latest, arch: x64, server-artifact: serial_server_mac_x64, client-artifact: serial_client_mac_x64} + - {os: macos-latest, arch: arm64, server-artifact: serial_server_mac_arm64, client-artifact: serial_client_mac_arm64} steps: - uses: actions/checkout@v3 - - name: Install dependencies + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install dependencies (Ubuntu) + if: startsWith(matrix.config.os, 'ubuntu') run: | sudo apt-get update sudo apt-get install -y libboost-all-dev - - name: Run tests - run: make test - docker: + - name: Install dependencies (macOS) + if: startsWith(matrix.config.os, 'macos') + run: | + brew install boost + + - name: Install dependencies (Windows) + if: startsWith(matrix.config.os, 'windows') + run: | + choco install boost-msvc-14.2 + + - name: Set up CMake (All platforms) + uses: lukka/get-cmake@latest + + - name: make build + run: | + make build + + container-image: runs-on: ubuntu-latest + strategy: + matrix: + component: [server, client] steps: - name: Checkout code @@ -29,5 +65,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Build and tag the Docker image - run: make build-images + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build container image + run: make build-${{ matrix.component }}-image diff --git a/CMakeLists.txt b/CMakeLists.txt index a5df549..3d76212 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ target_link_libraries(logging PUBLIC Boost::date_time ) -add_library(serial_server_lib src/logging.cpp src/server.cpp src/VirtualSerialPort.cpp) # Include VirtualSerialPort.cpp +add_library(serial_server_lib src/logging.cpp src/SerialServer.cpp src/VirtualSerialPort.cpp) target_include_directories(serial_server_lib PUBLIC ${CMAKE_SOURCE_DIR}/include) target_link_libraries(serial_server_lib PRIVATE Boost::system @@ -43,7 +43,7 @@ target_link_libraries(serial_server_lib PRIVATE pthread ) -add_executable(serial_server src/server.cpp) +add_executable(serial_server src/SerialServer.cpp) target_link_libraries(serial_server PRIVATE serial_server_lib logging @@ -54,7 +54,7 @@ target_link_libraries(serial_server PRIVATE Boost::date_time ) -add_executable(serial_client src/client.cpp) +add_executable(serial_client src/SerialClient.cpp) target_link_libraries(serial_client PRIVATE serial_server_lib logging diff --git a/Makefile b/Makefile index ed6d855..0aa59af 100644 --- a/Makefile +++ b/Makefile @@ -3,38 +3,52 @@ REPO = ghcr.io/janekbaraniewski/ser2net2ser VERSION ?= latest COMMIT_HASH ?= $(shell git rev-parse --short HEAD) -clean: +help: ## Show this help message + @echo "Usage: make [target]" + @echo "" + @echo "Available targets:" + @grep -E '^[a-zA-Z_\-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " %-30s %s\n", $$1, $$2}' + +clean: ## Clean build directory @rm -rf ${BUILD_DIR}/* +build: ## Build with cmake build: clean @cmake -S . -B build @cmake --build build +test: ## Build and test test: build @cd build && ctest -build-images: +build-images: ## Build container images +build-images: build-server-image build-client-image + +build-server-image: @docker build . -f Dockerfile.server -t $(REPO)/server:$(COMMIT_HASH) + +build-client-image: @docker build . -f Dockerfile.client -t $(REPO)/client:$(COMMIT_HASH) -tag-images: +tag-images: ## Tag container images +tag-images: tag-server-image tag-client-image + +tag-server-image: @docker tag $(REPO)/server:$(COMMIT_HASH) $(REPO)/server:$(VERSION) + +tag-client-image: @docker tag $(REPO)/client:$(COMMIT_HASH) $(REPO)/client:$(VERSION) -push-images: tag-images + +push-images: ## Push container images to registry +push-images: tag-images push-server-images push-client-images + +push-server-images: @docker push $(REPO)/server:$(COMMIT_HASH) @docker push $(REPO)/server:$(VERSION) + +push-client-images: @docker push $(REPO)/client:$(COMMIT_HASH) @docker push $(REPO)/client:$(VERSION) -help: - @echo "Makefile commands:" - @echo " clean - Remove build directory." - @echo " build - Build the project using cmake." - @echo " test - Run tests after building." - @echo " build-images - Build Docker images for both server and client." - @echo " tag-images - Tag Docker images with the latest or specified version." - @echo " push-images - Push Docker images to the registry." - @echo " help - Show this help message." - -.PHONY: clean build test build-images tag-images push-images help +.PHONY: clean build test build-images build-server-image build-client-image tag-images tag-client-image tag-server-image push-images push-client-images push-server-images help diff --git a/include/SerialClient.h b/include/SerialClient.h new file mode 100644 index 0000000..c7219ee --- /dev/null +++ b/include/SerialClient.h @@ -0,0 +1,26 @@ +#ifndef CLIENT_H +#define CLIENT_H + +#include "common.hpp" +#include "VirtualSerialPort.h" + +using namespace boost::asio; +using ip::tcp; +using std::string; + +class SerialClient { +public: + SerialClient(const string& server_ip, unsigned short server_port, const string& vsp_name); + void run(); + +private: + io_service io_service_; + tcp::socket socket_; + std::array buffer_; + VirtualSerialPort vsp_; + + void do_read_write(); +}; + + +#endif // CLIENT_H diff --git a/include/SerialServer.h b/include/SerialServer.h new file mode 100644 index 0000000..7c2b883 --- /dev/null +++ b/include/SerialServer.h @@ -0,0 +1,27 @@ +#ifndef SERVER_H +#define SERVER_H + +#include "common.hpp" +#include "ISerialPort.h" +#include "RealSerialPort.h" + +using namespace boost::asio; +using ip::tcp; +using std::string; + +class SerialServer { +public: + SerialServer(io_service& io, ISerialPort& serial, tcp::acceptor& acceptor); + void run(); + +private: + io_service& io_service_; + ISerialPort& serial_; + tcp::acceptor& acceptor_; + tcp::socket socket_; + + void start_accept(); + void do_read_write(); +}; + +#endif // SERVER_H diff --git a/include/client.h b/include/client.h deleted file mode 100644 index eac36bb..0000000 --- a/include/client.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CLIENT_H -#define CLIENT_H - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logging.h" -#include "VirtualSerialPort.h" - -using namespace boost::asio; -using ip::tcp; -using std::string; - -class SerialClient { -public: - SerialClient(const string& server_ip, unsigned short server_port, const string& vsp_name); - void run(); - -private: - io_service io_service_; - tcp::socket socket_; - std::array buffer_; - VirtualSerialPort vsp_; - - void do_read_write(); -}; - - -#endif // CLIENT_H diff --git a/include/common.hpp b/include/common.hpp index 198aab7..ca65d32 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -1,10 +1,17 @@ #ifndef COMMON_HPP #define COMMON_HPP -#include #include #include +#include +#include +#include +#include + +#include +#include +#include -// Common utilities, logging, and error handling functions could be defined here +#include "logging.h" #endif // COMMON_HPP diff --git a/include/logging.h b/include/logging.h index 27cdd40..0695e22 100644 --- a/include/logging.h +++ b/include/logging.h @@ -9,7 +9,7 @@ #include #include #include -#include // Ensure this is included for date-time support +#include // Namespace aliases namespace logging = boost::log; diff --git a/include/server.h b/include/server.h deleted file mode 100644 index 114c4fe..0000000 --- a/include/server.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef SERVER_H -#define SERVER_H - -#include -#include -#include "ISerialPort.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logging.h" -#include "ISerialPort.h" -#include "RealSerialPort.h" - -using namespace boost::asio; -using ip::tcp; -using std::string; - -class SerialServer { -public: - SerialServer(io_service& io, ISerialPort& serial, tcp::acceptor& acceptor); - void run(); - -private: - io_service& io_service_; - ISerialPort& serial_; - tcp::acceptor& acceptor_; - tcp::socket socket_; - - void start_accept(); - void do_read_write(); -}; - -#endif // SERVER_H diff --git a/src/client.cpp b/src/SerialClient.cpp similarity index 77% rename from src/client.cpp rename to src/SerialClient.cpp index f7a7ac4..a1f810f 100644 --- a/src/client.cpp +++ b/src/SerialClient.cpp @@ -1,4 +1,4 @@ -#include "client.h" +#include "SerialClient.h" using namespace boost::asio; using namespace boost::program_options; @@ -8,22 +8,6 @@ using std::cout; using std::cerr; using std::endl; -void init_logging() { - boost::log::add_console_log( - std::cout, - boost::log::keywords::format = "[%TimeStamp%] [%ThreadID%] [%Severity%] %Message%", - boost::log::keywords::auto_flush = true - ); - boost::log::add_file_log( - boost::log::keywords::file_name = "serial_client_%N.log", - boost::log::keywords::rotation_size = 10 * 1024 * 1024, - boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0), - boost::log::keywords::format = "[%TimeStamp%] [%Severity%] %Message%" - ); - boost::log::add_common_attributes(); - boost::log::register_simple_formatter_factory("Severity"); -} - SerialClient::SerialClient(const string& server_ip, unsigned short server_port, const string& vsp_name) : socket_(io_service_), vsp_(vsp_name) { BOOST_LOG_TRIVIAL(info) << "Initializing client..."; diff --git a/src/server.cpp b/src/SerialServer.cpp similarity index 68% rename from src/server.cpp rename to src/SerialServer.cpp index 14044a5..ab2914e 100644 --- a/src/server.cpp +++ b/src/SerialServer.cpp @@ -1,26 +1,4 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logging.h" -#include "ISerialPort.h" -#include "RealSerialPort.h" -#include "server.h" +#include "SerialServer.h" using namespace boost::asio; using namespace boost::program_options; @@ -28,22 +6,6 @@ using ip::tcp; using std::string; using std::endl; -void init_logging() { - boost::log::add_console_log( - std::cout, - boost::log::keywords::format = "[%TimeStamp%] [%ThreadID%] [%Severity%] %Message%", - boost::log::keywords::auto_flush = true - ); - boost::log::add_file_log( - boost::log::keywords::file_name = "serial_server_%N.log", - boost::log::keywords::rotation_size = 10 * 1024 * 1024, - boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0), - boost::log::keywords::format = "[%TimeStamp%] [%Severity%] %Message%" - ); - boost::log::add_common_attributes(); - boost::log::register_simple_formatter_factory("Severity"); -} - SerialServer::SerialServer(io_service& io, ISerialPort& serial, tcp::acceptor& acceptor) : io_service_(io), serial_(serial), acceptor_(acceptor), socket_(io) { BOOST_LOG_TRIVIAL(info) << "Starting server and waiting for connection...";