From e78353bbe675d1fcbff582f5469d523903b4c50c Mon Sep 17 00:00:00 2001 From: Vladyslav Voinov <32337080+vvvar@users.noreply.github.com> Date: Sun, 25 Jun 2023 14:17:49 -0700 Subject: [PATCH] Pluginval integration (#89) * Verify VST3 and AU versions * Simplify build * Fixed Windows local build * Added Linux local dev env(devcontainer) * Docs updated --- .devcontainer/Dockerfile | 13 ++ .devcontainer/devcontainer.json | 7 +- .github/workflows/build.yml | 21 ++- .github/workflows/publish_release_draft.yml | 23 ++- .gitignore | 16 +- .vscode/extensions.json | 1 + .vscode/settings.json | 6 + CMakeLists.txt | 73 ++++++--- README.md | 19 ++- assets/pluginval.png | Bin 0 -> 2682 bytes conanfile.py | 28 +--- config/conan/profiles/linux-arm | 8 + config/conan/profiles/windows | 3 + .../system/requirements.windows.choco.config | 1 + justfile | 60 +++++-- test/conanfile.py | 154 ------------------ test/test_compile_options.py | 39 +++++ 17 files changed, 228 insertions(+), 244 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 assets/pluginval.png create mode 100644 config/conan/profiles/linux-arm delete mode 100644 test/conanfile.py create mode 100644 test/test_compile_options.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..a620a0c0 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +FROM ubuntu + +RUN apt update +RUN apt install -y git +RUN apt install -y software-properties-common +RUN apt install -y ca-certificates +RUN apt install -y wget +RUN apt install -y curl +RUN apt install -y ssh +RUN apt install -y sudo +RUN apt install -y build-essential +RUN apt install -y python3 python3-pip +RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/bin \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2649183d..8c782f06 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,3 +1,8 @@ { - "image": "ubuntu:20.04" + "build": { + "dockerfile": "Dockerfile" + }, + "forwardPorts": [ + 3000 + ] } \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85b1801a..ed2746a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,8 +71,13 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v3 with: - name: macos + name: macos-build path: build/Release/peakeater_artefacts/Release/DMG/*.dmg + - name: Upload test artifacts + uses: actions/upload-artifact@v3 + with: + name: macos-test + path: build/Release/peakeater_artefacts/Release/Pluginval/*.txt windows: name: Windows @@ -91,8 +96,13 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v3 with: - name: windows + name: windows-build path: build/Release/peakeater_artefacts/Release/Archive/*.zip + - name: Upload test artifacts + uses: actions/upload-artifact@v3 + with: + name: windows-test + path: build/Release/peakeater_artefacts/Release/Pluginval/*.txt linux: name: Linux @@ -113,5 +123,10 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v3 with: - name: linux + name: linux-build path: build/Release/peakeater_artefacts/Release/Archive/*.zip + - name: Upload test artifacts + uses: actions/upload-artifact@v3 + with: + name: linux-test + path: build/Release/peakeater_artefacts/Release/Pluginval/*.txt diff --git a/.github/workflows/publish_release_draft.yml b/.github/workflows/publish_release_draft.yml index 775a32f2..81b44602 100644 --- a/.github/workflows/publish_release_draft.yml +++ b/.github/workflows/publish_release_draft.yml @@ -3,7 +3,7 @@ on: push: branches: - master - + jobs: build: name: Build @@ -27,17 +27,32 @@ jobs: - name: Download macOS build uses: actions/download-artifact@v3 with: - name: macos + name: macos-build + path: release + - name: Download macOS test results + uses: actions/download-artifact@v3 + with: + name: macos-test path: release - name: Download Windows build uses: actions/download-artifact@v3 with: - name: windows + name: windows-build + path: release + - name: Download Windows test results + uses: actions/download-artifact@v3 + with: + name: windows-test path: release - name: Download Linux build uses: actions/download-artifact@v3 with: - name: linux + name: linux-build + path: release + - name: Download Linux test results + uses: actions/download-artifact@v3 + with: + name: linux-test path: release - name: Get release version id: get-version diff --git a/.gitignore b/.gitignore index 94dd6f41..e919a675 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,16 @@ # MacOS indexing files .DS_Store +# Various vscode logs +/.vscode/*.log + # Build dir /build /test/build +# CMake workflow CMakeUserPresets.json -# Projucer auto-gen files -/JuceLibraryCode - -# Python cache -Scripts/Release/__pycache__ - -# node -node_modules - -# Various vscode logs -/.vscode/*.log - # Env .env config/.env \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9f7de0b1..38c34bf8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "ms-vscode.cpptools", "ms-python.python", + "josetr.cmake-language-support-vscode", "github.vscode-github-actions", "shd101wyy.markdown-preview-enhanced", "streetsidesoftware.code-spell-checker", diff --git a/.vscode/settings.json b/.vscode/settings.json index 712ae7ac..4e1cfbbc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -159,4 +159,10 @@ "zipresp" ], "sonarlint.pathToCompileCommands": "${workspaceFolder}/build/Release/compile_commands.json", + // Bandaid because occasinally git is not added to the Path which breaks just + // Keep untill this resolved - https://github.com/casey/just/issues/1517 + // VS Code-only since it is installation issue which works fine on a CI + "terminal.integrated.env.windows": { + "Path": "${env:Path};C:\\Program Files\\Git\\bin" + }, } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d187678f..fbb2589d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,14 @@ cmake_minimum_required(VERSION 3.15) project(${CMAKE_PROJECT_NAME} VERSION ${CMAKE_PROJECT_VERSION}) -# For this to work, the following ENV variables must be set: -# - MACOS_APPLE_DEVELOPER_ID: Apple Developer ID(for ex. "Developer ID Application: John Doe (XXXXX6XX42)") -option(CODESIGN "Codesign plugins and bundle(macOS only)" OFF) +include(FetchContent) +include(CTest) # Export compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) find_package(JUCE CONFIG REQUIRED) -include(FetchContent) FetchContent_Declare( clap-juce-extensions GIT_REPOSITORY https://github.com/free-audio/clap-juce-extensions.git @@ -95,64 +93,89 @@ target_link_libraries(${CMAKE_PROJECT_NAME} juce::juce_recommended_lto_flags juce::juce_recommended_warning_flags) +# Where to store the result of the Pluginval run +set(_PLUGINVAL_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}_artefacts/${CMAKE_BUILD_TYPE}/Pluginval") + +# Adding targets for testing, signing and packaging which are very platform-specific +# NOTE: only macOS supports signing at the moment if(APPLE) - if(CODESIGN) - if(NOT DEFINED ENV{MACOS_APPLE_DEVELOPER_ID}) - message(FATAL_ERROR "CODESIGN option is turned ON, but MACOS_APPLE_DEVELOPER_ID env variable is not defined") - endif() + # Test we haven't missed any compiler options. Makes sense only on macOS at the moment + add_test(NAME CompileOptions COMMAND python3 ${PROJECT_SOURCE_DIR}/test/test_compile_options.py --compile_commands=${CMAKE_BINARY_DIR}/compile_commands.json) + + # Marks as CFBundle. Setting this globally because: + # 1. Without it, TARGET_BUNDLE_DIR won't work + # 2. TARGET_BUNDLE_DIR is used while bundling, signing and testing for macOS + set_target_properties(${CMAKE_PROJECT_NAME}_AU ${CMAKE_PROJECT_NAME}_CLAP ${CMAKE_PROJECT_NAME}_LV2 ${CMAKE_PROJECT_NAME}_VST3 PROPERTIES BUNDLE TRUE) + + # Test VST3 and AU with Pluginval + add_test(NAME Pluginval_VST3 + COMMAND pluginval --strictness-level 10 --verbose --skip-gui-tests --validate-in-process --output-dir ${_PLUGINVAL_OUT_DIR} $) + add_test(NAME Pluginval_AU + COMMAND pluginval --strictness-level 10 --verbose --skip-gui-tests --validate-in-process --output-dir ${_PLUGINVAL_OUT_DIR} $) - # Codesign all plugins before we've bundled them + # Optionally, codesign all plugins before we've bundled them(ensure it with tests) + if(DEFINED ENV{MACOS_APPLE_DEVELOPER_ID}) add_custom_command(TARGET ${CMAKE_PROJECT_NAME}_AU VERBATIM POST_BUILD COMMAND codesign --force -s $ENV{MACOS_APPLE_DEVELOPER_ID} -v $ --deep --strict --options=runtime --timestamp COMMENT "Codesigning AU") + add_test(NAME Codesign_AU COMMAND codesign -dv --verify --verbose=4 $) + add_custom_command(TARGET ${CMAKE_PROJECT_NAME}_CLAP VERBATIM POST_BUILD COMMAND codesign --force -s $ENV{MACOS_APPLE_DEVELOPER_ID} -v $ --deep --strict --options=runtime --timestamp COMMENT "Codesigning CLAP") + add_test(NAME Codesign_CLAP COMMAND codesign -dv --verify --verbose=4 $) + add_custom_command(TARGET ${CMAKE_PROJECT_NAME}_LV2 VERBATIM POST_BUILD # lv2 is special, sign only TARGET_FILE COMMAND codesign --force -s $ENV{MACOS_APPLE_DEVELOPER_ID} -v $ --deep --strict --options=runtime --timestamp COMMENT "Codesigning LV2") + add_test(NAME Codesign_LV2 COMMAND codesign -dv --verify --verbose=4 $) + add_custom_command(TARGET ${CMAKE_PROJECT_NAME}_VST3 VERBATIM POST_BUILD COMMAND codesign --force -s $ENV{MACOS_APPLE_DEVELOPER_ID} -v $ --deep --strict --options=runtime --timestamp COMMENT "Codesigning VST3") + add_test(NAME Codesign_VST3 COMMAND codesign -dv --verify --verbose=4 $) endif() # Build DMG set(_OSX_DMG_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}_artefacts/${CMAKE_BUILD_TYPE}/DMG") set(_OSX_DMG_FILE "${_OSX_DMG_DIR}/${CMAKE_PROJECT_NAME}-v${CMAKE_PROJECT_VERSION}-macOS-universal.dmg") - - # macOS specific. Marks as CFBundle. Otherwise - TARGET_BUNDLE_DIR wouldn't work - set_target_properties(${CMAKE_PROJECT_NAME}_AU ${CMAKE_PROJECT_NAME}_CLAP ${CMAKE_PROJECT_NAME}_LV2 ${CMAKE_PROJECT_NAME}_VST3 PROPERTIES BUNDLE TRUE) get_target_property(_LV2_OUT_DIR ${CMAKE_PROJECT_NAME}_LV2 LIBRARY_OUTPUT_DIRECTORY) # Getting it like this since lv2 folder is not a bundle, so TARGET_BUNDLE_DIR wont work - add_custom_target(dmg ALL + add_custom_target(DMG ALL + BYPRODUCTS ${_OSX_DMG_FILE} VERBATIM COMMAND ${CMAKE_COMMAND} -E make_directory ${_OSX_DMG_DIR} COMMAND dmgbuild -s ${PROJECT_SOURCE_DIR}/config/dmgbuild.py -D au=$ -D clap=$ -D lv2=${_LV2_OUT_DIR} -D vst3=$ "${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}" ${_OSX_DMG_FILE} COMMENT "Building DMG") - add_dependencies(dmg ${CMAKE_PROJECT_NAME}_AU ${CMAKE_PROJECT_NAME}_CLAP ${CMAKE_PROJECT_NAME}_LV2 ${CMAKE_PROJECT_NAME}_VST3) + add_dependencies(DMG ${CMAKE_PROJECT_NAME}_AU ${CMAKE_PROJECT_NAME}_CLAP ${CMAKE_PROJECT_NAME}_LV2 ${CMAKE_PROJECT_NAME}_VST3) - if(CODESIGN) - # Codesign, notarise and staple DMG - add_custom_command(TARGET dmg VERBATIM POST_BUILD + # Optionally, codesign, notarize and stable the DMG(ensure it with test) + if(DEFINED ENV{MACOS_APPLE_DEVELOPER_ID}) + add_custom_command(TARGET DMG VERBATIM POST_BUILD COMMAND codesign --force -s $ENV{MACOS_APPLE_DEVELOPER_ID} -v ${_OSX_DMG_FILE} --deep --strict --options=runtime --timestamp COMMAND xcrun notarytool submit ${_OSX_DMG_FILE} --keychain-profile "APPLE_SIGN_PROFILE" --wait COMMAND xcrun stapler staple ${_OSX_DMG_FILE} COMMENT "Codesigning, notarizing and stapling DMG") + add_test(NAME Codesign_DMG COMMAND codesign -dv --verify --verbose=4 ${_OSX_DMG_FILE}) endif() else() + # The way of getting path to .vst3 is tricky since actual bin is inside ARCHIVE_OUTPUT_DIRECTORY + set(_VST3_BUNDLE "$>/${CMAKE_PROJECT_NAME}.vst3") + # Getting it like this since lv2 folder is not a bundle, so TARGET_BUNDLE_DIR wont work + get_target_property(_LV2_OUT_DIR ${CMAKE_PROJECT_NAME}_LV2 LIBRARY_OUTPUT_DIRECTORY) + # Archive everything into ZIP set(_ARCHIVE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}_artefacts/${CMAKE_BUILD_TYPE}/Archive") set(_ARCHIVE_FILE "${_ARCHIVE_DIR}/${CMAKE_PROJECT_NAME}-v${CMAKE_PROJECT_VERSION}-${CMAKE_HOST_SYSTEM_NAME}-x86_64.zip") - - # otherwise, TARGET_BUNDLE_DIR wouldn't work - get_target_property(_LV2_OUT_DIR ${CMAKE_PROJECT_NAME}_LV2 LIBRARY_OUTPUT_DIRECTORY) # Getting it like this since lv2 folder is not a bundle, so TARGET_BUNDLE_DIR wont work add_custom_target(archive ALL VERBATIM COMMAND ${CMAKE_COMMAND} -E make_directory ${_ARCHIVE_DIR} - COMMAND ${CMAKE_COMMAND} -E tar cf ${_ARCHIVE_FILE} --format=zip $ ${_LV2_OUT_DIR} $ + COMMAND ${CMAKE_COMMAND} -E tar cf ${_ARCHIVE_FILE} --format=zip $ ${_LV2_OUT_DIR} ${_VST3_BUNDLE} COMMENT "Archiving everything") add_dependencies(archive ${CMAKE_PROJECT_NAME}_CLAP ${CMAKE_PROJECT_NAME}_LV2 ${CMAKE_PROJECT_NAME}_VST3) -endif() -# Print available CMake targets for the debugging purpose -get_property(target_names DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY BUILDSYSTEM_TARGETS) -message(DEBUG " Available CMake targets are: ${target_names}") \ No newline at end of file + # Test VST3 with Pluginval(only non-macOS-specific format supported) + # NOTE#1: Unlike macOS, TARGET_FILE_DIR is used because unlike macOS is a file here + # NOTE#2: Only Windows supports GUI on a CI, thus check for it + add_test(NAME Pluginval_VST3 + COMMAND pluginval --strictness-level 10 --verbose $<$>:--skip-gui-tests> --validate-in-process --output-dir ${_PLUGINVAL_OUT_DIR} ${_VST3_BUNDLE}) +endif() diff --git a/README.md b/README.md index af7d4816..8a523071 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Table of Contents - [Resizing](#resizing) - [VST3, AU, LV2 and CLAP Support](#vst3-au-lv2-and-clap-support) - [macOS, Windows and Linux Support](#available-on-all-major-platforms) + - [Verified by Pluginval](#verified-by-pluginval) - [Installation](#installation) - [macOS](#macos) - [Windows](#windows) @@ -151,6 +152,12 @@ You can use it on macOS, Windows or Linux. > **Warning** Linux support is experimental and may be unstable. +### Verified by Pluginval Pluginval + +> pluginval is a cross-platform plugin validator and tester application. It is designed to be used by both plugin and host developers to ensure stability and compatibility between plugins and hosts. [GitHub Project Link](https://github.com/Tracktion/pluginval/tree/develop) + +VST3 and AU versions are verified by [Tracktion Pluginval](https://www.tracktion.com/develop/pluginval) for all platforms. Maximum possible strictness level 10 is used. For macOS and Linux - Headless Mode is being used and GUI Mode is used for Windows. Starting from v0.6.5, you may find proofs of Pluginval verification in the "Assets" section on the Release page(*.txt files are split by platform and plugin format). + ## Installation @@ -211,8 +218,12 @@ Extract the zip and copy the plugin in a format of your choice into the folder w - [Just](https://github.com/casey/just) 1.13.0+ - [Python](https://www.python.org) 3.11.4+ - [Brew](https://brew.sh) 4.0.23+(macOS only) +- [C++ Build Tools for Visual Studio 2022](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022)(Windows only) - [Chocolatey](https://chocolatey.org) 1.4.0+(Windows only) +> **Note**
+> It is recommended to use PowerShell on Windows + ### Building ```sh @@ -286,11 +297,3 @@ Distributed under the GPL-3.0 License. See [`LICENSE.md`](https://github.com/vvv - -[issues-shield]: https://img.shields.io/github/issues/vvvar/PeakEater?style=for-the-badge -[issues-url]: https://github.com/vvvar/PeakEater/issues -[license-shield]: https://img.shields.io/github/license/vvvar/PeakEater?style=for-the-badge -[license-url]: https://github.com/vvvar/PeakEater/blob/master/LICENSE.md -[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 -[linkedin-url]: https://www.linkedin.com/in/vladyslav-voinov-5126a793/ -[product-screenshot]: assets/screenshots/main.png diff --git a/assets/pluginval.png b/assets/pluginval.png new file mode 100644 index 0000000000000000000000000000000000000000..1493a9effd38976cffc0d0ab7afc3fb0aa6c78c9 GIT binary patch literal 2682 zcmbVOX;hL47bX|PHJ6~UGIK3NN{g~`EGbRYEHPdIa|D+(OdWM{!70rp7s_=o%`F#9 z;FZ87HFGy|&n0X!txOpc$4pJFrpb@<{hNQ^`OdlL-sid7x%bz-sTW+%s;lU#Kp+rx zyu%p+1hNG!!;3HlnSR(1=OHr+{UWwV1fpVrKVy3_YGQH9;}@a?34NDw ziY9X8Xu~H6TS*gV~-*2j4G1Q$TllZVnj8{X56=bksJ0J`E0p!M71bXv@Qoe zBJBEIgB>)SK6B7b3#U+i3#Oo%4LSCtXewneF>~xk+uG(^%F@Q0+Lw&YBN{o5d zKL5IHy~m@y(qW?m${g0+3KE9ZIK(EP58$f{9h!NDE3IIT0dn{AG8Ks;$EmsE<1ti0 zg|oeg%gG{`6wFqw@};wi`)_aOULqe@9AbWpjc~Yf^)ZUIJMCP3Kcn}K6H%gJ^X=PE zfA*DDj&t;}_(~0@rt25GB>6C|bRYs5@|d?=^HUAKIBPnW*OxhGz~inOh8Di!i#RtF ziu31;x-j5bgK8A3L9#37T#;eO**9WCfGlxxn)S0CzxNltI=PZ!^wyhb=v$Cnqkj&% zDeYyM9PkXv(ORbtt`mr;hj|N9c!=lVBS5VA>aS{W#FDN3WL)KvYl1o`QX#s>-pw=U^Msh-A^7<-1t_|0Ird^-(xEE(Ugo1#TF^2D$nnje zNA-_+B|fNqI@$ozwD#D2EY`^PuH@|>ov!EYwj9s^rwKFlXos8md==}XM4x$Z8un_Y z33KA*Za5>Dp21G2NVFsU@1j8y$MAVQ&hB=_1>6ROz`TpSOD&=y&t0OJU~b=N(v4jj z$z9XGSpCa99y-{Yt{xHbpOo z8FLovIPM_d?+q=4lEc?yvL$$|nK!hE-n)=WytxrrwPS&Ea&_JHnD0}oQq~wt>~I5S zIb@^RZ2eOhR+U!hw{`{a@jJOdE%a-;Ud5{%n4tw}j_4tL|CLVMN zcgE=CKQ-7fmv6j+4x|>&k+Zgvn+G`=k+;hv|@#J+J5G!FYDe(ni0*yvvzokECnHFj`833m2D^+C@x%? ziI>DiduunYgu4FY-zvu&dbm8N1%$cX(izUDT*8s?gVtD}Idd=r zSCfV`>s-li3UKwV-|7D87kP4%|25;SnI_mso{+126Hfc;Ee5;kTf?HXk8|?NMO=hV z^ebnA8@Go}u==|8r(=~EB%YWicu?~yLArAIX=q-k#o%Po6$O2o78P4^ixSpLyH8!EcDacCFR9TPoSp)+XIo6uI(Te)4 z8EonHVbDsp=gAfGf0R^9h^E!^CerU51KvW=^}Vq0+E!*{Gdu=%(xRQ~no5)p7ObB; zw-J?8UtSU+u!fKJACBBiJw|Mii+j)5WaWD;AHrO z^_g9|JgH5ZP25hMZd7**78ZE51jKzmc_}f04px%=;M$<@i+fw!0KzCvY}*fZ-o!#T z4}XaqJ!}Wq<&u-+0@Z%dKAr!xGxSs{E2%sJO7%QU1@`V@V!F4>8hH4_wXX2ei!NMt!sN9RbHR=`0T!KDh_H4B+qpA9RNR*zcyB` zFzkS%&yrZ@N!#Cr4DsbM(gB-wGvRSSIih+5Z2G-M`+^rg!Sopx7b1!$fqM9)?WL3LzoiDh!h$LYG&Y74syB{5JEoXf+ zJY_PY!!{4>1RHBrrWxoK(+&128su#wPr+jW+9g{?#{2NeaA+^RWE6p&*^Z~e`>;#l z;sEtOJ2Iowz{{oyM60&ZCikrIc0IoWW(yrm%jrZ_yoC>S;n|AS$o4~1rqe!Hz_^zN zA3spq{~}<~KBDarwM>_X_}wIx7_bqXx5OGh^CFvQO=AZX5h#H+_%`w&UYDo2 zlu>^wYdnKNt3NwWJpb>D06-(C2^`z{sGRmvy(6;6eNoiqt19T9@mqjAQqp(0mwqrI;xme$U|m`M842uJcWe|4p6cR=m!(LvwkRj9l3>3BlXBoZ;iB@&5tz C_5MKs literal 0 HcmV?d00001 diff --git a/conanfile.py b/conanfile.py index 1c379720..035545d6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,26 +1,21 @@ from conan import ConanFile from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake, cmake_layout -from conan.tools.files import copy from conan.tools.env import VirtualBuildEnv -import os - class PeakEater(ConanFile): name = "peakeater" - version = "0.6.4" + version = "0.6.5" user = "vvvar" channel = "testing" company = "T-Audio" url = "https://github.com/vvvar/PeakEater" settings = "os", "arch", "compiler", "build_type" - options = {"signed": [True, False]} - default_options = {"signed": False} - options_description = {"signed": "Whether binaries are signed with the certificate or not"} exports_sources = "assets", "modules", "source", "CMakeLists.txt" package_type = "application" requires = "juce/7.0.5@juce/release" + tool_requires = "pluginval/1.0.3@pluginval/release" def layout(self): cmake_layout(self) @@ -32,7 +27,6 @@ def generate(self): toolchain.cache_variables["CMAKE_PROJECT_VERSION"] = str(self.version) toolchain.cache_variables["CONAN_PROJECT_COMPANY"] = str(self.company) toolchain.cache_variables["CONAN_PROJECT_URL"] = str(self.url) - toolchain.cache_variables["CODESIGN"] = self.options.signed # type: ignore toolchain.generate() dependencies = CMakeDeps(self) dependencies.generate() @@ -43,21 +37,3 @@ def build(self): cmake = CMake(self) cmake.configure() cmake.build() - - def package_info(self): - self.cpp_info.resdirs = ["data"] - - def package(self): - # Package bins(plugins, installers, etc.) - bin_folder = os.path.join(str(self.package_folder), "bin") - artefacts_folder = os.path.join(self.build_folder, f"{self.name}_artefacts", self.settings.get_safe("build_type")) # type: ignore - copy(self, "*", src=os.path.join(artefacts_folder, "VST3"), dst=bin_folder) - copy(self, "*", src=os.path.join(artefacts_folder, "CLAP"), dst=bin_folder) - copy(self, "*", src=os.path.join(artefacts_folder, "LV2"), dst=bin_folder) - if self.settings.os == "Macos": # type: ignore - copy(self, "*", src=os.path.join(artefacts_folder, "AU"), dst=bin_folder) - copy(self, "*", src=os.path.join(artefacts_folder, "DMG"), dst=bin_folder) - # Package various build data(build logs, compile database, etc). Useful for analysis - data_folder = os.path.join(str(self.package_folder), "data") - copy(self, "compile_commands.json", src=self.build_folder, dst=data_folder) - copy(self, "build.ninja", src=self.build_folder, dst=data_folder) diff --git a/config/conan/profiles/linux-arm b/config/conan/profiles/linux-arm new file mode 100644 index 00000000..3bdb7305 --- /dev/null +++ b/config/conan/profiles/linux-arm @@ -0,0 +1,8 @@ +[settings] +arch=armv8 +build_type=Release +compiler=gcc +compiler.cppstd=20 +compiler.libcxx=libstdc++11 +compiler.version=11 +os=Linux \ No newline at end of file diff --git a/config/conan/profiles/windows b/config/conan/profiles/windows index ef08a6f2..ecec5c28 100644 --- a/config/conan/profiles/windows +++ b/config/conan/profiles/windows @@ -6,3 +6,6 @@ compiler.cppstd=20 compiler.runtime=dynamic compiler.version=193 os=Windows + +[conf] +tools.env.virtualenv:powershell=True \ No newline at end of file diff --git a/config/system/requirements.windows.choco.config b/config/system/requirements.windows.choco.config index 39675d07..9e382f80 100644 --- a/config/system/requirements.windows.choco.config +++ b/config/system/requirements.windows.choco.config @@ -1,5 +1,6 @@ + diff --git a/justfile b/justfile index d197973e..bed38131 100644 --- a/justfile +++ b/justfile @@ -7,17 +7,18 @@ conan_profile := if os() == "macos" { } else if os() == "windows" { "windows" } else if os() == "linux" { - "linux" + if arch() == "aarch64" { + "linux-arm" + } else { + "linux" + } } else { "default" } -# When MACOS_APPLE_DEVELOPER_ID is set then we can codesign -codesign := if env_var_or_default("MACOS_APPLE_DEVELOPER_ID", "") == "" { - "False" -} else { - "True" -} +# Just uses "sh" on Windows by default. +# Force use powershell since Conan fails to lift virtualenv with sh +set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] # Cleanup build, temp and all generated files [unix] @@ -44,6 +45,8 @@ setup-system: [windows] [private] setup-system: + # Whtout it PowerShell will ask any .ps1 script to be digitally signed + Set-ExecutionPolicy -ExecutionPolicy unrestricted choco install config/system/requirements.windows.choco.config --ignore-package-exit-codes [linux] @@ -78,8 +81,8 @@ setup: cleanup # Build, sign and bundle the project build: - conan install . -pr:h {{conan_profile}} -pr:b {{conan_profile}} -o signed={{codesign}} - conan build . -pr:h {{conan_profile}} -pr:b {{conan_profile}} -o signed={{codesign}} + conan install . -pr:h {{conan_profile}} -pr:b {{conan_profile}} + conan build . -pr:h {{conan_profile}} -pr:b {{conan_profile}} # Run Static Code Analysis sca: @@ -90,6 +93,41 @@ sca: run: open build/Release/peakeater_artefacts/Release/Standalone/peakeater.app -# Package an application as a Conan package and test it with test project +# Runs plugin as a standalone app +[windows] +run: + build/Release/peakeater_artefacts/Release/Standalone/peakeater.exe + +# Run a test suite +# macOS requires a special tratment because how Mac works with AU. +# macOS requires AU plugins to be registered in order to be discoverable. +# Registration means it is placed either in system or in user's AU folder and +# special utility is called. +# Discussion - https://github.com/Tracktion/pluginval/issues/39 +[macos] +test: + # In case there's no AU user dir + mkdir -p ~/Library/Audio/Plug-Ins/Components + # Cleanup leftovers from previous test + rm -rf ~/Library/Audio/Plug-Ins/Components/peakeater.component + # Copy AU to the user AU plugins folder(~/Library/Audio/Plug-Ins/Components) because macOS makes a scan there + cp -R build/Release/peakeater_artefacts/Release/AU/peakeater.component ~/Library/Audio/Plug-Ins/Components + # Trigger AudioComponentRegistrar and auval, this will force macOS to scan & register new AU plugin + killall -9 AudioComponentRegistrar + auval -a + # Finally, we can test. Source conanbuild.sh because path to pluginval is set there + source build/Release/generators/conanbuild.sh && ctest --progress --verbose --test-dir build/Release + # Cleanup at the end + rm -rf ~/Library/Audio/Plug-Ins/Components/peakeater.component + +# Run a test suite +[linux] +test: + # Source conanbuild.sh because path to pluginval is set there + . build/Release/generators/conanbuild.sh && ctest --progress --verbose --test-dir build/Release + +# Run a test suite +[windows] test: - conan export-pkg . -pr:h {{conan_profile}} -pr:b {{conan_profile}} -tf test + # Run conanbuild.ps1 because path to pluginval is set there + ./build/Release/generators/conanbuild.ps1; ctest --progress --verbose --test-dir build/Release diff --git a/test/conanfile.py b/test/conanfile.py deleted file mode 100644 index 7f3f6b83..00000000 --- a/test/conanfile.py +++ /dev/null @@ -1,154 +0,0 @@ -import os -import pathlib -import json -import subprocess - -from conan import ConanFile -from conan.tools.cmake import cmake_layout -from conan.tools.files import copy, rmdir -from conan.api.output import ConanOutput, Color - - -def log_conan_stage(f): - def wrapper(*args): - message = f"Calling {f.__name__}()" - ConanOutput().title(message) - args[0].output.highlight(message) - tmp = f(*args) - args[0].output.success(f"[{f.__name__}] Success!") - return tmp - - return wrapper - - -def mac_only(f): - def wrapper(*args): - if args[0].settings.os == "Macos": - return f(*args) - else: - args[0].output.info("This test is intended for macOS only, skipping") - - return wrapper - - -class Test(ConanFile): - settings = "os", "arch", "compiler", "build_type" - generators = "CMakeDeps" - - def requirements(self): - self.tool_requires("pluginval/1.0.3@pluginval/release") # type: ignore - self.requires(self.tested_reference_str) # type: ignore - - def build(self): - pass - - def layout(self): - self.cpp_info.set_property("tools.cmake.cmake_layout:build_folder_vars", ["settings.build_type"]) - cmake_layout(self) - - def test(self): - dependency = self.dependencies[self.tested_reference_str] - bin_dir = dependency.cpp_info.bindirs[0] - res_dir = dependency.cpp_info.resdirs[0] - self._log_dependency_under_test(dependency) - self._test_compile_commands(res_dir) - self._test_vst3(bin_dir) - self._test_au(bin_dir) - self._test_dmg(bin_dir) - - def _log_dependency_under_test(self, dependency): - ConanOutput().title(f"Under test: {dependency.ref.name}") - bin_dir = dependency.cpp_info.bindirs[0] - res_dir = dependency.cpp_info.resdirs[0] - self.output.highlight(f"Name: {dependency.ref.name}") - self.output.highlight(f"Channel: {dependency.ref.channel}") - self.output.highlight(f"Version: {dependency.ref.version}") - self.output.highlight(f"Revision: {dependency.ref.revision}") - self.output.highlight(f"Settings: {dependency.settings.serialize()}") - self.output.highlight(f"Options: {dependency.options.serialize()}") - self.output.highlight(f"Bin dir: {bin_dir}") - self.output.highlight(f"Res dir: {res_dir}") - self.output.highlight(f"Binaries: {os.listdir(bin_dir)}") - self.output.highlight(f"Resources: {os.listdir(res_dir)}") - - @log_conan_stage - def _test_compile_commands(self, res_dir): - compile_commands = "".join(filter(lambda a: "compile_commands.json" in a, os.listdir(res_dir))) - assert compile_commands, f"VST3 plugin not found in {res_dir}. What's actually inside: {str(os.listdir(res_dir))}" - self._test_compile_commands_macos_universal_bins(os.path.join(res_dir, compile_commands)) - self._test_compile_commands_macos_backwards_compatibility(os.path.join(res_dir, compile_commands)) - - @mac_only - @log_conan_stage - def _test_compile_commands_macos_universal_bins(self, compile_commands_path): - if self.settings.os == "Macos": # type: ignore - with open(compile_commands_path, encoding="utf-8") as f: - compile_commands = json.load(f) - for compilation_unit in compile_commands: - assert "-arch x86_64" in compilation_unit["command"] - assert "-arch arm64" in compilation_unit["command"] - else: - self.output.info("Non-macOS host, skipping") - - @mac_only - @log_conan_stage - def _test_compile_commands_macos_backwards_compatibility(self, compile_commands_path): - if self.settings.os == "Macos": # type: ignore - with open(compile_commands_path, encoding="utf-8") as f: - compile_commands = json.load(f) - for compilation_unit in compile_commands: - assert "-mmacosx-version-min=10.9" in compilation_unit["command"] - else: - self.output.info("Non-macOS host, skipping") - - @log_conan_stage - def _test_vst3(self, bin_dir): - """Test VST3 Plugin with pluginval""" - vst3 = "".join(filter(lambda a: ".vst3" in a, os.listdir(bin_dir))) - assert vst3, f"VST3 plugin not found in {bin_dir}. What's actually inside: {str(os.listdir(bin_dir))}" - self._pluginval(os.path.join(bin_dir, vst3)) - - @mac_only - @log_conan_stage - def _test_au(self, bin_dir): - """ - Test AU(.component) Plugin with pluginval - NOTE: Only relevant for macOS - NOTE: macOS requires AU plugins to be registered in order to be discoverable - NOTE: Registration means it is placed either in system or in user's AU folder - NOTE: Discussion - https://github.com/Tracktion/pluginval/issues/39 - """ - # Get AU plugin - au_plugin_name = "".join(filter(lambda a: ".component" in a, os.listdir(bin_dir))) - assert au_plugin_name, f"AU plugin not found in {bin_dir}. What's actually inside: {str(os.listdir(bin_dir))}" - # Copy it to user plugins folder(~/Library/Audio/Plug-Ins/Components) - user_au_plugins = os.path.join(pathlib.Path.home(), "Library", "Audio", "Plug-Ins", "Components") - rmdir(self, os.path.join(user_au_plugins, au_plugin_name)) - copy(self, "*", src=os.path.join(bin_dir, au_plugin_name), dst=os.path.join(user_au_plugins, au_plugin_name)) - self.run(f"file {os.path.join(user_au_plugins, au_plugin_name)}") # Verify it is copied - # Trigger AudioComponentRegistrar and auval, this will force macOS to register it - self.run("killall -9 AudioComponentRegistrar", ignore_errors=True) - self.run("auval -a", ignore_errors=True) - # Finally, test it from user plugin folder - self._pluginval(os.path.join(user_au_plugins, au_plugin_name)) - # Cleanup after the test - rmdir(self, os.path.join(user_au_plugins, au_plugin_name)) - - def _pluginval(self, plugin): - # Max level - strictness = 10 - # GUI does not work on a CI(except Windows) - skip_gui_test = "--skip-gui-tests" if self.settings.os in ["Macos", "Linux"] else "" # type: ignore - output_dir = os.path.join(self.build_folder, "pluginval") # type: ignore - self.run(f"pluginval --strictness-level {strictness} --verbose {skip_gui_test} --validate-in-process --output-dir {output_dir} --validate {plugin}") - - @mac_only - @log_conan_stage - def _test_dmg(self, bin_dir): - dmg = "".join(filter(lambda a: ".dmg" in a, os.listdir(bin_dir))) - assert dmg, f"DMG image not found in {bin_dir}. What's actually inside: {str(os.listdir(bin_dir))}" - process = subprocess.Popen(["codesign", "-dv", "--verify", "--verbose=4", os.path.join(bin_dir, dmg)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (_, stderr) = process.communicate() - assert 0 == process.returncode, f"codesign returned non-zero exit code: {process.returncode}. Stderr: {stderr.decode()}" - assert "valid on disk" in stderr.decode(), f"DMG file {os.path.join(bin_dir, dmg)} is not valid" - assert "satisfies its Designated Requirement" in stderr.decode(), f"DMG file {os.path.join(bin_dir, dmg)} has invalid signature" diff --git a/test/test_compile_options.py b/test/test_compile_options.py new file mode 100644 index 00000000..69171642 --- /dev/null +++ b/test/test_compile_options.py @@ -0,0 +1,39 @@ +import unittest +import sys +import argparse +import json + +global compile_commands_path + +IS_MACOS = sys.platform == "darwin" + + +class TestCompileOptions(unittest.TestCase): + @unittest.skipUnless(IS_MACOS, "Only relevant for macOS") + def test_macos_min_version(self): + """macOS establishes backwards compatibility through compile options""" + with open(compile_commands_path, encoding="utf-8") as f: + compile_commands = json.load(f) + for compilation_unit in compile_commands: + assert "-mmacosx-version-min=10.9" in compilation_unit["command"] + + @unittest.skipUnless(IS_MACOS, "Only relevant for macOS") + def test_macos_universal_binaries(self): + """macOS supports Universal Binaries that works for both Intel and Mx""" + with open(compile_commands_path, encoding="utf-8") as f: + compile_commands = json.load(f) + for compilation_unit in compile_commands: + assert "-arch x86_64" in compilation_unit["command"] + assert "-arch arm64" in compilation_unit["command"] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--compile_commands", required=True) + # This is a hack to make custom parameters work + # See https://stackoverflow.com/a/8660290 + parser.add_argument("unittest_args", nargs="*") + args = parser.parse_args() + compile_commands_path = args.compile_commands + sys.argv[1:] = args.unittest_args + unittest.main(argv=sys.argv, verbosity=2)