diff --git a/.ci/linux.yml b/.ci/linux.yml deleted file mode 100644 index 7944ec5..0000000 --- a/.ci/linux.yml +++ /dev/null @@ -1,55 +0,0 @@ -# Build Stage -build-linux: - image: my.local.registry/vcpkg_builder - stage: build - - tags: - - devops-linux - script: - - cp .vpm/.template/CMakePresetForLinuxCI.json CMakeUserPresets.json - - echo "Building project ..." - - cmake --preset CI - - cmake --build --preset Release - artifacts: - paths: - - build/bin - - build/lib - - build/test - expire_in: 1 hour - -# Test Stage -test-linux: - image: my.local.registry/vcpkg_builder - stage: test - needs: - - build-linux - tags: - - devops-linux - script: - - echo "Running tests..." - - cd build/test - - ctest --output-junit test_report.xml --output-on-failure - artifacts: - reports: - junit: build/test/test_report.xml - expire_in: 1 hour -# Deploy Stage -deploy-linux: - image: my.local.registry/vcpkg_builder - stage: deploy - needs: - - build-linux - - test-linux - only: - - master - tags: - - devops-linux - when: manual # Manual trigger for the deployment stage - script: - - echo "Deploying... (this is just a placeholder, no actual deployment steps)" - artifacts: - paths: - - - build/bin - - build/lib - expire_in: 1 week diff --git a/.ci/macos.yml b/.ci/macos.yml deleted file mode 100644 index 3099f93..0000000 --- a/.ci/macos.yml +++ /dev/null @@ -1,25 +0,0 @@ -build-macos: - stage: build - tags: - - mss - script: - - echo "Not yet implemented" -test-macos: - stage: test - needs: - - build-macos - tags: - - mss - script: - - echo "Not yet implemented" -deploy-macos: - stage: deploy - needs: - - build-macos - - test-macos - tags: - - mss - only: - - master - script: - - echo "Not yet implemented" \ No newline at end of file diff --git a/.ci/windows.yml b/.ci/windows.yml deleted file mode 100644 index 2495ea0..0000000 --- a/.ci/windows.yml +++ /dev/null @@ -1,51 +0,0 @@ -build-windows: - stage: build - tags: - - devops-win - script: - - $env:VCPKG_ROOT="D:\vcpkg" - - $env:PATH="$env:VCPKG_ROOT;$env:PATH" - - vcpkg integrate install - - $env:VCPKG_BINARY_SOURCES="clear;files,D:\vcpkg-cache,readwrite" - - Copy-Item .vpm/.template/CMakePresetForWindowsCI.json -Destination CMakeUserPresets.json - - echo "Building project ..." - - cmake --preset CI - - cmake --build --preset Release - artifacts: - paths: - - build/Release - - build/test - expire_in: 1 hour -test-windows: - image: my.local.registry/vcpkg_builder - stage: test - needs: - - build-windows - tags: - - devops-win - script: - - echo "Running tests..." - - cd build/test - - ctest --output-junit test_report.xml --output-on-failure -C Release - artifacts: - reports: - junit: build/test/test_report.xml - expire_in: 1 hour - -deploy-windows: - stage: deploy - needs: - - build-windows - - test-windows - only: - - master - tags: - - devops-win - when: manual # Manual trigger for the deployment stage - script: - - echo "Deploying... (this is just a placeholder, no actual deployment steps)" - artifacts: - paths: - - build/Release/bin - - build/Release/lib - expire_in: 1 week diff --git a/.github/workflows/linux-workflow.yml b/.github/workflows/linux-workflow.yml index 935567c..e2c7e3e 100644 --- a/.github/workflows/linux-workflow.yml +++ b/.github/workflows/linux-workflow.yml @@ -1,7 +1,6 @@ name: libtusclient CI Linux on: - push: pull_request: release: types: [created] @@ -63,30 +62,41 @@ jobs: key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} restore-keys: | vcpkg-${{ runner.os }} + + - name: Cache build folder + uses: actions/cache@v4 + with: + path: | + build + key: ${{ runner.os }}-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}-${{ github.ref_name }}-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt', 'vcpkg.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}-${{ github.ref_name }}- + ${{ runner.os }}-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}- + + - name: Configure CMake run: | cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=${{ matrix.vcpkg_root }}/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=${{ steps.strings.outputs.build-type }} - - + - name: Build Library - run: cmake --build build --target libtusclient --config ${{ steps.strings.outputs.build-type }} + run: cmake --build build --target tusclient --config ${{ steps.strings.outputs.build-type }} - name: Build Tests - run: cmake --build build --target libtusclient_test --config ${{ steps.strings.outputs.build-type }} + run: cmake --build build --target tusclient_test --config ${{ steps.strings.outputs.build-type }} - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts-linux - path: build/${{ steps.strings.outputs.test-folder }}/lib/liblibtusclient.so + path: build/build/tusclient/lib/libtusclient.so - name: Upload artifacts for test uses: actions/upload-artifact@v4 with: name: build-artifacts-for-test path: | - build/${{ steps.strings.outputs.test-folder }}/lib/liblibtusclient.so - build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test + build/build/tusclient/lib/libtusclient.so + build/build/tusclient_test/bin/tusclient_test test-linux: needs: build-linux @@ -116,7 +126,7 @@ jobs: uses: actions/download-artifact@v4 with: name: build-artifacts-for-test - path: build/${{ steps.strings.outputs.test-folder }} + path: build/build - name: Run Mockoon CLI uses: mockoon/cli-action@v2 @@ -132,9 +142,9 @@ jobs: - name: Test Project if: ${{ matrix.os == 'ubuntu-latest' }} run: | - chmod +x build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test - export LD_LIBRARY_PATH=build/${{ steps.strings.outputs.test-folder }}:$LD_LIBRARY_PATH - build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test --gtest_output=xml:build/${{ steps.strings.outputs.test-folder }}/test-results.xml + chmod +x build/build/tusclient_test/bin/tusclient_test + export LD_LIBRARY_PATH=build/build/tusclient/lib:$LD_LIBRARY_PATH + build/build/tusclient_test/bin/tusclient_test --gtest_output=xml:build/${{ steps.strings.outputs.test-folder }}/test-results.xml - name: Upload test results uses: actions/upload-artifact@v4 diff --git a/.github/workflows/mac-workflow.yml b/.github/workflows/mac-workflow.yml index c504dc7..a95efc0 100644 --- a/.github/workflows/mac-workflow.yml +++ b/.github/workflows/mac-workflow.yml @@ -1,7 +1,6 @@ name: libtusclient CI MacOS on: - push: pull_request: release: types: [created] @@ -62,29 +61,38 @@ jobs: key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} restore-keys: | vcpkg-${{ runner.os }} - + - name: Cache build folder + uses: actions/cache@v4 + with: + path: | + build + key: macos-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}-${{ github.ref_name }}-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt', 'vcpkg.json') }} + restore-keys: | + macos-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}-${{ github.ref_name }}- + macos-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}- + - name: Configure CMake run: | cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=${{ matrix.vcpkg_root }}/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=${{ steps.strings.outputs.build-type }} - name: Build Library - run: cmake --build build --target libtusclient --config ${{ steps.strings.outputs.build-type }} + run: cmake --build build --target tusclient --config ${{ steps.strings.outputs.build-type }} - name: Build Tests - run: cmake --build build --target libtusclient_test --config ${{ steps.strings.outputs.build-type }} + run: cmake --build build --target tusclient_test --config ${{ steps.strings.outputs.build-type }} - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts-mac - path: build/${{ steps.strings.outputs.test-folder }}/lib/liblibtusclient.dylib + path: build/build/tusclient/lib/libtusclient.dylib - name: Upload artifacts for test uses: actions/upload-artifact@v4 with: name: build-artifacts-for-test-mac path: | - build/${{ steps.strings.outputs.test-folder }}/lib/liblibtusclient.dylib - build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test + build/build/tusclient/lib/libtusclient.dylib + build/build/tusclient_test/bin/tusclient_test test-mac: needs: build-mac @@ -114,7 +122,7 @@ jobs: uses: actions/download-artifact@v4 with: name: build-artifacts-for-test-mac - path: build/${{ steps.strings.outputs.test-folder }} + path: build/build/ - name: Run Mockoon CLI uses: mockoon/cli-action@v2 @@ -134,9 +142,9 @@ jobs: - name: Test Project run: | - chmod +x build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test - export DYLD_LIBRARY_PATH=build/${{ steps.strings.outputs.test-folder }}:$DYLD_LIBRARY_PATH - build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test --gtest_output=xml:build/${{ steps.strings.outputs.test-folder }}/test-results.xml + chmod +x build/build/tusclient_test/bin/tusclient_test + export DYLD_LIBRARY_PATH=build/build/tusclient/lib:$DYLD_LIBRARY_PATH + build/build/tusclient_test/bin/tusclient_test --gtest_output=xml:build/${{ steps.strings.outputs.test-folder }}/test-results.xml - name: Upload test results uses: actions/upload-artifact@v4 diff --git a/.github/workflows/windows-workflow.yml b/.github/workflows/windows-workflow.yml index 1e04293..bbfad6c 100644 --- a/.github/workflows/windows-workflow.yml +++ b/.github/workflows/windows-workflow.yml @@ -1,7 +1,6 @@ name: libtusclient CI Windows on: - push: pull_request: release: types: [created] @@ -36,8 +35,10 @@ jobs: fi - name: Setup vcpkg run: | - git clone https://github.com/microsoft/vcpkg.git - & "${{ matrix.vcpkg_root }}\bootstrap-vcpkg.bat" + if (-not(Test-Path -Path "${{ matrix.vcpkg_root }}")) { + git clone https://github.com/microsoft/vcpkg.git "${{ matrix.vcpkg_root }}" + & "${{ matrix.vcpkg_root }}/bootstrap-vcpkg.bat" + } - name: Export GitHub Actions cache environment variables uses: actions/github-script@v7 with: @@ -60,29 +61,44 @@ jobs: key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} restore-keys: | vcpkg-${{ runner.os }} + - name: Cache build folder + uses: actions/cache@v4 + with: + path: | + build + key: windows-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}-${{ github.ref_name }}-${{ hashFiles('CMakeLists.txt', '**\CMakeLists.txt', 'vcpkg.json') }} + restore-keys: | + windows-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}-${{ github.ref_name }}- + windows-build-${{ matrix.c_compiler }}-${{ steps.strings.outputs.build-type }}- - name: Configure CMake run: | cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="vcpkg\scripts\buildsystems\vcpkg.cmake" -DCMAKE_BUILD_TYPE=${{ steps.strings.outputs.build-type }} - name: Build Library - run: cmake --build build --target libtusclient --config ${{ steps.strings.outputs.build-type }} + run: cmake --build build --target tusclient --config ${{ steps.strings.outputs.build-type }} + + - name: Copy library folder content to test build folder + run: | + if (!(Test-Path -Path "build/build/tusclient_test")) { + New-Item -ItemType Directory -Path "build/build/tusclient_test" + } + Copy-Item -Path "build/build/tusclient/bin/*" -Destination "build/build/tusclient_test/bin" -Recurse -Force - name: Build Tests - run: cmake --build build --target libtusclient_test --config ${{ steps.strings.outputs.build-type }} + run: cmake --build build --target tusclient_test --config ${{ steps.strings.outputs.build-type }} - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts-windows - path: build/${{ steps.strings.outputs.test-folder }}/bin/*.dll + path: build/build/tusclient/bin/ - name: Upload artifacts for test uses: actions/upload-artifact@v4 with: name: build-artifacts-for-test path: | - build/${{ steps.strings.outputs.test-folder }}/bin/*.dll - build/${{ steps.strings.outputs.test-folder }}/bin/libtusclient_test.exe + build/build/tusclient_test/bin/ test-windows: needs: build-windows @@ -94,7 +110,7 @@ jobs: uses: actions/download-artifact@v4 with: name: build-artifacts-for-test - path: build/ + path: build/build - name: Run Mockoon CLI uses: mockoon/cli-action@v2 @@ -114,4 +130,4 @@ jobs: - name: Run Tests run: | - build/${{ steps.strings.outputs.test-folder }}/libtusclient_test.exe --gtest_output=xml:test-results.xml + .\build\build\Debug\tusclient_test.exe --gtest_output=xml:test-results.xml diff --git a/.gitignore b/.gitignore index 90c8ee4..d85f279 100644 --- a/.gitignore +++ b/.gitignore @@ -1,61 +1,22 @@ -# Prerequisites -*.d +.idea +.vs +.vscode -# Compiled Object files -*.slo -*.lo -*.o -*.obj +.DS_Store +# Ignore all log files +*.log -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -.vscode/ -.vs/ -.vsconan/ -.idea/ -*.sln -*.vcxproj -*.vcxproj.filters -*.vcxproj.user +# Ignore Python cache files +.venv +__pycache__/ +*.pyc +*.pyo +#Ignore build and distribution directories +build/ +dist/ +# Ignore Python bytecode files -# CMake -CMakeFiles/ +# Ignore CMakeUserPresets CMakeCache.txt -CMakeScripts/ -CMakeSettings.json +CMakeFiles/ CMakeUserPresets.json - -# Build directory -build/ -out/ - -*.lock -graph_info.json -conanbuildinfo.txt -conaninfo.txt -.env -.venv - -vcpkg_installed/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index bbea6ae..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,40 +0,0 @@ -stages: - - build - - test - - deploy - - publish - -variables: - CMAKE_BUILD_PARALLEL_LEVEL: 4 -before_script: - - git submodule sync - - git submodule update --init --recursive - -include: - - .ci/linux.yml - - .ci/windows.yml - - .ci/macos.yml - -publish: - stage: publish - image: my.local.registry/vcpkg_builder - needs: - - build-linux - - test-linux - tags: - - devops-linux - rules: - - if: '$CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "push"' - when: never - - if: '$CI_COMMIT_TAG && $CI_DEFAULT_BRANCH == "master"' - when: always - - when: never - script: - - echo "Sending POST request to vcpkg-deployerexample.com" - - | - curl -X POST http://vcpkg-deployerexample.com/deploy \ - -H "Content-Type: application/json" \ - -d '{ - "tag": "'"$CI_COMMIT_TAG"'", - "repo": "'https://github.com/Cadons/libtusclient.git'" - }' diff --git a/.resources/templates/LICENSE_template.jinja b/.resources/templates/LICENSE_template.jinja new file mode 100644 index 0000000..44eeeca --- /dev/null +++ b/.resources/templates/LICENSE_template.jinja @@ -0,0 +1,34 @@ +{{ project_name }} - End User License Agreement (EULA) +Copyright (c) {{ year }} {{ organization }} + +IMPORTANT: PLEASE READ THIS AGREEMENT CAREFULLY BEFORE USING THIS SOFTWARE. + +By installing, copying, or otherwise using {{ project_name }} (the "Software"), you agree to be bound by the terms of this EULA. + +1. LICENSE GRANT +{{ organization }} grants you a non-exclusive, non-transferable license to use the Software solely for your personal or internal business purposes, subject to the terms of this Agreement. + +2. RESTRICTIONS +You may not: +- Modify, reverse engineer, decompile, or disassemble the Software. +- Distribute, sell, sublicense, or rent the Software to any third party. +- Remove any proprietary notices or labels on the Software. + +3. OWNERSHIP +The Software is licensed, not sold. {{ organization }} retains all rights, title, and interest in and to the Software. + +4. TERMINATION +This Agreement is effective until terminated. You may terminate it by deleting the Software. It will also terminate if you fail to comply with any terms herein. + +5. WARRANTY DISCLAIMER +The Software is provided "AS IS" without warranty of any kind. {{ organization }} disclaims all warranties, express or implied, including but not limited to fitness for a particular purpose. + +6. LIMITATION OF LIABILITY +In no event shall {{ organization }} be liable for any damages arising from the use or inability to use the Software. + +7. GOVERNING LAW +This Agreement shall be governed by the laws of the jurisdiction where {{ organization }} is located. + +If you do not agree to these terms, do not install or use the Software. + +For questions, contact: {{ organization }} - {{ contact_email | default('support@example.com') }} \ No newline at end of file diff --git a/.resources/templates/README_template.jinja b/.resources/templates/README_template.jinja new file mode 100644 index 0000000..3e89a25 --- /dev/null +++ b/.resources/templates/README_template.jinja @@ -0,0 +1,21 @@ +# {{ module_name }} + +**Type:** {{ module_type }} + +This module is part of a larger monolithic repository. +It is designed to be modular, reusable, and easy to integrate. + +## Overview + +{{ module_name }} provides core functionalities required by other parts of the project. +Its implementation follows modern C++ standards and best practices. + +## Usage + +To use this module, simply include it as a dependency in your `CMakeLists.txt`: + +```cmake +add_subdirectory({{ module_name }}) +target_link_libraries(your_target PRIVATE {{ module_name }}) + + diff --git a/.resources/templates/c++/class_template.cpp.jinja b/.resources/templates/c++/class_template.cpp.jinja new file mode 100644 index 0000000..636ae1e --- /dev/null +++ b/.resources/templates/c++/class_template.cpp.jinja @@ -0,0 +1,15 @@ +#include "{{ header_filename }}" + +{% if namespace -%} +using namespace {{ namespace }}; +{% endif %} + +{{ class_name }}::{{ class_name }}() { + // Constructor implementation +} + +{{ class_name }}::~{{ class_name }}() { + // Destructor implementation +} + +// TODO: Implement other methods diff --git a/.resources/templates/c++/class_template.h.jinja b/.resources/templates/c++/class_template.h.jinja new file mode 100644 index 0000000..726cf62 --- /dev/null +++ b/.resources/templates/c++/class_template.h.jinja @@ -0,0 +1,29 @@ +#pragma once + +{% if namespace -%} +namespace {{ namespace }} { + +{% set indent = " " %} +{%- else -%} +{% set indent = "" %} +{%- endif %} +{{indent}}class {{ class_name }} { +{{indent}}public: +{{indent}} {{ class_name }}(); +{{indent}} ~{{ class_name }}(); + +{{indent}} // Copy and move +{{indent}} {{ class_name }}(const {{ class_name }}&) = default; +{{indent}} {{ class_name }}& operator=(const {{ class_name }}&) = default; +{{indent}} {{ class_name }}({{ class_name }}&&) = default; +{{indent}} {{ class_name }}& operator=({{ class_name }}&&) = default; + +{{indent}} // TODO: Add public methods + +{{indent}}private: +{{indent}} // TODO: Add member variables +{{indent}}}; + +{% if namespace %} +} // namespace {{ namespace }} +{%- endif %} \ No newline at end of file diff --git a/.resources/templates/c++/main_template.cpp.jinja b/.resources/templates/c++/main_template.cpp.jinja new file mode 100644 index 0000000..a7b39de --- /dev/null +++ b/.resources/templates/c++/main_template.cpp.jinja @@ -0,0 +1,15 @@ +{% if app %} +#include +int main() { + // Your code goes here + std::cout << "Hello, {{ target_name }}!" << std::endl; + return 0; +} +{% else %} +#include +#include "module.h" + void {{ target_name }}() { + // Your code goes here + std::cout << "Hello, {{ target_name }}!" << std::endl; +} +{% endif %} \ No newline at end of file diff --git a/.resources/templates/c++/module_template.h.jinja b/.resources/templates/c++/module_template.h.jinja new file mode 100644 index 0000000..9445a77 --- /dev/null +++ b/.resources/templates/c++/module_template.h.jinja @@ -0,0 +1 @@ +// {{ target_name }} \ No newline at end of file diff --git a/.resources/templates/c++/test_main_template.cpp.jinja b/.resources/templates/c++/test_main_template.cpp.jinja new file mode 100644 index 0000000..a074d8e --- /dev/null +++ b/.resources/templates/c++/test_main_template.cpp.jinja @@ -0,0 +1,12 @@ +{% if use_gtest %} +#include + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +{% else %} +int main() { + return 0; +} +{% endif %} \ No newline at end of file diff --git a/.resources/templates/cmake/CMakeLists.txt.jinja b/.resources/templates/cmake/CMakeLists.txt.jinja new file mode 100644 index 0000000..d8688be --- /dev/null +++ b/.resources/templates/cmake/CMakeLists.txt.jinja @@ -0,0 +1,51 @@ +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(Common OPTIONAL) +get_version(${CMAKE_CURRENT_SOURCE_DIR}/project.json ${PROJECT_VERSION}) +message(STATUS "{{ project_name}} by {{ org_name | default('UnknownOrg') }} - ${PROJECT_VERSION}") +cmake_minimum_required(VERSION {{ cmake_min_version | default("3.23") }}) + +project({{ project_name }} + VERSION ${PROJECT_VERSION} + DESCRIPTION "{{ project_description | default('My Project') }}" + LANGUAGES CXX +) + +# Organization info +set(PROJECT_ORG_NAME "{{ org_name | default('UnknownOrg') }}") +set(PROJECT_ORG_URL "{{ org_url | default('https://example.com') }}") + +# Prefer Ninja if not explicitly specified +{% if prefer_ninja %} +if(NOT CMAKE_GENERATOR) + message(STATUS "No generator specified. Consider using Ninja for faster builds.") +endif() +{% endif %} + +# Set C++ standard +configure_compiler({{ cpp_standard | default("23") }}) + +# Default build type +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the build type." FORCE) +endif() + +# Enable IDE folder grouping +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +# Add extra cmake modules (optional) +include(QtUtils OPTIONAL) + +# Testing +include(CTest) +enable_testing() + +# Version header generation +configure_file( + ${CMAKE_SOURCE_DIR}/cmake/version.h.in + ${CMAKE_BINARY_DIR}/generated/version.h + @ONLY +) + +# === Add subdirectories === +{% for dir in subdirs %} +add_subdirectory({{ dir }}) +{% endfor %} \ No newline at end of file diff --git a/.resources/templates/cmake/app_CMakeLists.txt.jinja b/.resources/templates/cmake/app_CMakeLists.txt.jinja new file mode 100644 index 0000000..7b2ec52 --- /dev/null +++ b/.resources/templates/cmake/app_CMakeLists.txt.jinja @@ -0,0 +1,21 @@ +# Include common cmake scripts +include(${CMAKE_CURRENT_SOURCE_DIR}/.cmake/sources.cmake) +# Configure build output directories for the target +configure_build({{ target_name }}) +show_module_info(${CMAKE_CURRENT_LIST_DIR}/package.json) +# Create executable target +add_executable({{ target_name }} {{"${" + sources + "}"}} {{"${" + headers + "}"}} {{"${" + resources + "}"}}) + +# Include headers directory +target_include_directories({{ target_name }} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/{{ target_name }}) + +# Link libraries +#target_link_libraries({{ target_name }} PRIVATE) + +# Enable testing if tests exist +include(CTest) +if(BUILD_TESTING) + add_subdirectory(test) +endif() +# Install target +configure_install({{ target_name }}) \ No newline at end of file diff --git a/.resources/templates/cmake/lib_CMakeLists.txt.jinja b/.resources/templates/cmake/lib_CMakeLists.txt.jinja new file mode 100644 index 0000000..a5261d6 --- /dev/null +++ b/.resources/templates/cmake/lib_CMakeLists.txt.jinja @@ -0,0 +1,21 @@ +# Include common cmake scripts +include(${CMAKE_CURRENT_SOURCE_DIR}/.cmake/sources.cmake) +# Configure build output directories for the target +configure_build({{ target_name }}) +show_module_info(${CMAKE_CURRENT_LIST_DIR}/package.json) +# Create executable target +add_library({{ target_name }} {{"${" + sources + "}"}} {{"${" + headers + "}"}} {{"${" + resources + "}"}}) + +# Include headers directory +target_include_directories({{ target_name }} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/{{ target_name }}) + +# Link libraries +#target_link_libraries({{ target_name }} PRIVATE) + +# Enable testing if tests exist +include(CTest) +if(BUILD_TESTING) + add_subdirectory(test/{{ target_name }}) +endif() +# Install target +configure_install({{ target_name }}) \ No newline at end of file diff --git a/.resources/templates/cmake/test_CMakeLists.txt.jinja b/.resources/templates/cmake/test_CMakeLists.txt.jinja new file mode 100644 index 0000000..7d4b3e0 --- /dev/null +++ b/.resources/templates/cmake/test_CMakeLists.txt.jinja @@ -0,0 +1,33 @@ +include(${CMAKE_CURRENT_SOURCE_DIR}/../.cmake/sources.cmake) +{%if use_gtest%} +#Find GoogleTest package +find_package(GTest REQUIRED) + +configure_build({{test_target}}) + +# Include directories +include_directories( + ${CMAKE_SOURCE_DIR}/include/{{target_name}} +) + +# Add test executable +add_executable({{test_target}} {{"${"+sources+"}"}}) + +# Link libraries +target_link_libraries({{test_target}} PRIVATE GTest::gtest) + +#auto discover tests +gtest_discover_tests({{test_target}} + DISCOVERY_TIMEOUT 60 + PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +) +{%else%} +# No GoogleTest support + +add_executable({{test_target}} {{sources|join(" ")}}) +# Link libraries +target_link_libraries({{test_target}} PRIVATE pthread) + +# Add tests +add_test(NAME {{test_target}} COMMAND {{test_target}}) +{%endif%} \ No newline at end of file diff --git a/.resources/templates/docs/doxyfile_template.jinja b/.resources/templates/docs/doxyfile_template.jinja new file mode 100644 index 0000000..274eb07 --- /dev/null +++ b/.resources/templates/docs/doxyfile_template.jinja @@ -0,0 +1,118 @@ +# Doxyfile for {{ project_name }} +# Generated for VPM Project + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "{{ project_name }}" +PROJECT_NUMBER = "0.1.0" +PROJECT_BRIEF = "{{ project_name }} documentation" +OUTPUT_DIRECTORY = docs +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = YES + +#--------------------------------------------------------------------------- +# Input files +#--------------------------------------------------------------------------- +INPUT = app/{{ project_name }}/src \ + app/{{ project_name }}/include \ + lib/*/include \ + lib/*/src + +FILE_PATTERNS = *.cpp \ + *.h \ + *.hpp \ + *.md +RECURSIVE = YES +EXCLUDE_PATTERNS = */build/* \ + */test/* \ + */.cmake/* +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Source browsing options +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# HTML output options +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_TREEVIEW = YES +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# Preprocessing options +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = include +INCLUDE_FILE_PATTERNS = *.h \ + *.hpp + +#--------------------------------------------------------------------------- +# Dot tool options +#--------------------------------------------------------------------------- +HAVE_DOT = YES +DOT_NUM_THREADS = 0 +CLASS_DIAGRAMS = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +UML_LIMIT_NUM_FIELDS = 50 +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES + +#--------------------------------------------------------------------------- +# Output formats +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +GENERATE_RTF = NO +GENERATE_MAN = NO +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# Extra settings +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = doxygen_warnings.log \ No newline at end of file diff --git a/.resources/templates/gtest/gtest_fixture_template.cpp.jinja b/.resources/templates/gtest/gtest_fixture_template.cpp.jinja new file mode 100644 index 0000000..32011ee --- /dev/null +++ b/.resources/templates/gtest/gtest_fixture_template.cpp.jinja @@ -0,0 +1,23 @@ +#include +#include "{{ header_filename }}" + +{% if namespace %} +using namespace {{ namespace }}; +{% endif %} + +class {{ param_test_fixture_name }} : public ::testing::TestWithParam<{{ param_type }}> { +protected: + {{ class_name }} instance; +}; + +TEST_P({{ param_test_fixture_name }}, HandlesParam) { + auto param = GetParam(); + // TODO: Test using param and instance + SUCCEED(); +} + +INSTANTIATE_TEST_SUITE_P( + {{ param_suite_name }}, + {{ param_test_fixture_name }}, + ::testing::Values({{ param_values|join(', ') }}) +); \ No newline at end of file diff --git a/.resources/templates/gtest/gtest_parametrized_template.cpp.jinja b/.resources/templates/gtest/gtest_parametrized_template.cpp.jinja new file mode 100644 index 0000000..ab30d9b --- /dev/null +++ b/.resources/templates/gtest/gtest_parametrized_template.cpp.jinja @@ -0,0 +1,36 @@ +#include +#include "{{ header_filename }}" + +{% if namespace %} +using namespace {{ namespace }}; +{% endif %} + +class {{ test_suite_name }} : public ::testing::TestWithParam> { +protected: + {{ class_name }} instance; +}; + +TEST_P({{ test_suite_name }}, ParameterizedTest) { + auto [input, expected] = GetParam(); + // TODO: Add your parameterized test logic here + // Example: + // EXPECT_EQ(instance.someMethod(input), expected); + SUCCEED(); +} + +INSTANTIATE_TEST_SUITE_P( + {{ test_suite_name }}Values, + {{ test_suite_name }}, + ::testing::Values( + std::make_tuple(1, 2), + std::make_tuple(2, 4), + std::make_tuple(3, 6), + std::make_tuple(4, 8) + ), + [](const testing::TestParamInfo>& info) { + return "Input" + std::to_string(std::get<0>(info.param)) + + "_Expects" + std::to_string(std::get<1>(info.param)); + } +); + +// You can add more test cases here \ No newline at end of file diff --git a/.resources/templates/gtest/gtest_simple_template.cpp.jinja b/.resources/templates/gtest/gtest_simple_template.cpp.jinja new file mode 100644 index 0000000..40d616a --- /dev/null +++ b/.resources/templates/gtest/gtest_simple_template.cpp.jinja @@ -0,0 +1,15 @@ +#include +#include "{{ header_filename }}" + +{% if namespace %} +using namespace {{ namespace }}; +{% endif %} + +// Test suite: {{ test_suite_name }} +TEST({{ test_suite_name }}, DefaultConstructor) { + {{ class_name }} instance; + // Add your assertions here + SUCCEED(); +} + +// Add more tests here \ No newline at end of file diff --git a/.resources/templates/qt/qrc_template.qrc.jinja b/.resources/templates/qt/qrc_template.qrc.jinja new file mode 100644 index 0000000..b551814 --- /dev/null +++ b/.resources/templates/qt/qrc_template.qrc.jinja @@ -0,0 +1,13 @@ + + + + + + {%- if create_examples %} + + resources/images/icon.png + resources/styles/style.qss + {%- endif %} + + + diff --git a/.vpm/.assets/company_logo.txt b/.vpm/.assets/company_logo.txt deleted file mode 100644 index 48d0a17..0000000 --- a/.vpm/.assets/company_logo.txt +++ /dev/null @@ -1,5 +0,0 @@ -------------------------CMake Project Creator------------------------ - - Make C++ Faster with VCPKG and CMake - ------------------------------------- ---------------------------------------------------------------------- diff --git a/.vpm/.cache/config.json b/.vpm/.cache/config.json deleted file mode 100644 index 639dedd..0000000 --- a/.vpm/.cache/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "libtusclient", - "type": "shared", - "is_qt_project": false -} \ No newline at end of file diff --git a/.vpm/.gitignore b/.vpm/.gitignore deleted file mode 100644 index 1910205..0000000 --- a/.vpm/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -venv -__pycache__ -.venv \ No newline at end of file diff --git a/.vpm/.resources/platforms.json b/.vpm/.resources/platforms.json deleted file mode 100644 index b3fe851..0000000 --- a/.vpm/.resources/platforms.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "linux", - "windows", - "macos" -] \ No newline at end of file diff --git a/.vpm/.resources/project.json b/.vpm/.resources/project.json deleted file mode 100644 index 9583009..0000000 --- a/.vpm/.resources/project.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - "CMakeLists.txt", - "src/", - "include/", - "test/", - "test/CMakeLists.txt", - "vcpkg.json", - "vcpkg-configuration.json", - "CMakeUserPresets.json", - "CMakePresets.json", - "LICENSE", - "CONTRIBUTING.md", - "cmake/config.cmake", - "cmake/Config.cmake.in", - "cmake/config.h.in", - "Doxyfile", - ".ci/linux.yml", - ".ci/windows.yml", - ".ci/macos.yml" -] \ No newline at end of file diff --git a/.vpm/.resources/reserved_names.json b/.vpm/.resources/reserved_names.json deleted file mode 100644 index 761b930..0000000 --- a/.vpm/.resources/reserved_names.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - - "CMAKE_*", "BUILD_*", "CMAKE_SOURCE_DIR", "CMAKE_BINARY_DIR", - "CMAKE_CACHE_FILE", "CMAKE_C_FLAGS", "CMAKE_CXX_FLAGS", - "CMAKE_BUILD_TYPE", "CMAKE_INSTALL_PREFIX", "CMAKE_CXX_COMPILER", - "CMAKE_LINKER", "CMAKE_INSTALL_PREFIX", - - - "all", "install", "clean", "test", "run", "check", - "package", "install_manifest", "cmake", - - - "add_custom_target", "add_dependencies", "add_executable", - "add_library", "target_link_libraries", "target_include_directories", - "target_compile_definitions", "target_compile_options", "include", - "find_package", "install", "execute_process", "set", "foreach", - "if", "else", "endif", "function", "macro", "project", - - - "CMakeLists.txt", "CMakePresets.json", "CMakeUserPresets.json", - "CMakeCache.txt", "CMakeFiles", - - "install_manifest", "export", "install_files", "package_files" -] \ No newline at end of file diff --git a/.vpm/.template/.ci/linux.yml b/.vpm/.template/.ci/linux.yml deleted file mode 100644 index 7944ec5..0000000 --- a/.vpm/.template/.ci/linux.yml +++ /dev/null @@ -1,55 +0,0 @@ -# Build Stage -build-linux: - image: my.local.registry/vcpkg_builder - stage: build - - tags: - - devops-linux - script: - - cp .vpm/.template/CMakePresetForLinuxCI.json CMakeUserPresets.json - - echo "Building project ..." - - cmake --preset CI - - cmake --build --preset Release - artifacts: - paths: - - build/bin - - build/lib - - build/test - expire_in: 1 hour - -# Test Stage -test-linux: - image: my.local.registry/vcpkg_builder - stage: test - needs: - - build-linux - tags: - - devops-linux - script: - - echo "Running tests..." - - cd build/test - - ctest --output-junit test_report.xml --output-on-failure - artifacts: - reports: - junit: build/test/test_report.xml - expire_in: 1 hour -# Deploy Stage -deploy-linux: - image: my.local.registry/vcpkg_builder - stage: deploy - needs: - - build-linux - - test-linux - only: - - master - tags: - - devops-linux - when: manual # Manual trigger for the deployment stage - script: - - echo "Deploying... (this is just a placeholder, no actual deployment steps)" - artifacts: - paths: - - - build/bin - - build/lib - expire_in: 1 week diff --git a/.vpm/.template/.ci/macos.yml b/.vpm/.template/.ci/macos.yml deleted file mode 100644 index 3099f93..0000000 --- a/.vpm/.template/.ci/macos.yml +++ /dev/null @@ -1,25 +0,0 @@ -build-macos: - stage: build - tags: - - mss - script: - - echo "Not yet implemented" -test-macos: - stage: test - needs: - - build-macos - tags: - - mss - script: - - echo "Not yet implemented" -deploy-macos: - stage: deploy - needs: - - build-macos - - test-macos - tags: - - mss - only: - - master - script: - - echo "Not yet implemented" \ No newline at end of file diff --git a/.vpm/.template/.ci/windows.yml b/.vpm/.template/.ci/windows.yml deleted file mode 100644 index 2495ea0..0000000 --- a/.vpm/.template/.ci/windows.yml +++ /dev/null @@ -1,51 +0,0 @@ -build-windows: - stage: build - tags: - - devops-win - script: - - $env:VCPKG_ROOT="D:\vcpkg" - - $env:PATH="$env:VCPKG_ROOT;$env:PATH" - - vcpkg integrate install - - $env:VCPKG_BINARY_SOURCES="clear;files,D:\vcpkg-cache,readwrite" - - Copy-Item .vpm/.template/CMakePresetForWindowsCI.json -Destination CMakeUserPresets.json - - echo "Building project ..." - - cmake --preset CI - - cmake --build --preset Release - artifacts: - paths: - - build/Release - - build/test - expire_in: 1 hour -test-windows: - image: my.local.registry/vcpkg_builder - stage: test - needs: - - build-windows - tags: - - devops-win - script: - - echo "Running tests..." - - cd build/test - - ctest --output-junit test_report.xml --output-on-failure -C Release - artifacts: - reports: - junit: build/test/test_report.xml - expire_in: 1 hour - -deploy-windows: - stage: deploy - needs: - - build-windows - - test-windows - only: - - master - tags: - - devops-win - when: manual # Manual trigger for the deployment stage - script: - - echo "Deploying... (this is just a placeholder, no actual deployment steps)" - artifacts: - paths: - - build/Release/bin - - build/Release/lib - expire_in: 1 week diff --git a/.vpm/.template/.gitlab-ci_template_library.yml b/.vpm/.template/.gitlab-ci_template_library.yml deleted file mode 100644 index aa0de33..0000000 --- a/.vpm/.template/.gitlab-ci_template_library.yml +++ /dev/null @@ -1,40 +0,0 @@ -stages: - - build - - test - - deploy - - publish - -variables: - CMAKE_BUILD_PARALLEL_LEVEL: 4 -before_script: - - git submodule sync - - git submodule update --init --recursive - -include: - - .ci/linux.yml - - .ci/windows.yml - - .ci/macos.yml - -publish: - stage: publish - image: my.local.registry/vcpkg_builder - needs: - - build-linux - - test-linux - tags: - - devops-linux - rules: - - if: '$CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "push"' - when: never - - if: '$CI_COMMIT_TAG && $CI_DEFAULT_BRANCH == "master"' - when: always - - when: never - script: - - echo "Sending POST request to vcpkg-deployerexample.com" - - | - curl -X POST http://vcpkg-deployerexample.com/deploy \ - -H "Content-Type: application/json" \ - -d '{ - "tag": "'"$CI_COMMIT_TAG"'", - "repo": "''" - }' diff --git a/.vpm/.template/.gitlab-ci_template_standard.yml b/.vpm/.template/.gitlab-ci_template_standard.yml deleted file mode 100644 index e0b0a5c..0000000 --- a/.vpm/.template/.gitlab-ci_template_standard.yml +++ /dev/null @@ -1,14 +0,0 @@ -stages: - - build - - test - - deploy -variables: - CMAKE_BUILD_PARALLEL_LEVEL: 4 -before_script: - - git submodule sync - - git submodule update --init --recursive - -include: - - .ci/linux.yml - - .ci/windows.yml - - .ci/macos.yml diff --git a/.vpm/.template/CMakeList_template.txt b/.vpm/.template/CMakeList_template.txt deleted file mode 100644 index 11bd4d5..0000000 --- a/.vpm/.template/CMakeList_template.txt +++ /dev/null @@ -1,29 +0,0 @@ - -cmake_minimum_required(VERSION 3.12) -project( CXX) -include(${CMAKE_CURRENT_LIST_DIR}/cmake_tools_for_vcpkg/tools/cmake/cmakeProject.cmake) - -message(STATUS "${PROJECT_NAME} Build") - -# Find required packages - -# Configure file for headers -configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in ${CMAKE_CURRENT_LIST_DIR}/include/config.h) - - -set(PROJECT_LIBRARIES "") - - - -setup_install(${CMAKE_PROJECT_NAME}) -setup_package(${CMAKE_PROJECT_NAME}) -# Option to build tests -option(BUILD_TEST "Build the test suite" ON) -if(BUILD_TEST) - # Enable testing - enable_testing() - - - - add_subdirectory(test) -endif() diff --git a/.vpm/.template/CMakePresetForLinuxCI.json b/.vpm/.template/CMakePresetForLinuxCI.json deleted file mode 100644 index 7c5c5e8..0000000 --- a/.vpm/.template/CMakePresetForLinuxCI.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 3, - "configurePresets": [ - { - "name": "CI", - "hidden": false, - "generator": "Ninja", - "binaryDir": "${sourceDir}/build/", - "inherits": "vcpkg", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "/vcpkg/scripts/buildsystems/vcpkg.cmake" - } - } - ], - "buildPresets": [ - { - "name": "Debug", - "configurePreset": "CI", - "configuration": "Debug" - }, - { - "name": "Release", - "configurePreset": "CI", - "configuration": "Release" - } - ] -} \ No newline at end of file diff --git a/.vpm/.template/CMakePresetForWindowsCI.json b/.vpm/.template/CMakePresetForWindowsCI.json deleted file mode 100644 index 675d952..0000000 --- a/.vpm/.template/CMakePresetForWindowsCI.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 3, - "configurePresets": [ - { - "name": "CI", - "hidden": false, - "generator": "Visual Studio 17 2022", - "binaryDir": "${sourceDir}/build/", - "inherits": "vcpkg", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "D:/vcpkg/scripts/buildsystems/vcpkg.cmake" - } - } - ], - "buildPresets": [ - { - "name": "Debug", - "configurePreset": "CI", - "configuration": "Debug" - }, - { - "name": "Release", - "configurePreset": "CI", - "configuration": "Release" - } - ] -} \ No newline at end of file diff --git a/.vpm/.template/CMakePresets_template.json b/.vpm/.template/CMakePresets_template.json deleted file mode 100644 index 11130e0..0000000 --- a/.vpm/.template/CMakePresets_template.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": 3, - "configurePresets": [ - { - "hidden": true, - "name": "vcpkg", - "binaryDir": "${sourceDir}/build/", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - } - } - ] - } \ No newline at end of file diff --git a/.vpm/.template/CONTRIBUTING.md b/.vpm/.template/CONTRIBUTING.md deleted file mode 100644 index 0183e5e..0000000 --- a/.vpm/.template/CONTRIBUTING.md +++ /dev/null @@ -1,97 +0,0 @@ -# Contributing to 's C++ Project - -Welcome! To ensure your contributions align with our workflow, please follow the steps below. - -## Contribution Process - -### 1. **Create an Issue** - - Before making changes, **create an issue** in the GitLab repository. - - Include a clear description, steps to reproduce (if it's a bug), expected behavior, and any relevant screenshots or logs - - Add labels that characterize the issue type (e.g., bug, feature request, improvement, etc.). - -### 2. **Fork the Repository** - - If using a template like VPM, fork the repository to your GitLab account by clicking **Fork**. - -### 3. **Clone Your Fork** - Clone your fork to your local machine: - ```bash - git clone https://gitlab.com/your-username/your-forked-repo.git - ``` - -### 4. **Create a New Branch** - Create a branch based on `dev` for your feature or bug fix: - ```bash - git checkout -b 123-your-feature-name dev - ``` - -### 5. **Make Changes** - - Implement your changes according to the project's coding style and conventions. - - Ensure any new features are fully tested. - -### 6. **Write Tests** - Write tests to validate your changes. To run tests locally with CMake and CTest: - 1. **Configure the project**: - ```bash - cmake --preset - ``` - 2. **Build the project**: - ```bash - cmake --build --preset - ``` - 3. **Run tests**: - ```bash - ctest - ``` - - For detailed results: - ```bash - ctest --output-on-failure - ``` - -### 7. **Commit Your Changes** - Commit your changes with a clear message referencing the issue: - ```bash - git commit -m "Fix issue #123: Description of your fix" - ``` - -### 8. **Push to Your Fork** - Push your changes to your forked repository: - ```bash - git push origin 123-your-feature-name - ``` - -### 9. **Create a Merge Request (MR)** - - Create a **Merge Request (MR)** from your branch to `dev`. - - In the MR description, reference the issue (e.g., `Fixes #123`), summarize the changes, and list any testing performed. - -### 10. **Request Review** - - Request a review from team members or maintainers. Ensure all tests have passed and the MR is ready for review. - -### 11. **Revise and Finalize the MR** - - If revisions are requested, update your branch, commit the changes, and push them. - - Once approved, the MR will be merged into `dev`. - -### 12. **Merge into `dev`** - - After approval, your MR will be merged into `dev`. If unsure, consult a maintainer for guidance. - -### 13. **Clean Up** - - After merging, delete your feature branch setting delete branch in the MR settings - -## Code Style Guidelines - -- **Indentation**: 4 spaces per level (no tabs). -- **Naming**: - - Use camelCase for variables and functions. - - Use PascalCase for classes. -- **Documentation**: Document complex code and functions. - -## Reporting Bugs - When reporting bugs, provide: - - A clear description of the issue. - - Steps to reproduce (if applicable). - - Relevant screenshots, error logs, and environment details. - -## Additional Resources - -- [GitLab Workflow Documentation](https://docs.gitlab.com/ee/workflow/) -- [Creating a Merge Request in GitLab](https://docs.gitlab.com/ee/user/project/merge_requests/) -- [Git Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows) diff --git a/.vpm/.template/Doxyfile_template b/.vpm/.template/Doxyfile_template deleted file mode 100644 index f9de7ce..0000000 --- a/.vpm/.template/Doxyfile_template +++ /dev/null @@ -1,26 +0,0 @@ -# Doxyfile configuration file - -# Set the project name -PROJECT_NAME = "" - -# Enable dot (graphviz) to generate graphical class diagrams -HAVE_DOT = YES - -# Set the path to dot executable (if not in PATH) -DOT_PATH = /path/to/dot - -# Enable inheritance diagrams -CLASS_DIAGRAMS = YES - -# Enable collaboration diagrams (associations/relationships) -COLLABORATION_GRAPH = YES - -# Set the source directories -INPUT = . -# Set the header directories -INCLUDE_PATH = . -# Enable recursive directory scanning -RECURSIVE = YES -INPUT += README.md -USE_MDFILE_AS_MAINPAGE = README.md -OUTPUT_DIRECTORY = build/docs/ diff --git a/.vpm/.template/LICENSE b/.vpm/.template/LICENSE deleted file mode 100644 index 84ed87e..0000000 --- a/.vpm/.template/LICENSE +++ /dev/null @@ -1,39 +0,0 @@ -### *This is a DRAFT License* - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, and distribution as defined in this document. - "Licensor" means , the copyright holder or entity authorized by the copyright holder that is granting the License. - "You" (or "Your") means the individual or entity exercising the rights granted by this License. - "Software" means the software project, including any modifications or additions made to the software, distributed under this License. - -2. Grant of Copyright License. - Subject to the terms and conditions of this License, the Licensor hereby grants you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, reproduce, modify, distribute, and sublicense the Software. - -3. Grant of Patent License. - Subject to the terms and conditions of this License, the Licensor grants you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable patent license to make, use, sell, offer for sale, import, and otherwise transfer the Software, where such license applies only to those patents that are necessarily infringed by your use or distribution of the Software. - -4. Redistribution. - You may reproduce and distribute copies of the Software in source or binary form, provided that you meet the following conditions: - a) You must give any other recipients of the Software or derivative works a copy of this License. - b) You must state any significant changes or modifications you made to the Software. - c) You must not use the names, trademarks, or other identifiers of without prior written permission. - -5. Submission of Contributions. - If you contribute to this Software, you hereby grant the Licensor a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to use, reproduce, modify, distribute, and sublicense your contributions as part of the Software. - -6. No Warranty. - The Software is provided "as-is", without any express or implied warranties, including but not limited to warranties of merchantability, fitness for a particular purpose, or non-infringement. In no event shall the Licensor or any contributors be liable for any damages arising from the use or inability to use the Software. - -7. Termination. - This License and the rights granted hereunder will terminate automatically if you breach any of the terms and conditions. Upon termination, you must stop using, distributing, and sublicensing the Software. - -8. Miscellaneous. - a) This License is governed by the laws of Switzerland. - b) If any part of this License is found to be invalid or unenforceable, the remaining parts will continue in full force and effect. - -For questions or inquiries regarding the use of this Software, please contact at [your contact details]. diff --git a/.vpm/.template/QT_PROJECT_TEMPLATE_EXE.txt b/.vpm/.template/QT_PROJECT_TEMPLATE_EXE.txt deleted file mode 100644 index 150e14a..0000000 --- a/.vpm/.template/QT_PROJECT_TEMPLATE_EXE.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Find Qt packages -find_package(Qt6 REQUIRED COMPONENTS Qml Quick Core QuickControls2) - -#Enable modules -set(PROJECT_LIBRARIES Qt6::Core Qt6::Quick Qt6::QuickControls2) - -#Qt Resources -set(RESOURCES ${CMAKE_CURRENT_LIST_DIR}/resources/resources.qrc) - diff --git a/.vpm/.template/QT_PROJECT_TEMPLATE_LIB.txt b/.vpm/.template/QT_PROJECT_TEMPLATE_LIB.txt deleted file mode 100644 index 5391755..0000000 --- a/.vpm/.template/QT_PROJECT_TEMPLATE_LIB.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Find Qt packages -find_package(Qt6 REQUIRED COMPONENTS Core) - -#Enable modules -set(PROJECT_LIBRARIES Qt6::Core) - -#Qt Resources -set(RESOURCES "") - diff --git a/.vpm/.template/config.h.in b/.vpm/.template/config.h.in deleted file mode 100644 index f92b98f..0000000 --- a/.vpm/.template/config.h.in +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CONFIG_H_IN -#define CONFIG_H_IN -#define PROJECT_NAME "@PROJECT_NAME@" -#define PROJECT_VERSION "@PROJECT_VERSION@" -#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ -#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@ -#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ -#define PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@" -#define PROJECT_AUTHOR "@PROJECT_AUTHOR@" -#define PROJECT_AUTHOR_EMAIL "@PROJECT_AUTHOR_EMAIL@" -#define PROJECT_AUTHOR_WEBSITE "@PROJECT_AUTHOR_WEBSITE@" -#define PROJECT_LICENSE "@PROJECT_LICENSE@" -#define PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@" -#define PROJECT_TRADEMARK "@PROJECT_TRADEMARK@" -#define PROJECT_COMPANY "@PROJECT_COMPANY@" -#define PROJECT_COMPANY_NAME "@PROJECT_COMPANY_NAME@" -#define PROJECT_COMPANY_ADDRESS "@PROJECT_COMPANY_ADDRESS@" -#define PROJECT_COMPANY_CITY "@PROJECT_COMPANY_CITY@" -#define PROJECT_COMPANY_COUNTRY "@PROJECT_COMPANY_COUNTRY@" -#define PROJECT_COMPANY_PHONE "@PROJECT_COMPANY_PHONE@" -#define PROJECT_COMPANY_EMAIL "@PROJECT_COMPANY_EMAIL@" -#define PROJECT_COMPANY_WEBSITE "@PROJECT_COMPANY_WEBSITE@" -#define PROJECT_COMPANY_ZIPCODE "@PROJECT_COMPANY_ZIPCODE@" -#define PROJECT_COMPANY_FAX "@PROJECT_COMPANY_FAX@" - -#endif // CONFIG_H_IN \ No newline at end of file diff --git a/.vpm/.template/include/main.h.txt b/.vpm/.template/include/main.h.txt deleted file mode 100644 index 6f70f09..0000000 --- a/.vpm/.template/include/main.h.txt +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/.vpm/.template/include/shared_template.h.txt b/.vpm/.template/include/shared_template.h.txt deleted file mode 100644 index 36a7485..0000000 --- a/.vpm/.template/include/shared_template.h.txt +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#ifdef _WINDOWS - #ifndef EXPORT_ALL - #ifdef BUILD_SHARED - // Building the DLL (exporting) - #define LIB __declspec(dllexport) - #else - // Using the DLL (importing) - #define LIB __declspec(dllimport) - #endif - #else - #define LIB - #endif -#else - // Non-Windows platforms (optional, but typically use empty macro or other logic) - #define LIB -#endif - -void LIB shared_lib_function(); diff --git a/.vpm/.template/resources/qml/main.qml b/.vpm/.template/resources/qml/main.qml deleted file mode 100644 index 05b2967..0000000 --- a/.vpm/.template/resources/qml/main.qml +++ /dev/null @@ -1,22 +0,0 @@ -// HelloWorld.qml - -import QtQuick 2.15 -import QtQuick.Window 2.15 - -Window { - width: 640 - height: 480 - visible: true - title: qsTr("Hello World") - Rectangle { - width: 200 - height: 100 - color: "red" - - Text { - anchors.centerIn: parent - text: "Hello, World!" - } - -} -} diff --git a/.vpm/.template/resources/resources.qrc b/.vpm/.template/resources/resources.qrc deleted file mode 100644 index 84a2ddc..0000000 --- a/.vpm/.template/resources/resources.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - qml/main.qml - - diff --git a/.vpm/.template/src/qt_main_template.cpp.txt b/.vpm/.template/src/qt_main_template.cpp.txt deleted file mode 100644 index 56eb05a..0000000 --- a/.vpm/.template/src/qt_main_template.cpp.txt +++ /dev/null @@ -1,16 +0,0 @@ -#include "main.h" -#include -#include -#include -#include - -int main(int argc, char* argv[]) { - QGuiApplication app(argc, argv); - QQmlApplicationEngine engine; - //the follwing line is mandatory to make the qml working, don't remove it! - engine.addImportPath(app.applicationDirPath() + QDir::separator() + "qml"); - engine.load("qrc:/app.qml"); - app.exec(); - - return 0; -} diff --git a/.vpm/.template/test/CMakeList_template.txt b/.vpm/.template/test/CMakeList_template.txt deleted file mode 100644 index 180f91c..0000000 --- a/.vpm/.template/test/CMakeList_template.txt +++ /dev/null @@ -1,36 +0,0 @@ -cmake_minimum_required(VERSION 3.12) - -message(STATUS "${PROJECT_NAME} Unit Test") - -# Recursively find all .cpp and .h files in the current directory -file(GLOB_RECURSE TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.cpp" "${CMAKE_CURRENT_LIST_DIR}/*.h") - -#find_package here-------------------- - -#find_package(GTest REQUIRED)#GoogleTest package inclusion - -#------------------------------------- -#linking here-------------------- -set(TEST_LIBRARY_LINKING "")#comment this if you are using GoogleTest -#set(TEST_LIBRARY_LINKING GTest::GTest GTest::Main)#for GoogleTest -#------------------------------------- -# Create the executable for the tests -add_executable(${PROJECT_NAME}_test ${TEST_SOURCES}) - -# Assuming CONAN_LIBS and LIBRARY_NAME are defined in your conan setup -# Link the libraries -target_link_libraries(${PROJECT_NAME}_test PRIVATE ${PROJECT_LIB} ${TEST_LIBRARY_LINKING}) - -# Include directories for the project (if needed) -target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIRECTORIES}) - -#Include GoogleTest and setup tests - -#GoogleTest configuration -#include(GoogleTest) -#gtest_discover_tests(${PROJECT_NAME}_test) - -#Configuration for other test, this should need if you are using gtest -add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}_test) - -#other options here-------------------- diff --git a/.vpm/README.md b/.vpm/README.md deleted file mode 100644 index af46aa6..0000000 --- a/.vpm/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# VPM (Vcpkg Project Manager) - -**VPM** is a tool designed for managing CMake projects with Vcpkg integration. It simplifies project setup, updates configurations based on the environment, and ensures everything is in place for smooth CMake-Vcpkg usage. - -## Features: -- **Initialization**: `init.bat` (Windows) or `init.sh` (Linux) will initialize and configure a new CMake-Vcpkg project. -- **Configuration Update**: `vpm.bat` (Windows) or `vpm.sh` (Linux) updates your project's configuration files based on the local environment. -- **File Creation**: If any essential files are missing, VPM will automatically generate them. Existing source files are left untouched. -- **Configuration Management**: If your project already has a CMake configuration, VPM won't overwrite it. If you're experiencing issues, like failing tests due to conflicting CMake settings, you can manually copy your CMake configuration back into the updated `CMakeLists.txt` after running VPM. Conflicting settings will be avoided automatically. - -## How It Works: - -### 1. **Initialization (init.bat/init.sh)** - - Run the `init` script to set up your project and configure it for CMake and Vcpkg. - - This will set up the necessary directories, files, and environment variables required for a functional CMake project with Vcpkg integration. - -### 2. **Configuration Update (vpm.bat/vpm.sh)** - - Once the initial setup is done, run the `vpm` script to update your project configuration. This tool will adjust CMake settings based on your local environment, ensuring proper integration with Vcpkg. - -### 3. **File Management** - - **Missing Files**: VPM will create any missing files, such as CMake files or Vcpkg integration files, to ensure the project is properly configured. - - **Source Files**: If the project already has source files, they will not be modified. - -### 4. **Restoring Configuration (For Existing Projects)** - - If your project has an existing CMake configuration that you need to keep, run the `vpm` tool to update the environment. You may then manually restore any custom configurations into the updated `CMakeLists.txt`, ensuring to resolve any conflicts. - -## Important Notes: -- **Don't modify the core VPM structure**: The internal structure of VPM should not be changed unless you are sure of what you're doing. If in doubt, refer to this README. -- **Keep your configurations intact**: If you're using an existing CMake setup, ensure to copy back your custom configuration after running VPM to avoid overwriting any specific settings. diff --git a/.vpm/requirements.txt b/.vpm/requirements.txt deleted file mode 100644 index aa07481..0000000 --- a/.vpm/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -GitPython -coloredlogs -colorama \ No newline at end of file diff --git a/.vpm/src/cmake_presets_manager.py b/.vpm/src/cmake_presets_manager.py deleted file mode 100644 index 4f5fbce..0000000 --- a/.vpm/src/cmake_presets_manager.py +++ /dev/null @@ -1,140 +0,0 @@ -import os -import shutil -import json -import logging -import platform -import subprocess -from pathlib import Path -import vcpkg_manager as vcpkg -import project_utilities as utils - -# Logging configuration -logger = logging.getLogger("CMakeInitializer") - -class CMakePresetManager: - def __init__(self, repo_path,vcpkg_manager:vcpkg.VcpkgManager): - self.repo_path = repo_path - self.vcpkg_manager=vcpkg_manager - - @staticmethod - def get_script_dir(): - """Returns the directory where the current script is located.""" - return Path(__file__).resolve().parent - - def configure_cmake_presets(self): - """Sets up CMakePresets.json.""" - logger.info("Configuring CMakePresets...") - preset_path = Path(self.repo_path) / "CMakePresets.json" - if not preset_path.exists(): - template_path = utils.ProjectUtilities.get_template_path()/ "CMakePresets_template.json" - if template_path.exists(): - shutil.copy(template_path, preset_path) - logger.info("CMakePresets.json file created.") - return True - else: - logger.error(f"Template file '{template_path}' not found.") - return False - logger.info("CMakePresets.json already exists.") - return True - @staticmethod - def detect_generators(): - """Detects available generators based on platform and installed tools.""" - system_platform = platform.system() - generators = {} - - if system_platform == "Windows": - generators["Ninja"] = shutil.which("ninja") is not None - vswhere_path = utils.ProjectUtilities.get_resources_path() / "vswhere.exe" - if vswhere_path.exists(): - try: - # Run vswhere to find the installed Visual Studio versions - result = subprocess.run([vswhere_path, "-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild", "-property", "displayName"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if result.returncode == 0: - vs_version = result.stdout.strip().split(" ")[-1] - - # Extract Visual Studio version from the output - if vs_version == "2022": - vs_version = "17 " + vs_version - elif vs_version == "2019": - vs_version = "16 " + vs_version - elif vs_version == "2017": - vs_version = "15 " + vs_version - elif vs_version == "2015": - vs_version = "14 " + vs_version - else: - logger.warning(f"Unknown Visual Studio version: {vs_version}") - vs_version = None - - if vs_version: - generators[f"Visual Studio {vs_version}"] = True # Add the detected version - else: - logger.error("No Visual Studio installation found.") - - else: - raise Exception(f"Error detecting Visual Studio version: {result.stderr}") - except Exception as e: - logger.error(f"Error detecting Visual Studio version: {e}") - else: - logger.error("vswhere tool not found. Make sure Visual Studio is installed.") - else: - generators["Ninja"] = shutil.which("ninja") is not None - generators["Unix Makefiles"] = shutil.which("make") is not None - if system_platform == "Darwin": - generators["Xcode"] = shutil.which("xcodebuild") is not None - - return {gen: available for gen, available in generators.items() if available} - - def configure_cmake_user_presets(self, generator_to_use=""): - """Configures CMakeUserPresets.json based on available generators and vcpkg.""" - logger.info("Configuring CMakeUserPresets.json...") - output_path = Path(self.repo_path) / "CMakeUserPresets.json" - vcpkg_path = vcpkg.VcpkgManager.get_vcpkg_path() - vcpkg_directory = os.path.dirname(vcpkg_path).replace("\\", "/") - generators = self.detect_generators() - - selected_generator = generator_to_use or next(iter(generators), "") - if selected_generator not in generators: - logger.error("Invalid generator selected.") - return False - - presets = { - "version": 3, - "configurePresets": [], - "buildPresets": [] - } - - for generator in generators: - is_selected = generator == selected_generator - configure_preset = { - "name": generator, - "hidden": not is_selected, - "generator": generator, - "binaryDir": "${sourceDir}/build/", - "inherits": "vcpkg", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": f"{vcpkg_directory}/scripts/buildsystems/vcpkg.cmake", - } - } - presets["configurePresets"].append(configure_preset) - - if is_selected: - presets["buildPresets"].extend([ - { - "name": "Debug", - "configurePreset": generator, - "configuration": "Debug" - }, - { - "name": "Release", - "configurePreset": generator, - "configuration": "Release" - } - ]) - - with open(output_path, "w") as outfile: - json.dump(presets, outfile, indent=4) - self.vcpkg_manager.setup_vcpkg_environment() - logger.info("CMakeUserPresets.json configured successfully.") - return True - diff --git a/.vpm/src/git_manager.py b/.vpm/src/git_manager.py deleted file mode 100644 index 50760fa..0000000 --- a/.vpm/src/git_manager.py +++ /dev/null @@ -1,95 +0,0 @@ -import os -import git -import logging -import coloredlogs - -# Set up logger with colored output -logger = logging.getLogger("CMakeInitializer") -class GitRepositoryManager: - def __init__(self, repo_path): - """ - Initializes the Git repository manager. - - Args: - repo_path (str): Path to the Git repository. - """ - self.repo_path = repo_path - self.repo = None - - # Check if the repository exists at the given path - if os.path.exists(repo_path): - try: - self.repo = git.Repo(repo_path) - logger.info(f"Git repository successfully opened at: {repo_path}") - except Exception as e: - logger.critical(f"Error while opening the Git repository: {e}") - else: - logger.error(f"Repository path not found: {repo_path}") - - def configure_tools_repo(self): - """ - Configures the Git repository by initializing and updating submodules. - - Returns: - bool: True if configuration is successful, False otherwise. - """ - logger.info("Configuring CMakeTools...") - - try: - # Initialize and update Git submodules - self.repo.git.submodule("init") - self.repo.git.submodule("update", "--remote") - logger.info("Submodules initialized and updated.") - except Exception as e: - logger.critical(f"An error occurred while configuring submodules: {e}") - return False - - return True - - def add_to_gitignore(self, gitignore_path, entry): - """ - Adds a specified entry to the .gitignore file if it doesn't already exist. - - Args: - gitignore_path (str): Path to the .gitignore file. - entry (str): The entry to add to the .gitignore file. - - Returns: - bool: True if the entry was added or already exists, False if an error occurred. - """ - # Check if the .gitignore file exists - if not os.path.exists(gitignore_path): - logger.error(f"Gitignore file '{gitignore_path}' not found.") - return False - - try: - # Open the .gitignore file and add the entry if it doesn't already exist - with open(gitignore_path, "r+") as gitignore_file: - gitignore_content = gitignore_file.read() - if entry not in gitignore_content: - gitignore_file.seek(0, os.SEEK_END) - gitignore_file.write(f"\n{entry}") - logger.info(f"Added '{entry}' to .gitignore.") - else: - logger.warning(f"'{entry}' already exists in .gitignore.") - except Exception as e: - logger.error(f"An error occurred while modifying the .gitignore file: {e}") - return False - - return True - def get_untracked_files(self): - """ - Retrieves a list of untracked files in the repository. - - Returns: - list: A list of untracked files. - """ - return self.repo.untracked_files - def get_repo_remote_url(self): - """ - Retrieves the remote URL of the Git repository. - - Returns: - str: The remote URL of the Git repository. - """ - return self.repo.remotes.origin.url \ No newline at end of file diff --git a/.vpm/src/project_manager.py b/.vpm/src/project_manager.py deleted file mode 100644 index 01926a9..0000000 --- a/.vpm/src/project_manager.py +++ /dev/null @@ -1,394 +0,0 @@ -import json -import os -import logging -import shutil -import coloredlogs -import re -import git_manager as gm -import project_structure_manager as ps -import vcpkg_manager as vm -import cmake_presets_manager as cpm -import project_utilities as utils -import sys -from pathlib import Path -sys.path.append(str(Path(__file__).resolve().parent.parent.parent / "cmake_tools_for_vcpkg/tools")) -from setup import main as cmake_tools # type: ignore -# Set up logger with colored output -logger = logging.getLogger("CMakeInitializer") - -class CMakeProjectManager: - - def __init__(self, repo_path:Path, project_name:str, project_type:str, is_qt_project:bool): - self.repo_path = repo_path - self.project_name = project_name - self.project_type = project_type - self.is_qt_project = is_qt_project - self.file_path=os.path.dirname(__file__) - self.template_path = utils.ProjectUtilities.get_template_path() - self.resources_path= utils.ProjectUtilities.get_resources_path() - self.vcpkg_manager = vm.VcpkgManager(repo_path) - self.git_manager = gm.GitRepositoryManager(repo_path) - self.config_bkp={} - self.already_created=False - self.structure_manager= ps.ProjectStructureManager(repo_path,self.git_manager) - self.cmake_presets_manager=cpm.CMakePresetManager(repo_path,self.vcpkg_manager) - - @staticmethod - def is_valid_package_name(name)->bool: - """Check if the package name is valid according to vcpkg rules.""" - # Check for lowercase alphanumeric characters and hyphens only - return bool(re.match(r'^[a-z0-9-]+$', name)) - def is_executable(self)->bool: - return self.project_type=="exe" - def is_shared_library(self)->bool: - return self.project_type=="shared" - def is_static_library(self)->bool: - return self.project_type=="static" - def configure_cmake(self)->bool: - if os.path.exists(self.repo_path/"CMakeLists.txt"): - #check project name in the file - with open(self.repo_path/"CMakeLists.txt", "r") as cmake_file: - content = cmake_file.read() - if f"project({self.project_name}" in content: - logger.warning("Project already configured.") - self.already_created=True - return True - else: - logger.info("Project not configured. Configuring...") - self.already_created=False - else: - logger.info("CMakeLists.txt not found. Configuring...") - template_path = os.path.join(self.template_path, "CMakeList_template.txt") - if not os.path.exists(template_path): - logger.error(f"Template file '{template_path}' not found.") - return False - with open(template_path, "r") as template_file: - template_content = template_file.read() - - is_qt_project_str="" - if self.is_qt_project: - is_qt_project_str="true" - else: - is_qt_project_str="false" - self.project_types = { - "exe": f"create_executable(\"{self.project_name}\" {is_qt_project_str})", - "shared": f"create_shared_library(\"{self.project_name}\" {is_qt_project_str})", - "static": f"create_static_library(\"{self.project_name}\" {is_qt_project_str})" - } - - if self.project_type not in self.project_types: - logger.error("Invalid artifact type selected.") - return False - self.configure_vcpkg() - template_content=self._configure_qt(self.is_qt_project, template_content) - template_content = template_content.replace("", self.project_name) - template_content = template_content.replace("", self.project_types[self.project_type]) - - if self.is_executable(): - template_content = template_content.replace("", f"create_static_library(\"{self.project_name}_lib\" {is_qt_project_str})") - template_content = template_content.replace("",f"set(PROJECT_LIB \"{self.project_name}_lib\")") - else: - template_content = template_content.replace("", "") - template_content = template_content.replace("", f"set(PROJECT_LIB \"{self.project_name}\")") - #set LOGO - logger.info("Adding logo to the project...") - logo_content=self.get_logo() - #put # at the beginning of each line - logo_content = "# " + logo_content.replace("\n", "\n# ") - template_content = template_content.replace("",logo_content) - # Write final CMake content - with open("CMakeLists.txt", "w") as cmake_file: - cmake_file.write(template_content) - logger.info("CMakeLists.txt configured.") - return True - - def configure_vcpkg(self)->bool: - if os.path.exists(self.repo_path/"vcpkg.json"): - self.vcpkg_manager.create_vcpkg_json(self.project_name) - logger.info("vcpkg.json file configured....") - return True - def write_project_configuration(self): - #write in .vpm/.cache the file config.json with the name, project type and isqt - project_config = { - "name": self.project_name, - "type": self.project_type, - "is_qt_project": self.is_qt_project - } - if not os.path.exists(os.path.join(self.repo_path, ".vpm", ".cache")): - os.makedirs(os.path.join(self.repo_path, ".vpm", ".cache"), exist_ok=True) - with open(os.path.join(self.repo_path, ".vpm", ".cache", "config.json"), "w") as config_file: - json.dump(project_config, config_file, indent=4) - logger.info("Project configuration written.") - - def load_project_configuration(self): - config_path = os.path.join(self.repo_path, ".vpm", ".cache", "config.json") - self.already_created=False - if os.path.exists(config_path): - with open(config_path, "r") as config_file: - config = json.load(config_file) - self.config_bkp=config - self.project_name = config.get("name", "") - self.project_type = config.get("type", "") - self.is_qt_project = config.get("is_qt_project", False) - self.already_created=True - logger.info("Project configuration loaded.") - - - def configure(self)->bool: - logger.info("Configuring CMakeLists.txt...") - - self.load_project_configuration() - - if self.is_project_initialized(): - logger.info("Project already initialized.") - return True - os.chdir(self.repo_path) - if (not self.already_created and not self.is_project_initialized()) or not os.path.exists(self.repo_path/"CMakeLists.txt") or os.path.exists(self.repo_path/"vcpkg.json"): - self.configure_vcpkg() - if self.project_type !="exe": - self.vcpkg_manager.add_vcpkg_library_dependencies() - self.configure_cmake() - self.write_project_configuration() - - if not self.is_project_initialized(): - if not self.structure_manager.configure(self.project_name,self.project_type,self.is_qt_project): - raise Exception("Project structure configuration failed") - - # Run the CMakeTools setup - logger.info("Updating project configuration....") - cmake_tools(False, "CMakeLists.txt") - return True - - def get_cmake_presets_manager(self): - return self.cmake_presets_manager - def configure_with_inputs(self, name, type, is_qt_project)->bool: - self.project_name = name - self.project_type = type - self.is_qt_project=is_qt_project - return self.configure() - def _configure_qt(self, is_qt_project,template_content)->str: - """Configure the qt project""" - - if is_qt_project and self.is_executable(): - qt_template_path = os.path.join(self.template_path, "QT_PROJECT_TEMPLATE_EXE.txt") - if not os.path.exists(qt_template_path): - logger.error(f"Qt template file '{qt_template_path}' not found.") - return False - with open(qt_template_path, "r") as qt_template_file: - qt_template_content = qt_template_file.read() - template_content = template_content.replace("", qt_template_content) - template_content = template_content.replace("set(PROJECT_LIBRARIES \"\")", "") - #update vcpkg.json with the qtbase, qtdeclarative and qttranslations - logger.info("Adding Qt libraries to the manifest...") - self.vcpkg_manager.add_dependency("qtbase") - self.vcpkg_manager.add_dependency("qtdeclarative") - self.vcpkg_manager.add_dependency("qttranslations") - elif is_qt_project and (self.is_shared_library() or self.is_static_library()): - qt_template_path = os.path.join(self.template_path, "QT_PROJECT_TEMPLATE_LIB.txt") - if not os.path.exists(qt_template_path): - logger.error(f"Qt template file '{qt_template_path}' not found.") - return False - with open(qt_template_path, "r") as qt_template_file: - qt_template_content = qt_template_file.read() - template_content = template_content.replace("", qt_template_content) - template_content = template_content.replace("set(PROJECT_LIBRARIES \"\")", "") - logger.info("Adding Qt libraries to the manifest...") - self.vcpkg_manager.add_dependency("qtbase") - else: - template_content = template_content.replace("", "") - return template_content - - def is_project_initialized(self) -> bool: - """ - Verifica se il progetto è inizializzato correttamente. - - Returns: - bool: True se il progetto è inizializzato correttamente, False altrimenti. - """ - if not self.check_project_files(): - return False - if not self.check_cmake_file(): - return False - if not self.check_vcpkg_json(): - return False - - if not self.check_config_json(): - return False - - - return True - - def check_cmake_file(self) -> bool: - """ - Checks if the CMakeLists.txt file exists and contains the project name. - - Returns: - bool: True if the project is already configured, False otherwise. - """ - if os.path.exists(self.repo_path / "CMakeLists.txt"): - with open(self.repo_path / "CMakeLists.txt", "r") as cmake_file: - content = cmake_file.read() - if f"project({self.project_name}" in content: - logger.warning("Project already configured.") - self.already_created = True - return True - else: - return False - return False - - def check_project_files(self) -> bool: - """ - Checks if the files listed in the project.json file exist in the repository. - - Returns: - bool: True if all files exist, False otherwise. - """ - project_json_path = os.path.join(self.resources_path, "project.json") - if not os.path.exists(project_json_path): - logger.error("No project.json found in the resources.") - return False - - with open(project_json_path, "r") as project_json_file: - project_json_content = json.load(project_json_file) - for file in project_json_content: - file_path = os.path.join(self.repo_path, file.replace("/", os.sep)) - if not os.path.exists(file_path): - logger.debug(f"File {file} not found in the project.") - return False - logger.debug(f"File {file} found in the project.") - - return True - - def check_vcpkg_json(self) -> bool: - """ - Checks the validity of the vcpkg.json file. - - Returns: - bool: True if vcpkg.json exists and is valid, False otherwise. - """ - vcpkg_json_path = os.path.join(self.repo_path, "vcpkg.json") - if not os.path.exists(vcpkg_json_path): - logger.error("No vcpkg.json found in the project.") - return False - - with open(vcpkg_json_path, "r") as vcpkg_json_file: - vcpkg_json_content = json.load(vcpkg_json_file) - if "name" not in vcpkg_json_content or "version" not in vcpkg_json_content: - logger.error("vcpkg.json is not valid.") - return False - - return True - - def check_config_json(self) -> bool: - """ - Checks the validity of the config.json file in the cache. - - Returns: - bool: True if config.json exists and is valid, False otherwise. - """ - cache_path = utils.ProjectUtilities.get_cache_path() - config_json_path = os.path.join(cache_path, "config.json") - - if not os.path.exists(config_json_path): - logger.error("No config.json found in the cache.") - return False - - with open(config_json_path, "r") as config_file: - config = json.load(config_file) - if not config.get("name"): - logger.error("Project name is void or null.") - return False - if not config.get("type"): - logger.error("Project type is void or null.") - return False - if config.get("type") not in ["exe", "shared", "static"]: - logger.error("Project type is not valid.") - return False - - return True - - def get_reserved_names(self)->json: - content="" - with open(os.path.join(self.resources_path, "reserved_names.json"), "r") as reserved_names_file: - content = json.load(reserved_names_file) - return content - def configure_automations(self,generator=""): - if self.is_project_initialized(): - return self.cmake_presets_manager.configure_cmake_user_presets(generator) - - if self.cmake_presets_manager.configure_cmake_presets() and self.cmake_presets_manager.configure_cmake_user_presets(generator): - if os.path.exists(os.path.join(self.repo_path, "init.sh")): - shutil.move(os.path.join(self.repo_path,"init.sh"), os.path.join(self.repo_path,"vpm.sh")) - if os.path.exists(os.path.join(self.repo_path, "init.bat")): - shutil.move(os.path.join(self.repo_path, "init.bat"), os.path.join(self.repo_path, "vpm.bat")) - - - logger.info("init scripts moved to initializers") - - return True - else: - logger.error("Error configuring CMakeUserPresets.json.") - return False - - def reset(self): - newfiles = self.git_manager.get_untracked_files() - - for file in newfiles: - file_path = Path(self.repo_path) / file - if file_path.is_dir(): - logger.warning(f"Removing directory: {file_path}") - shutil.rmtree(file_path, ignore_errors=True) - elif file_path.is_file(): - logger.warning(f"Removing file: {file_path}") - file_path.unlink() - - cmake_user_presets = Path(self.repo_path) / "CMakeUserPresets.json" - cmake_presets = Path(self.repo_path) / "CMakePresets.json" - cmake_folder = Path(self.repo_path) / "cmake" - include_folder = Path(self.repo_path) / "include" - src_folder = Path(self.repo_path) / "src" - test_folder = Path(self.repo_path) / "test" - resources_folder = Path(self.repo_path) / "resources" - ci_folder=Path(self.repo_path) / ".ci" - - #clear .cache folder - cache_folder = Path(self.repo_path) / ".vpm" / ".cache" - if cache_folder.exists(): - shutil.rmtree(cache_folder, ignore_errors=True) - logger.warning(f"Removed directory: {cache_folder}") - #restore config backup if it isn't void - if len(self.config_bkp)>0: - os.makedirs(os.path.join(self.repo_path, ".vpm", ".cache"), exist_ok=True) - with open(os.path.join(self.repo_path, ".vpm", ".cache", "config.json"), "w") as config_file: - json.dump(self.config_bkp, config_file, indent=4) - logger.info("Restored config cache") - if not self.already_created: - if cmake_user_presets.exists(): - cmake_user_presets.unlink() - logger.warning(f"Removed file: {cmake_user_presets}") - - if cmake_presets.exists(): - cmake_presets.unlink() - logger.warning(f"Removed file: {cmake_presets}") - - if cmake_folder.exists() and not any(cmake_folder.iterdir()): - cmake_folder.rmdir() - logger.warning(f"Removed empty directory: {cmake_folder}") - if ci_folder.exists() and not any(ci_folder.iterdir()): - ci_folder.rmdir() - logger.warning(f"Removed empty directory: {ci_folder}") - for folder in [include_folder, src_folder, test_folder,resources_folder]: - if folder.exists() and folder.is_dir(): - logger.warning(f"Removing directory and its contents: {folder}") - shutil.rmtree(folder, ignore_errors=True) - - - logger.info("Project folder cleaned.") - - @staticmethod - def get_logo(): - #read company_logo.txt in .assets folder - logo="" - with open(os.path.join(utils.ProjectUtilities.get_assets_path(), "company_logo.txt"), "r") as file: - logo = file.read() - return logo \ No newline at end of file diff --git a/.vpm/src/project_structure_manager.py b/.vpm/src/project_structure_manager.py deleted file mode 100644 index bc0efab..0000000 --- a/.vpm/src/project_structure_manager.py +++ /dev/null @@ -1,257 +0,0 @@ -import os -import shutil -import logging -import coloredlogs -from pathlib import Path -import project_utilities as utils -import git_manager as gm -import json -# Logging configuration -logger = logging.getLogger("CMakeInitializer") -class ProjectStructureManager: - def __init__(self, repo_path,git_manager:gm.GitRepositoryManager): - self.repo_path = repo_path - self.git_manager=git_manager - self.project_name = "" - self.is_executable = False - self.is_shared_lib = False - self.is_qt_project = False - with open(os.path.join(utils.ProjectUtilities.get_resources_path(), "platforms.json"),"r") as file: - self.platforms = json.load(file) - - - def configure(self,project_name, project_type,is_qt_project=False)->bool: - """Configures the entire project structure.""" - self.is_qt_project = is_qt_project - self.project_name=project_name - if project_type =="exe": - self.is_executable = True - self.is_shared_lib = False - elif project_type =="shared": - self.is_executable = False - self.is_shared_lib = True - else: - self.is_executable = False - self.is_shared_lib = False - if not self._check_existing_structure(): - self._create_directories() - - if self.is_qt_project and self.is_executable: - self._create_qt_project_sources() - elif self.is_executable: - self._create_default_executable() - elif self.is_shared_lib: - self._create_shared_lib() - else: - self._create_static_lib() - logger.info("Creating tests....") - self._setup_test_files() - logger.info("Setting up configuration files....") - self._create_config_file() - logger.info("Setting up CI/CD....") - self._configure_ci_cd() - self._add_license_and_contributing_files() - self._configure_doxyfile() - logger.info("Project structure configured successfully.") - return True - # Helper function to get the current script directory - @staticmethod - def _get_script_dir(): - """Returns the directory where the current script is located.""" - return Path(__file__).resolve().parent - - # Helper function to get a standardized project path - def _get_project_path(self, *paths): - """Returns a standardized path inside the project.""" - return Path(self.repo_path).joinpath(*paths) - def _get_cmake_folder_project_path(self, *paths): - """Returns a standardized path inside the project.""" - if not os.path.exists(Path(self.repo_path).joinpath("cmake")): - os.makedirs(Path(self.repo_path).joinpath("cmake")) - return Path(self.repo_path).joinpath("cmake", *paths) - # Helper functions - @staticmethod - def _create_directory(path): - """Creates a directory if it doesn't already exist.""" - os.makedirs(path, exist_ok=True) - logger.debug(f"Directory '{path}' created.") - - @staticmethod - def _is_empty_directory(path): - """Checks if a directory is empty.""" - return not os.listdir(path) - - @staticmethod - def _copy_file(src: Path, dest: Path) -> bool: - """Copies a file if it exists.""" - src = Path(src) - dest = Path(dest) - try: - if src.exists(): - shutil.copy(src, dest) - logger.debug(f"Copied '{src}' to '{dest}'") - return True - else: - logger.error(f"Template file '{src}' not found.") - return False - except Exception as e: - logger.error(f"Failed to copy '{src}' to '{dest}': {e}") - return False - - def _check_existing_structure(self): - """Checks if the project structure already exists.""" - required_dirs = ["src", "include", "test"] - if all(self._get_project_path(d).exists() for d in required_dirs): - logger.warning("Project structure already exists.") - return True - return False - - def _create_directories(self): - """Creates the main directories of the project.""" - for folder in ["src", "include", "test"]: - self._create_directory(self._get_project_path(folder)) - logger.info("Project directories created.") - - def _create_qt_project_sources(self): - """Sets up source files for a Qt project.""" - template_dir = utils.ProjectUtilities.get_template_path() - - if self._is_empty_directory(self._get_project_path("include")): - self._copy_file(template_dir / "include" / "main.h.txt", - self._get_project_path("include", "main.h")) - - if self._is_empty_directory(self._get_project_path("src")): - self._copy_file(template_dir / "src" / "qt_main_template.cpp.txt", - self._get_project_path("src", "main.cpp")) - if not self._get_project_path("resources").exists(): - self._create_directory(self._get_project_path("resources")) - if self._is_empty_directory(self._get_project_path("resources")): - self._copy_file(template_dir / "resources/resources.qrc", - self._get_project_path("resources/resources.qrc")) - qml_path = self._get_project_path("resources", "qml") - if not qml_path.exists(): - self._create_directory(qml_path) - self._copy_file(template_dir / "resources/qml/main.qml", - qml_path / "main.qml") - - - - logger.info("Qt project source files created.") - - def _create_default_executable(self): - """Creates the main files for a standard C++ executable.""" - if self._is_empty_directory(self._get_project_path("src")): - with open(self._get_project_path("src", "main.cpp"), "w") as main_file: - main_file.write("#include \n\nint main() {\n std::cout << \"Hello, World!\" << std::endl;\n return 0;\n}") - if self._is_empty_directory(self._get_project_path("include")): - with open(self._get_project_path("include", "main.h"), "w") as main_include: - main_include.write("#pragma once\n") - logger.info("Default executable main.cpp and main.h files created.") - - def _create_shared_lib(self): - """Creates the main files for a shared library.""" - template_dir = utils.ProjectUtilities.get_template_path() - - if self._is_empty_directory(self._get_project_path("include")): - self._copy_file(template_dir / "include" / "shared_template.h.txt", self._get_project_path("include", "shared_lib.h")) - if self._is_empty_directory(self._get_project_path("src")): - with open(self._get_project_path("src", "shared_lib.cpp"), "w") as lib_file: - lib_file.write("#include \n#include \"shared_lib.h\"\nvoid shared_lib_function() {\n std::cout << \"Shared library function.\" << std::endl;\n}") - logger.info("Shared library files created.") - - def _create_static_lib(self): - """Creates the main files for a static library.""" - if self._is_empty_directory(self._get_project_path("include")): - with open(self._get_project_path("include", "static_lib.h"), "w") as lib_file: - lib_file.write("#pragma once\nvoid static_lib_function();") - if self._is_empty_directory(self._get_project_path("src")): - with open(self._get_project_path("src", "static_lib.cpp"), "w") as lib_file: - lib_file.write("#include \n#include \"static_lib.h\"\nvoid static_lib_function() {\n std::cout << \"Static library function.\" << std::endl;\n}") - logger.info("Static library files created.") - - def _setup_test_files(self): - """Sets up the test files.""" - template_dir = utils.ProjectUtilities.get_template_path() - if self._is_empty_directory(self._get_project_path("test")): - - self._copy_file(template_dir / "test" / "CMakeList_template.txt", self._get_project_path("test", "CMakeLists.txt")) - - with open(self._get_project_path("test", "test.cpp"), "w") as test_file: - test_file.write("int main() {\n return 0;\n}\n") - logger.info("Test files created.") - - def _create_config_file(self): - """Copies the configuration header file.""" - if os.path.exists(os.path.join(self.repo_path,"cmake","confi.h.in")): - return - template_dir = utils.ProjectUtilities.get_template_path() - self._copy_file(template_dir / "config.h.in", self._get_cmake_folder_project_path("config.h.in")) - logger.info("Config.h.in file created.") - def _check_platforms(self)->bool: - for platform in self.platforms: - if not os.path.exists(os.path.join(self.repo_path, ".ci", f"{platform}.yml")): - return False - def _configure_ci_cd(self): - """Sets up CI/CD files.""" - if os.path.exists(os.path.join(self.repo_path,".gitlab-ci.yml")) and self._check_platforms(): - return - template_dir = utils.ProjectUtilities.get_template_path() - ci_cd_template = ".gitlab-ci_template_library.yml" if not self.is_executable else ".gitlab-ci_template_standard.yml" - if not self.is_executable: - - template_dir = utils.ProjectUtilities.get_template_path() - ci_cd_template = ".gitlab-ci_template_library.yml" - with open(template_dir / ci_cd_template, "r") as ci_cd_file: - ci_cd_content = ci_cd_file.read() - ci_cd_content = ci_cd_content.replace("", self.git_manager.get_repo_remote_url()) - with open(self._get_project_path(".gitlab-ci.yml"), "w") as ci_cd_file: - ci_cd_file.write(ci_cd_content) - - else: - self._copy_file(template_dir / ci_cd_template, self._get_project_path(".gitlab-ci.yml")) - - if not os.path.exists(os.path.join(self.repo_path, ".ci")): - os.makedirs(os.path.join(self.repo_path, ".ci"), exist_ok=True) - - template_dir = utils.ProjectUtilities.get_template_path() - ci_template_dir = template_dir / ".ci" - - - for platform in self.platforms: - if not os.path.exists(os.path.join(self.repo_path, ".ci", f"{platform}.yml")): - self._copy_file(ci_template_dir / f"{platform}.yml", self._get_project_path(".ci", f"{platform}.yml")) - - logger.info("CI/CD configured.") - - def _configure_doxyfile(self): - """Configures the Doxyfile for documentation.""" - if os.path.exists(os.path.join(self.repo_path,"Doxyfile")): - return - template_dir = utils.ProjectUtilities.get_template_path() - with open(template_dir / "Doxyfile_template", "r") as doxyfile: - doxyfile_content = doxyfile.read() - doxyfile_content = doxyfile_content.replace("", self.project_name) - with open(self._get_project_path("Doxyfile"), "w") as doxyfile: - doxyfile.write(doxyfile_content) - logger.info("Doxyfile configured.") - - - def _add_license_and_contributing_files(self): - """Adds LICENSE and CONTRIBUTING files.""" - template_dir = utils.ProjectUtilities.get_template_path() - - license_path = self._get_project_path("LICENSE") - contributing_path = self._get_project_path("CONTRIBUTING.md") - - if not license_path.exists(): - if not self._copy_file(template_dir / "LICENSE", license_path): - logger.warning("Failed to add LICENSE file.") - else: - logger.info("LICENSE file added.") - - if not contributing_path.exists(): - if not self._copy_file(template_dir / "CONTRIBUTING.md", contributing_path): - logger.warning("Failed to add CONTRIBUTING.md file.") - else: - logger.info("CONTRIBUTING.md file added.") - diff --git a/.vpm/src/project_utilities.py b/.vpm/src/project_utilities.py deleted file mode 100644 index 536ae95..0000000 --- a/.vpm/src/project_utilities.py +++ /dev/null @@ -1,40 +0,0 @@ -import os -import os -from pathlib import Path -class ProjectUtilities: - @staticmethod - def get_template_path()->Path: - """ - Returns the path to the template directory. - """ - return Path(os.path.join(os.path.dirname(__file__),".." ,'.template')) - @staticmethod - def get_assets_path()->Path: - """ - Returns the path to the assets directory. - """ - return Path(os.path.join(os.path.dirname(__file__), "..", '.assets')) - @staticmethod - def get_script_path()->Path: - """ - Returns the path to the script directory. - """ - return Path(os.path.dirname(__file__)) - @staticmethod - def get_resources_path()->Path: - """ - Returns the path to the resources directory. - """ - return Path(os.path.join(os.path.dirname(__file__), "..", '.resources')) - @staticmethod - def get_project_path()->Path: - """ - Returns the path to the project directory. - """ - return Path(os.path.dirname(os.path.dirname(__file__),"..","..")) - @staticmethod - def get_cache_path()->Path: - """ - Returns the path to the cache directory. - """ - return Path(os.path.join(os.path.dirname(__file__), "..", '.cache')) \ No newline at end of file diff --git a/.vpm/src/vcpkg_manager.py b/.vpm/src/vcpkg_manager.py deleted file mode 100644 index f16ff6c..0000000 --- a/.vpm/src/vcpkg_manager.py +++ /dev/null @@ -1,160 +0,0 @@ -import os -import subprocess -import json -import logging -import platform -import coloredlogs -import re -# Set up logger with colored output for better visibility -logger = logging.getLogger("CMakeInitializer") -class VcpkgManager: - def __init__(self, repo_path): - """ - Initializes the VcpkgManager instance with the provided paths. - - Args: - vcpkg_path (str): Path to the VCPKG executable. - repo_path (str): Path to the repository. - vcpkg_root_path (str): Path to the VCPKG installation directory. - """ - self.vcpkg_path = self.get_vcpkg_path() - self.repo_path = repo_path - self.vcpkg_root_path = VcpkgManager.get_vcpkg_path() - - def configure_vcpkg(self): - """ - Configures the VCPKG project by running the appropriate VCPKG command to initialize it. - - Returns: - bool: True if the project was successfully configured, False otherwise. - """ - logger.info("Configuring VCPKG project...") - - os.chdir(self.repo_path) - - # Check if vcpkg.json already exists - if not os.path.exists("vcpkg.json"): - # If it doesn't exist, create a new VCPKG project - command = [self.vcpkg_path, 'new', "--application"] - try: - subprocess.run(command, check=True) - logger.info("VCPKG project configured.") - except subprocess.CalledProcessError as e: - logger.error(f"An error occurred while configuring VCPKG: {e}") - return False - - return True - - def create_vcpkg_json(self, project_name): - """ - Creates or updates the `vcpkg.json` file with the given project name and version. - If the file exists, it updates the name and version without overriding other properties. - - Args: - project_name (str): The name of the project. - version (str): The version of the project (default is "1.0.0"). - """ - vcpkg_file_path = os.path.join(self.repo_path, "vcpkg.json") - - # Initialize an empty dictionary for JSON content - vcpkg_data = {} - - # If vcpkg.json exists, load its content - if os.path.exists(vcpkg_file_path): - with open(vcpkg_file_path, "r") as vcpkg_file: - try: - vcpkg_data = json.load(vcpkg_file) - logger.info("Loaded existing vcpkg.json content.") - except json.JSONDecodeError: - logger.warning("Invalid JSON format in vcpkg.json. Proceeding with an empty template.") - - # Update the name and version - vcpkg_data["name"] = project_name - if vcpkg_data.get("version") is None: - vcpkg_data["version"] = "1.0.0" - if vcpkg_data.get("dependencies") is None: - vcpkg_data["dependencies"] = [] - # Write the updated content back to vcpkg.json - with open(vcpkg_file_path, "w") as vcpkg_file: - json.dump(vcpkg_data, vcpkg_file, indent=4) - - logger.info("vcpkg.json file created or updated with project name and version.") - - def setup_vcpkg_environment(self): - """ - Sets up environment variables for VCPKG by configuring VCPKG_ROOT and updating PATH. - """ - try: - # Set the VCPKG_ROOT environment variable - os.environ["VCPKG_ROOT"] = self.vcpkg_root_path - logger.debug(f"Set VCPKG_ROOT to: {self.vcpkg_root_path}") - - # Modify the PATH environment variable to include VCPKG_ROOT - os.environ["PATH"] = f"{self.vcpkg_root_path};{os.environ.get('PATH', '')}" - logger.debug(f"Updated PATH to include VCPKG_ROOT.") - except Exception as e: - logger.critical(f"An error occurred while setting environment variables: {e}") - - @staticmethod - def get_vcpkg_path(): - """ - Attempts to find the VCPKG executable path based on the platform using either "where" (Windows) or "which" (Linux/Mac). - - Returns: - str: Path to the vcpkg executable, or None if not found. - """ - system_platform = platform.system() - logger.debug(f"Detected system platform: {system_platform}") - command = "where" if system_platform == "Windows" else "which" - logger.debug(f"Using command: {command}") - - try: - # Run the command to find vcpkg - result = subprocess.run([command, "vcpkg"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - logger.debug(f"Command output: {result.stdout.strip()}") - - if result.returncode == 0: - vcpkg_path = result.stdout.strip().splitlines()[0] - return vcpkg_path - else: - raise FileNotFoundError("vcpkg not found in the system's PATH.") - except Exception as e: - logger.critical(f"An error occurred while finding vcpkg: {e}") - return None - @staticmethod - def is_valid_package_name(name): - """Check if the package name is valid according to vcpkg rules.""" - # Check for lowercase alphanumeric characters and hyphens only - return bool(re.match(r'^[a-z0-9-]+$', name)) - def add_dependency(self, dependency_name): - """ - Adds a dependency to the vcpkg.json file. - - Args: - dependency_name (str): The name of the dependency to add. - """ - # Read the existing vcpkg.json file - with open(os.path.join(self.repo_path, "vcpkg.json"), "r") as vcpkg_file: - vcpkg_json_content = json.load(vcpkg_file) - - # Check if the dependency already exists - if dependency_name not in vcpkg_json_content["dependencies"]: - # Add the dependency to the "dependencies" array - vcpkg_json_content["dependencies"].append(dependency_name) - - # Write the updated content back to the vcpkg.json file - with open(os.path.join(self.repo_path, "vcpkg.json"), "w") as vcpkg_file: - json.dump(vcpkg_json_content, vcpkg_file, indent=4) - - logger.info(f"Added '{dependency_name}' to vcpkg.json.") - else: - logger.warning(f"'{dependency_name}' already exists in vcpkg.json.") - def add_vcpkg_library_dependencies(self): - self.add_dependency("vcpkg-cmake") - cmake_config = { - "name": "vcpkg-cmake-config", - "host": True - } - self.add_dependency(cmake_config) - - diff --git a/.vpm/src/vpm.py b/.vpm/src/vpm.py deleted file mode 100644 index 7fe4c61..0000000 --- a/.vpm/src/vpm.py +++ /dev/null @@ -1,226 +0,0 @@ -import argparse -from pathlib import Path -import platform -import sys -import project_manager as pm -import git_manager as gm -import vcpkg_manager as vm -import cmake_presets_manager as cpm -import coloredlogs, logging -import os -import project_utilities as utils - -logger = logging.getLogger("CMakeInitializer") -coloredlogs.install(level='INFO', logger=logger) - - -def parse_arguments(): - """Parse command line arguments.""" - parser = argparse.ArgumentParser(description="Configure and create a CMake project.") - parser.add_argument("--name", help="The name of the project", type=str, default=None) - parser.add_argument("--type", help="The type of the project (exe, shared, static)", type=str, default=None) - parser.add_argument("--isQt", help="Whether the project is a Qt project (True or False)", type=bool, default=None) - parser.add_argument("--generator", help="The generator to use for CMake", type=str, default="") - parser.add_argument("--reset", help="Reset the folder", action="store_true") - return parser.parse_args() - - -def display_environment_info(repo_path): - """Display environment information.""" - system_platform = platform.system() - architecture = platform.architecture()[0] - logger.info("-----------------ENVIROMENT INFO----------------") - logger.info(f"Detected system platform: {system_platform}") - logger.info(f"Architecture: {architecture}") - logger.info(f"Repo path: {repo_path}") - logger.info(f"Vcpkg path: {vm.VcpkgManager.get_vcpkg_path()}") - logger.info("------------------------------------------------") - - -def reset_cache_if_requested(): - """Reset cache if the reset argument is provided.""" - cache_path = utils.ProjectUtilities.get_cache_path() - config_path = os.path.join(cache_path, "config.json") - if os.path.exists(config_path): - os.remove(config_path) - logger.info("Cache reset successfully.") - - -def configure_tools_and_vcpkg(git_manager, vcpkg_manager): - """Configure tools repository and VCPKG.""" - if not git_manager.configure_tools_repo(): - raise Exception("Error configuring the tools repository.") - if not vcpkg_manager.configure_vcpkg(): - raise Exception("Error configuring VCPKG.") - -def requested_project_data_to_user(project_manager: pm.CMakeProjectManager): - """ - Prompts the user for the project name, type, and whether it is a Qt project. - """ - # Prompt the user for a valid project name - while True: - logger.info("Enter a valid project name:") - project_name = input().strip() - - # Check if the project name is valid and not reserved - if project_name not in project_manager.get_reserved_names() and project_manager.is_valid_package_name(project_name): - break - elif project_name in project_manager.get_reserved_names(): - logger.warning(f"The name '{project_name}' is reserved. Please choose another name.") - else: - logger.error("Invalid name. Package names must be lowercase alphanumeric characters and hyphens.") - - # Mapping for artifact type selection - artifact_type_map = {"1": "exe", "2": "shared", "3": "static"} - - # Loop until a valid artifact type is chosen - while True: - logger.info("Select artifact type:\n1) Executable\n2) Dynamic Library\n3) Static Library") - artifact_type_selected = input().strip() - - if artifact_type_selected in artifact_type_map: - selected_artifact_type = artifact_type_map[artifact_type_selected] - logger.info(f"Selected artifact type: {selected_artifact_type}") - break - else: - logger.error("Invalid selection. Please enter 1, 2, or 3.") - - # Loop to confirm if the project is a Qt project - while True: - logger.info("Is this a Qt project? (y/n): ") - is_qt_project_input = input().strip().lower() - - if is_qt_project_input == "y": - is_qt_project = True - logger.info("Qt project selected.") - break - elif is_qt_project_input == "n": - is_qt_project = False - logger.info("Non-Qt project selected.") - break - else: - logger.error("Invalid input. Please enter 'y' for yes or 'n' for no.") - - return {"name": project_name, "type": selected_artifact_type, "isQt": is_qt_project} -def request_generator(cmake_preset_manager: cpm.CMakePresetManager): - """ - Prompts the user to select a CMake generator from the available options. - """ - logger.info("Available generators:") - generators = cmake_preset_manager.detect_generators() - - # Check if no generators are detected - if not generators: - raise Exception("No generators found. Please install a CMake generator.") - - # Display the list of available generators - for idx, generator in enumerate(generators, start=1): - logger.info(f"{idx}) {generator}") - - # Loop until a valid generator is selected - while True: - try: - logger.info(f"Select a generator (1-{len(generators)}): ") - selected_generator_index = int(input().strip()) - 1 - - # Validate the selected index - if 0 <= selected_generator_index < len(generators): - selected_generator = list(generators.keys())[selected_generator_index] - logger.info(f"Selected generator: {selected_generator}") - return selected_generator - else: - logger.error(f"Invalid selection. Please choose a number between 1 and {len(generators)}.") - except ValueError: - logger.error("Invalid input. Please enter a valid number.") - -def request_project_data(project_manager): - """Request project data from the user.""" - input_data = requested_project_data_to_user(project_manager) - return input_data["name"], input_data["type"], input_data["isQt"] - - -def configure_project(project_manager: pm.CMakeProjectManager, input_data=None): - """Configure the CMake project.""" - if input_data: - return project_manager.configure_with_inputs(*input_data) - return project_manager.configure() - - -def handle_project_creation(project_manager: pm.CMakeProjectManager, is_project_ready: bool) -> bool: - """Handle project creation based on its initialization state.""" - - if is_project_ready: - logger.info("Project already configured.") - return True - - cmake_lists_path = "CMakeLists.txt" - vcpkg_json_path = "vcpkg.json" - config_json_path = os.path.join(utils.ProjectUtilities.get_cache_path(), "config.json") - - if os.path.exists(cmake_lists_path) and os.path.exists(vcpkg_json_path) and project_manager.check_config_json(): - logger.info("Checking existing project configuration.") - return configure_project(project_manager) - - if not os.path.exists(cmake_lists_path) and os.path.exists(config_json_path)and project_manager.check_config_json(): - logger.info("Using cached project configuration.") - return configure_project(project_manager) - - logger.info("Requesting project data from user.") - input_data = request_project_data(project_manager) - - return configure_project(project_manager, input_data) - - -def main(): - repo_path = Path(__file__).resolve().parent.parent.parent - logger.info(pm.CMakeProjectManager.get_logo()) - - args = parse_arguments() - - if args.reset: - reset_cache_if_requested() - - # Setup environment and managers - display_environment_info(repo_path) - git_manager = gm.GitRepositoryManager(repo_path) - vcpkg_manager = vm.VcpkgManager(repo_path) - project_manager = pm.CMakeProjectManager(repo_path, args.name, args.type, args.isQt) - - try: - is_project_ready = project_manager.is_project_initialized() - - # Step 1: Configure tools and VCPKG - configure_tools_and_vcpkg(git_manager, vcpkg_manager) - - # Step 2: Handle project creation - if not handle_project_creation(project_manager, is_project_ready): - raise Exception("Project creation failed.") - - # Step 3: Configure CMake generator - cmake_preset_manager = project_manager.get_cmake_presets_manager() - generator = args.generator or request_generator(cmake_preset_manager) - - # Step 4: Configure automations or update presets - if not is_project_ready: - if not project_manager.configure_automations(generator): - raise Exception("Error configuring project automations.") - logger.info("Project created successfully.") - else: - cmake_preset_manager.configure_cmake_user_presets(generator) - logger.info("Project updated for local configuration.") - - except KeyboardInterrupt: - logger.warning("Process interrupted. Cleaning up before exiting...") - if not project_manager.is_project_initialized(): - project_manager.reset() - logger.info("Cleanup complete.") - sys.exit(1) - - except Exception as e: - project_manager.reset() - logger.error(f"Error: {str(e)}") - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fcb5a5..a44d8ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,41 +1,59 @@ -# ------------------------CMake Project Creator------------------------ -# -# Make C++ Faster with VCPKG and CMake -# ------------------------------------- -# --------------------------------------------------------------------- -# - -cmake_minimum_required(VERSION 3.12) -project(libtusclient CXX) - -include(${CMAKE_CURRENT_LIST_DIR}/cmake_tools_for_vcpkg/tools/cmake/cmakeProject.cmake) - -message(STATUS "${PROJECT_NAME} Build") - -# Find required packages - -# Configure file for headers -configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in ${CMAKE_CURRENT_LIST_DIR}/include/config.h) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(Common OPTIONAL) +get_version(${CMAKE_CURRENT_LIST_DIR}/project.json PROJECT_VERSION) +message(STATUS "tusclient by Cadons: ${PROJECT_VERSION}") +cmake_minimum_required(VERSION 3.23) + +project(tusclient + VERSION ${PROJECT_VERSION} + DESCRIPTION "A monorepo project" + LANGUAGES CXX +) + +#import dependencies find_package(CURL REQUIRED) find_package(boost_lexical_cast CONFIG REQUIRED) find_package(boost_uuid CONFIG REQUIRED) find_package(nlohmann_json REQUIRED) find_package(glog REQUIRED) -set(PROJECT_LIBRARIES CURL::libcurl Boost::uuid Boost::lexical_cast nlohmann_json::nlohmann_json glog::glog) -set(EXPORT_ALL 1) -create_shared_library("libtusclient" false) +# Organization info +set(PROJECT_ORG_NAME "Cadons") +set(PROJECT_ORG_URL "https://example.com") + +# Prefer Ninja if not explicitly specified + +if (NOT CMAKE_GENERATOR) + message(STATUS "No generator specified. Consider using Ninja for faster builds.") +endif () + + +# Set C++ standard +configure_compiler(20) + +# Default build type +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the build type." FORCE) +endif () + +# Enable IDE folder grouping +set_property(GLOBAL PROPERTY USE_FOLDERS ON) -setup_install(${CMAKE_PROJECT_NAME}) -setup_package(${CMAKE_PROJECT_NAME}) -# Option to build tests -option(BUILD_TEST "Build the test suite" ON) +# Add extra cmake modules (optional) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(Common OPTIONAL) +include(QUtils OPTIONAL) -if(BUILD_TEST) - # Enable testing - enable_testing() +# Testing +include(CTest) +enable_testing() +# Version header generation +configure_file( + ${CMAKE_SOURCE_DIR}/cmake/version.h.in + ${CMAKE_BINARY_DIR}/generated/version.h + @ONLY +) - set(PROJECT_LIB "libtusclient") - add_subdirectory(test) -endif() +# === Add subdirectories === +add_subdirectory(lib) diff --git a/CMakePresets.json b/CMakePresets.json index 11130e0..a65a4a2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,13 +1,10 @@ { - "version": 3, - "configurePresets": [ - { - "hidden": true, - "name": "vcpkg", - "binaryDir": "${sourceDir}/build/", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - } - } - ] - } \ No newline at end of file + "version": 3, + "configurePresets": [ + { + "name": "vcpkg-default", + "hidden": true, + "binaryDir": "${sourceDir}/build/" + } + ] +} \ No newline at end of file diff --git a/Doxyfile b/Doxyfile deleted file mode 100644 index 700a13b..0000000 --- a/Doxyfile +++ /dev/null @@ -1,26 +0,0 @@ -# Doxyfile configuration file - -# Set the project name -PROJECT_NAME = "libtusclient" - -# Enable dot (graphviz) to generate graphical class diagrams -HAVE_DOT = YES - -# Set the path to dot executable (if not in PATH) -DOT_PATH = /path/to/dot - -# Enable inheritance diagrams -CLASS_DIAGRAMS = YES - -# Enable collaboration diagrams (associations/relationships) -COLLABORATION_GRAPH = YES - -# Set the source directories -INPUT = . -# Set the header directories -INCLUDE_PATH = . -# Enable recursive directory scanning -RECURSIVE = YES -INPUT += README.md -USE_MDFILE_AS_MAINPAGE = README.md -OUTPUT_DIRECTORY = build/docs/ diff --git a/README.md b/README.md index c7f30d1..91f6fc8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # TusClient: C++ Implementation -![](tus-logo.png) +![](doc/tus-logo.png) [![libtusclient CI Linux](https://github.com/Cadons/libtusclient/actions/workflows/linux-workflow.yml/badge.svg)](https://github.com/Cadons/libtusclient/actions/workflows/linux-workflow.yml) [![libtusclient CI MacOS](https://github.com/Cadons/libtusclient/actions/workflows/mac-workflow.yml/badge.svg)](https://github.com/Cadons/libtusclient/actions/workflows/mac-workflow.yml) @@ -41,12 +41,12 @@ Run the appropriate script for your platform: • Linux/macOS: ``` -./vpm.sh +./configure.sh ``` • Windows: ``` -./vpm.bat +./configure.bat ``` 3. Build the project with CMake: diff --git a/cmake/Common.cmake b/cmake/Common.cmake new file mode 100644 index 0000000..820c577 --- /dev/null +++ b/cmake/Common.cmake @@ -0,0 +1,124 @@ +function(get_json_value MANIFEST_JSON KEY OUT_VALUE) + # Check if the manifest file exists + file(READ ${MANIFEST_JSON} JSON_CONTENT) + # Extract value for the specified key + string(JSON VALUE GET "${JSON_CONTENT}" ${KEY}) + # Set the output variable to the extracted value + set(${OUT_VALUE} "${VALUE}" PARENT_SCOPE) +endfunction() +#get version from manifest.json +function(get_version MANIFEST_JSON OUT_VERSION) + # Check if the manifest file exists + get_json_value(${MANIFEST_JSON} "version" VERSION) + # Set the output variable to the extracted version + set(${OUT_VERSION} "${VERSION}" PARENT_SCOPE) +endfunction() + +function(get_organization MANIFEST_JSON OUT_ORG) + get_json_value(${MANIFEST_JSON} "organization" ORG) + # Check if the organization key exists in the JSON + set(${OUT_ORG} "${ORG}" PARENT_SCOPE) +endfunction() + +function(get_name MANIFEST_JSON OUT_NAME) + # Check if the manifest file exists + get_json_value(${MANIFEST_JSON} "name" NAME) + # Set the output variable to the extracted name + set(${OUT_NAME} "${NAME}" PARENT_SCOPE) +endfunction() + +function(show_module_info MANIFEST_JSON) + # Get the module name + get_name(${MANIFEST_JSON} MODULE_NAME) + # Get the module version + get_version(${MANIFEST_JSON} MODULE_VERSION) + # Display the module information + message(STATUS "Module: ${MODULE_NAME}, Version: ${MODULE_VERSION}") +endfunction() + + + +# Function to configure build directories for a specific module. +# Arguments: +# module_name - The name of the module for which the build directories are configured. +# Example usage: configure_build(my_module) +function(configure_build module_name) + # Set the base output directory for the module. + set(MODULE_OUTPUT_BASE "${CMAKE_BINARY_DIR}/build/${module_name}") + + # Set runtime, library, and archive output directories for the module. + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${MODULE_OUTPUT_BASE}/bin" PARENT_SCOPE) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${MODULE_OUTPUT_BASE}/lib" PARENT_SCOPE) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${MODULE_OUTPUT_BASE}/lib" PARENT_SCOPE) + + # Create the necessary directories for binaries and libraries. + file(MAKE_DIRECTORY "${MODULE_OUTPUT_BASE}/bin") + file(MAKE_DIRECTORY "${MODULE_OUTPUT_BASE}/lib") + source_group("Headers" REGULAR_EXPRESSION "\\.(h|hh|hpp|ipp)$") + source_group("Sources" REGULAR_EXPRESSION "\\.(c|cc|cxx|cpp|m|mm)$") + source_group("QML" REGULAR_EXPRESSION "\\.(qml|qmltypes)$") + source_group("Resources" REGULAR_EXPRESSION "\\.(qrc|ui|qss|qm)$") + source_group("Images" REGULAR_EXPRESSION "\\.(png|jpg|jpeg|gif|bmp)$") + source_group("Icons" REGULAR_EXPRESSION "\\.(ico|icns)$") + source_group("Misc" REGULAR_EXPRESSION "\\.(txt|md|json|xml|yaml|yml)$") + source_group("CMake" REGULAR_EXPRESSION "\\.(cmake|cmake.in)$") +endfunction() + +# Function to configure installation rules for a target. +# Arguments: +# target_name - The name of the target to configure installation for. +# Example usage: configure_install(my_target) +function(configure_install target_name) + # Install the target's runtime, library, and archive files to their respective destinations. + install(TARGETS ${target_name} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) + + # Install header files from the include directory to the installation include directory. + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp") +endfunction() + +# Function to configure compiler settings. +# Arguments: +# cpp_standard - The C++ standard to use (e.g., 11, 14, 17, etc.). +# Example usage: configure_compiler(17) + +function(configure_compiler cpp_standard) + # Set C++ standard requirements + set(CMAKE_CXX_STANDARD ${cpp_standard}) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + + if(MSVC) + # MSVC specific settings + add_compile_options( + /permissive- # Strict standards compliance + /Zc:__cplusplus # Correct __cplusplus macro + /Zc:preprocessor # Standards-compliant preprocessor + /std:c++latest # Enable latest C++ features + ) + add_compile_options(/MP) # Enable multi-processor compilation + # Debug information format configuration + if(POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT + "$,$>, + $<$:EditAndContinue>, + $<$:ProgramDatabase>>") + endif() + else() + add_compile_options(-std=c++${cpp_standard}) + endif() + + # Default build type + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Debug CACHE STRING + "Build type (Debug/Release/RelWithDebInfo/MinSizeRel)" FORCE) + endif() + + # IDE folder organization + set_property(GLOBAL PROPERTY USE_FOLDERS ON) +endfunction() \ No newline at end of file diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in deleted file mode 100644 index 11bd79f..0000000 --- a/cmake/Config.cmake.in +++ /dev/null @@ -1,13 +0,0 @@ -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/libtusclientTargets.cmake") - -include(CMakeFindDependencyMacro) - -find_dependency(CURL REQUIRED) -find_dependency(boost_lexical_cast REQUIRED) -find_dependency(boost_uuid REQUIRED) -find_dependency(nlohmann_json REQUIRED) -find_dependency(glog REQUIRED) - -check_required_components(libtusclient) \ No newline at end of file diff --git a/cmake/QtUtils.cmake b/cmake/QtUtils.cmake new file mode 100644 index 0000000..cdd3f1c --- /dev/null +++ b/cmake/QtUtils.cmake @@ -0,0 +1,92 @@ +# This file is part of the Qt Utils project, which provides utility functions for CMake projects using Qt. + +# Function to add an icon to the program. +# Arguments: +# target - The name of the target to which the icon should be added. +# icon_path - The path to the directory containing the icon files. +# icon_name - The name of the icon file (without extension). Defaults to "app_icon". +# Example usage: add_program_icon(my_target "${CMAKE_SOURCE_DIR}/resources/images" "app_icon") +function(add_program_icon target icon_path icon_name) + # Default icon_name to "app_icon" if not provided + if(NOT icon_name) + set(icon_name "app_icon") + endif() + + if(WIN32) + # Windows: inject the .rc resource file + set(windows_rc "${icon_path}/${icon_name}.rc") + if(EXISTS "${windows_rc}") + target_sources(${target} PRIVATE "${windows_rc}") + else() + message(WARNING "add_program_icon: ${windows_rc} not found") + endif() + endif() + + if(APPLE) + # macOS: bundle the .icns and set the bundle property + set(icns_file "${icon_path}/${icon_name}.icns") + if(EXISTS "${icns_file}") + # Tell CMake to include it in the .app bundle Resources + set_source_files_properties( + "${icns_file}" + PROPERTIES MACOSX_PACKAGE_LOCATION Resources + ) + target_sources(${target} PRIVATE "${icns_file}") + + # And set the bundle icon for the target + set_target_properties( + ${target} + PROPERTIES MACOSX_BUNDLE_ICON_FILE "${icon_name}.icns" + ) + else() + message(WARNING "add_program_icon: ${icns_file} not found") + endif() + endif() +endfunction() + +# Function to automatically deploy Qt for a specific target. +# Arguments: +# target_name - The name of the target for which to deploy Qt. +# Example usage: auto_deploy_qt(my_target) +function(auto_deploy_qt target_name) + if(WIN32) + # Set deployment tool for Windows + set(DEPLOYMENT_TOOL "$<$:windeployqt.debug.bat>$<$:windeployqt.exe>") + + # Set QML directory with proper path handling + if(Qt6_DIR) + # Construct potential QML directories based on configuration + set(QML_DIR_DEBUG "${Qt6_DIR}/../../debug/Qt6/qml") + set(QML_DIR_RELEASE "${Qt6_DIR}/../../Qt6/qml") + + # Use generator expressions to select the right path based on build configuration + set(QML_DIR "$<$:${QML_DIR_DEBUG}>$<$:${QML_DIR_RELEASE}>") + + # Check if QML directory exists and set fallback if needed + if(NOT (EXISTS "${QML_DIR_DEBUG}" OR EXISTS "${QML_DIR_RELEASE}")) + set(QML_DIR "None") # Fallback if the directory does not exist + message(STATUS "QML directories not found, setting to 'None' for ${target_name}") + endif() + else() + set(QML_DIR "None") # Qt6_DIR not defined + message(WARNING "Qt6_DIR not defined, setting QML_DIR to 'None' for ${target_name}") + endif() + + # Set variables for build path and target file + set(BUILD_ROOT "${CMAKE_BINARY_DIR}") + set(TARGET_FILE_PATH $) + + # Normalize paths for Windows + file(TO_NATIVE_PATH "${TARGET_FILE_PATH}" TARGET_FILE_PATH) + file(TO_NATIVE_PATH "${BUILD_ROOT}" BUILD_ROOT) + file(TO_NATIVE_PATH "${QML_DIR}" QML_DIR) + file(TO_NATIVE_PATH "${DEPLOYMENT_TOOL}" DEPLOYMENT_TOOL) + + # Add custom command to deploy Qt after the target is built + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Deploying Qt for ${target_name}" + COMMAND cmd /c call "${CMAKE_SOURCE_DIR}/cmake/scripts/DeployWindowsQtForVcpkg.bat" "${BUILD_ROOT}" "${DEPLOYMENT_TOOL}" "${target_name}" "${QML_DIR}" "${TARGET_FILE_PATH}" + VERBATIM + ) + endif() +endfunction() \ No newline at end of file diff --git a/cmake/VcpkgUtils.cmake b/cmake/VcpkgUtils.cmake new file mode 100644 index 0000000..fdd2d13 --- /dev/null +++ b/cmake/VcpkgUtils.cmake @@ -0,0 +1,65 @@ + +# Function to deploy a shared library from vcpkg. +# Arguments: +# TARGET_NAME - The name of the target that depends on the shared library. +# FILENAME - The name of the shared library file to deploy. +# Example usage: deploy_vcpkg_shared_lib(my_target "libssl-x64.dll") +function(deploy_vcpkg_shared_lib TARGET_NAME FILENAME) + # Initialize variables for architecture, OS, and library details. + set(ARCH "none") + set(OS_NAME "none") + set(TRIPLET_POSTFIX "-dynamic") + set(LIB_EXTENSION "none") + set(SHARED_LIB_FOLDER "none") + set(LIB_PREFIX "none") + string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" PROCESSOR_LOWERCASE) + + # Determine the architecture based on the processor type. + if (PROCESSOR_LOWERCASE MATCHES "x86_64|amd64") + set(ARCH "x64") + elseif (PROCESSOR_LOWERCASE MATCHES "armv7l|armv8l|arm") # ARM 32-bit + set(ARCH "arm") + elseif (PROCESSOR_LOWERCASE MATCHES "aarch64|arm64") # ARM 64-bit + set(ARCH "arm64") + else() + # Fail if the architecture is unsupported. + message(FATAL_ERROR "Architecture not supported: ${CMAKE_SYSTEM_PROCESSOR}") + return() + endif() + + # Configure settings for Windows. + if(WIN32) + set(OS_NAME "windows") + set(TRIPLET_POSTFIX "") + set(LIB_EXTENSION "dll") + set(SHARED_LIB_FOLDER "bin") + set(LIB_PREFIX "") + else () + # Exit if the OS is not Windows. + return() + endif () + + # Log the triplet being used. + message(STATUS "Triplet: ${ARCH}-${OS_NAME}${TRIPLET_POSTFIX}") + + # Define the source file path for the shared library. + set(SOURCE_FILE_PATH "$,${CMAKE_BINARY_DIR}/vcpkg_installed/${ARCH}-${OS_NAME}${TRIPLET_POSTFIX}/debug/${SHARED_LIB_FOLDER}/${LIB_PREFIX}${FILENAME}.${LIB_EXTENSION},${PROJECT_VCPKG_INSTALLED_ROOT}/${ARCH}-${OS_NAME}${TRIPLET_POSTFIX}/${SHARED_LIB_FOLDER}/${LIB_PREFIX}${FILENAME}.${LIB_EXTENSION}>") + + # Get the target type (e.g., executable, library). + get_target_property(TARGET_TYPE ${TARGET_NAME} TYPE) + + # Define the destination path for the shared library. + if(WIN32) + set(DESTINATION_PATH "${CMAKE_BINARY_DIR}/build/${TARGET_NAME}/${SHARED_LIB_FOLDER}/$,Debug,Release>/${LIB_PREFIX}${FILENAME}.${LIB_EXTENSION}") + else() + set(DESTINATION_PATH "${CMAKE_BINARY_DIR}/build/${TARGET_NAME}/${SHARED_LIB_FOLDER}/${LIB_PREFIX}${FILENAME}.${LIB_EXTENSION}") + endif() + + # Add a custom command to copy the shared library after the target is built. + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Copying library: ${SOURCE_FILE_PATH} -> ${DESTINATION_PATH}" + COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_FILE_PATH}" "${DESTINATION_PATH}" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + VERBATIM + ) +endfunction() \ No newline at end of file diff --git a/cmake/config.cmake b/cmake/config.cmake deleted file mode 100644 index 8789218..0000000 --- a/cmake/config.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# config.cmake - -set(MY_CMAKE_PROJECT_NAME "libtusclient") -message(STATUS "MY_CMAKE_PROJECT_NAME set to: ${MY_CMAKE_PROJECT_NAME}") - -set(MY_CMAKE_PROJECT_VERSION "1.0.0") -message(STATUS "MY_CMAKE_PROJECT_VERSION set to: ${MY_CMAKE_PROJECT_VERSION}") - -set(MY_CMAKE_PROJECT_DESCRIPTION "Library implementing the tus protocol for resumable uploads") -message(STATUS "MY_CMAKE_PROJECT_DESCRIPTION set to: ${MY_CMAKE_PROJECT_DESCRIPTION}") - -set(MY_CMAKE_PROJECT_HOMEPAGE "https://github.com/Cadons/libtusclient") -message(STATUS "MY_CMAKE_PROJECT_HOMEPAGE set to: ${MY_CMAKE_PROJECT_HOMEPAGE}") - -set(MY_CMAKE_PROJECT_LICENSE "MIT") -message(STATUS "MY_CMAKE_PROJECT_LICENSE set to: ${MY_CMAKE_PROJECT_LICENSE}") - -set(MY_CMAKE_PROJECT_VERSION-STRING "1.0") -message(STATUS "MY_CMAKE_PROJECT_VERSION-STRING set to: ${MY_CMAKE_PROJECT_VERSION-STRING}") - -set(MY_CMAKE_PROJECT_SEMVER "1.0.0") -message(STATUS "MY_CMAKE_PROJECT_SEMVER set to: ${MY_CMAKE_PROJECT_SEMVER}") - diff --git a/cmake/config.h.in b/cmake/config.h.in deleted file mode 100644 index f92b98f..0000000 --- a/cmake/config.h.in +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CONFIG_H_IN -#define CONFIG_H_IN -#define PROJECT_NAME "@PROJECT_NAME@" -#define PROJECT_VERSION "@PROJECT_VERSION@" -#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ -#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@ -#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ -#define PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@" -#define PROJECT_AUTHOR "@PROJECT_AUTHOR@" -#define PROJECT_AUTHOR_EMAIL "@PROJECT_AUTHOR_EMAIL@" -#define PROJECT_AUTHOR_WEBSITE "@PROJECT_AUTHOR_WEBSITE@" -#define PROJECT_LICENSE "@PROJECT_LICENSE@" -#define PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@" -#define PROJECT_TRADEMARK "@PROJECT_TRADEMARK@" -#define PROJECT_COMPANY "@PROJECT_COMPANY@" -#define PROJECT_COMPANY_NAME "@PROJECT_COMPANY_NAME@" -#define PROJECT_COMPANY_ADDRESS "@PROJECT_COMPANY_ADDRESS@" -#define PROJECT_COMPANY_CITY "@PROJECT_COMPANY_CITY@" -#define PROJECT_COMPANY_COUNTRY "@PROJECT_COMPANY_COUNTRY@" -#define PROJECT_COMPANY_PHONE "@PROJECT_COMPANY_PHONE@" -#define PROJECT_COMPANY_EMAIL "@PROJECT_COMPANY_EMAIL@" -#define PROJECT_COMPANY_WEBSITE "@PROJECT_COMPANY_WEBSITE@" -#define PROJECT_COMPANY_ZIPCODE "@PROJECT_COMPANY_ZIPCODE@" -#define PROJECT_COMPANY_FAX "@PROJECT_COMPANY_FAX@" - -#endif // CONFIG_H_IN \ No newline at end of file diff --git a/cmake/qt-utils.cmake b/cmake/qt-utils.cmake new file mode 100644 index 0000000..f0d7184 --- /dev/null +++ b/cmake/qt-utils.cmake @@ -0,0 +1,30 @@ +function(add_program_icon target icon_path) + if (WIN32) + #todo: implement + elseif(APPLE) + #todo: implement + + elseif(UNIX AND NOT APPLE) + #todo: implement + + endif() +endfunction() + +function (auto_deploy_libraries target_name) + if (WIN32) + #todo: implement + + endif () + if (APPLE) + #todo: implement + + endif () + if (UNIX AND NOT APPLE) + #todo: implement + + endif () +endfunction() + +function(create_i18n_language target_name language) + #todo: implement +endfunction() \ No newline at end of file diff --git a/cmake/scripts/DeployWindowsQtForVcpkg.bat b/cmake/scripts/DeployWindowsQtForVcpkg.bat new file mode 100644 index 0000000..86258f1 --- /dev/null +++ b/cmake/scripts/DeployWindowsQtForVcpkg.bat @@ -0,0 +1,47 @@ +@echo off +setlocal EnableDelayedExpansion + +if "%~1"=="" ( + echo Usage: %~nx0 ^ ^ ^ ^ ^ + exit /b 1 +) +set "BUILD_DIR=%~1" +set "LOCK_FILE=%BUILD_DIR%\%~3_QtDeploy.lock" +set "WINDEPLOY_EXE=%~2" +set "QML_DIR=%~4" +set "TARGET_FILE_PATH=%~5" +set "ARCH=x64" + +if not exist "%LOCK_FILE%" ( + echo Creating lock file : "%LOCK_FILE%" + echo. 2> "%LOCK_FILE%" + echo Deploying Qt libraries... + if "%WINDEPLOY_EXE%"=="" ( + echo ERROR: windeployqt.exe not found in PATH. + del "%LOCK_FILE%" + exit /b 1 + ) + set "QML_DEPLOY=--qmldir "%QML_DIR%" + if %QML_DIR%=="None" ( + set "QML_DEPLOY=" + ) + + echo "%BUILD_DIR%\vcpkg_installed\%ARCH%-windows\tools\Qt6\bin\%WINDEPLOY_EXE%" ^ + --force-openssl ^ + %QML_DEPLOY% ^ + "%TARGET_FILE_PATH%" + call "%BUILD_DIR%\vcpkg_installed\%ARCH%-windows\tools\Qt6\bin\%WINDEPLOY_EXE%" ^ + --force-openssl ^ + --qmldir "%QML_DIR%" ^ + "%TARGET_FILE_PATH%" + if $? == False ( + echo Deploy failed with error code %errorlevel%. + echo "Deleting lock file: %LOCK_FILE%" + del "%LOCK_FILE%" + exit /b 1 + ) + echo Deploy completed successfully. +) else ( + echo Lock file already exists: "%LOCK_FILE%" + echo Deploy already in progress or completed. Exiting without changes. +) diff --git a/cmake/version.h.in b/cmake/version.h.in new file mode 100644 index 0000000..6efca9d --- /dev/null +++ b/cmake/version.h.in @@ -0,0 +1,7 @@ +#pragma once + +#define PROJECT_NAME "@PROJECT_NAME@" +#define PROJECT_VERSION "@PROJECT_VERSION@" +#define PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@" +#define PROJECT_ORG_NAME "@PROJECT_ORG_NAME@" +#define PROJECT_ORG_URL "@PROJECT_ORG_URL@" \ No newline at end of file diff --git a/cmake_tools_for_vcpkg/.gitignore b/cmake_tools_for_vcpkg/.gitignore deleted file mode 100644 index bfe6485..0000000 --- a/cmake_tools_for_vcpkg/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -venv/ -.venv -.idea -.vs -.vscode -cmake/config.cmake \ No newline at end of file diff --git a/cmake_tools_for_vcpkg/setup_project.bat b/cmake_tools_for_vcpkg/setup_project.bat deleted file mode 100644 index a07be60..0000000 --- a/cmake_tools_for_vcpkg/setup_project.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off -SETLOCAL -cd /d "%~dp0" -@echo off -SETLOCAL -cd /d "%~dp0" - -REM Check for skip-submodule argument -SET "SKIP_SUBMODULE=false" -IF "%1"=="skip-submodule" ( - SET "SKIP_SUBMODULE=true" -) - -echo Initializing project... -cd .. -REM Clone submodules if skip-submodule argument is not passed -IF "%SKIP_SUBMODULE%"=="false" ( - echo Cloning submodules... - git submodule update --init --recursive || set "errorlevel=1" -) ELSE ( - echo Skipping submodule initialization. -) - -cd cmake_tools_for_vcpkg -REM Create virtual environment -echo Creating virtual environment... -python -m venv venv - -REM Activate virtual environment -call venv\Scripts\activate - -REM Install requirements -IF EXIST "tools\requirements.txt" ( - echo Installing requirements from requirements.txt... - pip install -r tools\requirements.txt || set "errorlevel=1" -) ELSE ( - echo No requirements.txt found, skipping installation. -) - -REM Run the setup script -echo Running setup.py... -python tools\setup.py || set "errorlevel=1" - -IF %errorlevel% NEQ 0 ( - echo Project initialization failed. - exit /b 1 -) - -echo Project initialized successfully. -ENDLOCAL diff --git a/cmake_tools_for_vcpkg/setup_project.sh b/cmake_tools_for_vcpkg/setup_project.sh deleted file mode 100644 index efab700..0000000 --- a/cmake_tools_for_vcpkg/setup_project.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -set -e - -# Check for skip-submodule argument -SKIP_SUBMODULE=false -if [ "$1" == "skip-submodule" ]; then - SKIP_SUBMODULE=true -fi - -echo "Initializing project..." -cd "$(dirname "$0")/.." - -# Clone submodules if skip-submodule argument is not passed -if [ "$SKIP_SUBMODULE" = false ]; then - echo "Cloning submodules..." - git submodule update --init --recursive || errorlevel=1 -else - echo "Skipping submodule initialization." -fi -cd cmake_tools_for_vcpkg -# Create and activate virtual environment -echo "Creating virtual environment..." -python3 -m venv venv - -# Activate virtual environment -echo "Activating virtual environment..." -source venv/bin/activate - -# Install requirements -if [ -f "tools/requirements.txt" ]; then - echo "Installing requirements from requirements.txt..." - pip install -r tools/requirements.txt -else - echo "No requirements.txt found, skipping installation." -fi - -# Run the setup script -echo "Running setup.py..." -python3 tools/setup.py - -# Print completion message -echo "Project initialized successfully." diff --git a/cmake_tools_for_vcpkg/tools/.gitignore b/cmake_tools_for_vcpkg/tools/.gitignore deleted file mode 100644 index 366a371..0000000 --- a/cmake_tools_for_vcpkg/tools/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -venv/ -.venv -.idea -.vs -.vscode -cmake/config.cmake -__pycache__ \ No newline at end of file diff --git a/cmake_tools_for_vcpkg/tools/cmake/Qt6Deployer.cmake b/cmake_tools_for_vcpkg/tools/cmake/Qt6Deployer.cmake deleted file mode 100644 index d698e4e..0000000 --- a/cmake_tools_for_vcpkg/tools/cmake/Qt6Deployer.cmake +++ /dev/null @@ -1,17 +0,0 @@ -function(deploy_qt6_dependencies target_name) - if(WIN32) - # Configura il deployment tool e la directory QML in base alla configurazione - set(DEPLOYMENT_TOOL "$<$:.debug.bat>$<$:.exe>") - set(QML_DIR "$<$:debug/Qt6/qml>$<$:Qt6/qml>") - - # Percorso per windeployqt in base alla configurazione - set(WINDEPLOYQT6_PATH "${PROJECT_VCPKG_INSTALLED_ROOT}/x64-windows/tools/Qt6/bin/windeployqt${DEPLOYMENT_TOOL}") - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo "Deploying Qt dependencies for $ configuration of ${target_name}..." - COMMAND cmd /c "${WINDEPLOYQT6_PATH} --force-openssl --qmldir \"${Qt6_DIR}/../../${QML_DIR}\" $ >nul 2>&1 || exit 0" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND_EXPAND_LISTS - VERBATIM - ) - endif() -endfunction() diff --git a/cmake_tools_for_vcpkg/tools/cmake/cmakeProject.cmake b/cmake_tools_for_vcpkg/tools/cmake/cmakeProject.cmake deleted file mode 100644 index 3f26e06..0000000 --- a/cmake_tools_for_vcpkg/tools/cmake/cmakeProject.cmake +++ /dev/null @@ -1,74 +0,0 @@ -# Load the external configuration file -if(POLICY CMP0177) - cmake_policy(SET CMP0177 NEW) -endif() -include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/config.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/targetCreator.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/deploy.cmake) - -# Function to set the project name -function(set_project_name) - if(DEFINED MY_CMAKE_PROJECT_NAME) - set(CMAKE_PROJECT_NAME ${MY_CMAKE_PROJECT_NAME} PARENT_SCOPE) - message(STATUS "Project name set to: ${CMAKE_PROJECT_NAME}") - else() - message(FATAL_ERROR "Project not configured, use setup.py in the tools folder. Add name to vcpkg.json") - endif() -endfunction() - -# Function to display project information -function(print_project_info) - message("-----------------CMake Project-------------------------") - message("Project Name: ${MY_CMAKE_PROJECT_NAME}") - message("Version Semver: ${MY_CMAKE_PROJECT_SEMVER}") - message("Version: ${MY_CMAKE_PROJECT_VERSION}") - message("------------------------------------------") -endfunction() - -# Function to configure MSVC hot reload -function(configure_msvc_hot_reload) - if (POLICY CMP0141) - cmake_policy(SET CMP0141 NEW) - set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT - "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") - endif() -endfunction() - -# Function to set project properties -function(set_project_properties) - set(PROJECT_VERSION "${MY_CMAKE_PROJECT_VERSION}" PARENT_SCOPE) - set_property(GLOBAL PROPERTY USE_FOLDERS ON) - set_property(GLOBAL PROPERTY PROJECT_LABEL "${CMAKE_PROJECT_NAME} ${CMAKE_PROJECT_VERSION}") - set(CMAKE_CXX_STANDARD 20 PARENT_SCOPE) - set(CMAKE_CXX_STANDARD_REQUIRED ON PARENT_SCOPE) -endfunction() - -# Function to configure output directories -function(configure_output_directories) - set(ARTIFACT_FOLDER "${CMAKE_BINARY_DIR}/Packages" PARENT_SCOPE) - set(PROJECT_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}" PARENT_SCOPE) - set(PROJECT_VCPKG_INSTALLED_ROOT "${CMAKE_BINARY_DIR}/vcpkg_installed" PARENT_SCOPE) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$/bin" PARENT_SCOPE) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$/lib" PARENT_SCOPE) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$/lib" PARENT_SCOPE) -endfunction() - -# Function to collect source and header files -function(collect_source_files) - file(GLOB_RECURSE Lib_SRCS "${CMAKE_CURRENT_LIST_DIR}/../../../src/*.cpp" "${CMAKE_CURRENT_LIST_DIR}/../../../src/*.cxx") - file(GLOB_RECURSE Lib_HDRS "${CMAKE_CURRENT_LIST_DIR}/../../../include/*.h" "${CMAKE_CURRENT_LIST_DIR}/../../../include/*.hpp") - set(SOURCES ${Lib_SRCS} ${Lib_HDRS} PARENT_SCOPE) -endfunction() - -# Call functions in sequence -set_project_name() -set_project_properties() -print_project_info() -configure_msvc_hot_reload() - -configure_output_directories() -collect_source_files() - -# Debugging: print project name and source files -message("Configuring Project.....") -message("Project Name: ${CMAKE_PROJECT_NAME}") diff --git a/cmake_tools_for_vcpkg/tools/cmake/deploy.cmake b/cmake_tools_for_vcpkg/tools/cmake/deploy.cmake deleted file mode 100644 index 1d7dbeb..0000000 --- a/cmake_tools_for_vcpkg/tools/cmake/deploy.cmake +++ /dev/null @@ -1,65 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/Qt6Deployer.cmake) -# Include GNU install directories for standard installation locations -include(GNUInstallDirs) - -# Function for installation -function(setup_install project_target) - # Deployment section for Qt dependencies - if(Qt6) - deploy_qt6_dependencies(${project_target}) - endif() - - # Install settings - install(TARGETS ${project_target} - EXPORT ${project_target}Targets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${project_target} - ) - - # Install header files - if(EXISTS "${EXPORTED_HEADER}") - install(FILES ${EXPORTED_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${project_target}) - else() - install(DIRECTORY include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${project_target} - FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*_p.h" EXCLUDE - ) - endif() - - # Install project_target export file - install(EXPORT ${project_target}Targets - FILE ${project_target}Targets.cmake - NAMESPACE ${project_target}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/../share/${project_target} - ) - - # Generate and install Config file for package - include(CMakePackageConfigHelpers) - configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/${project_target}Config.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/../share/${project_target} - ) - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${project_target}Config.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/../share/${project_target} - ) - - # Install additional files from the build directory - if(EXISTS ${CMAKE_BINARY_DIR}/$/bin) - install(DIRECTORY ${CMAKE_BINARY_DIR}/$/bin/ - DESTINATION ${CMAKE_INSTALL_BINDIR} - FILES_MATCHING PATTERN "*" - ) - endif() -endfunction() - -# Function for packaging -function(setup_package project_target) - # Set the output directory for the package - set(CPACK_OUTPUT_DIRECTORY "${ARTIFACT_FOLDER}") - set(CPACK_PACKAGE_NAME "${project_target}") - include(CPack) -endfunction() - diff --git a/cmake_tools_for_vcpkg/tools/cmake/portfile_template.cmake b/cmake_tools_for_vcpkg/tools/cmake/portfile_template.cmake deleted file mode 100644 index f2393cc..0000000 --- a/cmake_tools_for_vcpkg/tools/cmake/portfile_template.cmake +++ /dev/null @@ -1,104 +0,0 @@ -# Common Ambient Variables: -# CURRENT_BUILDTREES_DIR = ${VCPKG_ROOT_DIR}\buildtrees\${PORT} -# CURRENT_PACKAGES_DIR = ${VCPKG_ROOT_DIR}\packages\${PORT}_${TARGET_TRIPLET} -# CURRENT_PORT_DIR = ${VCPKG_ROOT_DIR}\ports\${PORT} -# CURRENT_INSTALLED_DIR = ${VCPKG_ROOT_DIR}\installed\${TRIPLET} -# DOWNLOADS = ${VCPKG_ROOT_DIR}\downloads -# PORT = current port name (zlib, etc) -# TARGET_TRIPLET = current triplet (x86-windows, x64-windows-static, etc) -# VCPKG_CRT_LINKAGE = C runtime linkage type (static, dynamic) -# VCPKG_LIBRARY_LINKAGE = target library linkage type (static, dynamic) -# VCPKG_ROOT_DIR = -# VCPKG_TARGET_ARCHITECTURE = target architecture (x64, x86, arm) -# VCPKG_TOOLCHAIN = ON OFF -# TRIPLET_SYSTEM_ARCH = arm x86 x64 -# BUILD_ARCH = "Win32" "x64" "ARM" -# DEBUG_CONFIG = "Debug Static" "Debug Dll" -# RELEASE_CONFIG = "Release Static"" "Release DLL" -# VCPKG_TARGET_IS_WINDOWS -# VCPKG_TARGET_IS_UWP -# VCPKG_TARGET_IS_LINUX -# VCPKG_TARGET_IS_OSX -# VCPKG_TARGET_IS_FREEBSD -# VCPKG_TARGET_IS_ANDROID -# VCPKG_TARGET_IS_MINGW -# VCPKG_TARGET_EXECUTABLE_SUFFIX -# VCPKG_TARGET_STATIC_LIBRARY_SUFFIX -# VCPKG_TARGET_SHARED_LIBRARY_SUFFIX -# -# See additional helpful variables in /docs/maintainers/vcpkg_common_definitions.md - -# Also consider vcpkg_from_* functions if you can; the generated code here is for any web accessable -# source archive. -# vcpkg_from_github -# vcpkg_from_gitlab -# vcpkg_from_bitbucket -# vcpkg_from_sourceforge -# Set variables for git, the repository URL, and the reference -set(GIT_URL "") -set(REF "") -set(SOURCE_PATH "${CURRENT_BUILDTREES_DIR}/src") - -# Find git executable -find_program(GIT git) -if(NOT GIT) - message(FATAL_ERROR "Git executable not found.") -endif() - - -# Clone the repository with submodules and checkout the specified REF -if(NOT EXISTS "${SOURCE_PATH}") - message(STATUS "Cloning ${GIT_URL} into ${SOURCE_PATH} with submodules") - execute_process( - COMMAND ${GIT} clone --recurse-submodules ${GIT_URL} ${SOURCE_PATH} - WORKING_DIRECTORY ${CURRENT_BUILDTREES_DIR} - RESULT_VARIABLE GIT_CLONE_RESULT - ) - - if(NOT GIT_CLONE_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to clone repository with submodules.") - endif() - - # Checkout the specified REF - execute_process( - COMMAND ${GIT} checkout ${REF} - WORKING_DIRECTORY ${SOURCE_PATH} - RESULT_VARIABLE GIT_CHECKOUT_RESULT - ) - - if(NOT GIT_CHECKOUT_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to checkout REF: ${REF}.") - endif() -endif() - -# # Check if one or more features are a part of a package installation. -# # See /docs/maintainers/vcpkg_check_features.md for more details -# vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS -# FEATURES -# tbb WITH_TBB -# INVERTED_FEATURES -# tbb ROCKSDB_IGNORE_PACKAGE_TBB -# ) - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - OPTIONS -DBUILD_TEST=OFF - # OPTIONS -DUSE_THIS_IN_ALL_BUILDS=1 -DUSE_THIS_TOO=2 - # OPTIONS_RELEASE -DOPTIMIZE=1 - # OPTIONS_DEBUG -DDEBUGGABLE=1 -) - -vcpkg_cmake_install() - -# # Moves all .cmake files from /debug/share/tasknet/ to /share/tasknet/ -# # See /docs/maintainers/ports/vcpkg-cmake-config/vcpkg_cmake_config_fixup.md for more details -# When you uncomment "vcpkg_cmake_config_fixup()", you need to add the following to "dependencies" vcpkg.json: -#{ -# "name": "vcpkg-cmake-config", -# "host": true -#} -vcpkg_cmake_config_fixup() - -# Uncomment the line below if necessary to install the license file for the port -# as a file named `copyright` to the directory `${CURRENT_PACKAGES_DIR}/share/${PORT}` -# vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") diff --git a/cmake_tools_for_vcpkg/tools/cmake/targetCreator.cmake b/cmake_tools_for_vcpkg/tools/cmake/targetCreator.cmake deleted file mode 100644 index 5af628c..0000000 --- a/cmake_tools_for_vcpkg/tools/cmake/targetCreator.cmake +++ /dev/null @@ -1,80 +0,0 @@ -include(GNUInstallDirs) -include(${CMAKE_CURRENT_LIST_DIR}/Qt6Deployer.cmake) -# Function for executables -function(create_executable target_name is_qt_project) - if(${is_qt_project}) - set(CMAKE_AUTOMOC ON) - set(CMAKE_AUTORCC ON) - set(CMAKE_AUTOUIC ON) - QT6_ADD_RESOURCES(SOURCES ${RESOURCES}) - QT6_ADD_EXECUTABLE(${target_name} ${SOURCES}) - else() - add_executable(${target_name} ${SOURCES}) - endif() - - target_link_libraries(${target_name} PRIVATE ${PROJECT_LIBRARIES}) - target_include_directories(${target_name} PUBLIC - "$" - "$" - ) - if(${is_qt_project}) - deploy_qt6_dependencies(${target_name}) - endif() -endfunction() - -# Function for dynamic libraries -function(create_shared_library target_name is_qt_project) -if(WIN32) - add_compile_definitions(BUILD_SHARED) - - if(DEFINED EXPORT_ALL AND EXPORT_ALL) - message("All symbols will be exported") - add_compile_definitions(EXPORT_ALL) - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - endif() - endif() - if(${is_qt_project}) - set(CMAKE_AUTOMOC ON) - set(CMAKE_AUTORCC ON) - set(CMAKE_AUTOUIC ON) - QT6_ADD_RESOURCES(SOURCES ${RESOURCES}) - QT6_ADD_LIBRARY(${target_name} SHARED ${SOURCES}) - else() - add_library(${target_name} SHARED ${SOURCES}) - endif() - - target_link_libraries(${target_name} PRIVATE ${PRIVATE_PROJECT_LIBRARIES}) - target_link_libraries(${target_name} PUBLIC ${PROJECT_LIBRARIES}) - target_link_libraries(${target_name} INTERFACE ${INTERFACE_PROJECT_LIBRARIES}) - target_include_directories(${target_name} PUBLIC - "$" - "$" - ) - if(${is_qt_project}) - deploy_qt6_dependencies(${target_name}) - endif() -endfunction() - -# Function for static libraries -function(create_static_library target_name is_qt_project) - if(${is_qt_project}) - set(CMAKE_AUTOMOC ON) - set(CMAKE_AUTORCC ON) - set(CMAKE_AUTOUIC ON) - QT6_ADD_RESOURCES(SOURCES ${RESOURCES}) - QT6_ADD_LIBRARY(${target_name} STATIC ${SOURCES}) - else() - add_library(${target_name} STATIC ${SOURCES}) - endif() - - target_link_libraries(${target_name} PRIVATE ${PRIVATE_PROJECT_LIBRARIES}) - target_link_libraries(${target_name} PUBLIC ${PROJECT_LIBRARIES}) - target_link_libraries(${target_name} INTERFACE ${INTERFACE_PROJECT_LIBRARIES}) - target_include_directories(${target_name} PUBLIC - "$" - "$" - ) - if(${is_qt_project}) - deploy_qt6_dependencies(${target_name}) - endif() -endfunction() diff --git a/cmake_tools_for_vcpkg/tools/deploy_vcpkg.py b/cmake_tools_for_vcpkg/tools/deploy_vcpkg.py deleted file mode 100644 index 0c61215..0000000 --- a/cmake_tools_for_vcpkg/tools/deploy_vcpkg.py +++ /dev/null @@ -1,162 +0,0 @@ -import os -import subprocess -import shutil -import json -import stat -import gc -import sys -from time import sleep -from git import Repo, exc -import coloredlogs, logging -logger = logging.getLogger(__name__) -coloredlogs.install(level='INFO', logger=logger) -logger.addHandler(logging.StreamHandler(sys.stdout)) -# Constants -VCPKG_REPO_URL = "git@srv-gitexample.com:mysw_dev/vcpkg.git" # Replace with actual VCPKG repo URL -PROJECT_DIR = os.path.dirname(os.path.abspath(__file__)) -TMP_DIR = os.path.join(PROJECT_DIR, "tmp") -VCPKG_CLONE_PATH = os.path.join(TMP_DIR, "vcpkg_temp") - -# Paths -vcpkg_json_path = os.path.join(PROJECT_DIR, "..","..", "vcpkg.json") -portfile_template_path = os.path.join(PROJECT_DIR, "cmake", "portfile_template.cmake") -portfile_output_path = os.path.join(TMP_DIR, "ports") - -# Create tmp directory -os.makedirs(TMP_DIR, exist_ok=True) - -# Utilities -def find_git_root(path): - """Find the root Git directory by traversing upwards.""" - while not os.path.isdir(os.path.join(path, ".git")): - parent = os.path.dirname(path) - if parent == path: - raise exc.InvalidGitRepositoryError(f"No .git directory found in {PROJECT_DIR} or its parents.") - path = parent - return path - -def get_project_name(): - """Read the project name from vcpkg.json.""" - with open(vcpkg_json_path, "r") as f: - data = json.load(f) - return data["name"] - -def handle_remove_readonly(func, path, exc_info): - """Error handler for removing read-only files.""" - os.chmod(path, stat.S_IWRITE) - func(path) - -def delete_with_retries(path, retries=5, delay=2): - """Retry deletion with exponential backoff on failure.""" - os.chdir(PROJECT_DIR) - for attempt in range(retries): - try: - shutil.rmtree(path, onerror=handle_remove_readonly) - logger.info(f"Deleted {path} successfully.") - return - except Exception as e: - if attempt < retries - 1: - logger.warning(f"Retry {attempt + 1}/{retries} to delete {path} failed due to: {e}. Retrying...") - sleep(delay * (2 ** attempt)) # Exponential backoff - else: - logger.error(f"Failed to delete {path} after {retries} attempts: {e}") - -# Git operations -def clone_vcpkg_repo(): - """Clone the vcpkg repository.""" - if os.path.exists(VCPKG_CLONE_PATH): - delete_with_retries(VCPKG_CLONE_PATH) - Repo.clone_from(VCPKG_REPO_URL, VCPKG_CLONE_PATH).close() - -def add_and_commit(repo, message): - """Add all changes and commit to the provided repository.""" - repo.git.add(A=True) - repo.index.commit(message) - -# Portfile creation - -def create_portfile(repo_url, last_tag, project_name): - """Generate portfile.cmake from the template.""" - os.makedirs(portfile_output_path, exist_ok=True) - - with open(portfile_template_path, "r") as template_file: - template_content = template_file.read() - - portfile_content = template_content.replace("", repo_url).replace("", last_tag) - port_folder_path = os.path.join(portfile_output_path, project_name) - portfile_path = os.path.join(port_folder_path, "portfile.cmake") - - if not os.path.exists(port_folder_path): - logger.info(f"Creating {port_folder_path}") - os.makedirs(port_folder_path, exist_ok=True) - - logger.info(f"Creating {portfile_path}") - with open(portfile_path, "w") as portfile_file: - portfile_file.write(portfile_content) - shutil.copyfile(vcpkg_json_path, os.path.join(portfile_output_path, project_name, "vcpkg.json")) - return port_folder_path - -# Vcpkg operations -def run_vcpkg_commands(project_name): - """Run vcpkg format-manifest and x-add-version commands.""" - vcpkg_json_path_local = os.path.join("ports", project_name, "vcpkg.json") - try: - subprocess.run(["vcpkg", "format-manifest", vcpkg_json_path_local], check=True) - subprocess.run([ - "vcpkg", - "--x-builtin-ports-root=./ports", - "--x-builtin-registry-versions-dir=./versions", - "x-add-version", "--all", "--verbose", "--overwrite-version" - ], check=True) - except subprocess.CalledProcessError as e: - logger.error(f"Error while running vcpkg commands: {e}") - raise - -# Main workflow -try: - # Get project and repo details - project_name = get_project_name() - git_root = find_git_root(PROJECT_DIR) - - # Retrieve repository info - repo = Repo(git_root) - repo_url = next(repo.remote().urls) - last_tag = repo.git.describe("--tags", "--abbrev=0") - logger.info(f"Last tag: {last_tag}") - # Get the commit hash of the tag - tag_commit_hash = repo.git.rev_parse(last_tag) - logger.info(f"Commit hash of the last tag: {tag_commit_hash}") - del repo # Free resources - #run setup.py to generate vcpkg.json - subprocess.run(["python", os.path.join(os.path.dirname(__file__), "setup.py")], check=True) - # Generate portfile.cmake and clone vcpkg repository - port_folder_path=create_portfile(repo_url, tag_commit_hash, project_name) - clone_vcpkg_repo() - shutil.copytree(port_folder_path, os.path.join(VCPKG_CLONE_PATH, "ports", project_name),dirs_exist_ok=True) - - # Commit the new portfile to vcpkg repository - repo = Repo(VCPKG_CLONE_PATH) - add_and_commit(repo, f"{project_name} files added to the registry") - - # Run vcpkg commands - os.chdir(VCPKG_CLONE_PATH) - run_vcpkg_commands(project_name) - - # Finalize by committing the registry addition and pushing - add_and_commit(repo, f"{project_name} added to the registry") - repo.git.push() - - # Show the last commit hash - last_commit = repo.head.commit.hexsha - logger.info(f"Last commit hash in vcpkg repository (update vcpkg-configuration.json with this baseline):") - logger.info(f"Baseline: {last_commit}") - - # Clean up - del repo - gc.collect() # Force garbage collection - -finally: - # Ensure temporary vcpkg clone and ports are deleted - delete_with_retries(TMP_DIR) - -logger.info(f"Process completed: {project_name} added to the vcpkg registry.") \ No newline at end of file diff --git a/cmake_tools_for_vcpkg/tools/requirements.txt b/cmake_tools_for_vcpkg/tools/requirements.txt deleted file mode 100644 index aa07481..0000000 --- a/cmake_tools_for_vcpkg/tools/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -GitPython -coloredlogs -colorama \ No newline at end of file diff --git a/cmake_tools_for_vcpkg/tools/setup.py b/cmake_tools_for_vcpkg/tools/setup.py deleted file mode 100644 index 58c022e..0000000 --- a/cmake_tools_for_vcpkg/tools/setup.py +++ /dev/null @@ -1,144 +0,0 @@ -import json -import os -import re -from git import Repo, GitCommandError -import coloredlogs, logging -logger = logging.getLogger(__name__) -coloredlogs.install(level='INFO', logger=logger) -# Define paths to required files -vcpkg_json_path = os.path.join(os.path.dirname(__file__), "..", "..", "vcpkg.json") -cmakelists_path = os.path.join(os.path.dirname(__file__), "..", "..", "CMakeLists.txt") - -def load_json(file_path): - """Load a JSON file and return its content as a dictionary.""" - try: - with open(file_path, 'r') as file: - return json.load(file) - except FileNotFoundError: - logger.error(f"Error: {file_path} not found.") - return None - except json.JSONDecodeError: - logger.error(f"Error: {file_path} is not a valid JSON file.") - return None - -def update_json(file_path, data): - """Update a JSON file with new data.""" - try: - with open(file_path, 'r+') as file: - json_data = json.load(file) - json_data.update(data) - file.seek(0) - json.dump(json_data, file, indent=4) - file.truncate() - logger.info(f"Updated {file_path} with new data.") - except Exception as e: - logger.error(f"Failed to update {file_path}: {e}") - -def create_semver(vcpkg_data): - """Create a SemVer string from available version fields in vcpkg data.""" - version = vcpkg_data.get("version") - if version and re.match(r'^\d+\.\d+\.\d+$', version): - return version - semver = vcpkg_data.get("version-semver", "1.0.0").split(".") - return '.'.join((semver + ["0", "0", "0"])[:3]) - -def get_git_tag(repo): - """Return the latest tag associated with the current Git branch.""" - try: - return repo.git.describe("--tags", "--abbrev=0") - except GitCommandError: - logger.warning("Warning: No tags found in repository.") - return None - -def collect_vcpkg_data(vcpkg_data_input, tag_version=True): - """Collect environment data to write to config.cmake.""" - vcpkg_data = {} - needed_keys = ["name", "version", "maintainers", "description", "documentation", - "homepage", "license", "version-semver", "version-string", "version-date"] - - for key in needed_keys: - if key in vcpkg_data_input: - vcpkg_data[f"MY_CMAKE_PROJECT_{key.upper()}"] = str(vcpkg_data_input[key]) - - vcpkg_data["MY_CMAKE_PROJECT_SEMVER"] = create_semver(vcpkg_data_input) - - local_repo_path = os.path.join(os.path.dirname(__file__), "..", "..") - try: - local_repo = Repo(local_repo_path) - if tag_version: - tag_name = get_git_tag(local_repo) - if tag_name: - vcpkg_data["MY_CMAKE_PROJECT_VERSION"] = tag_name - vcpkg_data["MY_CMAKE_PROJECT_SEMVER"] = tag_name - update_json(vcpkg_json_path, {"version": tag_name}) - else: - vcpkg_data["MY_CMAKE_PROJECT_VERSION"] = vcpkg_data["MY_CMAKE_PROJECT_SEMVER"] - else: - vcpkg_data["MY_CMAKE_PROJECT_VERSION"] = vcpkg_data["MY_CMAKE_PROJECT_SEMVER"] - except Exception as e: - logger.error("Error retrieving version:", e) - vcpkg_data["MY_CMAKE_PROJECT_VERSION"] = vcpkg_data.get("MY_CMAKE_PROJECT_SEMVER", "1.0.0") - - return vcpkg_data - -def write_config_cmake(vcpkg_data): - """Write the collected environment data to config.cmake.""" - cmake_dir = os.path.join(os.path.dirname(__file__), "..", "..", "cmake") - os.makedirs(cmake_dir, exist_ok=True) - config_file_path = os.path.join(cmake_dir, "config.cmake") - - with open(config_file_path, 'w') as f: - f.write("# config.cmake\n\n") - for key, value in vcpkg_data.items(): - f.write(f"set({key} \"{value}\")\n") - f.write(f"message(STATUS \"{key} set to: ${{{key}}}\")\n\n") - logger.info(f"Configuration written to {config_file_path}") - -def extract_dependencies(cmakelists_path_): - """Extract dependency names from find_package calls in CMakeLists.txt.""" - dependencies = [] - find_package_pattern = re.compile(r'find_package\((\w+)') - try: - with open(cmakelists_path_, 'r') as file: - dependencies = [match.group(1) for line in file if (match := find_package_pattern.search(line))] - except FileNotFoundError: - logger.error(f"Error: {cmakelists_path_} not found.") - - logger.info(f"Dependencies extracted: {dependencies}") - return dependencies - -def write_config_cmake_in(vcpkg_data, dependencies): - """Create the Config.cmake.in file with template values.""" - target_name = vcpkg_data.get("MY_CMAKE_PROJECT_NAME", "UnknownProject") - cmake_dir = os.path.join(os.path.dirname(__file__), "..", "..", "cmake") - config_in_file_path = os.path.join(cmake_dir, "Config.cmake.in") - - os.makedirs(cmake_dir, exist_ok=True) - with open(config_in_file_path, 'w') as f: - f.write("# Config.cmake.in\n\n") - f.write("@PACKAGE_INIT@\n\n") - f.write(f"include(\"${{CMAKE_CURRENT_LIST_DIR}}/{target_name}Targets.cmake\")\n\n") - f.write(f"check_required_components({target_name})\n\n") - f.write("include(CMakeFindDependencyMacro)\n\n") - for dep in dependencies: - f.write(f"find_dependency({dep})\n") - logger.info(f"Config.cmake.in written to {config_in_file_path}") - -def main(tag_version=True, cmake_path=cmakelists_path): - try: - vcpkg_data = load_json(vcpkg_json_path) - if not vcpkg_data: - raise ValueError("vcpkg.json could not be loaded.") - - vcpkg_data = collect_vcpkg_data(vcpkg_data, tag_version) - write_config_cmake(vcpkg_data) - - dependencies = extract_dependencies(cmake_path) - write_config_cmake_in(vcpkg_data, dependencies) - - logger.info("Project configured successfully.") - except Exception as e: - logger.error(f"Error in configuration: {e}") - -if __name__ == "__main__": - main(True) diff --git a/config.h.in b/config.h.in deleted file mode 100644 index 8f68462..0000000 --- a/config.h.in +++ /dev/null @@ -1,4 +0,0 @@ - -#define PROJECT_NAME "@PROJECT_NAME@" -#define VERSION "@PROJECT_VERSION@" -#define LICENSE "@PROJECT_LICENSE@" diff --git a/mockoon-data.json b/configuration/mockoon-data.json similarity index 100% rename from mockoon-data.json rename to configuration/mockoon-data.json diff --git a/nginx.conf b/configuration/nginx.conf similarity index 100% rename from nginx.conf rename to configuration/nginx.conf diff --git a/configure.bat b/configure.bat new file mode 100644 index 0000000..a57c894 --- /dev/null +++ b/configure.bat @@ -0,0 +1,55 @@ +@echo off +REM Check if Python is installed +where python3 >nul 2>&1 +if %errorlevel% equ 0 ( + set pythonCmd=python3 +) else ( + where python >nul 2>&1 + if %errorlevel% equ 0 ( + set pythonCmd=python + ) else ( + echo Python is not installed. Please install Python to use this script. + exit /b 1 + ) +) + +REM Check if pip is installed +where pip3 >nul 2>&1 +if %errorlevel% equ 0 ( + set pipCmd=pip3 +) else ( + where pip >nul 2>&1 + if %errorlevel% equ 0 ( + set pipCmd=pip + ) else ( + echo Pip is not installed. Please install pip to use this script. + exit /b 1 + ) +) + +REM Move working directory to script location +cd /d %~dp0 + +REM Create virtual environment if it doesn't exist +if not exist ".venv" ( + echo Creating virtual environment... + %pythonCmd% -m venv .venv +) + +REM Activate virtual environment +if exist ".venv\Scripts\activate.bat" ( + call ".venv\Scripts\activate.bat" +) else ( + echo Virtual environment activation script not found. + exit /b 1 +) + +REM Install requirements if requirements.txt exists +if exist "tools\requirements.txt" ( + echo Installing requirements... + %pipCmd% install -r tools\requirements.txt +) else ( + echo requirements.txt not found. Skipping installation. +) +REM Keep the virtual environment active after script ends +cmd /k diff --git a/configure.sh b/configure.sh new file mode 100755 index 0000000..d219da7 --- /dev/null +++ b/configure.sh @@ -0,0 +1,48 @@ +#!/bin/bash +#check if python is installed or python3 +if command -v python3 &>/dev/null; then + PYTHON_CMD="python3" +elif command -v python &>/dev/null; then + PYTHON_CMD="python" +else + echo "Python is not installed. Please install Python to use this script." + exit 1 +fi +#pip or pip3 +if command -v pip3 &>/dev/null; then + PIP_CMD="pip3" +elif command -v pip &>/dev/null; then + PIP_CMD="pip" +else + echo "Pip is not installed. Please install pip to use this script." + exit 1 +fi + +#move working dir to root/ +cd "$(dirname "$0")" + +#create virtual environment +if [ ! -d "venv" ]; then + echo "Creating virtual environment..." + $PYTHON_CMD -m venv .venv +fi +#activate virtual environment +if [ -f ".venv/bin/activate" ]; then + source .venv/bin/activate +elif [ -f ".venv/Scripts/activate" ]; then + source .venv/Scripts/activate +else + echo "Virtual environment activation script not found." + exit 1 +fi +cd tools +#install requirements +if [ -f "requirements.txt" ]; then + echo "Installing requirements..." + $PIP_CMD install -r requirements.txt +else + echo "requirements.txt not found. Skipping installation." +fi +cd .. +# Keep the virtual environment active after script ends +$SHELL diff --git a/openapi3.yaml b/doc/protocol.yaml similarity index 100% rename from openapi3.yaml rename to doc/protocol.yaml diff --git a/tus-logo.png b/doc/tus-logo.png similarity index 100% rename from tus-logo.png rename to doc/tus-logo.png diff --git a/docker-compose.yml b/docker-compose.yml index 869f593..fb52670 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: mockoon/cli container_name: mockoon volumes: - - ./mockoon-data.json:/data/mockoon-data.json + - ./configuration/mockoon-data.json:/data/mockoon-data.json environment: - MOCKOON_DATA=/data/mockoon-data.json command: --data /data/mockoon-data.json @@ -30,4 +30,4 @@ services: ports: - "80:80" volumes: - - ./nginx.conf:/etc/nginx/nginx.conf \ No newline at end of file + - ./configuration/nginx.conf:/etc/nginx/nginx.conf \ No newline at end of file diff --git a/include/config.h b/include/config.h deleted file mode 100644 index b684907..0000000 --- a/include/config.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CONFIG_H_IN -#define CONFIG_H_IN -#define PROJECT_NAME "libtusclient" -#define PROJECT_VERSION "1.0.0" -#define PROJECT_VERSION_MAJOR -#define PROJECT_VERSION_MINOR -#define PROJECT_VERSION_PATCH -#define PROJECT_DESCRIPTION "" -#define PROJECT_AUTHOR "" -#define PROJECT_AUTHOR_EMAIL "" -#define PROJECT_AUTHOR_WEBSITE "" -#define PROJECT_LICENSE "" -#define PROJECT_COPYRIGHT "" -#define PROJECT_TRADEMARK "" -#define PROJECT_COMPANY "" -#define PROJECT_COMPANY_NAME "" -#define PROJECT_COMPANY_ADDRESS "" -#define PROJECT_COMPANY_CITY "" -#define PROJECT_COMPANY_COUNTRY "" -#define PROJECT_COMPANY_PHONE "" -#define PROJECT_COMPANY_EMAIL "" -#define PROJECT_COMPANY_WEBSITE "" -#define PROJECT_COMPANY_ZIPCODE "" -#define PROJECT_COMPANY_FAX "" - -#endif // CONFIG_H_IN diff --git a/include/libtusclient.h b/include/libtusclient.h deleted file mode 100644 index deef03e..0000000 --- a/include/libtusclient.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2024 Matteo Cadoni - * This file is part of libtusclient, licensed under the MIT License. - * See the LICENSE file in the project root for more information. - */ - -#ifndef INCLUDE_LIBTUSCLIENT_H_ -#define INCLUDE_LIBTUSCLIENT_H_ -#define TUS_PROTOCOL_VERSION "1.0.0" -#ifdef _WINDOWS -#include -#ifndef EXPORT_ALL -# if defined(BUILD_SHARED) - - // Building the DLL (exporting) -# define EXPORT_LIBTUSCLIENT __declspec(dllexport) -#else - // Using the DLL (importing) -# define EXPORT_LIBTUSCLIENT __declspec(dllimport) -#endif -#else - #define EXPORT_LIBTUSCLIENT -#endif -#else -// Non-Windows platforms (optional, but typically use empty macro or other logic) -#define EXPORT_LIBTUSCLIENT -#endif - -#endif // INCLUDE_LIBTUSCLIENT_H_ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..2649976 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(tusclient) diff --git a/lib/tusclient/.cmake/sources.cmake b/lib/tusclient/.cmake/sources.cmake new file mode 100644 index 0000000..df4fce6 --- /dev/null +++ b/lib/tusclient/.cmake/sources.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/tusclient_sources.cmake) diff --git a/lib/tusclient/.cmake/tusclient_sources.cmake b/lib/tusclient/.cmake/tusclient_sources.cmake new file mode 100644 index 0000000..d966d56 --- /dev/null +++ b/lib/tusclient/.cmake/tusclient_sources.cmake @@ -0,0 +1,50 @@ +set(TUSCLIENT_HEADERS + include/tusclient/TusClient.h + include/tusclient/TusStatus.h + include/tusclient/cache/CacheRepository.h + include/tusclient/cache/ICacheManager.h + include/tusclient/cache/TUSFile.h + include/tusclient/chunk/FileChunker.h + include/tusclient/chunk/IFileChunker.h + include/tusclient/chunk/TUSChunk.h + include/tusclient/chunk/utility/ChunkUtility.h + include/tusclient/config.h + include/tusclient/exceptions/TUSException.h + include/tusclient/http/HttpClient.h + include/tusclient/http/IHttpClient.h + include/tusclient/http/Request.h + include/tusclient/http/RequestTask.h + include/tusclient/libtusclient.h + include/tusclient/logging/GLoggingService.h + include/tusclient/logging/ILogger.h + include/tusclient/repository/IRepository.h + include/tusclient/verifiers/IFileVerifier.h + include/tusclient/verifiers/Md5Verifier.h +) + +set(TUSCLIENT_SOURCES + src/tusclient/TusClient.cpp + src/tusclient/cache/CacheRepository.cpp + src/tusclient/cache/TUSFile.cpp + src/tusclient/chunk/FileChunker.cpp + src/tusclient/chunk/TUSChunk.cpp + src/tusclient/chunk/utility/ChunkUtility.cpp + src/tusclient/http/HttpClient.cpp + src/tusclient/http/Request.cpp + src/tusclient/http/RequestTask.cpp + src/tusclient/libtusclient.cpp + src/tusclient/logging/GLoggingService.cpp + src/tusclient/verifiers/Md5Verifier.cpp +) + +set(TUSCLIENT_TEST_SOURCES + FileChunkerTest.cpp + TusClientTest.cpp + http/HttpClientTest.cpp + main.cpp + repository/CacheRepositoryTest.cpp + verifiers/FileVerifiersTest.cpp +) + +set(TUSCLIENT_RESOURCES +) diff --git a/lib/tusclient/CMakeLists.txt b/lib/tusclient/CMakeLists.txt new file mode 100644 index 0000000..83b1b18 --- /dev/null +++ b/lib/tusclient/CMakeLists.txt @@ -0,0 +1,25 @@ +# Include common cmake scripts +include(${CMAKE_CURRENT_SOURCE_DIR}/.cmake/sources.cmake) +show_module_info(${CMAKE_CURRENT_LIST_DIR}/package.json) + +# Configure build output directories for the target +configure_build(tusclient) + +# Create executable target +add_library(tusclient SHARED ${TUSCLIENT_SOURCES} ${TUSCLIENT_HEADERS} ${TUSCLIENT_RESOURCES}) + +# Include headers directory +target_include_directories(tusclient PUBLIC + $ + $) + +# Link libraries +target_link_libraries(tusclient PUBLIC CURL::libcurl Boost::uuid Boost::lexical_cast nlohmann_json::nlohmann_json glog::glog) +target_compile_definitions(tusclient PRIVATE TUSCLIENT_EXPORTS) +# Enable testing if tests exist +include(CTest) +if(BUILD_TESTING) + add_subdirectory(test) +endif() +# Install target +configure_install(tusclient) \ No newline at end of file diff --git a/lib/tusclient/LICENSE b/lib/tusclient/LICENSE new file mode 100644 index 0000000..75110a2 --- /dev/null +++ b/lib/tusclient/LICENSE @@ -0,0 +1,34 @@ +tusclient - End User License Agreement (EULA) +Copyright (c) Unknown Organization + +IMPORTANT: PLEASE READ THIS AGREEMENT CAREFULLY BEFORE USING THIS SOFTWARE. + +By installing, copying, or otherwise using tusclient (the "Software"), you agree to be bound by the terms of this EULA. + +1. LICENSE GRANT +Unknown Organization grants you a non-exclusive, non-transferable license to use the Software solely for your personal or internal business purposes, subject to the terms of this Agreement. + +2. RESTRICTIONS +You may not: +- Modify, reverse engineer, decompile, or disassemble the Software. +- Distribute, sell, sublicense, or rent the Software to any third party. +- Remove any proprietary notices or labels on the Software. + +3. OWNERSHIP +The Software is licensed, not sold. Unknown Organization retains all rights, title, and interest in and to the Software. + +4. TERMINATION +This Agreement is effective until terminated. You may terminate it by deleting the Software. It will also terminate if you fail to comply with any terms herein. + +5. WARRANTY DISCLAIMER +The Software is provided "AS IS" without warranty of any kind. Unknown Organization disclaims all warranties, express or implied, including but not limited to fitness for a particular purpose. + +6. LIMITATION OF LIABILITY +In no event shall Unknown Organization be liable for any damages arising from the use or inability to use the Software. + +7. GOVERNING LAW +This Agreement shall be governed by the laws of the jurisdiction where Unknown Organization is located. + +If you do not agree to these terms, do not install or use the Software. + +For questions, contact: Unknown Organization - None \ No newline at end of file diff --git a/lib/tusclient/README.md b/lib/tusclient/README.md new file mode 100644 index 0000000..cfce765 --- /dev/null +++ b/lib/tusclient/README.md @@ -0,0 +1,5 @@ +# TusClient + +This is the module of the TusClient library, which is a C++ client for the TUS protocol. It provides an easy way to upload large files to a TUS server. + +You have to build the library before using it. The library is built using CMake, and it requires C++17 or later. \ No newline at end of file diff --git a/lib/tusclient/docs/.gitignore b/lib/tusclient/docs/.gitignore new file mode 100644 index 0000000..54a4bc8 --- /dev/null +++ b/lib/tusclient/docs/.gitignore @@ -0,0 +1,3 @@ +html +latex +out \ No newline at end of file diff --git a/lib/tusclient/docs/Doxyfile b/lib/tusclient/docs/Doxyfile new file mode 100644 index 0000000..a1053a2 --- /dev/null +++ b/lib/tusclient/docs/Doxyfile @@ -0,0 +1,100 @@ + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "tusclient" +PROJECT_NUMBER = "1.0.2" +PROJECT_BRIEF = "tusclient documentation" +OUTPUT_DIRECTORY = out +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = YES + +#--------------------------------------------------------------------------- +# Input files +#--------------------------------------------------------------------------- +INPUT = . \ + .. \ + ../src \ + ../include \ + ../README.md + +FILE_PATTERNS = *.cpp \ + *.h \ + *.hpp \ + *.md +RECURSIVE = YES +EXCLUDE_PATTERNS = */build/* \ + */test/* \ + */.cmake/* \ + */docs/* +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Source browsing options +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# HTML output options +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_DYNAMIC_SECTIONS = NO +GENERATE_TREEVIEW = YES +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# Preprocessing options +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = include +INCLUDE_FILE_PATTERNS = *.h \ + *.hpp + +#--------------------------------------------------------------------------- +# Dot tool options +#--------------------------------------------------------------------------- +HAVE_DOT = YES +DOT_NUM_THREADS = 0 +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +UML_LIMIT_NUM_FIELDS = 50 +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = SVG +INTERACTIVE_SVG = YES +MAX_DOT_GRAPH_DEPTH = 0 \ No newline at end of file diff --git a/include/TusClient.h b/lib/tusclient/include/tusclient/TusClient.h similarity index 99% rename from include/TusClient.h rename to lib/tusclient/include/tusclient/TusClient.h index 910eb98..7a8009a 100644 --- a/include/TusClient.h +++ b/lib/tusclient/include/tusclient/TusClient.h @@ -243,7 +243,7 @@ namespace TUS { * @brief Handle errors encountered during the upload process. * @param header An integer representing the specific error encountered. */ - void handleUploadError(const string &header); + [[noreturn]] void handleUploadError(const string &header); }; } // namespace TUS #endif // INCLUDE_TUSCLIENT_H_ diff --git a/include/TusStatus.h b/lib/tusclient/include/tusclient/TusStatus.h similarity index 100% rename from include/TusStatus.h rename to lib/tusclient/include/tusclient/TusStatus.h diff --git a/include/cache/CacheRepository.h b/lib/tusclient/include/tusclient/cache/CacheRepository.h similarity index 90% rename from include/cache/CacheRepository.h rename to lib/tusclient/include/tusclient/cache/CacheRepository.h index 6261e68..a24c3ba 100644 --- a/include/cache/CacheRepository.h +++ b/lib/tusclient/include/tusclient/cache/CacheRepository.h @@ -20,7 +20,7 @@ namespace TUS::Cache { class EXPORT_LIBTUSCLIENT CacheRepository : public Repository::IRepository { public: explicit CacheRepository(std::string appName, bool clearCache = false); - + static std::shared_ptr create(std::string appName, bool clearCache = false); ~CacheRepository() override; void add(std::shared_ptr) override; @@ -33,7 +33,7 @@ namespace TUS::Cache { bool open() override; - bool save() override; + bool save() noexcept override; void clearCache(); diff --git a/include/cache/ICacheManager.h b/lib/tusclient/include/tusclient/cache/ICacheManager.h similarity index 96% rename from include/cache/ICacheManager.h rename to lib/tusclient/include/tusclient/cache/ICacheManager.h index 778b7a8..520f653 100644 --- a/include/cache/ICacheManager.h +++ b/lib/tusclient/include/tusclient/cache/ICacheManager.h @@ -31,7 +31,7 @@ namespace TUS { * * @param file A shared pointer to the TUSFile to be stored. */ - virtual void store(std::shared_ptr file) = 0; + virtual void store(auto file) = 0; /** * @brief Retrieve a TUS file from the cache. diff --git a/include/cache/TUSFile.h b/lib/tusclient/include/tusclient/cache/TUSFile.h similarity index 100% rename from include/cache/TUSFile.h rename to lib/tusclient/include/tusclient/cache/TUSFile.h diff --git a/include/chunk/FileChunker.h b/lib/tusclient/include/tusclient/chunk/FileChunker.h similarity index 98% rename from include/chunk/FileChunker.h rename to lib/tusclient/include/tusclient/chunk/FileChunker.h index 04d78b0..7c9dd97 100644 --- a/include/chunk/FileChunker.h +++ b/lib/tusclient/include/tusclient/chunk/FileChunker.h @@ -47,7 +47,7 @@ namespace TUS::Chunk { FileChunker(string appName, string uuid, path filepath, int chunkSize = 0, std::unique_ptr verifier = nullptr); - ~FileChunker() = default; + ~FileChunker() override = default; bool loadChunks() override; diff --git a/include/chunk/IFileChunker.h b/lib/tusclient/include/tusclient/chunk/IFileChunker.h similarity index 99% rename from include/chunk/IFileChunker.h rename to lib/tusclient/include/tusclient/chunk/IFileChunker.h index 2cbf774..3b438fe 100644 --- a/include/chunk/IFileChunker.h +++ b/lib/tusclient/include/tusclient/chunk/IFileChunker.h @@ -9,7 +9,6 @@ #include #include #include - #include "libtusclient.h" using std::string; diff --git a/include/chunk/TUSChunk.h b/lib/tusclient/include/tusclient/chunk/TUSChunk.h similarity index 100% rename from include/chunk/TUSChunk.h rename to lib/tusclient/include/tusclient/chunk/TUSChunk.h diff --git a/include/chunk/utility/ChunkUtility.h b/lib/tusclient/include/tusclient/chunk/utility/ChunkUtility.h similarity index 100% rename from include/chunk/utility/ChunkUtility.h rename to lib/tusclient/include/tusclient/chunk/utility/ChunkUtility.h diff --git a/lib/tusclient/include/tusclient/config.h b/lib/tusclient/include/tusclient/config.h new file mode 100644 index 0000000..c421a42 --- /dev/null +++ b/lib/tusclient/include/tusclient/config.h @@ -0,0 +1,4 @@ +#ifndef CONFIG_H_IN +#define CONFIG_H_IN +constexpr auto PROJECT_NAME = "tusclient"; +#endif // CONFIG_H_IN diff --git a/include/exceptions/TUSException.h b/lib/tusclient/include/tusclient/exceptions/TUSException.h similarity index 100% rename from include/exceptions/TUSException.h rename to lib/tusclient/include/tusclient/exceptions/TUSException.h diff --git a/include/http/HttpClient.h b/lib/tusclient/include/tusclient/http/HttpClient.h similarity index 100% rename from include/http/HttpClient.h rename to lib/tusclient/include/tusclient/http/HttpClient.h diff --git a/include/http/IHttpClient.h b/lib/tusclient/include/tusclient/http/IHttpClient.h similarity index 100% rename from include/http/IHttpClient.h rename to lib/tusclient/include/tusclient/http/IHttpClient.h diff --git a/include/http/Request.h b/lib/tusclient/include/tusclient/http/Request.h similarity index 100% rename from include/http/Request.h rename to lib/tusclient/include/tusclient/http/Request.h diff --git a/include/http/RequestTask.h b/lib/tusclient/include/tusclient/http/RequestTask.h similarity index 100% rename from include/http/RequestTask.h rename to lib/tusclient/include/tusclient/http/RequestTask.h diff --git a/lib/tusclient/include/tusclient/libtusclient.h b/lib/tusclient/include/tusclient/libtusclient.h new file mode 100644 index 0000000..c183b5d --- /dev/null +++ b/lib/tusclient/include/tusclient/libtusclient.h @@ -0,0 +1,21 @@ +#ifndef INCLUDE_LIBTUSCLIENT_H_ +#define INCLUDE_LIBTUSCLIENT_H_ +#include + +constexpr auto TUS_PROTOCOL_VERSION = "1.0.0"; + +#if defined(_WIN32) || defined(__CYGWIN__) + #ifdef TUSCLIENT_EXPORTS + #define EXPORT_LIBTUSCLIENT __declspec(dllexport) + #else + #define EXPORT_LIBTUSCLIENT __declspec(dllimport) + #endif +#else + #ifdef TUSCLIENT_EXPORTS + #define EXPORT_LIBTUSCLIENT __attribute__((visibility("default"))) + #else + #define EXPORT_LIBTUSCLIENT + #endif +#endif + +#endif // INCLUDE_LIBTUSCLIENT_H_ \ No newline at end of file diff --git a/include/logging/GLoggingService.h b/lib/tusclient/include/tusclient/logging/GLoggingService.h similarity index 95% rename from include/logging/GLoggingService.h rename to lib/tusclient/include/tusclient/logging/GLoggingService.h index 7877564..9ce873e 100644 --- a/include/logging/GLoggingService.h +++ b/lib/tusclient/include/tusclient/logging/GLoggingService.h @@ -30,7 +30,7 @@ class EXPORT_LIBTUSCLIENT GLoggingService : public ILogger { LogLevel m_level=LogLevel::_INFO_; static inline bool isInitialized = false; - static inline std::atomic instanceCount{0}; + static inline std::atomic instanceCount{0}; static inline std::mutex initMutex; }; diff --git a/include/logging/ILogger.h b/lib/tusclient/include/tusclient/logging/ILogger.h similarity index 100% rename from include/logging/ILogger.h rename to lib/tusclient/include/tusclient/logging/ILogger.h diff --git a/include/repository/IRepository.h b/lib/tusclient/include/tusclient/repository/IRepository.h similarity index 100% rename from include/repository/IRepository.h rename to lib/tusclient/include/tusclient/repository/IRepository.h diff --git a/include/verifiers/IFileVerifier.h b/lib/tusclient/include/tusclient/verifiers/IFileVerifier.h similarity index 100% rename from include/verifiers/IFileVerifier.h rename to lib/tusclient/include/tusclient/verifiers/IFileVerifier.h diff --git a/include/verifiers/Md5Verifier.h b/lib/tusclient/include/tusclient/verifiers/Md5Verifier.h similarity index 100% rename from include/verifiers/Md5Verifier.h rename to lib/tusclient/include/tusclient/verifiers/Md5Verifier.h diff --git a/lib/tusclient/package.json b/lib/tusclient/package.json new file mode 100644 index 0000000..5a4eedf --- /dev/null +++ b/lib/tusclient/package.json @@ -0,0 +1,6 @@ +{ + "name": "tusclient", + "version": "1.0.1", + "type": "lib", + "description": "tusclient module of type lib" +} \ No newline at end of file diff --git a/src/TusClient.cpp b/lib/tusclient/src/tusclient/TusClient.cpp similarity index 97% rename from src/TusClient.cpp rename to lib/tusclient/src/tusclient/TusClient.cpp index 2e8d3a3..6092c8c 100644 --- a/src/TusClient.cpp +++ b/lib/tusclient/src/tusclient/TusClient.cpp @@ -80,8 +80,8 @@ std::string extractHeaderValue(const std::string &header, const std::string &key) { const auto toLower = [](const std::string &s) { std::string result = s; - std::transform(result.begin(), result.end(), result.begin(), - [](unsigned char c) { return std::tolower(c); }); + std::ranges::transform(result, result.begin(), + [](unsigned char c) { return std::tolower(c); }); return result; }; @@ -93,14 +93,14 @@ std::string extractHeaderValue(const std::string &header, if (lineEnd == std::string::npos) lineEnd = header.size(); - size_t colonPos = header.find(':', pos); - if (colonPos != std::string::npos && colonPos < lineEnd) { + + if (const size_t colonPos = header.find(':', pos); colonPos != std::string::npos && colonPos < lineEnd) { std::string lineKey = header.substr(pos, colonPos - pos); std::string lineValue = header.substr(colonPos + 1, lineEnd - colonPos - 1); // Trim spaces helper auto trim = [](std::string &str) { - const char* whitespace = " \t"; + const char *whitespace = " \t"; size_t start = str.find_first_not_of(whitespace); size_t end = str.find_last_not_of(whitespace); if (start == std::string::npos) { @@ -220,7 +220,7 @@ void TusClient::handleSuccessfulUpload(const string &header) { } void TusClient::handleUploadConflict(const string &header) { - if ( m_retry < 3) { + if (m_retry < 3) { m_retry++; m_logger->warning("Conflict detected, retrying the upload"); getUploadInfo(); @@ -233,7 +233,7 @@ void TusClient::handleUploadConflict(const string &header) { std::this_thread::sleep_for(m_requestTimeout); } -void TusClient::handleUploadError(const string &header) { + void TusClient::handleUploadError(const string &header) { m_logger->error(std::format("Error: Unable to upload chunk {}", m_uploadedChunks)); m_logger->error(header); m_status.store(TusStatus::FAILED); diff --git a/src/cache/CacheRepository.cpp b/lib/tusclient/src/tusclient/cache/CacheRepository.cpp similarity index 69% rename from src/cache/CacheRepository.cpp rename to lib/tusclient/src/tusclient/cache/CacheRepository.cpp index b9d7a94..9d75424 100644 --- a/src/cache/CacheRepository.cpp +++ b/lib/tusclient/src/tusclient/cache/CacheRepository.cpp @@ -12,6 +12,8 @@ #include "cache/CacheRepository.h" +#include + using json = nlohmann::json; using TUS::Cache::CacheRepository; @@ -22,13 +24,17 @@ CacheRepository::CacheRepository(std::string appName, bool clear) if (!std::filesystem::exists(m_path.parent_path())) { std::filesystem::create_directories(m_path.parent_path()); } + +} +std::shared_ptr CacheRepository::create(std::string appName, bool clear) { + auto repository = std::make_shared(std::move(appName), clear); if (clear) { - clearCache(); + repository->clearCache(); } else { - CacheRepository::open(); + repository->open(); } + return repository; } - CacheRepository::~CacheRepository() { CacheRepository::save(); } @@ -50,11 +56,9 @@ void CacheRepository::remove(std::shared_ptr item) { } std::shared_ptr CacheRepository::findByHash(const std::string &id) const { - auto it = std::ranges::find_if(m_cache, [&id](const std::shared_ptr &file) { + if (auto it = std::ranges::find_if(m_cache, [&id](const std::shared_ptr &file) { return file->getIdentificationHash() == id; - }); - - if (it != m_cache.end()) { + }); it != m_cache.end()) { return *it; } @@ -120,7 +124,7 @@ bool CacheRepository::open() { boost::uuids::string_generator gen; std::string uuidString = item["uuid"]; boost::uuids::uuid uuid = gen(uuidString); - std::shared_ptr tusFile = std::make_shared(filePath, uploadUrl, appName, uuid); + auto tusFile = std::make_shared(filePath, uploadUrl, appName, uuid); tusFile->setUploadOffset(item["uploadOffset"]); tusFile->setResumeFrom(item["resumeFrom"]); tusFile->setLastEdit(item["lastEdit"]); @@ -138,35 +142,40 @@ void CacheRepository::clearCache() { open(); } -bool CacheRepository::save() { - json j; - if (!m_cache.empty()) { - for (const auto &file: m_cache) { - json item; - item["uuid"] = boost::uuids::to_string(file->getUuid()); - item["lastEdit"] = file->getLastEdit(); - item["hash"] = file->getIdentificationHash(); - item["filePath"] = file->getFilePath(); - item["appName"] = file->getAppName(); - item["uploadUrl"] = file->getUploadUrl(); - item["uploadOffset"] = file->getUploadOffset(); - item["resumeFrom"] = file->getResumeFrom(); - item["tusId"] = file->getTusIdentifier(); - item["chunkNumber"] = file->getChunkNumber(); - j.push_back(item); +bool CacheRepository::save() noexcept { + try { + json j; + if (!m_cache.empty()) { + for (const auto &file: m_cache) { + json item; + item["uuid"] = boost::uuids::to_string(file->getUuid()); + item["lastEdit"] = file->getLastEdit(); + item["hash"] = file->getIdentificationHash(); + item["filePath"] = file->getFilePath(); + item["appName"] = file->getAppName(); + item["uploadUrl"] = file->getUploadUrl(); + item["uploadOffset"] = file->getUploadOffset(); + item["resumeFrom"] = file->getResumeFrom(); + item["tusId"] = file->getTusIdentifier(); + item["chunkNumber"] = file->getChunkNumber(); + j.push_back(item); + } + } else { + j = json::array(); } - } else { - j = json::array(); - } - std::ofstream file(m_path); + std::ofstream file(m_path); - if (!file.is_open()) { - return false; - } + if (!file.is_open()) { + return false; + } - file << j.dump(); + file << j.dump(); - file.close(); - return true; + file.close(); + return true; + } catch (const std::exception &e) { + std::cerr << "Error saving cache: " << e.what() << std::endl; + return false; + } } diff --git a/src/cache/TUSFile.cpp b/lib/tusclient/src/tusclient/cache/TUSFile.cpp similarity index 100% rename from src/cache/TUSFile.cpp rename to lib/tusclient/src/tusclient/cache/TUSFile.cpp diff --git a/src/chunk/FileChunker.cpp b/lib/tusclient/src/tusclient/chunk/FileChunker.cpp similarity index 100% rename from src/chunk/FileChunker.cpp rename to lib/tusclient/src/tusclient/chunk/FileChunker.cpp diff --git a/src/chunk/TUSChunk.cpp b/lib/tusclient/src/tusclient/chunk/TUSChunk.cpp similarity index 100% rename from src/chunk/TUSChunk.cpp rename to lib/tusclient/src/tusclient/chunk/TUSChunk.cpp diff --git a/src/chunk/utility/ChunkUtility.cpp b/lib/tusclient/src/tusclient/chunk/utility/ChunkUtility.cpp similarity index 95% rename from src/chunk/utility/ChunkUtility.cpp rename to lib/tusclient/src/tusclient/chunk/utility/ChunkUtility.cpp index fdacd1d..7e222ae 100644 --- a/src/chunk/utility/ChunkUtility.cpp +++ b/lib/tusclient/src/tusclient/chunk/utility/ChunkUtility.cpp @@ -5,7 +5,7 @@ */ #include "chunk/utility/ChunkUtility.h" -#define KB 1000 +constexpr auto KB = 1000; using TUS::Chunk::Utility::ChunkUtility; diff --git a/src/http/HttpClient.cpp b/lib/tusclient/src/tusclient/http/HttpClient.cpp similarity index 100% rename from src/http/HttpClient.cpp rename to lib/tusclient/src/tusclient/http/HttpClient.cpp diff --git a/src/http/Request.cpp b/lib/tusclient/src/tusclient/http/Request.cpp similarity index 92% rename from src/http/Request.cpp rename to lib/tusclient/src/tusclient/http/Request.cpp index 704e754..d049911 100644 --- a/src/http/Request.cpp +++ b/lib/tusclient/src/tusclient/http/Request.cpp @@ -21,10 +21,7 @@ Request::Request() { setOnErrorCallback(defaultErrorCallback()); } -Request::Request(const Request &request): url(request.url), body(request.body), method(request.method), - headers(request.headers), m_onSuccessCallback(request.m_onSuccessCallback), - m_onErrorCallback(request.m_onErrorCallback) { -} +Request::Request(const Request &request)=default; Request::Request(string url) { this->url = std::move(url); @@ -154,7 +151,7 @@ Request::SuccessCallback Request::defaultSuccessCallback() { } Request::ErrorCallback Request::defaultErrorCallback() { - return [](const string &header, const string &data) { + return [](const string &, const string &) { std::cout << "Failed callback not implemented" << std::endl; }; } diff --git a/src/http/RequestTask.cpp b/lib/tusclient/src/tusclient/http/RequestTask.cpp similarity index 100% rename from src/http/RequestTask.cpp rename to lib/tusclient/src/tusclient/http/RequestTask.cpp diff --git a/src/libtusclient.cpp b/lib/tusclient/src/tusclient/libtusclient.cpp similarity index 100% rename from src/libtusclient.cpp rename to lib/tusclient/src/tusclient/libtusclient.cpp diff --git a/src/logging/GLoggingService.cpp b/lib/tusclient/src/tusclient/logging/GLoggingService.cpp similarity index 100% rename from src/logging/GLoggingService.cpp rename to lib/tusclient/src/tusclient/logging/GLoggingService.cpp diff --git a/src/verifiers/Md5Verifier.cpp b/lib/tusclient/src/tusclient/verifiers/Md5Verifier.cpp similarity index 82% rename from src/verifiers/Md5Verifier.cpp rename to lib/tusclient/src/tusclient/verifiers/Md5Verifier.cpp index 8a661d3..df67de5 100644 --- a/src/verifiers/Md5Verifier.cpp +++ b/lib/tusclient/src/tusclient/verifiers/Md5Verifier.cpp @@ -26,11 +26,11 @@ string Md5Verifier::hash(const std::vector &buffer) const { boost::uuids::detail::md5::digest_type digest; hash.get_digest(digest); - std::ostringstream result; - for (unsigned char i: digest) { - result << std::hex << std::setw(2) << std::setfill('0') << static_cast(i); + std::string result; + for (const unsigned char i : digest) { + result += std::format("{:02x}", static_cast(i)); } - return result.str(); + return result; } bool Md5Verifier::verify(const std::vector &buffer, const string &hash) const { diff --git a/lib/tusclient/test/CMakeLists.txt b/lib/tusclient/test/CMakeLists.txt new file mode 100644 index 0000000..9fad05e --- /dev/null +++ b/lib/tusclient/test/CMakeLists.txt @@ -0,0 +1,24 @@ +include(${CMAKE_CURRENT_SOURCE_DIR}/../.cmake/sources.cmake) +#Find GoogleTest package +find_package(GTest REQUIRED) + +configure_build(tusclient_test) +find_package(GTest REQUIRED)#GoogleTest package inclusion +find_package(libzippp REQUIRED) +# Include directories +include_directories( + ${CMAKE_SOURCE_DIR}/include/ +) + +# Add test executable + +# Use the modified copies +add_executable(tusclient_test ${TUSCLIENT_TEST_SOURCES}) +# Link libraries +target_link_libraries(tusclient_test PRIVATE GTest::GTest GTest::gmock libzippp::libzippp tusclient) + +#auto discover tests +gtest_discover_tests(tusclient_test + DISCOVERY_TIMEOUT 60 + PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +) diff --git a/test/FileChunkerTest.cpp b/lib/tusclient/test/FileChunkerTest.cpp similarity index 99% rename from test/FileChunkerTest.cpp rename to lib/tusclient/test/FileChunkerTest.cpp index 6b71675..d3dc46e 100644 --- a/test/FileChunkerTest.cpp +++ b/lib/tusclient/test/FileChunkerTest.cpp @@ -13,7 +13,7 @@ #include "chunk/TUSChunk.h" class FileChunkerTest : public ::testing::Test { -protected: +public: void SetUp() override { // Create a temporary file for testing testFilePath = std::filesystem::temp_directory_path() / "testfile.bin"; diff --git a/test/TusClientTest.cpp b/lib/tusclient/test/TusClientTest.cpp similarity index 88% rename from test/TusClientTest.cpp rename to lib/tusclient/test/TusClientTest.cpp index 55dba1c..74addb6 100644 --- a/test/TusClientTest.cpp +++ b/lib/tusclient/test/TusClientTest.cpp @@ -19,7 +19,7 @@ */ namespace TUS::Test { class TusClientTest : public ::testing::Test { - protected: + public: const std::string URL = "http://localhost:8080/files/"; Logging::LogLevel logLevel = Logging::LogLevel::_DEBUG_; @@ -36,7 +36,7 @@ namespace TUS::Test { std::filesystem::remove("test.zip"); for (int i = 0; i < MAX_CLEANUP_FILES; ++i) { - std::filesystem::remove(std::to_string(i) + ".dat"); + std::filesystem::remove(std::format("{}.dat", i)); } } private: @@ -59,11 +59,13 @@ namespace TUS::Test { threads.reserve(size); for (int i = 0; i < size; ++i) { - threads.emplace_back([&, i]() { + threads.emplace_back([&dis,&gen,i]() { std::vector data(1024 * 1024); - std::generate(data.begin(), data.end(), [&]() { return static_cast(dis(gen)); }); + std::generate(data.begin(), data.end(), [&dis, &gen]() { + return static_cast(dis(gen)); + }); - std::ofstream datFile(std::to_string(i) + ".dat", std::ios::binary); + std::ofstream datFile(std::format("{}.dat", i), std::ios::binary); datFile.write(data.data(), data.size()); }); } @@ -74,7 +76,7 @@ namespace TUS::Test { zipArchive.open(libzippp::ZipArchive::New); for (int i = 0; i < size; ++i) { - zipArchive.addFile(std::to_string(i), std::to_string(i) + ".dat"); + zipArchive.addFile(std::to_string(i), std::format("{}.dat", i)); } zipArchive.close(); @@ -124,7 +126,7 @@ namespace TUS::Test { TUS::TusClient client("testapp", URL, path, logLevel); client.setRequestTimeout(std::chrono::milliseconds(100)); - std::thread uploadThread([&]() { client.upload(); }); + std::thread uploadThread([&client]() { client.upload(); }); waitUpload(client, 10); client.pause(); uploadThread.join(); @@ -137,7 +139,7 @@ namespace TUS::Test { TUS::TusClient client("testapp", URL, path, logLevel); client.setRequestTimeout(std::chrono::milliseconds(10)); - std::thread uploadThread([&]() { client.upload(); }); + std::thread uploadThread([&client]() { client.upload(); }); waitUpload(client, 10); client.pause(); uploadThread.join(); @@ -145,7 +147,7 @@ namespace TUS::Test { EXPECT_EQ(client.status(), TUS::TusStatus::PAUSED); float progress = client.progress(); - std::thread resumeThread([&]() { client.resume(); }); + std::thread resumeThread([&client]() { client.resume(); }); waitUpload(client, progress); resumeThread.join(); @@ -166,7 +168,7 @@ namespace TUS::Test { TUS::TusClient client("testapp", URL, path, logLevel); client.setRequestTimeout(std::chrono::milliseconds(10)); - std::thread uploadThread([&]() { client.upload(); }); + std::thread uploadThread([&client]() { client.upload(); }); waitUpload(client, 10); client.cancel(); uploadThread.join(); @@ -178,7 +180,7 @@ namespace TUS::Test { TUS::TusClient client("testapp", URL, path, logLevel); client.setRequestTimeout(std::chrono::milliseconds(10)); - std::thread uploadThread([&]() { client.upload(); }); + std::thread uploadThread([&client]() { client.upload(); }); waitUpload(client, 10); client.cancel(); uploadThread.join(); diff --git a/test/http/HttpClientTest.cpp b/lib/tusclient/test/http/HttpClientTest.cpp similarity index 85% rename from test/http/HttpClientTest.cpp rename to lib/tusclient/test/http/HttpClientTest.cpp index 00ab582..007f904 100644 --- a/test/http/HttpClientTest.cpp +++ b/lib/tusclient/test/http/HttpClientTest.cpp @@ -32,16 +32,15 @@ namespace TUS::Test::Http { }; class HttpClientParameterizedTest : public testing::TestWithParam { - protected: + public: void SetUp() override { - m_httpClient = new HttpClient(); + m_httpClient = std::make_unique(); } void TearDown() override { - delete m_httpClient; } - HttpClient *m_httpClient{}; + std::unique_ptr m_httpClient= nullptr; const int m_timeout = 2; //seconds }; @@ -49,7 +48,7 @@ namespace TUS::Test::Http { const TestParams &testCase = GetParam(); std::string finalResult; - std::function onDataReceivedCallback = [& + std::function onDataReceivedCallback = [&finalResult ](const std::string &header, const std::string &data) { std::cout << data << std::endl; std::cout << header << std::endl; @@ -104,7 +103,7 @@ namespace TUS::Test::Http { }); TEST_F(HttpClientParameterizedTest, CheckWrongMethod) { - Request request("http://localhost:3000/files", "", HttpMethod::_GET); + const Request request("http://localhost:3000/files", "", HttpMethod::_GET); EXPECT_THROW(m_httpClient->put(request), std::runtime_error); } @@ -119,15 +118,15 @@ namespace TUS::Test::Http { TEST_F(HttpClientParameterizedTest, HttpsRequest) { Request request("https://www.google.com", "", HttpMethod::_GET); - request.setOnSuccessCallback([this](const std::string &header, + request.setOnSuccessCallback([](const std::string &header, const std::string &data) { std::cout << data << std::endl; std::cout << header << std::endl; ASSERT_TRUE(!data.empty()); }); - request.setOnErrorCallback([this](const std::string &header, - const std::string &data) { + request.setOnErrorCallback([](const std::string &, + const std::string &) { ASSERT_TRUE(false); }); m_httpClient->get(request); @@ -136,12 +135,12 @@ namespace TUS::Test::Http { TEST_F(HttpClientParameterizedTest, AuthorizedRequestSuccess) { Request request("http://localhost:3000/auth/files", "", HttpMethod::_POST); - request.setOnSuccessCallback([this](const std::string &header, - const std::string &data) { + request.setOnSuccessCallback([](const std::string &header, + const std::string &) { ASSERT_EQ(HttpClient::getHttpReturnCode(header), 200); }); - request.setOnErrorCallback([this](const std::string &header, - const std::string &data) { + request.setOnErrorCallback([](const std::string &, + const std::string &) { FAIL(); }); m_httpClient->setAuthorization( @@ -152,12 +151,12 @@ namespace TUS::Test::Http { TEST_F(HttpClientParameterizedTest, AuthorizedRequestFail) { Request request("http://localhost:3000/auth/files", "", HttpMethod::_POST); - request.setOnSuccessCallback([this](const std::string &header, - const std::string &data) { + request.setOnSuccessCallback([](const std::string &, + const std::string &) { FAIL(); }); - request.setOnErrorCallback([this](const std::string &header, - const std::string &data) { + request.setOnErrorCallback([](const std::string &header, + const std::string &) { ASSERT_EQ(HttpClient::getHttpReturnCode(header), 401); }); m_httpClient->setAuthorization("wrongToken"); diff --git a/test/main.cpp b/lib/tusclient/test/main.cpp similarity index 100% rename from test/main.cpp rename to lib/tusclient/test/main.cpp diff --git a/test/repository/CacheRepositoryTest.cpp b/lib/tusclient/test/repository/CacheRepositoryTest.cpp similarity index 80% rename from test/repository/CacheRepositoryTest.cpp rename to lib/tusclient/test/repository/CacheRepositoryTest.cpp index 6ddf783..259776c 100644 --- a/test/repository/CacheRepositoryTest.cpp +++ b/lib/tusclient/test/repository/CacheRepositoryTest.cpp @@ -18,7 +18,7 @@ using TUS::Cache::CacheRepository; using TUS::Cache::TUSFile; class CacheRepositoryTest : public ::testing::Test { -protected: +public: void SetUp() override { // Set up code here. //create test.txt file @@ -26,7 +26,7 @@ class CacheRepositoryTest : public ::testing::Test { file << "test-app"; file.close(); - cacheRepository = std::make_unique("test-app", true); + cacheRepository = CacheRepository::create("test-app", true); cacheRepository->clearCache(); } @@ -36,12 +36,12 @@ class CacheRepositoryTest : public ::testing::Test { } const std::filesystem::path m_filePath = std::filesystem::current_path() / "test.txt"; - std::unique_ptr cacheRepository; + std::shared_ptr cacheRepository; const boost::uuids::uuid m_uuid = boost::uuids::random_generator()(); }; TEST_F(CacheRepositoryTest, add) { - std::shared_ptr file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", + auto file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", m_uuid, "1234567890e39484"); cacheRepository->add(file); auto result = cacheRepository->findByHash(file->getIdentificationHash()); @@ -49,7 +49,7 @@ TEST_F(CacheRepositoryTest, add) { } TEST_F(CacheRepositoryTest, remove) { - std::shared_ptr file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", + auto file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", m_uuid, "1234567890e39484"); cacheRepository->add(file); cacheRepository->remove(file); @@ -58,7 +58,7 @@ TEST_F(CacheRepositoryTest, remove) { } TEST_F(CacheRepositoryTest, findByHash) { - std::shared_ptr file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", + auto file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", m_uuid, "1234567890e39484"); cacheRepository->add(file); auto result = cacheRepository->findByHash(file->getIdentificationHash()); @@ -66,7 +66,7 @@ TEST_F(CacheRepositoryTest, findByHash) { } TEST_F(CacheRepositoryTest, findAll) { - std::shared_ptr file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", + auto file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", m_uuid, "1234567890e39484"); cacheRepository->add(file); auto result = cacheRepository->findAll(); @@ -75,7 +75,7 @@ TEST_F(CacheRepositoryTest, findAll) { } TEST_F(CacheRepositoryTest, saveAndOpen) { - std::shared_ptr file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", + auto file = std::make_shared(m_filePath, "http://localhost:1080/upload", "test-app", m_uuid, "1234567890e39484"); EXPECT_EQ(file->getAppName(), "test-app"); EXPECT_EQ(file->getFilePath(), m_filePath); diff --git a/test/verifiers/FileVerifiersTest.cpp b/lib/tusclient/test/verifiers/FileVerifiersTest.cpp similarity index 80% rename from test/verifiers/FileVerifiersTest.cpp rename to lib/tusclient/test/verifiers/FileVerifiersTest.cpp index 7dcfedc..79a7583 100644 --- a/test/verifiers/FileVerifiersTest.cpp +++ b/lib/tusclient/test/verifiers/FileVerifiersTest.cpp @@ -17,11 +17,8 @@ class FileVerifierTest : public ::testing::TestWithParam< }; TEST_P(FileVerifierTest, VerifyHash) { - auto param = GetParam(); - auto verifier = std::get<0>(param); - std::vector buffer = std::get<1>(param); - std::string expected_hash = std::get<2>(param); - std::string hash = verifier->hash(buffer); + const auto& [verifier, buffer, expected_hash] = GetParam(); + const std::string hash = verifier->hash(buffer); ASSERT_EQ(hash, expected_hash); ASSERT_TRUE(verifier->verify(buffer, hash)); } diff --git a/portfile.cmake b/portfile.cmake deleted file mode 100644 index 084dd01..0000000 --- a/portfile.cmake +++ /dev/null @@ -1,17 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO Cadons/libtusclient - REF 1.0.1 - SHA512 dbe0aeb64eee28aa8fe3f0acedcbfe09d3424ba79ed25168a8919c1ff43b262ecac0410fb6be2a23fec5769bdb6f0b2f44d996545bd2ab1d0f129b234e5549c7 -) - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - OPTIONS - -DBUILD_TEST=OFF -) - -vcpkg_cmake_install() - -vcpkg_cmake_config_fixup() - diff --git a/project.json b/project.json new file mode 100644 index 0000000..212341f --- /dev/null +++ b/project.json @@ -0,0 +1,7 @@ +{ + "name": "tusclient", + "organization": "Cadons", + "package_manager": "vcpkg", + "version": "1.0.1", + "subtrees": [] +} \ No newline at end of file diff --git a/scripts/install_for_mac.sh b/scripts/install_for_mac.sh deleted file mode 100644 index 957da20..0000000 --- a/scripts/install_for_mac.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/zsh -#run this script to install the necessary packages for mac -brew install cmake autoconf automake libtool pkg-config m4 \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 1fc8581..0000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required(VERSION 3.12) - -message(STATUS "${PROJECT_NAME} Unit Test") - -# Recursively find all .cpp and .h files in the current directory -file(GLOB_RECURSE TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.cpp" "${CMAKE_CURRENT_LIST_DIR}/*.h") -#find_package here-------------------- - -find_package(GTest REQUIRED)#GoogleTest package inclusion -find_package(libzippp REQUIRED) -#remove_definitions(-DBUILD_SHARED)#this is IMPORTANT if you are exporting symbols with macro in particular if you have QObjects -#------------------------------------- -#linking here-------------------- -#set(TEST_LIBRARY_LINKING "")#comment this if you are using GoogleTest -set(TEST_LIBRARY_LINKING GTest::GTest GTest::gmock libzippp::libzippp)#for GoogleTest -#------------------------------------- -# Create the executable for the tests -add_executable(${PROJECT_LIB}_test ${TEST_SOURCES}) - -# Link the libraries -target_link_libraries(${PROJECT_LIB}_test PRIVATE ${PROJECT_LIB} ${TEST_LIBRARY_LINKING}) - -# Include directories for the project (if needed) -target_include_directories(${PROJECT_LIB}_test PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - -#Include GoogleTest and setup tests - -#GoogleTest configuration -include(GoogleTest) -gtest_discover_tests(${PROJECT_LIB}_test) - -#Configuration for other test, this should need if you are using gtest -#add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}_test) - -#other options here-------------------- diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/cli/unix/create_source_file.sh b/tools/cli/unix/create_source_file.sh new file mode 100644 index 0000000..624fd55 --- /dev/null +++ b/tools/cli/unix/create_source_file.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +echo "Welcome to Quick Operations Script" +echo "--------------------------------" +#move working dir to root/tools +cd "$(dirname "$0")/../.." +# Display template options +echo "Available template types:" +echo "1) class - Standard C++ Class" +echo "2) gtest - Google Test" +echo "3) gtest_fixture - Google Test with Fixture" +echo "4) gtest_parametrized - Parametrized Google Test" +echo "5) qrc - Qt Resource File" + +# Get template selection +while true; do + read -p "Select template type (1-5): " template_number + case $template_number in + 1) template_type="class" ; break ;; + 2) template_type="gtest" ; break ;; + 3) template_type="gtest_fixture" ; break ;; + 4) template_type="gtest_parametrized" ; break ;; + 5) template_type="qrc" ; break ;; + *) echo "Invalid selection. Please choose a number between 1 and 5." ;; + esac +done + +# Request remaining inputs +read -p "Enter name for the new file/class: " name +read -p "Enter namespace (press Enter for none): " namespace +read -p "Enter output directory (press Enter for 'src'): " output_dir + +# Set default output directory if empty +if [ -z "$output_dir" ]; then + output_dir="src" +fi + +# Construct the command +cmd="python dev_tools.py source create" +cmd="$cmd --type '$template_type'" +cmd="$cmd --name '$name'" + +if [ ! -z "$namespace" ]; then + cmd="$cmd --namespace '$namespace'" +fi + +cmd="$cmd --output-dir '$output_dir'" + +# Execute the command +echo -e "\nExecuting: $cmd" +eval $cmd \ No newline at end of file diff --git a/tools/cli/unix/fast_sources.sh b/tools/cli/unix/fast_sources.sh new file mode 100644 index 0000000..d0f520f --- /dev/null +++ b/tools/cli/unix/fast_sources.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +echo "Welcome to Quick Operations Script" +echo "----------------------------------" + +# Go to project root (2 levels up from script dir) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR/../.." || exit 1 +PYTHON_CMD="$(command -v python3 || command -v python)" +if ! PYTHON_CMD="$(command -v python3 || command -v python)"; then + echo "Error: Python not found in PATH" + exit 1 +fi +# Select operation +echo "Select an operation:" +echo "1) Create new source/template" +echo "2) Update existing sources" + +while true; do + read -rp "Choose operation (1-2): " operation_number + case "$operation_number" in + 1) operation="create"; break ;; + 2) operation="update"; break ;; + *) echo "Invalid selection. Please choose 1 or 2." ;; + esac +done + +# ---- CREATE ---- +if [[ "$operation" == "create" ]]; then + echo + echo "Available template types:" + echo "1) class - Standard C++ Class" + echo "2) gtest - Google Test" + echo "3) gtest_fixture - Google Test with Fixture" + echo "4) gtest_parametrized - Parametrized Google Test" + echo "5) qrc - Qt Resource File" + + while true; do + read -rp "Select template type (1-5): " template_number + case "$template_number" in + 1) template_type="class"; break ;; + 2) template_type="gtest"; break ;; + 3) template_type="gtest_fixture"; break ;; + 4) template_type="gtest_parametrized"; break ;; + 5) template_type="qrc"; break ;; + *) echo "Invalid selection. Please choose a number between 1 and 5." ;; + esac + done + + # Always ask for name + read -rp "Enter name: " name + + # Start command + cmd=("$PYTHON_CMD" dev_tools.py source create --type "$template_type" --name "$name") + + # Add optional args only for non-qrc + if [[ "$template_type" != "qrc" ]]; then + read -rp "Enter namespace (press Enter for none): " namespace + + + [[ -n "$namespace" ]] && cmd+=("--namespace" "$namespace") + + fi + read -rp "Enter output directory (press Enter for 'src'): " output_dir + + # Default output directory + if [[ -z "$output_dir" ]]; then + output_dir="$SCRIPT_DIR/../../../src" + fi + cmd+=("--output-dir" "$output_dir") +# ---- UPDATE ---- +elif [[ "$operation" == "update" ]]; then + read -rp "Enter directory path to update sources: " update_path + cmd=("$PYTHON_CMD" dev_tools.py update sync "$update_path") +fi + +# ---- EXECUTE ---- +echo +echo "Executing: ${cmd[*]}" +"${cmd[@]}" diff --git a/tools/cli/unix/update_sources.sh b/tools/cli/unix/update_sources.sh new file mode 100644 index 0000000..91428e6 --- /dev/null +++ b/tools/cli/unix/update_sources.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +echo "Welcome to Module Sync Script" +echo "----------------------------" + +read -p "Enter directory path: " path +#move working dir to root/tools +cd "$(dirname "$0")/../.." +cmd="python dev_tools.py update sync \"$path\"" + + + +# Execute the command if option 1 or 2 was selected +if [ "$option_number" != "3" ]; then + echo -e "\nExecuting: $cmd" + eval $cmd +fi \ No newline at end of file diff --git a/tools/cli/windows/create_source_file.bat b/tools/cli/windows/create_source_file.bat new file mode 100644 index 0000000..f1b077a --- /dev/null +++ b/tools/cli/windows/create_source_file.bat @@ -0,0 +1,58 @@ +@echo off +cd /d "%~dp0\..\.." +echo Welcome to Quick Operations Script +echo -------------------------------- + +:: Display template options +echo Available template types: +echo 1) class - Standard C++ Class +echo 2) gtest - Google Test +echo 3) gtest_fixture - Google Test with Fixture +echo 4) gtest_parametrized - Parametrized Google Test +echo 5) qrc - Qt Resource File + +:: Get template selection +:template_selection +set /p template_number="Select template type (1-5): " + +:: Set template type based on selection +if "%template_number%"=="1" ( + set template_type=class +) else if "%template_number%"=="2" ( + set template_type=gtest +) else if "%template_number%"=="3" ( + set template_type=gtest_fixture +) else if "%template_number%"=="4" ( + set template_type=gtest_parametrized +) else if "%template_number%"=="5" ( + set template_type=qrc +) else ( + echo Invalid selection. Please choose a number between 1 and 5. + goto template_selection +) + +:: Request remaining inputs +set /p name="Enter name for the new file/class: " +set /p namespace="Enter namespace (press Enter for none): " +set /p output_dir="Enter output directory (press Enter for 'src'): " + +:: Set default output directory if empty +if "%output_dir%"=="" set output_dir=src + +:: Construct the command +set cmd=python ../../dev_tools.py source create +set cmd=%cmd% --type "%template_type%" +set cmd=%cmd% --name "%name%" + +if not "%namespace%"=="" ( + set cmd=%cmd% --namespace "%namespace%" +) + +set cmd=%cmd% --output-dir "%output_dir%" + +:: Execute the command +echo. +echo Executing: %cmd% +%cmd% + +pause \ No newline at end of file diff --git a/tools/cli/windows/fast_sources.bat b/tools/cli/windows/fast_sources.bat new file mode 100644 index 0000000..4774a62 --- /dev/null +++ b/tools/cli/windows/fast_sources.bat @@ -0,0 +1,89 @@ +@echo off +cd /d "%~dp0\..\.." +echo Welcome to Quick Operations Script +echo -------------------------------- + +:: Display operation options +echo Select an operation: +echo 1) Create new source/template +echo 2) Update existing sources + +:operation_selection +set /p operation_number="Choose operation (1-2): " + +if "%operation_number%"=="1" ( + goto create_source +) else if "%operation_number%"=="2" ( + goto update_sources +) else ( + echo Invalid selection. Please choose 1 or 2. + goto operation_selection +) + +:create_source +echo. +echo Available template types: +echo 1) class - Standard C++ Class +echo 2) gtest - Google Test +echo 3) gtest_fixture - Google Test with Fixture +echo 4) gtest_parametrized - Parametrized Google Test +echo 5) qrc - Qt Resource File + +:template_selection +set /p template_number="Select template type (1-5): " + +if "%template_number%"=="1" ( + set template_type=class +) else if "%template_number%"=="2" ( + set template_type=gtest +) else if "%template_number%"=="3" ( + set template_type=gtest_fixture +) else if "%template_number%"=="4" ( + set template_type=gtest_parametrized +) else if "%template_number%"=="5" ( + set template_type=qrc +) else ( + echo Invalid selection. Please choose a number between 1 and 5. + goto template_selection +) + +:: Request remaining inputs +if "%template_type%" NEQ "qrc" ( +set /p name="Enter name for the new file/class: " +set /p namespace="Enter namespace (press Enter for none): " +) else ( +set /p name="Enter name for the new Qt Resource File: " +) +set /p output_dir="Enter output directory (press Enter for 'src'): " + +:: Set default output directory if empty +if "%output_dir%"=="" set output_dir=%~dp0..\..\..\app + +:: Construct the create command +set cmd=python "%~dp0..\..\dev_tools.py" source create +set cmd=%cmd% --type "%template_type%" +set cmd=%cmd% --name "%name%" + +if not "%namespace%"=="" ( + set cmd=%cmd% --namespace "%namespace%" +) + +set cmd=%cmd% --output-dir "%output_dir%" + +goto execute_cmd + +:update_sources +echo. +set /p update_path="Enter directory path to update sources: " + +:: Construct the update command +set cmd=python "%~dp0..\..\dev_tools.py" update sync "%update_path%" + +goto execute_cmd + +:execute_cmd +echo. +echo Executing: %cmd% +%cmd% +cd .. +pause diff --git a/tools/cli/windows/update_sources.bat b/tools/cli/windows/update_sources.bat new file mode 100644 index 0000000..aa4f3a2 --- /dev/null +++ b/tools/cli/windows/update_sources.bat @@ -0,0 +1,18 @@ +@echo off +cd /d "%~dp0\..\.." +echo Welcome to Module Sync Script +echo ---------------------------- + +:menu + +set /p path="Enter directory path: " +set cmd=python ../../dev_tools.py update sync "%path%" +goto execute + + +:execute +echo. +echo Executing: %cmd% +%cmd% + +pause \ No newline at end of file diff --git a/tools/dev_tools.py b/tools/dev_tools.py new file mode 100644 index 0000000..3ffe242 --- /dev/null +++ b/tools/dev_tools.py @@ -0,0 +1,133 @@ +import os +import sys +import click +import importlib.util +from typing import Dict, Any, Optional, Tuple +from rich.console import Console +from rich.panel import Panel +from rich import box + +console = Console() + +# --- Tool configuration --- +TOOLS: Dict[str, Tuple[str, str]] = { + 'subtree': ('subtree_manager', 'cli'), + 'module': ('create_module', 'cli'), + 'source': ('create_source_file', 'cli'), + 'init': ('initialize_project', 'cli'), + 'update': ('update_files', 'cli'), + 'configure': ('configure_project', 'cli'), +} + +# --- Path setup --- +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +PROJECT_ROOT = os.path.dirname(CURRENT_DIR) + +# Centralized search paths +MODULE_SEARCH_PATHS = [ + CURRENT_DIR, + PROJECT_ROOT, + os.path.join(CURRENT_DIR, "src", "source_management"), + os.path.join(CURRENT_DIR, "src", "module_creation"), + os.path.join(CURRENT_DIR, "src", "project_management"), + os.path.join(CURRENT_DIR, "src", "subtree") +] + + +def setup_python_path(): + """Ensure all necessary paths are on sys.path.""" + for path in MODULE_SEARCH_PATHS: + if os.path.exists(path) and path not in sys.path: + sys.path.insert(0, path) + + +# --- Module loading utilities --- + +def import_module_from_file(module_name: str, file_path: str) -> Optional[Any]: + try: + spec = importlib.util.spec_from_file_location(module_name, file_path) + if not spec or not spec.loader: + raise ImportError(f"Could not create spec for {file_path}") + + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module + except Exception as e: + console.print(f"[bold red]Import Error:[/bold red] {e}") + return None + + +def find_tool_path(module_name: str) -> str: + """Search for the file path of a tool module.""" + for directory in MODULE_SEARCH_PATHS: + path = os.path.join(directory, f"{module_name}.py") + if os.path.exists(path): + return path + + raise FileNotFoundError(f"Module '{module_name}' not found in known locations.") + + +# --- CLI commands --- + +@click.group() +def cli(): + """Project management tools suite.""" + pass + + +@cli.command() +def info(): + """Show available tools.""" + print_welcome() + + +def print_welcome(): + """Display welcome message with tool list.""" + tool_list = "\n".join( + f"- [green]{name}[/green]: {module}" for name, (module, _) in TOOLS.items() + ) + console.print(Panel.fit( + f"[bold blue]Project Management Tools[/bold blue]\n\n" + f"Available tools:\n{tool_list}\n\n" + "Use --help with any command for more information", + title="Welcome", + border_style="blue", + box=box.ROUNDED + )) + + +def load_commands(): + """Dynamically import and register subcommands.""" + setup_python_path() + + for command_name, (module_name, cli_func_name) in TOOLS.items(): + try: + path = find_tool_path(module_name) + module = import_module_from_file(module_name, path) + if not module: + continue + + cli_func = getattr(module, cli_func_name, None) + if callable(cli_func): + cli.add_command(cli_func, command_name) + else: + console.print(f"[yellow]Warning:[/yellow] {module_name} has no function '{cli_func_name}'") + except Exception as e: + console.print(f"[yellow]Warning:[/yellow] Could not load tool '{command_name}': {e}") + + +# --- Entry point --- + +def main(): + try: + print_welcome() + load_commands() + cli() + except Exception as e: + console.print(f"[bold red]Fatal error:[/bold red] {e}") + sys.exit(1) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..79df1b7 --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,5 @@ +jinja2 +gitpython +click +semver +rich \ No newline at end of file diff --git a/tools/src/__init__.py b/tools/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/module_creation/__init__.py b/tools/src/module_creation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/module_creation/create_module.py b/tools/src/module_creation/create_module.py new file mode 100644 index 0000000..2805ac2 --- /dev/null +++ b/tools/src/module_creation/create_module.py @@ -0,0 +1,74 @@ +import os +import click +from rich.console import Console +from tools.src.module_creation import module_creator + +console = Console() +MODULE_TYPES = ["app", "lib"] + + +@click.group() +def cli(): + """Module creation commands.""" + pass + + +@cli.command() +@click.argument('name', type=str) +@click.option('--type', '-t', 'module_type', + type=click.Choice(MODULE_TYPES), + default='lib', + help='Type of module to create (app or lib)') +@click.option('--gtest/--no-gtest', + default=True, + help='Include Google Test support') +def create(name: str, module_type: str, gtest: bool): + """ + Create a new module with the specified name and type. + + NAME is the name of the module to create. + """ + try: + if not name: + console.print("[bold red]Module name is required.[/bold red]") + return + + module_creator_instance = module_creator.ModuleCreator(name, module_type, gtest) + + if not module_creator_instance.module_path: + console.print("[bold red]Module path is not set. Cannot create module.[/bold red]") + return + + if not module_creator_instance.create_module(): + return + + # Update CMakeLists.txt parent + cmake_file_path = os.path.join(module_creator_instance.module_path, "..", "CMakeLists.txt") + if os.path.exists(cmake_file_path): + with open(cmake_file_path, 'r') as cmake_file: + cmake_content = cmake_file.read() + if f"add_subdirectory({name})" not in cmake_content: + with open(cmake_file_path, 'a') as cmake_file: + cmake_file.write(f"\nadd_subdirectory({name})\n") + else: + with open(cmake_file_path, 'w') as cmake_file: + cmake_file.write(f"add_subdirectory({name})\n") + + console.print( + f"[green]Module '{name}' of type '{module_type}' created successfully at {module_creator_instance.module_path}[/green]") + + except Exception as e: + console.print(f"[bold red]Error creating module: {str(e)}[/bold red]") + raise click.Abort() + + +@cli.command() +def list_types(): + """List available module types.""" + console.print("\n[bold blue]Available module types:[/bold blue]") + for type_name in MODULE_TYPES: + console.print(f" - {type_name}") + + +if __name__ == "__main__": + cli() \ No newline at end of file diff --git a/tools/src/module_creation/module_creator.py b/tools/src/module_creation/module_creator.py new file mode 100644 index 0000000..bf76775 --- /dev/null +++ b/tools/src/module_creation/module_creator.py @@ -0,0 +1,177 @@ +import os +import json +from jinja2 import Environment, FileSystemLoader +from rich import print +from tools.src.source_management.file_synchronizer import FileSynchronizer + + +class ModuleCreator: + def __init__(self, module_name: str, module_type: str = "app", use_gtest: bool = True): + self.module_name = module_name + self.module_type = module_type + self.project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) + + self.module_path = os.path.join(self.project_root, self.module_type, self.module_name) + if self.module_type == "app": + self.module_path = os.path.join(self.project_root, "app") + + self.templates_path = os.path.join(self.project_root, ".resources", "templates") + self.file_synchronizer = FileSynchronizer(self.module_path) + self.gtest = use_gtest + + def _get_folder_structure(self): + if self.module_type == "lib": + return { + "src": ["src", self.module_name], + "include": ["include", self.module_name], + "test": ["test"], + "docs": ["docs"], + ".cmake": [".cmake"], + "resources": ["resources"] + } + else: + return { + "src": ["src"], + "include": ["include"], + "test": ["test"], + "docs": ["docs"], + ".cmake": [".cmake"], + "resources": ["resources"] + } + + def _create_module_json(self): + module_json = { + "name": self.module_name, + "version": "0.1.0", + "type": self.module_type, + "description": f"{self.module_name} module of type {self.module_type}" + } + module_json_path = os.path.join(self.module_path, "package.json") + with open(module_json_path, 'w') as f: + json.dump(module_json, f, indent=2) + print(f"[bold green]Module configuration saved to[/bold green] {module_json_path}") + + def _get_project_data(self): + project_json_path = os.path.join(self.project_root, "project.json") + if not os.path.exists(project_json_path): + print(f"[bold red]Project configuration file not found at {project_json_path}.[/bold red]") + return {} + try: + with open(project_json_path, 'r') as f: + return json.load(f) + except json.JSONDecodeError: + print("[bold red]Failed to parse project.json.[/bold red]") + return {} + + def _create_module_structure(self): + for folder_paths in self._get_folder_structure().values(): + folder_path = os.path.join(self.module_path, *folder_paths) + os.makedirs(folder_path, exist_ok=True) + + def _create_files(self): + project_info = self._get_project_data() + files_config = { + "README.md": { + "template": "README_template.jinja", + "params": { + "project_name": self.module_name, + "module_type": self.module_type + } + }, + "LICENSE": { + "template": "LICENSE_template.jinja", + "params": { + "project_name": self.module_name, + "organization": project_info.get("organization", "Unknown Organization"), + "contact_email": project_info.get("contact_email") + } + }, + "include/module.h": { + "template": "c++/module_template.h.jinja", + "params": {"target_name": self.module_name} + }, + "CMakeLists.txt": { + "template": f"cmake/{self.module_type}_CMakeLists.txt.jinja", + "params": { + "target_name": self.module_name, + "sources": f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_SOURCES", + "headers": f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_HEADERS", + "resources": f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_RESOURCES" + } + }, + "src/main.cpp": { + "template": "c++/main_template.cpp.jinja", + "params": { + "target_name": self.module_name, + "app": self.module_type == "app" + } + }, + "test/CMakeLists.txt": { + "template": "cmake/test_CMakeLists.txt.jinja", + "params": { + "test_target": self.module_name+"_test", + "use_gtest": self.gtest, + "sources": f"{self.module_name.upper().replace('-', '_')}_TEST_SOURCES", + + } + }, + "test/test_main.cpp": { + "template": "c++/test_main_template.cpp.jinja", + "params": { + "target_name": self.module_name, + "use_gtest": self.gtest + } + }, + "docs/Doxyfile":{ + "template": "docs/doxyfile_template.jinja", + "params": { + "project_name": self.module_name, + } + } + + } + + for rel_path, config in files_config.items(): + self.create_file_from_template( + template_name=config["template"], + output_path=os.path.join(self.module_path, rel_path), + **config["params"] + ) + + def create_file_from_template(self, template_name: str, output_path: str, **kwargs): + template_path = os.path.join(self.templates_path, template_name) + if not os.path.exists(template_path): + print(f"[bold red]Template not found: {template_path}[/bold red]") + return + + env = Environment(loader=FileSystemLoader(self.templates_path), + + trim_blocks=True, + lstrip_blocks=True + ) + template = env.get_template(template_name) + content = template.render(**kwargs) + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, 'w') as f: + f.write(content) + print(f"[bold green]Created[/bold green] {os.path.relpath(output_path, self.project_root)}") + + def create_module(self) -> bool: + # Check if module already exists + if os.path.exists(self.module_path): + print(f"[bold red]Module '{self.module_name}' already exists at {self.module_path}[/bold red]") + return False + + try: + # Create module structure and files + self._create_module_structure() + self._create_module_json() + self._create_files() + self.file_synchronizer.synchronize() + + print(f"[bold green]Successfully created module '{self.module_name}' at {self.module_path}[/bold green]") + return True + except Exception as e: + print(f"[bold red]Failed to create module: {str(e)}[/bold red]") + return False \ No newline at end of file diff --git a/tools/src/package_managers_initializers/__init__.py b/tools/src/package_managers_initializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/package_managers_initializers/package_manager.py b/tools/src/package_managers_initializers/package_manager.py new file mode 100644 index 0000000..54a4f52 --- /dev/null +++ b/tools/src/package_managers_initializers/package_manager.py @@ -0,0 +1,168 @@ +import os +import re +import subprocess +from abc import ABC, abstractmethod + +from rich.console import Console +from rich.prompt import IntPrompt + +console = Console() + +class PackageManager(ABC): + """ + Base class for package managers. + """ + + def __init__(self, name: str): + self.name = name + + + @abstractmethod + def initialize(self): + pass + + @abstractmethod + def configure_cmake_presets(self): + pass + @abstractmethod + def get_toolchain_file(self): + """ + Returns the path to the toolchain file for this package manager. + """ + pass + @abstractmethod + def get_parent_preset(self): + """ + Returns the name of the parent preset for this package manager. + """ + pass + + @staticmethod + def list_generators(): + """ + Get list of available CMake generators, excluding specific IDE generators. + + Returns: + list: List of available CMake generator names + """ + output = PackageManager._get_cmake_help_output() + if not output: + return [] + + return PackageManager._parse_generators(output) + + @staticmethod + def _get_cmake_help_output(): + """ + Execute cmake --help command and return its output. + + Returns: + str: Command output or empty string on error + """ + try: + return subprocess.check_output( + ['cmake', '--help'], + universal_newlines=True + ) + except subprocess.CalledProcessError: + return "" + + @staticmethod + def _parse_generators(cmake_output): + """ + Parse cmake help output to extract generator names. + + Args: + cmake_output (str): Output from cmake --help command + + Returns: + list: List of generator names + """ + generators = [] + + # Find the generators section + lines = cmake_output.splitlines() + try: + generators_start = next( + i for i, line in enumerate(lines) + if "Generators" in line + ) + # Skip the header line + generators_start += 2 + except StopIteration: + return [] + #exclude IDE generators + # Parse generator entries + for line in lines[generators_start:]: + if not line.strip(): + break + + match = re.match(r'^\s*(\S.+?)\s+=', line) + if not match: + continue + generator = match.group(1).replace("*", "").strip() + generators.append(generator) + + return generators + @staticmethod + def generate_cmake_user_presets(inherit_from="",toolchain_file=None): + generators = PackageManager.list_generators() + if not generators: + console.print("[bold red]No CMake generators found.[/bold red]") + return + + cmake_presets_json = { + "version": 3, + "configurePresets": [], + "buildPresets": [], + } + cpus = os.cpu_count() or 4 + + console.print("[bold green]Available CMake Generators:[/bold green]") + for idx, generator in enumerate(generators, 1): + console.print(f"[cyan]{idx}[/cyan]. {generator}") + + console.print("[yellow]Warning: Be sure that the selected generator is available on your machine[/yellow]") + + selected_preset = None + while selected_preset is None: + try: + selected_idx = IntPrompt.ask("Select generator number", default=1, show_default=False) + selected_preset = generators[selected_idx - 1] + except (ValueError, IndexError): + console.print("[red]Invalid selection. Please choose a valid number.[/red]") + + generator = selected_preset + generator_name = generator.replace(" ", "_").replace("-", "_") + cmake_presets_json["configurePresets"].append({ + "name": generator_name, + "hidden": selected_preset != generator, + "generator": generator, + "binaryDir": "${sourceDir}/build/${presetName}", + "inherits": inherit_from + }) + if toolchain_file: + cmake_presets_json["configurePresets"][-1]["cacheVariables"]={ + "CMAKE_TOOLCHAIN_FILE": toolchain_file + } + cmake_presets_json["buildPresets"].append({ + "name": f"{generator_name} Debug", + "hidden": selected_preset != generator, + "configurePreset": generator_name, + "configuration": "Debug", + "jobs": cpus, + }) + cmake_presets_json["buildPresets"].append({ + "name": f"{generator_name} Release", + "hidden": selected_preset != generator, + "configurePreset": generator_name, + "configuration": "Release", + "jobs": cpus, + }) + + cmake_user_presets_path = os.path.join(__file__,"..","..","..","..", "CMakeUserPresets.json") + import json + with open(cmake_user_presets_path, 'w') as f: + json.dump(cmake_presets_json, f, indent=2) + + console.print(f"[bold green]CMake user presets generated at[/bold green] {cmake_user_presets_path}") diff --git a/tools/src/package_managers_initializers/vcpkg_initializer.py b/tools/src/package_managers_initializers/vcpkg_initializer.py new file mode 100644 index 0000000..17a47de --- /dev/null +++ b/tools/src/package_managers_initializers/vcpkg_initializer.py @@ -0,0 +1,112 @@ +from .package_manager import PackageManager +import os +import json +from rich.console import Console +from rich.prompt import Confirm + +console = Console() + +class VCPKGInitializer(PackageManager): + """ + VCPKG package manager initializer. + """ + VCPKG_CONFIG_FILE = "vcpkg.json" + CMAKE_PRESETS_FILE = "CMakePresets.json" + + def __init__(self, project_name: str = "my_project"): + super().__init__("vcpkg") + self.project_name = project_name.lower().replace(" ", "_") + + self.vcpkg_location = "${VCPKG_ROOT}" + if os.name != 'nt': + vcpkg_path = os.popen("which vcpkg").read().strip() + if vcpkg_path: + self.find_vcpkg_root(vcpkg_path) + else: + console.print( + "[bold red]VCPKG not found. Please ensure vcpkg is installed and configured correctly.[/bold red]") + exit(1) + else: + vcpkg_path = os.popen("where vcpkg").read().strip() + if vcpkg_path: + self.find_vcpkg_root(vcpkg_path) + else: + console.print( + "[bold red]VCPKG not found. Please ensure vcpkg is installed and configured correctly.[/bold red]") + exit(1) + self.toolchain_file =f"{self.vcpkg_location}/scripts/buildsystems/vcpkg.cmake" + self.parent_preset = "vcpkg-default" + + def find_vcpkg_root(self, vcpkg_path): + if vcpkg_path: + vcpkg_dir = os.path.dirname(os.path.abspath(vcpkg_path)) + self.vcpkg_location = vcpkg_dir.replace("\\", "/") + return self.vcpkg_location + return None + def create_vcpkg_json(self): + return os.system(f"vcpkg new --application --name {self.project_name} --version 0.1.0") + + def initialize(self): + console.print("[bold green]Initializing VCPKG project[/bold green]") + if os.path.exists(self.VCPKG_CONFIG_FILE): + overwrite = Confirm.ask(f"[yellow]{self.VCPKG_CONFIG_FILE} already exists. Overwrite?[/yellow]") + if not overwrite: + console.print("[cyan]Skipping vcpkg.json creation.[/cyan]") + return + else: + ret = self.create_vcpkg_json() + if ret != 0: + console.print("[bold red]Failed to create vcpkg.json via 'vcpkg new'[/bold red]") + return + + vcpkg_json_path = os.path.abspath(self.VCPKG_CONFIG_FILE) + if os.path.exists(vcpkg_json_path): + with open(vcpkg_json_path, 'r') as file: + vcpkg_data = json.load(file) + + vcpkg_data.update({ + "name": self.project_name, + "version": "0.1.0", + "dependencies": ["gtest"] + }) + + with open(vcpkg_json_path, 'w') as file: + json.dump(vcpkg_data, file, indent=2) + console.print(f"[green]{self.VCPKG_CONFIG_FILE} updated successfully[/green]") + else: + console.print(f"[bold red]Warning:[/bold red] {self.VCPKG_CONFIG_FILE} not found at {vcpkg_json_path}") + + def configure_cmake_presets(self): + """ + Create generic CMake presets for vcpkg with toolchain file link. + """ + cmake_presets = { + "version": 3, + "configurePresets": [ + { + "name": "vcpkg-default", + "hidden": True, + "binaryDir": "${sourceDir}/build/" + } + ] + } + + cmake_presets_path = os.path.join(__file__,"..","..","..","..", self.CMAKE_PRESETS_FILE) + if not os.path.exists(cmake_presets_path): + with open(cmake_presets_path, 'w') as f: + json.dump(cmake_presets, f, indent=2) + console.print(f"[green]CMake presets created at {cmake_presets_path}[/green]") + else: + console.print(f"[cyan]CMake presets already exist at {cmake_presets_path}. Skipping creation.[/cyan]") + + self.generate_cmake_user_presets(inherit_from=self.parent_preset, toolchain_file=self.toolchain_file) + def get_toolchain_file(self): + """ + Returns the path to the vcpkg toolchain file. + """ + return self.toolchain_file + def get_parent_preset(self): + """ + Returns the parent preset for vcpkg. + """ + return self.parent_preset \ No newline at end of file diff --git a/tools/src/project_management/__init__.py b/tools/src/project_management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/project_management/configure_project.py b/tools/src/project_management/configure_project.py new file mode 100644 index 0000000..421e12d --- /dev/null +++ b/tools/src/project_management/configure_project.py @@ -0,0 +1,67 @@ +import os + +import click +from rich.console import Console + +from tools.src.package_managers_initializers.package_manager import PackageManager +from tools.src.package_managers_initializers.vcpkg_initializer import VCPKGInitializer +from tools.src.project_management.project_configuration import ProjectConfig + + +@click.group() +def cli(): + """Project configuration commands.""" + pass + +@cli.command() +def configure(): + """ + Configure the project using the specified package manager. + + PACKAGE_MANAGER_NAME is the name of the package manager to use. + """ + console = Console() + try: + #ask witch generator to use + console.print("[bold cyan]Configuring project with package manager...[/bold cyan]") + + + console.print("[bold cyan]Configuring CMake presets...[/bold cyan]") + project_configuration = ProjectConfig.load(os.path.join(__file__,"..","..","..","..", "project.json")) + package_manager=project_configuration.package_manager + print(f"[bold cyan]Using package manager: {package_manager}[/bold cyan]") + if not package_manager: + console.print("[bold red]No package manager configured in project.json.[/bold red]") + return + toolchain_path = "" + parent_preset="" + if package_manager == "vcpkg": + vcpkg_instance = VCPKGInitializer("") + toolchain_path=vcpkg_instance.get_toolchain_file() + parent_preset = vcpkg_instance.get_parent_preset() + else: + console.print(f"[bold yellow]Package manager '{package_manager}' is not supported yet.[/bold yellow]") + console.print("[yellow]Warning: Be sure that the selected generator is available on your machine[/yellow]") + + if len(toolchain_path)>0 and len(parent_preset)>0: + console.print(f"[bold green]Using toolchain file: {toolchain_path}[/bold green]") + PackageManager.generate_cmake_user_presets( + inherit_from=parent_preset, + toolchain_file=toolchain_path + ) + else: + PackageManager.generate_cmake_user_presets() + console.print("[bold green]CMake presets configured successfully.[/bold green]") + except FileNotFoundError: + console.print("[bold red]Project configuration file not found. Please run 'init' command first.[/bold red]") + raise click.Abort() +@cli.command() +def list_generators(): + """List available CMake generators.""" + console = Console() + generator_list = PackageManager.list_generators() + console.print("[bold cyan]Available CMake generators for this OS:[/bold cyan]") + for gen in generator_list: + console.print(f" - {gen}") +if __name__ == "__main__": + cli() diff --git a/tools/src/project_management/initialize_project.py b/tools/src/project_management/initialize_project.py new file mode 100644 index 0000000..eebedeb --- /dev/null +++ b/tools/src/project_management/initialize_project.py @@ -0,0 +1,139 @@ + +import os +import click +from rich import print +from rich.prompt import Confirm +from jinja2 import Environment, FileSystemLoader + +from tools.src.module_creation.module_creator import ModuleCreator +from tools.src.package_managers_initializers.package_manager import PackageManager +from project_configuration import ProjectConfig + +PROJECT_FILE_NAME = "project.json" + +def get_package_manager_initializer(package_manager: str, **kwargs): + if package_manager == "vcpkg": + from tools.src.package_managers_initializers.vcpkg_initializer import VCPKGInitializer + return VCPKGInitializer(**kwargs) + else: + raise NotImplementedError(f"[red]Package manager '{package_manager}' is not implemented yet.[/red]") + +def generate_cmakelists(cmake_template_path, output_path, project_config: ProjectConfig): + if not os.path.exists(cmake_template_path): + print(f"[red]CMake template not found at {cmake_template_path}. Please check the template path.[/red]") + raise click.Abort() + + env = Environment(loader=FileSystemLoader(os.path.dirname(cmake_template_path))) + template = env.get_template(os.path.basename(cmake_template_path)) + + cmake_content = template.render( + project_name=project_config.name, + project_version=project_config.version, + project_description="A monorepo project", + org_name=project_config.organization, + org_url="https://example.com", + cpp_standard="23", + prefer_ninja=True, + subdirs=["app", "lib"] + ) + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, 'w') as cmake_file: + cmake_file.write(cmake_content) + + print(f"[bold green]CMakeLists.txt created at[/bold green] {output_path}") + +@click.group() +def cli(): + """Project initialization commands.""" + pass + +@cli.command() +@click.option('--name', prompt='Project name', help='Name of the project') +@click.option('--org', prompt='Organization name', default='default_org', help='Organization name') +@click.option('--package-manager', type=click.Choice(['vcpkg', 'custom', 'conan']), + default='vcpkg', prompt='Package manager to use', help='Package manager for dependencies') +@click.option('--force', is_flag=True, help='Force re-initialization even if project.json exists') +def init(name: str, org: str, package_manager: str, force: bool): + """Initialize a new project.""" + try: + root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) + os.chdir(root_dir) + project_json_path = os.path.join(os.getcwd(), PROJECT_FILE_NAME) + + if os.path.exists(project_json_path) and not force: + print("[yellow]Project already initialized. Use --force to reinitialize.[/yellow]") + return + + if force and os.path.exists(PROJECT_FILE_NAME): + if not Confirm.ask("Existing project.json found. Do you want to overwrite it?"): + print("[red]Operation cancelled by user.[/red]") + return + os.remove(PROJECT_FILE_NAME) + print("[green]Existing project.json removed.[/green]") + + project_config = ProjectConfig( + name=name, + organization=org, + package_manager=package_manager, + version="0.1.0", + subtrees=[] + ) + + print("[cyan]Initializing project...[/cyan]") + initializer = get_package_manager_initializer(package_manager, project_name=name) + initializer.initialize() + + # Save project configuration + project_config.save(project_json_path) + print(f"[bold green]Project configuration saved to[/bold green] {project_json_path}") + + # Generate CMakeLists.txt + cmake_template_path = os.path.join(os.path.dirname(__file__), "..","..","..", ".resources", "templates", "cmake", "CMakeLists.txt.jinja") + cmake_output_path = os.path.join(os.getcwd(), "CMakeLists.txt") + generate_cmakelists(cmake_template_path, cmake_output_path, project_config) + + initializer.configure_cmake_presets() + print("[bold green]Project initialized successfully.[/bold green]") + + #create app folder structure + app_dir = os.path.join(os.getcwd(), "app") + module_creator = ModuleCreator("app", "app", True) + module_creator.create_module() + #create lib folder structure + lib_dir = os.path.join(os.getcwd(), "lib") + #create .gitkeep in lib + os.makedirs(lib_dir, exist_ok=True) + #create CMakeLists.txt + cmake_lib_path = os.path.join(lib_dir, "CMakeLists.txt") + if not os.path.exists(cmake_lib_path): + with open(cmake_lib_path, 'w') as f: + f.write("# CMakeLists.txt for lib\n") + + except Exception as e: + print(f"[bold red]Error initializing project: {str(e)}[/bold red]") + raise click.Abort() + +@cli.command() +def configure(): + """Configure CMake presets for the project.""" + try: + project_json_path = os.path.join(os.getcwd(), PROJECT_FILE_NAME) + project_config = ProjectConfig.load(project_json_path) + initializer = get_package_manager_initializer(project_config.package_manager) + initializer.configure_cmake_presets() + print("[bold green]CMake presets configured successfully.[/bold green]") + except FileNotFoundError: + print("[red]Project not initialized. Please run 'init' command first.[/red]") + raise click.Abort() + +@cli.command() +def list_generators(): + """List available CMake generators.""" + generator_list = PackageManager.list_generators() + print("[bold cyan]Available CMake generators for this OS:[/bold cyan]") + for gen in generator_list: + print(f" - {gen}") + +if __name__ == "__main__": + cli() \ No newline at end of file diff --git a/tools/src/project_management/project_configuration.py b/tools/src/project_management/project_configuration.py new file mode 100644 index 0000000..4888005 --- /dev/null +++ b/tools/src/project_management/project_configuration.py @@ -0,0 +1,102 @@ +from dataclasses import dataclass +from typing import List, Optional +import json +import os + +@dataclass +class SubtreeConfig: + name: str + git_url: str + main_branch: str + staging_branch: str + path: str + + @classmethod + def from_dict(cls, data: dict) -> 'SubtreeConfig': + return cls( + name=data['name'], + git_url=data['git_url'], + main_branch=data['main_branch'], + staging_branch=data['staging_branch'], + path=data['path'] + ) + + +@dataclass +class ProjectConfig: + name: str + organization: str + package_manager: str + version: str + subtrees: List[SubtreeConfig] + + @classmethod + def load(cls, file_path: str) -> 'ProjectConfig': + """Load configuration from a JSON file.""" + if not os.path.exists(file_path): + raise FileNotFoundError(f"Configuration file not found: {file_path}") + + with open(file_path, 'r') as f: + data = json.load(f) + + return cls( + name=data['name'], + organization=data['organization'], + package_manager=data['package_manager'], + version=data['version'], + subtrees=[SubtreeConfig.from_dict(st) for st in data.get('subtrees', [])] + ) + + def save(self, file_path: str) -> None: + """Save configuration to a JSON file.""" + data = { + 'name': self.name, + 'organization': self.organization, + 'package_manager': self.package_manager, + 'version': self.version, + 'subtrees': [ + { + 'name': st.name, + 'git_url': st.git_url, + 'main_branch': st.main_branch, + 'staging_branch': st.staging_branch, + 'path': st.path + } + for st in self.subtrees + ] + } + + with open(file_path, 'w') as f: + json.dump(data, f, indent=2) + + def add_subtree(self, subtree: SubtreeConfig) -> None: + """Add a new subtree configuration.""" + if any(st.name == subtree.name for st in self.subtrees): + raise ValueError(f"Subtree with name '{subtree.name}' already exists") + self.subtrees.append(subtree) + + def remove_subtree(self, name: str) -> Optional[SubtreeConfig]: + """Remove a subtree configuration by name.""" + for i, subtree in enumerate(self.subtrees): + if subtree.name == name: + return self.subtrees.pop(i) + return None + + def get_subtree(self, name: str) -> Optional[SubtreeConfig]: + """Get a subtree configuration by name.""" + for subtree in self.subtrees: + if subtree.name == name: + return subtree + return None + + def update_subtree(self, name: str, **kwargs) -> bool: + """Update a subtree configuration.""" + subtree = self.get_subtree(name) + if not subtree: + return False + + for key, value in kwargs.items(): + if hasattr(subtree, key): + setattr(subtree, key, value) + + return True diff --git a/tools/src/source_management/__init__.py b/tools/src/source_management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/source_management/create_source_file.py b/tools/src/source_management/create_source_file.py new file mode 100644 index 0000000..fab9430 --- /dev/null +++ b/tools/src/source_management/create_source_file.py @@ -0,0 +1,181 @@ +import os +import click +from rich.console import Console +from tools.src.module_creation.module_creator import ModuleCreator +from typing import Dict + +console = Console() + + +def to_pascal_case(text: str) -> str: + if not text: + return text + if '_' not in text and text[0].isupper(): + return text + components = text.split('_') + return ''.join(x.title() for x in components) + + +def compose_namespace_path(namespace: str, path: str = "src") -> str: + """Convert a namespace string into a directory path.""" + if not namespace: + return path + return os.path.join(path, *namespace.split('::')) + + +TEMPLATE_TYPES = { + "class": ("Standard C++ Class", "ClassTemplateHandler"), + "gtest": ("Google Test", "GTestTemplateHandler"), + "gtest_fixture": ("Google Test with Fixture", "GTestTemplateHandler"), + "gtest_parametrized": ("Parametrized Google Test", "GTestTemplateHandler"), + "qrc": ("Qt Resource File", "QrcTemplateHandler") +} + + +@click.group() +def cli(): + """Source file creation commands.""" + pass + + +@cli.command() +def list_templates(): + """List available template types.""" + console.print("\n[bold cyan]Available template types:[/bold cyan]") + for key, (desc, _) in TEMPLATE_TYPES.items(): + console.print(f" - {key}: {desc}") + + +@cli.command() +@click.option('--type', '-t', 'template_type', + type=click.Choice(list(TEMPLATE_TYPES.keys())), + required=True, + help='Type of template to use') +@click.option('--name', '-n', + required=True, + help='Name for the new file/class') +@click.option('--namespace', '-ns', + default="", + help='Namespace (for C++ files)') +@click.option('--output-dir', '-o', + default="src", + help='Output directory') +def create(template_type: str, name: str, namespace: str, output_dir: str): + """Create new source files from templates.""" + try: + creator = ModuleCreator(".", "lib", True) + + if template_type.startswith("gtest"): + create_test_files(template_type, name, namespace, output_dir, creator) + elif template_type == "class": + create_class_files(name, namespace, output_dir, creator) + elif template_type == "qrc": + create_qrc_file(name, output_dir, creator) + + try: + from tools.src.source_management.update_files import synchronize_module + console.print("\n[bold blue]Updating CMake files...[/bold blue]") + if synchronize_module(os.path.abspath(output_dir)): + console.print("[bold green]CMake files updated successfully![/bold green]") + else: + console.print("[bold yellow]Warning: Failed to update CMake files[/bold yellow]") + except ImportError: + console.print("[bold yellow]Warning: Could not import update_files.py[/bold yellow]") + except Exception as e: + console.print(f"[bold yellow]Warning: Failed to update CMake files: {str(e)}[/bold yellow]") + + except Exception as e: + console.print(f"[bold red]Error creating files: {str(e)}[/bold red]") + raise click.Abort() + + +def create_class_files(name: str, namespace: str, output_dir: str, creator: ModuleCreator): + """Create C++ class header and source files.""" + source_output_dir = os.path.join(output_dir, "src") + header_output_dir = os.path.join(output_dir, "include") + + if namespace: + header_output_dir = compose_namespace_path(namespace, header_output_dir) + source_output_dir = compose_namespace_path(namespace, source_output_dir) + os.makedirs(header_output_dir, exist_ok=True) + os.makedirs(source_output_dir, exist_ok=True) + + header_filename = f"{to_pascal_case(name)}.h" + template_params = { + "class_name": name, + "header_filename": header_filename, + "namespace": namespace + } + + console.print("\n[bold blue]Creating class files...[/bold blue]") + + # Create header file + header_path = os.path.join(header_output_dir, header_filename) + creator.create_file_from_template( + "c++/class_template.h.jinja", + header_path, + **template_params + ) + + # Create source file + source_path = os.path.join(source_output_dir, f"{to_pascal_case(name)}.cpp") + creator.create_file_from_template( + "c++/class_template.cpp.jinja", + source_path, + **template_params + ) + + +def create_test_files(template_type: str, name: str, namespace: str, output_dir: str, creator: ModuleCreator): + """Create test files based on template type.""" + test_output_dir = os.path.join(output_dir, "test") + if namespace: + test_output_dir = compose_namespace_path(namespace, test_output_dir) + os.makedirs(test_output_dir, exist_ok=True) + + test_params = { + "class_name": name, + "header_filename": f"{to_pascal_case(name)}.h", + "namespace": namespace, + "test_suite_name": f"{name}Test" + } + + if template_type == "gtest_fixture": + test_params.update({ + "param_test_fixture_name": f"{name}TestFixture", + "param_type": "int", + "param_suite_name": f"{name}TestSuite", + "param_values": ["1", "2", "3"] + }) + template_name = "gtest_fixture_template.cpp.jinja" + elif template_type == "gtest_parametrized": + template_name = "gtest_parametrized_template.cpp.jinja" + else: + template_name = "gtest_simple_template.cpp.jinja" + + console.print("\n[bold blue]Creating test files...[/bold blue]") + test_path = os.path.join(test_output_dir, f"{to_pascal_case(name)}Test.cpp") + creator.create_file_from_template( + f"gtest/{template_name}", + test_path, + **test_params + ) + + +def create_qrc_file(name: str, output_dir: str, creator: ModuleCreator): + """Create Qt resource file.""" + console.print("\n[bold blue]Creating QRC file...[/bold blue]") + if not os.path.exists(os.path.join(output_dir,"resources")): + os.makedirs(os.path.join(output_dir, "resources"), exist_ok=True) + + qrc_path = os.path.join(output_dir,"resources", f"{name.lower()}.qrc") + creator.create_file_from_template( + "qt/qrc_template.qrc.jinja", + qrc_path, + prefix=name, + create_examples=True + ) + + +if __name__ == "__main__": + cli() \ No newline at end of file diff --git a/tools/src/source_management/file_synchronizer.py b/tools/src/source_management/file_synchronizer.py new file mode 100644 index 0000000..c20b7c0 --- /dev/null +++ b/tools/src/source_management/file_synchronizer.py @@ -0,0 +1,95 @@ +import os +import glob + +class FileSynchronizer: + def __init__(self, module_path): + self.module_path= module_path + self.module_name = os.path.basename(module_path) + self.header_variable_name = f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_HEADERS" + self.source_variable_name = f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_SOURCES" + self.test_source_variable_name = f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_TEST_SOURCES" + self.resource_variable_name = f"{self.module_name.upper().replace('-', '_').replace(' ','_')}_RESOURCES" + self.header_files = [] + self.source_files = [] + self.resource_files = [] + self.header_file_extensions = ['.h', '.hpp'] + self.source_file_extensions = ['.cpp', '.c', '.cc', '.cxx', '.mm'] + self.test_source_files = ['.cpp', '.h', '.hpp'] + self.resource_file_extensions = ['.qrc'] + + def scan_folder(self, extensions, folder, relative_to_module=True): + output = [] + for ext in extensions: + output.extend(glob.glob(os.path.join(self.module_path, folder, '**', f'*{ext}'), recursive=True)) + if not output: + return [] + + base_path = self.module_path if relative_to_module else os.path.join(self.module_path, folder) + output = [os.path.relpath(file, base_path) for file in output] + output = [file.replace('\\', '/') for file in output] + result = list(set(output)) + result.sort() + return result + def synchronize(self): + """ + Synchronize header and source files in the module path. + """ + + # Remove duplicates + self.header_files = self.scan_folder(self.header_file_extensions, 'include/', + relative_to_module=True) + self.source_files = self.scan_folder(self.source_file_extensions, 'src/', + relative_to_module=True) + #remove main.cpp from source files if it exists + if 'src/main.cpp' in self.source_files: + self.source_files.remove('src/main.cpp') + self.test_source_files = self.scan_folder(self.test_source_files, 'test/', + relative_to_module=False) + self.resource_files = self.scan_folder(self.resource_file_extensions, 'resources/', + relative_to_module=True) + + #write cmake/module_name_sources.cmake + cmake_sources_path = os.path.join(self.module_path, '.cmake', f'{self.module_name}_sources.cmake') + os.makedirs(os.path.dirname(cmake_sources_path), exist_ok=True) + with open(cmake_sources_path, 'w') as f: + # Headers + f.write(f"set({self.header_variable_name}\n") + for header in self.header_files: + f.write(f" {header}\n") + f.write(")\n\n") + + # Sources + f.write(f"set({self.source_variable_name}\n") + for source in self.source_files: + f.write(f" {source}\n") + f.write(")\n\n") + + # Test sources + f.write(f"set({self.test_source_variable_name}\n") + for test_source in self.test_source_files: + f.write(f" {test_source}\n") + f.write(")\n\n") + + # Resources + f.write(f"set({self.resource_variable_name}\n") + for resource in self.resource_files: + f.write(f" {resource}\n") + f.write(")\n") + #create cmake/sources.cmake that includes all module sources + cmake_sources_include_path = os.path.join(self.module_path, '.cmake', 'sources.cmake') + #check if include is already present if missing append otherwise do nothing + include_str = f"include(${{CMAKE_CURRENT_LIST_DIR}}/{self.module_name}_sources.cmake)\n" + if os.path.exists(cmake_sources_include_path): + with open(cmake_sources_include_path, 'r') as f: + content = f.read() + if include_str not in content: + with open(cmake_sources_include_path, 'a') as f: + f.write(include_str) + else: + os.makedirs(os.path.dirname(cmake_sources_include_path), exist_ok=True) + with open(cmake_sources_include_path, 'w') as f: + f.write(include_str) + + print(f"[bold green]Synchronized files for module '{self.module_path}'[/bold green]") + + diff --git a/tools/src/source_management/update_files.py b/tools/src/source_management/update_files.py new file mode 100644 index 0000000..d6759b7 --- /dev/null +++ b/tools/src/source_management/update_files.py @@ -0,0 +1,101 @@ +import os +from typing import Optional +import click +from rich.console import Console +from tools.src.source_management.file_synchronizer import FileSynchronizer + +console = Console() + + +def detect_module_structure(module_path: str) -> Optional[str]: + """Detect the module name from the given path.""" + if not os.path.exists(module_path): + return None + return os.path.basename(os.path.normpath(module_path)) + + +def synchronize_module(module_path: str) -> bool: + """Synchronize the specified module's files.""" + if not os.path.exists(module_path): + console.print(f"[bold red]Module path {module_path} does not exist[/bold red]") + return False + + try: + synchronizer = FileSynchronizer(module_path) + synchronizer.synchronize() + console.print(f"[bold green]Successfully synchronized module at {module_path}[/bold green]") + return True + except Exception as e: + console.print(f"[bold red]Failed to synchronize module: {str(e)}[/bold red]") + return False + + +@click.group() +def cli(): + """Update and synchronize module files.""" + pass + + +@cli.command() +@click.argument('path', type=click.Path(exists=True), required=False) +def sync(path: Optional[str]): + """Synchronize a module's files. + + If PATH is not provided, will use current directory. + """ + try: + # Use provided path or current directory + module_path = os.path.abspath(path if path else os.getcwd()) + + # Detect and validate module + module_name = detect_module_structure(module_path) + if not module_name: + raise click.ClickException(f"Invalid module path: {module_path}") + + console.print(f"[bold cyan]Processing module:[/bold cyan] {module_name}") + + # Synchronize the module + if not synchronize_module(module_path): + raise click.ClickException("Synchronization failed") + + except click.ClickException as e: + console.print(f"[bold red]Error:[/bold red] {str(e)}") + raise + except Exception as e: + console.print(f"[bold red]Unexpected error:[/bold red] {str(e)}") + raise click.ClickException(str(e)) + + +@cli.command() +def list_modules(): + """List all available modules in the project.""" + try: + # Get project root (2 levels up from this file) + project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + # Check common module directories + module_dirs = ['app', 'lib'] + found_modules = [] + + for dir_name in module_dirs: + dir_path = os.path.join(project_root, dir_name) + if os.path.exists(dir_path): + modules = [d for d in os.listdir(dir_path) + if os.path.isdir(os.path.join(dir_path, d))] + for module in modules: + found_modules.append(f"{dir_name}/{module}") + + if found_modules: + console.print("[bold cyan]Available modules:[/bold cyan]") + for module in sorted(found_modules): + console.print(f" - {module}") + else: + console.print("[yellow]No modules found in the project[/yellow]") + + except Exception as e: + console.print(f"[bold red]Error listing modules:[/bold red] {str(e)}") + raise click.ClickException(str(e)) + + +if __name__ == '__main__': + cli() \ No newline at end of file diff --git a/tools/src/subtree/__init__.py b/tools/src/subtree/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/subtree/subtree_manager.py b/tools/src/subtree/subtree_manager.py new file mode 100644 index 0000000..b9c8c82 --- /dev/null +++ b/tools/src/subtree/subtree_manager.py @@ -0,0 +1,284 @@ +import os +import subprocess +from typing import Optional, List + +import click +from rich.console import Console +from rich.table import Table + +from tools.src.project_management.project_configuration import ProjectConfig, SubtreeConfig + +console = Console() + + +class SubtreeManager: + @staticmethod + def find_project_config() -> str: + """Find the project.json file in the parent directory of 'tools'.""" + current_dir = os.path.dirname(os.path.abspath(__file__)) + if os.path.basename(current_dir) == 'tools': + parent_dir = os.path.dirname(current_dir) + else: + parent_dir = current_dir + + + config_path = os.path.join(parent_dir,"..","..","..", 'project.json') + + if not os.path.exists(config_path): + raise FileNotFoundError(f"Cannot find project.json in {parent_dir}") + + return config_path + + def __init__(self): + + self.config_path = self.find_project_config() + self.config = ProjectConfig.load(self.config_path) + self.root_dir = os.path.dirname(self.config_path) + os.chdir(self.root_dir) + + + def _run_git_command(self, command: List[str], check: bool = True) -> subprocess.CompletedProcess: + """Execute a git command and return the result.""" + try: + result = subprocess.run( + ["git"] + command, + check=check, + capture_output=True, + text=True + ) + return result + except subprocess.CalledProcessError as e: + console.print(f"[bold red]Git command failed:[/bold red] {e.stderr}") + raise + + def add_subtree(self, subtree: SubtreeConfig) -> bool: + """Add a new subtree to the repository.""" + try: + if not self._handle_existing_path(subtree): + return False + + if not self._add_or_update_subtree(subtree): + return False + + self._update_configuration(subtree) + console.print(f"[green]Successfully added/updated subtree {subtree.name}[/green]") + return True + + except Exception as e: + return self._handle_error(e, subtree.name) + + def _handle_existing_path(self, subtree: SubtreeConfig) -> bool: + """Handle case when subtree path already exists.""" + if not os.path.exists(os.path.join(self.root_dir, subtree.path)): + return True + + console.print(f"[yellow]Warning: Path '{subtree.path}' already exists.[/yellow]") + if not click.confirm("Do you want to remove the existing directory and re-add the subtree?", default=False): + return False + + self._run_git_command(["rm", "-rf", subtree.path]) + self._run_git_command(["commit", "-m", f"Remove existing {subtree.path} before re-adding as subtree"]) + return True + + + + def _add_or_update_subtree(self, subtree: SubtreeConfig) -> bool: + """Add new subtree or update existing one.""" + try: + self._run_git_command([ + "subtree", "add", + "--prefix", subtree.path, + "--squash", + subtree.name, subtree.main_branch + ]) + return True + except subprocess.CalledProcessError as e: + return self._handle_existing_prefix(e, subtree) + + def _handle_existing_prefix(self, error: subprocess.CalledProcessError, subtree: SubtreeConfig) -> bool: + """Handle case when subtree prefix already exists.""" + if "prefix already exists" not in error.stderr: + raise error + + if not click.confirm("Prefix already exists. Try to pull updates instead?", default=True): + return False + + self._run_git_command([ + "subtree", "pull", + "--prefix", subtree.path, + "--squash", + subtree.name, subtree.main_branch + ]) + return True + + def _update_configuration(self, subtree: SubtreeConfig) -> None: + """Update and save configuration with new subtree.""" + self.config.add_subtree(subtree) + self.config.save(self.config_path) + + def _handle_error(self, error: Exception, subtree_name: str) -> bool: + """Handle errors during subtree operations.""" + if isinstance(error, subprocess.CalledProcessError): + if "already exists" in error.stderr: + console.print(f"[yellow]Remote {subtree_name} already exists.[/yellow]") + elif "working tree" in error.stderr: + console.print("[bold red]Error:[/bold red] Must be run from the root of the git repository") + else: + console.print(f"[bold red]Git command failed:[/bold red] {error.stderr}") + else: + console.print(f"[bold red]Failed to add subtree:[/bold red] {str(error)}") + return False + + def remove_subtree(self, name: str) -> bool: + """Remove a subtree from the repository.""" + subtree = self.config.get_subtree(name) + if not subtree: + console.print(f"[yellow]No subtree found with name: {name}[/yellow]") + return False + + try: + # Remove the subtree files + self._run_git_command(["rm", "-r", subtree.path]) + + # Remove the remote + self._run_git_command(["remote", "remove", name]) + + # Update configuration + self.config.remove_subtree(name) + self.config.save(self.config_path) + + console.print(f"[green]Successfully removed subtree {name}[/green]") + return True + + except Exception as e: + console.print(f"[bold red]Failed to remove subtree:[/bold red] {str(e)}") + return False + + def pull_subtree(self, name: str, branch: Optional[str] = None) -> bool: + """Pull updates from a subtree's remote repository.""" + subtree = self.config.get_subtree(name) + if not subtree: + console.print(f"[yellow]No subtree found with name: {name}[/yellow]") + return False + + try: + target_branch = branch or subtree.main_branch + self._run_git_command([ + "subtree", "pull", + "--prefix", subtree.path, + "--squash", + name, target_branch + ]) + + console.print(f"[green]Successfully pulled updates for subtree {name}[/green]") + return True + + except Exception as e: + console.print(f"[bold red]Failed to pull subtree:[/bold red] {str(e)}") + return False + + def push_subtree(self, name: str, branch: Optional[str] = None) -> bool: + """Push local changes to a subtree's remote repository.""" + subtree = self.config.get_subtree(name) + url = self.config.get_subtree(name).git_url + if not subtree: + console.print(f"[yellow]No subtree found with name: {name}[/yellow]") + return False + + try: + target_branch = branch or subtree.staging_branch + self._run_git_command([ + "subtree", "push", + "--prefix", subtree.path, + url, target_branch + ]) + + console.print(f"[green]Successfully pushed changes for subtree {name}[/green]") + return True + + except Exception as e: + console.print(f"[bold red]Failed to push subtree:[/bold red] {str(e)}") + return False + + def list_subtrees(self) -> None: + """Display all configured subtrees in a table.""" + table = Table(show_header=True, header_style="bold magenta") + table.add_column("Name", style="cyan") + table.add_column("Path", style="green") + table.add_column("Git URL", style="bold blue", overflow="fold") + table.add_column("Main Branch", style="yellow") + table.add_column("Staging Branch", style="yellow") + + for subtree in self.config.subtrees: + table.add_row( + subtree.name, + subtree.path, + subtree.git_url, + subtree.main_branch, + subtree.staging_branch + ) + + console.print(table) + + +@click.group() +def cli(): + """Manage Git subtrees for your project.""" + pass + + +@cli.command() +@click.argument('name') +@click.argument('git_url') +@click.argument('path') +@click.option('--main-branch', default='main', help='Main branch name') +@click.option('--staging-branch', default='staging', help='Staging branch name') +def add(name: str, git_url: str, path: str, main_branch: str, staging_branch: str): + """Add a new subtree.""" + if not path.startswith('lib/'): + console.print("[bold red]Error:[/bold red] The subtree path must be inside the 'lib/' directory") + return + + if '..' in path: + console.print("[bold red]Error:[/bold red] Invalid path: directory traversal not allowed") + return + + manager = SubtreeManager() + subtree = SubtreeConfig(name, git_url, main_branch, staging_branch, path) + manager.add_subtree(subtree) + +@cli.command() +@click.argument('name') +def remove(name: str): + """Remove a subtree.""" + manager = SubtreeManager() + manager.remove_subtree(name) + + +@cli.command() +@click.argument('name') +@click.option('--branch', help='Branch to pull from') +def pull(name: str, branch: Optional[str]): + """Pull updates from a subtree's remote.""" + manager = SubtreeManager() + manager.pull_subtree(name, branch) + + +@cli.command() +@click.argument('name') +@click.option('--branch', help='Branch to push to') +def push(name: str, branch: Optional[str]): + """Push changes to a subtree's remote.""" + manager = SubtreeManager() + manager.push_subtree(name, branch) + + +@cli.command(name='list') +def list_cmd(): + """List all configured subtrees.""" + manager = SubtreeManager() + manager.list_subtrees() + + +if __name__ == '__main__': + cli() diff --git a/tools/src/wizard/wizard.py b/tools/src/wizard/wizard.py new file mode 100644 index 0000000..9b8a21a --- /dev/null +++ b/tools/src/wizard/wizard.py @@ -0,0 +1,95 @@ +import click +from typing import List, Dict, Any, Callable +from dataclasses import dataclass +from abc import ABC, abstractmethod + + +@dataclass +class WizardStep: + """Represents a single step in the wizard""" + name: str + prompt: str + type: type + validator: Callable[[Any], bool] = lambda x: True + choices: List[str] = None + default: Any = None + help_text: str = "" + + +class BaseWizard(ABC): + """Base class for creating wizards""" + + def __init__(self, title: str): + self.title = title + self.steps: List[WizardStep] = [] + self.results: Dict[str, Any] = {} + self.setup_steps() + + @abstractmethod + def setup_steps(self) -> None: + """Define the wizard steps - must be implemented by subclasses""" + pass + + @abstractmethod + def process_results(self) -> None: + """Process the collected results - must be implemented by subclasses""" + pass + + def add_step(self, step: WizardStep) -> None: + """Add a step to the wizard""" + self.steps.append(step) + + def run(self) -> Dict[str, Any]: + """Run the wizard and collect inputs""" + click.echo(f"\n=== {self.title} ===\n") + + for step in self.steps: + while True: + try: + if step.help_text: + click.echo(f"Help: {step.help_text}") + if step.choices: + click.echo(f"Choices: {', '.join(step.choices)}") + value = click.prompt( + step.prompt, + type=click.Choice(step.choices), + default=step.default + ) + else: + value = click.prompt( + step.prompt, + type=step.type, + default=step.default + ) + + if step.validator(value): + self.results[step.name] = value + break + else: + click.echo("Invalid input. Please try again.") + except click.Abort: + if click.confirm("Do you want to exit the wizard?"): + raise click.Abort() + except Exception as e: + click.echo(f"Error: {str(e)}") + if not click.confirm("Do you want to try again?"): + raise click.Abort() + + self.process_results() + return self.results + + +def wizard_command(wizard_class): + """Decorator to create a click command from a wizard class""" + + @click.command() + def wrapper(): + try: + wizard = wizard_class() + wizard.run() + except click.Abort: + click.echo("\nWizard cancelled.") + return + + return wrapper + diff --git a/vcpkg.json b/vcpkg.json index 77fd4b3..4119bed 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,50 +1,49 @@ { - "name": "libtusclient", - "license": "MIT", - "version": "1.0.0", - "description": "Library implementing the tus protocol for resumable uploads", - "homepage": "https://github.com/Cadons/libtusclient", - "dependencies": [ - "vcpkg-cmake", - { - "name": "vcpkg-cmake-config", - "host": true - }, - { - "name":"pkgconf" - }, - { - "name": "curl" - }, - { - "name": "boost-uuid" - }, - { - "name":"boost-lexical-cast" - }, - { - "name": "libzippp" - }, - { - "name": "gtest", - "default-features": true - }, - { - "name": "nlohmann-json" - }, - { - "name": "glog" - }, - "vcpkg-cmake", - { - "name": "vcpkg-cmake-config", - "host": true - } - ], - "features": { - "shared": { - "description": "Build shared libraries" - } + "name": "tusclient", + "license": "MIT", + "version": "1.0.2", + "description": "Library implementing the tus protocol for resumable uploads", + "homepage": "https://github.com/Cadons/libtusclient", + "dependencies": [ + "vcpkg-cmake", + { + "name": "vcpkg-cmake-config", + "host": true + }, + { + "name":"pkgconf" + }, + { + "name": "curl" + }, + { + "name": "boost-uuid" + }, + { + "name":"boost-lexical-cast" + }, + { + "name": "libzippp" + }, + { + "name": "gtest", + "default-features": true + }, + { + "name": "nlohmann-json" + }, + { + "name": "glog" + }, + "vcpkg-cmake", + { + "name": "vcpkg-cmake-config", + "host": true } - + ], + "features": { + "shared": { + "description": "Build shared libraries" + } + } } \ No newline at end of file diff --git a/vpm.bat b/vpm.bat deleted file mode 100644 index 73566be..0000000 --- a/vpm.bat +++ /dev/null @@ -1,132 +0,0 @@ -@echo off -SETLOCAL ENABLEDELAYEDEXPANSION - -REM Check for arguments -SET "SKIP_SUBMODULE=false" -SET "RESET=false" - -IF /I "%1"=="skip-submodule" ( - SET "SKIP_SUBMODULE=true" -) ELSE IF /I "%1"=="new" ( - SET "RESET=true" -) - -echo Initializing project... - -REM Clone submodules if skip-submodule argument is not passed -IF /I "%SKIP_SUBMODULE%"=="false" ( - echo Cloning submodules... - git submodule update --init --recursive || ( - echo Error: Failed to clone submodules. - EXIT /B 1 - ) -) ELSE ( - echo Skipping submodule initialization. -) - -REM Check for Python, Git, pip, Vcpkg, and CMake availability -echo Checking availability of Python, Git, Vcpkg, CMake, and pip... - -python --version >nul 2>&1 -IF ERRORLEVEL 1 ( - echo Error: Python is not available. Please install Python and try again. - EXIT /B 1 -) - -git --version >nul 2>&1 -IF ERRORLEVEL 1 ( - echo Error: Git is not available. Please install Git and try again. - EXIT /B 1 -) - -vcpkg --version >nul 2>&1 -IF ERRORLEVEL 1 ( - echo Error: Vcpkg is not available. Please install Vcpkg and try again. - EXIT /B 1 -) - -cmake --version >nul 2>&1 -IF ERRORLEVEL 1 ( - echo Error: CMake is not available. Please install CMake and try again. - EXIT /B 1 -) - -pip --version >nul 2>&1 -IF ERRORLEVEL 1 ( - echo Error: pip is not available. Please ensure pip is installed with Python. - EXIT /B 1 -) - -REM Move to .vpm directory and configure project -cd .vpm || ( - echo Error: Failed to change directory to .vpm. - EXIT /B 1 -) - -REM Check if virtual environment exists -IF EXIST venv ( - echo Virtual environment already exists. -) ELSE ( - echo Creating virtual environment... - python -m venv venv - IF ERRORLEVEL 1 ( - echo Error: Failed to create virtual environment. - EXIT /B 1 - ) -) - -REM Activate virtual environment -call venv\Scripts\activate.bat >nul 2>&1 -IF ERRORLEVEL 1 ( - echo Error: Failed to activate virtual environment. - EXIT /B 1 -) - -REM Install requirements -IF EXIST "requirements.txt" ( - echo Installing requirements from requirements.txt... - pip install -r requirements.txt - IF ERRORLEVEL 1 ( - echo Error: Failed to install requirements. - EXIT /B 1 - ) -) ELSE ( - echo No requirements.txt found, skipping installation. -) - -REM If RESET is true, ask for confirmation -IF /I "%RESET%"=="true" ( - echo Warning: This will remove all project configurations, including vcpkg dependencies and CMake build files. - CHOICE /M "Are you sure you want to continue?" /C YN - IF ERRORLEVEL 2 ( - echo Operation cancelled by user. - call venv\Scripts\deactivate.bat >nul 2>&1 - EXIT /B 0 - ) - echo Running with --reset argument... - python src\vpm.py --reset - IF ERRORLEVEL 1 ( - echo Error: vpm.py encountered an issue with --reset. - EXIT /B 1 - ) -) ELSE ( - echo Running initialization script... - python src\vpm.py - IF ERRORLEVEL 1 ( - echo Error: vpm.py encountered an issue. - EXIT /B 1 - ) -) - -REM Deactivate virtual environment if the script exists -IF EXIST venv\Scripts\deactivate.bat ( - call venv\Scripts\deactivate.bat >nul 2>&1 - IF ERRORLEVEL 1 ( - echo Warning: Failed to deactivate virtual environment. - ) -) - -cd .. - -echo Project setup completed successfully. -exit /B 0 diff --git a/vpm.sh b/vpm.sh deleted file mode 100755 index 1a9a548..0000000 --- a/vpm.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Check for arguments -SKIP_SUBMODULE=false -RESET=false - -if [[ "${1:-}" == "skip-submodule" ]]; then - SKIP_SUBMODULE=true -elif [[ "${1:-}" == "new" ]]; then - RESET=true -fi - -echo "Initializing project..." - -# Clone submodules if skip-submodule argument is not passed -if [[ "$SKIP_SUBMODULE" == false ]]; then - echo "Cloning submodules..." - git submodule update --init --recursive || { - echo "Error: Failed to clone submodules." - exit 1 - } -else - echo "Skipping submodule initialization." -fi - -# Check for Python, Git, Vcpkg, CMake, and pip availability -echo "Checking availability of Python, Git, Vcpkg, CMake, and pip..." - -for cmd in python3 git vcpkg cmake pip; do - if ! command -v "$cmd" &> /dev/null; then - echo "Error: $cmd is not available. Please install $cmd and try again." - exit 1 - fi -done - -# Move to .vpm directory and configure project -cd .vpm || { - echo "Error: Failed to change directory to .vpm." - exit 1 -} - -# Check if virtual environment exists -if [[ -d "venv" ]]; then - echo "Virtual environment already exists." -else - echo "Creating virtual environment..." - python3 -m venv venv || { - echo "Error: Failed to create virtual environment." - exit 1 - } -fi - -# Activate virtual environment -source venv/bin/activate - -# Install requirements -if [[ -f "requirements.txt" ]]; then - echo "Installing requirements from requirements.txt..." - pip install -r requirements.txt || { - echo "Error: Failed to install requirements." - exit 1 - } -else - echo "No requirements.txt found, skipping installation." -fi - -# Prompt confirmation if --reset argument is passed -if [[ "$RESET" == true ]]; then - echo "Warning: This will remove all project configurations, including vcpkg dependencies and CMake build files." - read -rp "Are you sure you want to continue? [y/N]: " confirm - if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then - echo "Operation cancelled by user." - deactivate - exit 0 - fi - - echo "Running with --reset argument..." - python3 src/vpm.py --reset || { - echo "Error: vpm.py encountered an issue with --reset." - exit 1 - } -else - echo "Running initialization script..." - python3 src/vpm.py || { - echo "Error: vpm.py encountered an issue." - exit 1 - } -fi - -# Deactivate virtual environment -deactivate - -cd .. - -echo "Project setup completed successfully." - -exit 0