diff --git a/.github/workflows/mac-release.yml b/.github/workflows/mac-release.yml new file mode 100644 index 0000000000..9b2979278d --- /dev/null +++ b/.github/workflows/mac-release.yml @@ -0,0 +1,129 @@ +name: SerialPrograms MacOS Release + +on: + push: + tags: + # Start MacOS release flow when a new version tag is created + - 'v*' + workflow_dispatch: + # Manual release / testing + inputs: + version: + description: 'Version for the manual release' + required: true + jobs: + description: 'Number of parallel jobs to build with' + default: 4 + type: number + qt_version: + description: 'Qt version to use' + required: true + default: '6.8.2' + type: choice + options: + - '6.8.2' + - '6.5.3' + runner: + description: "MacOS Runner" + required: true + default: "macos-13" + type: choice + options: + - "macos-15" + - "macos-13" + + +jobs: + build: + runs-on: ${{ github.event.inputs.runner }} + steps: + - name: Checkout Arduino-Source + uses: actions/checkout@v4 + with: + path: Arduino-Source + + - name: Checkout Packages + uses: actions/checkout@v4 + with: + repository: 'PokemonAutomation/Packages' + path: Packages + + # MacOS runners have Homebrew, Xcode, and Cmake already installed + - name: Install Dependencies + run: | + brew install tesseract + brew install tesseract-lang + brew install opencv + + - uses: jurplel/install-qt-action@v4 + with: + version: ${{ github.event.inputs.qt_version }} + modules: 'qtmultimedia qtserialport' + + - name: Build SerialPrograms.app + run: | + cd Arduino-Source/SerialPrograms + mkdir bin + cd bin + cmake .. -DUNIX_LINK_TESSERACT:BOOL=true -DCMAKE_BUILD_TYPE:STRING=Release + cmake --build . -j ${{ github.event.inputs.jobs }} + cp -r SerialPrograms.app ../../../SerialPrograms.app + + # Important: GitHub MacOS runners do not have the lib area in its rpath by default. It must manually be added for frameworks to be discovered and added to the bundle + # Some libraries are not copied in QT deployment on the runner but will still be proprely linked if they're added prior to deployment + - name: rpath resolution + run: | + BREW_PREFIX=$(brew --prefix) + install_name_tool -add_rpath $BREW_PREFIX/lib SerialPrograms.app/Contents/MacOS/SerialPrograms + otool -l SerialPrograms.app/Contents/MacOS/SerialPrograms | grep -A2 LC_RPATH + mkdir SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/protobuf/29.3/lib/libutf8_validity.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/webp/1.5.0/lib/libsharpyuv.0.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/jpeg-xl/0.11.1/lib/libjxl_cms.0.11.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkCommonComputationalGeometry-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkFiltersVerdict-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkfmt-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkFiltersGeometry-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkFiltersCore-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkCommonCore-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + cp $BREW_PREFIX/Cellar/vtk/9.4.1_2/lib/libvtkCommonSystem-9.4.1.dylib SerialPrograms.app/Contents/Frameworks + + # Use macdeployqt to bundle the app with dependencies + - name: Run macdeployqt + run: macdeployqt SerialPrograms.app -verbose=3 + + # Dummy codesign + - name: Codesign + run: codesign --force --deep --sign - SerialPrograms.app + + # Create a disk image for installation + - name: Create disk image + run: | + brew install create-dmg + mkdir dmg + cd dmg + mv ../SerialPrograms.app ./SerialPrograms.app + create-dmg --volname "SerialPrograms Installer" \ + --window-pos 150 150 \ + --window-size 600 400 \ + --icon-size 128 \ + --icon "SerialPrograms.app" 140 160 \ + --hide-extension "SerialPrograms.app" \ + --app-drop-link 450 160 \ + "SerialPrograms-Installer.dmg" \ + "./" + mv SerialPrograms-Installer.dmg $GITHUB_WORKSPACE/SerialPrograms-Installer.dmg + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: SerialPrograms-Installer.dmg + path: SerialPrograms-Installer.dmg + + - name: GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: SerialPrograms-Installer.dmg + # Tag should be automatically set in the case of a tag push + tag_name: ${{ github.event.inputs.version }} + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index ee1f975e9c..8624ecdaba 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -2199,6 +2199,14 @@ if (APPLE) MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in RESOURCE ${SerialPrograms_ICON} ) + + add_custom_command( + TARGET SerialPrograms + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/../../Packages/SerialPrograms/Resources" + "$/../Resources" + ) else() # WIN and Linux: add_executable(SerialPrograms WIN32 ${MAIN_SOURCES}) endif() diff --git a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp index 20d0f1c0c7..69b46204fd 100644 --- a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp +++ b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/LifetimeSanitizer.h" @@ -106,15 +107,25 @@ GlobalSettings::GlobalSettings() false, "Stats File:
Use the stats file here. Multiple instances of the program can use the same file.", LockMode::LOCK_WHILE_RUNNING, +#if defined(__APPLE__) + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString() + "/UserSettings/PA-Stats.txt", + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString() + "/UserSettings/PA-Stats.txt" +#else "UserSettings/PA-Stats.txt", "UserSettings/PA-Stats.txt" +#endif ) , TEMP_FOLDER( false, "Temp Folder:
Place temporary files in this directory.", LockMode::LOCK_WHILE_RUNNING, +#if defined(__APPLE__) + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString() + "/TempFiles/", + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString() + "/TempFiles/" +#else "TempFiles/", "TempFiles/" +#endif ) , THEME(CONSTRUCT_TOKEN) , WINDOW_SIZE( diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index 89e0479525..996bd65d80 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -112,9 +113,14 @@ std::string get_training_path(){ } std::string get_runtime_base_path(){ + // On MacOS, find the writable application support directory if (QSysInfo::productType() == "macos" || QSysInfo::productType() == "osx"){ - QString application_dir_path = get_application_base_dir_path(); - return application_dir_path.toStdString() + "/"; + QString appSupportPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QDir dir(appSupportPath); + if (!dir.exists()) { + dir.mkpath("."); + } + return appSupportPath.toStdString() + "/"; } return "./"; }