├── .clang-format ├── .gersemirc ├── .github ├── actions │ ├── build-plugin │ │ └── action.yaml │ ├── check-changes │ │ └── action.yaml │ ├── package-plugin │ │ └── action.yaml │ ├── run-clang-format │ │ └── action.yaml │ ├── run-gersemi │ │ └── action.yaml │ └── setup-macos-codesigning │ │ └── action.yaml ├── scripts │ ├── .Aptfile │ ├── .Brewfile │ ├── Build-Windows.ps1 │ ├── Package-Windows.ps1 │ ├── build-macos │ ├── build-ubuntu │ ├── package-macos │ ├── package-ubuntu │ ├── utils.pwsh │ │ ├── Ensure-Location.ps1 │ │ ├── Invoke-External.ps1 │ │ └── Logger.ps1 │ └── utils.zsh │ │ ├── check_macos │ │ ├── check_ubuntu │ │ ├── log_debug │ │ ├── log_error │ │ ├── log_group │ │ ├── log_info │ │ ├── log_output │ │ ├── log_status │ │ ├── log_warning │ │ ├── mkcd │ │ ├── set_loglevel │ │ └── setup_ubuntu └── workflows │ ├── build-project.yaml │ ├── check-format.yaml │ ├── dispatch.yaml │ ├── pr-pull.yaml │ └── push.yaml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── PLUGIN-TEMPLATE.md ├── README.md ├── build-aux ├── .functions │ ├── log_debug │ ├── log_error │ ├── log_group │ ├── log_info │ ├── log_output │ ├── log_status │ ├── log_warning │ └── set_loglevel ├── .run-format.zsh ├── run-clang-format ├── run-gersemi └── run-swift-format ├── buildspec.json ├── cmake ├── common │ ├── bootstrap.cmake │ ├── buildnumber.cmake │ ├── buildspec_common.cmake │ ├── ccache.cmake │ ├── compiler_common.cmake │ ├── helpers_common.cmake │ └── osconfig.cmake ├── linux │ ├── compilerconfig.cmake │ ├── defaults.cmake │ └── helpers.cmake ├── macos │ ├── buildspec.cmake │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ ├── resources │ │ ├── ccache-launcher-c.in │ │ ├── ccache-launcher-cxx.in │ │ ├── create-package.cmake.in │ │ ├── distribution.in │ │ └── installer-macos.pkgproj.in │ └── xcode.cmake └── windows │ ├── buildspec.cmake │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ └── resources │ ├── installer-Windows.iss.in │ └── resource.rc.in ├── data └── locale │ └── en-US.ini └── src ├── media-playlist-source.c ├── media-playlist-source.h ├── playlist.h ├── plugin-main.c ├── plugin-support.c.in ├── plugin-support.h ├── shuffler.c └── shuffler.h /.clang-format: -------------------------------------------------------------------------------- 1 | # please use clang-format version 16 or later 2 | 3 | Standard: c++17 4 | AccessModifierOffset: -8 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: false 12 | AllowAllConstructorInitializersOnNextLine: false 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: false 18 | AllowShortLambdasOnASingleLine: Inline 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: false 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BraceWrapping: 27 | AfterClass: false 28 | AfterControlStatement: false 29 | AfterEnum: false 30 | AfterFunction: true 31 | AfterNamespace: false 32 | AfterObjCDeclaration: false 33 | AfterStruct: false 34 | AfterUnion: false 35 | AfterExternBlock: false 36 | BeforeCatch: false 37 | BeforeElse: false 38 | IndentBraces: false 39 | SplitEmptyFunction: true 40 | SplitEmptyRecord: true 41 | SplitEmptyNamespace: true 42 | BreakBeforeBinaryOperators: None 43 | BreakBeforeBraces: Custom 44 | BreakBeforeTernaryOperators: true 45 | BreakConstructorInitializers: BeforeColon 46 | BreakStringLiterals: false # apparently unpredictable 47 | ColumnLimit: 120 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 50 | ConstructorInitializerIndentWidth: 8 51 | ContinuationIndentWidth: 8 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | FixNamespaceComments: true 56 | ForEachMacros: 57 | - 'json_object_foreach' 58 | - 'json_object_foreach_safe' 59 | - 'json_array_foreach' 60 | - 'HASH_ITER' 61 | IncludeBlocks: Preserve 62 | IndentCaseLabels: false 63 | IndentPPDirectives: None 64 | IndentWidth: 8 65 | IndentWrappedFunctionNames: false 66 | KeepEmptyLinesAtTheStartOfBlocks: true 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | ObjCBinPackProtocolList: Auto 70 | ObjCBlockIndentWidth: 8 71 | ObjCSpaceAfterProperty: true 72 | ObjCSpaceBeforeProtocolList: true 73 | 74 | PenaltyBreakAssignment: 10 75 | PenaltyBreakBeforeFirstCallParameter: 30 76 | PenaltyBreakComment: 10 77 | PenaltyBreakFirstLessLess: 0 78 | PenaltyBreakString: 10 79 | PenaltyExcessCharacter: 100 80 | PenaltyReturnTypeOnItsOwnLine: 60 81 | 82 | PointerAlignment: Right 83 | ReflowComments: false 84 | SortIncludes: false 85 | SortUsingDeclarations: false 86 | SpaceAfterCStyleCast: false 87 | SpaceAfterLogicalNot: false 88 | SpaceAfterTemplateKeyword: false 89 | SpaceBeforeAssignmentOperators: true 90 | SpaceBeforeCtorInitializerColon: true 91 | SpaceBeforeInheritanceColon: true 92 | SpaceBeforeParens: ControlStatements 93 | SpaceBeforeRangeBasedForLoopColon: true 94 | SpaceInEmptyParentheses: false 95 | SpacesBeforeTrailingComments: 1 96 | SpacesInAngles: false 97 | SpacesInCStyleCastParentheses: false 98 | SpacesInContainerLiterals: false 99 | SpacesInParentheses: false 100 | SpacesInSquareBrackets: false 101 | StatementMacros: 102 | - 'Q_OBJECT' 103 | TabWidth: 8 104 | TypenameMacros: 105 | - 'DARRAY' 106 | UseTab: ForContinuationAndIndentation 107 | --- 108 | Language: ObjC 109 | AccessModifierOffset: 2 110 | AlignArrayOfStructures: Right 111 | AlignConsecutiveAssignments: None 112 | AlignConsecutiveBitFields: None 113 | AlignConsecutiveDeclarations: None 114 | AlignConsecutiveMacros: 115 | Enabled: true 116 | AcrossEmptyLines: false 117 | AcrossComments: true 118 | AllowShortBlocksOnASingleLine: Never 119 | AllowShortEnumsOnASingleLine: false 120 | AllowShortFunctionsOnASingleLine: Empty 121 | AllowShortIfStatementsOnASingleLine: Never 122 | AllowShortLambdasOnASingleLine: None 123 | AttributeMacros: ['__unused', '__autoreleasing', '_Nonnull', '__bridge'] 124 | BitFieldColonSpacing: Both 125 | #BreakBeforeBraces: Webkit 126 | BreakBeforeBraces: Custom 127 | BraceWrapping: 128 | AfterCaseLabel: false 129 | AfterClass: true 130 | AfterControlStatement: Never 131 | AfterEnum: false 132 | AfterFunction: true 133 | AfterNamespace: false 134 | AfterObjCDeclaration: false 135 | AfterStruct: false 136 | AfterUnion: false 137 | AfterExternBlock: false 138 | BeforeCatch: false 139 | BeforeElse: false 140 | BeforeLambdaBody: false 141 | BeforeWhile: false 142 | IndentBraces: false 143 | SplitEmptyFunction: false 144 | SplitEmptyRecord: false 145 | SplitEmptyNamespace: true 146 | BreakAfterAttributes: Never 147 | BreakArrays: false 148 | BreakBeforeConceptDeclarations: Allowed 149 | BreakBeforeInlineASMColon: OnlyMultiline 150 | BreakConstructorInitializers: AfterColon 151 | BreakInheritanceList: AfterComma 152 | ColumnLimit: 120 153 | ConstructorInitializerIndentWidth: 4 154 | ContinuationIndentWidth: 4 155 | EmptyLineAfterAccessModifier: Never 156 | EmptyLineBeforeAccessModifier: LogicalBlock 157 | ExperimentalAutoDetectBinPacking: false 158 | FixNamespaceComments: true 159 | IndentAccessModifiers: false 160 | IndentCaseBlocks: false 161 | IndentCaseLabels: true 162 | IndentExternBlock: Indent 163 | IndentGotoLabels: false 164 | IndentRequiresClause: true 165 | IndentWidth: 4 166 | IndentWrappedFunctionNames: true 167 | InsertBraces: false 168 | InsertNewlineAtEOF: true 169 | KeepEmptyLinesAtTheStartOfBlocks: false 170 | LambdaBodyIndentation: Signature 171 | NamespaceIndentation: All 172 | ObjCBinPackProtocolList: Auto 173 | ObjCBlockIndentWidth: 4 174 | ObjCBreakBeforeNestedBlockParam: false 175 | ObjCSpaceAfterProperty: true 176 | ObjCSpaceBeforeProtocolList: true 177 | PPIndentWidth: -1 178 | PackConstructorInitializers: NextLine 179 | QualifierAlignment: Leave 180 | ReferenceAlignment: Right 181 | RemoveSemicolon: false 182 | RequiresClausePosition: WithPreceding 183 | RequiresExpressionIndentation: OuterScope 184 | SeparateDefinitionBlocks: Always 185 | ShortNamespaceLines: 1 186 | SortIncludes: false 187 | #SortUsingDeclarations: LexicographicNumeric 188 | SortUsingDeclarations: true 189 | SpaceAfterCStyleCast: true 190 | SpaceAfterLogicalNot: false 191 | SpaceAroundPointerQualifiers: Default 192 | SpaceBeforeCaseColon: false 193 | SpaceBeforeCpp11BracedList: true 194 | SpaceBeforeCtorInitializerColon: true 195 | SpaceBeforeInheritanceColon: true 196 | SpaceBeforeParens: ControlStatements 197 | SpaceBeforeRangeBasedForLoopColon: true 198 | SpaceBeforeSquareBrackets: false 199 | SpaceInEmptyBlock: false 200 | SpaceInEmptyParentheses: false 201 | SpacesBeforeTrailingComments: 2 202 | SpacesInConditionalStatement: false 203 | SpacesInLineCommentPrefix: 204 | Minimum: 1 205 | Maximum: -1 206 | Standard: c++17 207 | TabWidth: 4 208 | UseTab: Never 209 | -------------------------------------------------------------------------------- /.gersemirc: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/master/gersemi/configuration.schema.json 2 | 3 | color: false 4 | definitions: [] 5 | line_length: 120 6 | indent: 2 7 | list_expansion: favour-inlining 8 | quiet: false 9 | unsafe: false 10 | workers: 10 11 | warn_about_unknown_commands: false 12 | -------------------------------------------------------------------------------- /.github/actions/build-plugin/action.yaml: -------------------------------------------------------------------------------- 1 | name: Set up and build plugin 2 | description: Builds the plugin for specified architecture and build config 3 | inputs: 4 | target: 5 | description: Target architecture for dependencies 6 | required: true 7 | config: 8 | description: Build configuration 9 | required: false 10 | default: RelWithDebInfo 11 | codesign: 12 | description: Enable codesigning (macOS only) 13 | required: false 14 | default: 'false' 15 | codesignIdent: 16 | description: Developer ID for application codesigning (macOS only) 17 | required: false 18 | default: '-' 19 | workingDirectory: 20 | description: Working directory for packaging 21 | required: false 22 | default: ${{ github.workspace }} 23 | runs: 24 | using: composite 25 | steps: 26 | - name: Run macOS Build 27 | if: runner.os == 'macOS' 28 | shell: zsh --no-rcs --errexit --pipefail {0} 29 | working-directory: ${{ inputs.workingDirectory }} 30 | env: 31 | CCACHE_DIR: ${{ inputs.workingDirectory }}/.ccache 32 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 33 | CODESIGN_TEAM: ${{ inputs.codesignTeam }} 34 | run: | 35 | : Run macOS Build 36 | 37 | local -a build_args=(--config ${{ inputs.config }}) 38 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 39 | 40 | if [[ '${{ inputs.codesign }}' == 'true' ]] build_args+=(--codesign) 41 | 42 | .github/scripts/build-macos ${build_args} 43 | 44 | - name: Install Dependencies 🛍️ 45 | if: runner.os == 'Linux' 46 | shell: bash 47 | run: | 48 | : Install Dependencies 🛍️ 49 | echo ::group::Install Dependencies 50 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 51 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 52 | brew install --quiet zsh 53 | echo ::endgroup:: 54 | 55 | - name: Run Ubuntu Build 56 | if: runner.os == 'Linux' 57 | shell: zsh --no-rcs --errexit --pipefail {0} 58 | working-directory: ${{ inputs.workingDirectory }} 59 | env: 60 | CCACHE_DIR: ${{ inputs.workingDirectory }}/.ccache 61 | run: | 62 | : Run Ubuntu Build 63 | 64 | local -a build_args=( 65 | --target ubuntu-${{ inputs.target }} 66 | --config ${{ inputs.config }} 67 | ) 68 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 69 | 70 | .github/scripts/build-ubuntu ${build_args} 71 | 72 | - name: Run Windows Build 73 | if: runner.os == 'Windows' 74 | shell: pwsh 75 | run: | 76 | # Run Windows Build 77 | if ( $Env:RUNNER_DEBUG -ne $null ) { 78 | Set-PSDebug -Trace 1 79 | } 80 | 81 | $BuildArgs = @{ 82 | Target = '${{ inputs.target }}' 83 | Configuration = '${{ inputs.config }}' 84 | } 85 | 86 | .github/scripts/Build-Windows.ps1 @BuildArgs 87 | 88 | - name: Create Summary 📊 89 | if: contains(fromJSON('["Linux", "macOS"]'),runner.os) 90 | shell: zsh --no-rcs --errexit --pipefail {0} 91 | env: 92 | CCACHE_DIR: ${{ inputs.workingDirectory }}/.ccache 93 | run: | 94 | : Create Summary 📊 95 | 96 | local -a ccache_data 97 | if (( ${+RUNNER_DEBUG} )) { 98 | setopt XTRACE 99 | ccache_data=("${(fA)$(ccache -s -vv)}") 100 | } else { 101 | ccache_data=("${(fA)$(ccache -s)}") 102 | } 103 | 104 | print '### ${{ runner.os }} Ccache Stats (${{ inputs.target }})' >> $GITHUB_STEP_SUMMARY 105 | print '```' >> $GITHUB_STEP_SUMMARY 106 | for line (${ccache_data}) { 107 | print ${line} >> $GITHUB_STEP_SUMMARY 108 | } 109 | print '```' >> $GITHUB_STEP_SUMMARY 110 | -------------------------------------------------------------------------------- /.github/actions/check-changes/action.yaml: -------------------------------------------------------------------------------- 1 | name: Check For Changed Files 2 | description: Checks for changed files compared to specific git reference and glob expression 3 | inputs: 4 | baseRef: 5 | description: Git reference to check against 6 | required: false 7 | ref: 8 | description: Git reference to check with 9 | required: false 10 | default: HEAD 11 | checkGlob: 12 | description: Glob expression to limit check to specific files 13 | required: false 14 | useFallback: 15 | description: Use fallback compare against prior commit 16 | required: false 17 | default: 'true' 18 | diffFilter: 19 | description: git diff-filter string to use 20 | required: false 21 | default: '' 22 | outputs: 23 | hasChangedFiles: 24 | value: ${{ steps.checks.outputs.hasChangedFiles }} 25 | description: True if specified files were changed in comparison to specified git reference 26 | changedFiles: 27 | value: ${{ steps.checks.outputs.changedFiles }} 28 | description: List of changed files 29 | runs: 30 | using: composite 31 | steps: 32 | - name: Check For Changed Files ✅ 33 | shell: bash 34 | id: checks 35 | env: 36 | GIT_BASE_REF: ${{ inputs.baseRef }} 37 | GIT_REF: ${{ inputs.ref }} 38 | GITHUB_EVENT_FORCED: ${{ github.event.forced }} 39 | GITHUB_REF_BEFORE: ${{ github.event.before }} 40 | USE_FALLBACK: ${{ inputs.useFallback }} 41 | DIFF_FILTER: ${{ inputs.diffFilter }} 42 | run: | 43 | : Check for Changed Files ✅ 44 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 45 | shopt -s extglob 46 | shopt -s dotglob 47 | 48 | if [[ "${GIT_BASE_REF}" ]]; then 49 | if ! git cat-file -e "${GIT_BASE_REF}" &> /dev/null; then 50 | echo "::warning::Provided base reference ${GIT_BASE_REF} is invalid" 51 | if [[ "${USE_FALLBACK}" == 'true' ]]; then 52 | GIT_BASE_REF='HEAD~1' 53 | fi 54 | fi 55 | else 56 | if ! git cat-file -e ${GITHUB_REF_BEFORE} &> /dev/null; then 57 | GITHUB_REF_BEFORE='4b825dc642cb6eb9a060e54bf8d69288fbee4904' 58 | fi 59 | 60 | GIT_BASE_REF='HEAD~1' 61 | case "${GITHUB_EVENT_NAME}" in 62 | pull_request) GIT_BASE_REF="origin/${GITHUB_BASE_REF}" ;; 63 | push) if [[ "${GITHUB_EVENT_FORCED}" != 'true' ]]; then GIT_BASE_REF="${GITHUB_REF_BEFORE}"; fi ;; 64 | *) ;; 65 | esac 66 | fi 67 | 68 | changes=($(git diff --name-only --diff-filter="${DIFF_FILTER}" ${GIT_BASE_REF} ${GIT_REF} -- ${{ inputs.checkGlob }})) 69 | 70 | if (( ${#changes[@]} )); then 71 | file_string="${changes[*]}" 72 | echo "hasChangedFiles=true" >> $GITHUB_OUTPUT 73 | echo "changedFiles=[\"${file_string// /\",\"}\"]" >> $GITHUB_OUTPUT 74 | else 75 | echo "hasChangedFiles=false" >> $GITHUB_OUTPUT 76 | echo "changedFiles=[]" >> GITHUB_OUTPUT 77 | fi 78 | -------------------------------------------------------------------------------- /.github/actions/package-plugin/action.yaml: -------------------------------------------------------------------------------- 1 | name: Package plugin 2 | description: Packages the plugin for specified architecture and build config. 3 | inputs: 4 | target: 5 | description: Build target for dependencies 6 | required: true 7 | config: 8 | description: Build configuration 9 | required: false 10 | default: RelWithDebInfo 11 | codesign: 12 | description: Enable codesigning (macOS only) 13 | required: false 14 | default: 'false' 15 | notarize: 16 | description: Enable notarization (macOS only) 17 | required: false 18 | default: 'false' 19 | codesignIdent: 20 | description: Developer ID for application codesigning (macOS only) 21 | required: false 22 | default: '-' 23 | installerIdent: 24 | description: Developer ID for installer package codesigning (macOS only) 25 | required: false 26 | default: '' 27 | codesignTeam: 28 | description: Developer team for codesigning (macOS only) 29 | required: false 30 | default: '' 31 | codesignUser: 32 | description: Apple ID username for notarization (macOS only) 33 | required: false 34 | default: '' 35 | codesignPass: 36 | description: Apple ID password for notarization (macOS only) 37 | required: false 38 | default: '' 39 | package: 40 | description: Create Windows or macOS installation package 41 | required: false 42 | default: 'false' 43 | workingDirectory: 44 | description: Working directory for packaging 45 | required: false 46 | default: ${{ github.workspace }} 47 | runs: 48 | using: composite 49 | steps: 50 | - name: Run macOS Packaging 51 | if: runner.os == 'macOS' 52 | shell: zsh --no-rcs --errexit --pipefail {0} 53 | working-directory: ${{ inputs.workingDirectory }} 54 | env: 55 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 56 | CODESIGN_IDENT_INSTALLER: ${{ inputs.installerIdent }} 57 | CODESIGN_TEAM: ${{ inputs.codesignTeam }} 58 | CODESIGN_IDENT_USER: ${{ inputs.codesignUser }} 59 | CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }} 60 | run: | 61 | : Run macOS Packaging 62 | 63 | local -a package_args=(--config ${{ inputs.config }}) 64 | if (( ${+RUNNER_DEBUG} )) package_args+=(--debug) 65 | 66 | if [[ '${{ inputs.codesign }}' == 'true' ]] package_args+=(--codesign) 67 | if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(--notarize) 68 | if [[ '${{ inputs.package }}' == 'true' ]] package_args+=(--package) 69 | 70 | .github/scripts/package-macos ${package_args} 71 | 72 | - name: Install Dependencies 🛍️ 73 | if: runner.os == 'Linux' 74 | shell: bash 75 | run: | 76 | : Install Dependencies 🛍️ 77 | echo ::group::Install Dependencies 78 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 79 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 80 | brew install --quiet zsh 81 | echo ::endgroup:: 82 | 83 | - name: Run Ubuntu Packaging 84 | if: runner.os == 'Linux' 85 | shell: zsh --no-rcs --errexit --pipefail {0} 86 | working-directory: ${{ inputs.workingDirectory }} 87 | run: | 88 | : Run Ubuntu Packaging 89 | package_args=( 90 | --target ubuntu-${{ inputs.target }} 91 | --config ${{ inputs.config }} 92 | ) 93 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 94 | 95 | if [[ '${{ inputs.package }}' == 'true' ]] package_args+=(--package) 96 | 97 | .github/scripts/package-ubuntu ${package_args} 98 | 99 | - name: Run Windows Packaging 100 | if: runner.os == 'Windows' 101 | shell: pwsh 102 | run: | 103 | # Run Windows Packaging 104 | if ( $Env:RUNNER_DEBUG -ne $null ) { 105 | Set-PSDebug -Trace 1 106 | } 107 | 108 | $PackageArgs = @{ 109 | Target = '${{ inputs.target }}' 110 | Configuration = '${{ inputs.config }}' 111 | } 112 | 113 | if ( '${{ inputs.package }}' -eq 'true' ) { 114 | $PackageArgs += @{BuildInstaller = $true} 115 | } 116 | 117 | .github/scripts/Package-Windows.ps1 @PackageArgs 118 | -------------------------------------------------------------------------------- /.github/actions/run-clang-format/action.yaml: -------------------------------------------------------------------------------- 1 | name: Run clang-format 2 | description: Runs clang-format and checks for any changes introduced by it 3 | inputs: 4 | failCondition: 5 | description: Controls whether failed checks also fail the workflow run 6 | required: false 7 | default: never 8 | workingDirectory: 9 | description: Working directory for checks 10 | required: false 11 | default: ${{ github.workspace }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Check Runner Operating System 🏃‍♂️ 16 | if: runner.os == 'Windows' 17 | shell: bash 18 | run: | 19 | : Check Runner Operating System 🏃‍♂️ 20 | echo "::notice::run-clang-format action requires a macOS-based or Linux-based runner." 21 | exit 2 22 | 23 | - name: Check for Changed Files ✅ 24 | uses: ./.github/actions/check-changes 25 | id: checks 26 | with: 27 | checkGlob: "'*.c' '*.h' '*.cpp' '*.hpp' '*.m' '*.mm'" 28 | diffFilter: 'ACM' 29 | 30 | - name: Install Dependencies 🛍️ 31 | if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles) 32 | shell: bash 33 | run: | 34 | : Install Dependencies 🛍️ 35 | echo ::group::Install Dependencies 36 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 37 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 38 | echo "/home/linuxbrew/.linuxbrew/opt/clang-format@17/bin" >> $GITHUB_PATH 39 | brew install --quiet zsh 40 | echo ::endgroup:: 41 | 42 | - name: Run clang-format 🐉 43 | if: fromJSON(steps.checks.outputs.hasChangedFiles) 44 | id: result 45 | shell: zsh --no-rcs --errexit --pipefail {0} 46 | working-directory: ${{ inputs.workingDirectory }} 47 | env: 48 | CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }} 49 | run: | 50 | : Run clang-format 🐉 51 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 52 | 53 | print ::group::Install clang-format-17 54 | brew install --quiet obsproject/tools/clang-format@17 55 | print ::endgroup:: 56 | 57 | print ::group::Run clang-format-17 58 | local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/}) 59 | ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check ${changes} 60 | print ::endgroup:: 61 | -------------------------------------------------------------------------------- /.github/actions/run-gersemi/action.yaml: -------------------------------------------------------------------------------- 1 | name: Run gersemi 2 | description: Runs gersemi and checks for any changes introduced by it 3 | inputs: 4 | failCondition: 5 | description: Controls whether failed checks also fail the workflow run 6 | required: false 7 | default: never 8 | workingDirectory: 9 | description: Working directory for checks 10 | required: false 11 | default: ${{ github.workspace }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Check Runner Operating System 🏃‍♂️ 16 | if: runner.os == 'Windows' 17 | shell: bash 18 | run: | 19 | : Check Runner Operating System 🏃‍♂️ 20 | echo "::notice::run-gersemi action requires a macOS-based or Linux-based runner." 21 | exit 2 22 | 23 | - name: Check for Changed Files ✅ 24 | uses: ./.github/actions/check-changes 25 | id: checks 26 | with: 27 | checkGlob: "'*.cmake' '*CMakeLists.txt'" 28 | diffFilter: 'ACM' 29 | 30 | - name: Install Dependencies 🛍️ 31 | if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles) 32 | shell: bash 33 | run: | 34 | : Install Dependencies 🛍️ 35 | echo ::group::Install Dependencies 36 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 37 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 38 | brew install --quiet zsh 39 | echo ::endgroup:: 40 | 41 | - name: Run gersemi 🎛️ 42 | if: fromJSON(steps.checks.outputs.hasChangedFiles) 43 | id: result 44 | shell: zsh --no-rcs --errexit --pipefail {0} 45 | working-directory: ${{ github.workspace }} 46 | env: 47 | CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }} 48 | run: | 49 | : Run gersemi 🎛️ 50 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 51 | 52 | print ::group::Install gersemi 53 | brew install --quiet obsproject/tools/gersemi 54 | print ::endgroup:: 55 | 56 | print ::group::Run gersemi 57 | local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/}) 58 | ./build-aux/run-gersemi --fail-${{ inputs.failCondition }} --check ${changes} 59 | print ::endgroup:: 60 | -------------------------------------------------------------------------------- /.github/actions/setup-macos-codesigning/action.yaml: -------------------------------------------------------------------------------- 1 | name: Set up macOS codesigning 2 | description: Sets up code signing certificates, provisioning profiles, and notarization information 3 | inputs: 4 | codesignIdentity: 5 | description: Codesigning identity 6 | required: true 7 | installerIdentity: 8 | description: Codesigning identity for package installer 9 | required: false 10 | codesignCertificate: 11 | description: PKCS12 certificate in base64 format 12 | required: true 13 | certificatePassword: 14 | description: Password required to install PKCS12 certificate 15 | required: true 16 | keychainPassword: 17 | description: Password to use for temporary keychain 18 | required: false 19 | notarizationUser: 20 | description: Apple ID to use for notarization 21 | required: false 22 | notarizationPassword: 23 | description: Application password for notarization 24 | provisioningProfile: 25 | description: Provisioning profile in base64 format 26 | required: false 27 | outputs: 28 | haveCodesignIdent: 29 | description: True if necessary codesigning credentials were found 30 | value: ${{ steps.codesign.outputs.haveCodesignIdent }} 31 | haveProvisioningProfile: 32 | description: True if necessary provisioning profile credentials were found 33 | value: ${{ steps.provisioning.outputs.haveProvisioningProfile || steps.codesign.outputs.haveProvisioningProfile }} 34 | provisioningProfileUUID: 35 | description: UUID of imported provisioning profile 36 | value: ${{ steps.provisioning.outputs.provisioningProfileUUID }} 37 | haveNotarizationUser: 38 | description: True if necessary notarization credentials were found 39 | value: ${{ steps.notarization.outputs.haveNotarizationUser || steps.codesign.outputs.haveNotarizationUser }} 40 | codesignIdent: 41 | description: Codesigning identity 42 | value: ${{ steps.codesign.outputs.codesignIdent }} 43 | installerIdent: 44 | description: Codesigning identity for package installer 45 | value: ${{ steps.codesign.outputs.installerIdent }} 46 | codesignTeam: 47 | description: Codesigning team 48 | value: ${{ steps.codesign.outputs.codesignTeam }} 49 | runs: 50 | using: composite 51 | steps: 52 | - name: Check Runner Operating System 🏃‍♂️ 53 | if: runner.os != 'macOS' 54 | shell: bash 55 | run: | 56 | : Check Runner Operating System 🏃‍♂️ 57 | echo "setup-macos-codesigning action requires a macOS-based runner." 58 | exit 2 59 | 60 | - name: macOS Codesigning ✍️ 61 | shell: zsh --no-rcs --errexit --pipefail {0} 62 | id: codesign 63 | env: 64 | MACOS_SIGNING_IDENTITY: ${{ inputs.codesignIdentity }} 65 | MACOS_SIGNING_IDENTITY_INSTALLER: ${{ inputs.installerIdentity}} 66 | MACOS_SIGNING_CERT: ${{ inputs.codesignCertificate }} 67 | MACOS_SIGNING_CERT_PASSWORD: ${{ inputs.certificatePassword }} 68 | MACOS_KEYCHAIN_PASSWORD: ${{ inputs.keychainPassword }} 69 | run: | 70 | : macOS Code Signing ✍️ 71 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 72 | 73 | if [[ ${MACOS_SIGNING_IDENTITY} && ${MACOS_SIGNING_IDENTITY_INSTALLER} && ${MACOS_SIGNING_CERT} ]] { 74 | print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT 75 | 76 | local -r certificate_path="${RUNNER_TEMP}/build_certificate.p12" 77 | local -r keychain_path="${RUNNER_TEMP}/app-signing.keychain-db" 78 | 79 | print -n "${MACOS_SIGNING_CERT}" | base64 --decode --output=${certificate_path} 80 | 81 | : "${MACOS_KEYCHAIN_PASSWORD:="$(print ${RANDOM} | shasum | head -c 32)"}" 82 | 83 | print '::group::Keychain setup' 84 | security create-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" ${keychain_path} 85 | security set-keychain-settings -lut 21600 ${keychain_path} 86 | security unlock-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" ${keychain_path} 87 | 88 | security import "${certificate_path}" -P "${MACOS_SIGNING_CERT_PASSWORD}" -A \ 89 | -t cert -f pkcs12 -k ${keychain_path} \ 90 | -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/xcrun 91 | 92 | security set-key-partition-list -S 'apple-tool:,apple:' -k "${MACOS_KEYCHAIN_PASSWORD}" \ 93 | ${keychain_path} &> /dev/null 94 | 95 | security list-keychain -d user -s ${keychain_path} 'login-keychain' 96 | print '::endgroup::' 97 | 98 | local -r team_id="${${MACOS_SIGNING_IDENTITY##* }//(\(|\))/}" 99 | 100 | print "codesignIdent=${MACOS_SIGNING_IDENTITY}" >> $GITHUB_OUTPUT 101 | print "installerIdent=${MACOS_SIGNING_IDENTITY_INSTALLER}" >> $GITHUB_OUTPUT 102 | print "MACOS_KEYCHAIN_PASSWORD=${MACOS_KEYCHAIN_PASSWORD}" >> $GITHUB_ENV 103 | print "codesignTeam=${team_id}" >> $GITHUB_OUTPUT 104 | } else { 105 | print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT 106 | print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT 107 | print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT 108 | } 109 | 110 | - name: Provisioning Profile 👤 111 | shell: zsh --no-rcs --errexit --pipefail {0} 112 | id: provisioning 113 | if: ${{ fromJSON(steps.codesign.outputs.haveCodesignIdent) }} 114 | env: 115 | MACOS_SIGNING_PROVISIONING_PROFILE: ${{ inputs.provisioningProfile }} 116 | run: | 117 | : Provisioning Profile 👤 118 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 119 | 120 | if [[ "${MACOS_SIGNING_PROVISIONING_PROFILE}" ]] { 121 | print 'haveProvisioningProfile=true' >> $GITHUB_OUTPUT 122 | 123 | local -r profile_path="${RUNNER_TEMP}/build_profile.provisionprofile" 124 | print -n "${MACOS_SIGNING_PROVISIONING_PROFILE}" \ 125 | | base64 --decode --output="${profile_path}" 126 | 127 | print '::group::Provisioning Profile Setup' 128 | mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles 129 | security cms -D -i ${profile_path} -o ${RUNNER_TEMP}/build_profile.plist 130 | local -r uuid="$(plutil -extract UUID raw ${RUNNER_TEMP}/build_profile.plist)" 131 | local -r team_id="$(plutil -extract TeamIdentifier.0 raw -expect string ${RUNNER_TEMP}/build_profile.plist)" 132 | 133 | if [[ ${team_id} != '${{ steps.codesign.outputs.codesignTeam }}' ]] { 134 | print '::notice::Code Signing team in provisioning profile does not match certificate.' 135 | } 136 | 137 | cp ${profile_path} ~/Library/MobileDevice/Provisioning\ Profiles/${uuid}.provisionprofile 138 | print "provisioningProfileUUID=${uuid}" >> $GITHUB_OUTPUT 139 | print '::endgroup::' 140 | } else { 141 | print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT 142 | } 143 | 144 | - name: Notarization 🧑‍💼 145 | id: notarization 146 | if: fromJSON(steps.codesign.outputs.haveCodesignIdent) 147 | shell: zsh --no-rcs --errexit --pipefail {0} 148 | env: 149 | MACOS_NOTARIZATION_USERNAME: ${{ inputs.notarizationUser }} 150 | MACOS_NOTARIZATION_PASSWORD: ${{ inputs.notarizationPassword }} 151 | run: | 152 | : Notarization 🧑‍💼 153 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 154 | 155 | if [[ ${MACOS_NOTARIZATION_USERNAME} && ${MACOS_NOTARIZATION_PASSWORD} ]] { 156 | print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT 157 | } else { 158 | print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT 159 | } 160 | -------------------------------------------------------------------------------- /.github/scripts/.Aptfile: -------------------------------------------------------------------------------- 1 | package 'cmake' 2 | package 'ccache' 3 | package 'git' 4 | package 'jq' 5 | package 'ninja-build', bin: 'ninja' 6 | package 'pkg-config' 7 | -------------------------------------------------------------------------------- /.github/scripts/.Brewfile: -------------------------------------------------------------------------------- 1 | brew "ccache" 2 | brew "coreutils" 3 | brew "cmake" 4 | brew "jq" 5 | brew "xcbeautify" 6 | -------------------------------------------------------------------------------- /.github/scripts/Build-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('x64')] 4 | [string] $Target = 'x64', 5 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 6 | [string] $Configuration = 'RelWithDebInfo' 7 | ) 8 | 9 | $ErrorActionPreference = 'Stop' 10 | 11 | if ( $DebugPreference -eq 'Continue' ) { 12 | $VerbosePreference = 'Continue' 13 | $InformationPreference = 'Continue' 14 | } 15 | 16 | if ( $env:CI -eq $null ) { 17 | throw "Build-Windows.ps1 requires CI environment" 18 | } 19 | 20 | if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) { 21 | throw "A 64-bit system is required to build the project." 22 | } 23 | 24 | if ( $PSVersionTable.PSVersion -lt '7.2.0' ) { 25 | Write-Warning 'The obs-studio PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 26 | exit 2 27 | } 28 | 29 | function Build { 30 | trap { 31 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 32 | Write-Error $_ 33 | Log-Group 34 | exit 2 35 | } 36 | 37 | $ScriptHome = $PSScriptRoot 38 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 39 | 40 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 41 | 42 | foreach($Utility in $UtilityFunctions) { 43 | Write-Debug "Loading $($Utility.FullName)" 44 | . $Utility.FullName 45 | } 46 | 47 | Push-Location -Stack BuildTemp 48 | Ensure-Location $ProjectRoot 49 | 50 | $CmakeArgs = @('--preset', "windows-ci-${Target}") 51 | $CmakeBuildArgs = @('--build') 52 | $CmakeInstallArgs = @() 53 | 54 | if ( $DebugPreference -eq 'Continue' ) { 55 | $CmakeArgs += ('--debug-output') 56 | $CmakeBuildArgs += ('--verbose') 57 | $CmakeInstallArgs += ('--verbose') 58 | } 59 | 60 | $CmakeBuildArgs += @( 61 | '--preset', "windows-${Target}" 62 | '--config', $Configuration 63 | '--parallel' 64 | '--', '/consoleLoggerParameters:Summary', '/noLogo' 65 | ) 66 | 67 | $CmakeInstallArgs += @( 68 | '--install', "build_${Target}" 69 | '--prefix', "${ProjectRoot}/release/${Configuration}" 70 | '--config', $Configuration 71 | ) 72 | 73 | Log-Group "Configuring ${ProductName}..." 74 | Invoke-External cmake @CmakeArgs 75 | 76 | Log-Group "Building ${ProductName}..." 77 | Invoke-External cmake @CmakeBuildArgs 78 | 79 | Log-Group "Installing ${ProductName}..." 80 | Invoke-External cmake @CmakeInstallArgs 81 | 82 | Pop-Location -Stack BuildTemp 83 | Log-Group 84 | } 85 | 86 | Build 87 | -------------------------------------------------------------------------------- /.github/scripts/Package-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('x64')] 4 | [string] $Target = 'x64', 5 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 6 | [string] $Configuration = 'RelWithDebInfo', 7 | [switch] $BuildInstaller = $false 8 | ) 9 | 10 | $ErrorActionPreference = 'Stop' 11 | 12 | if ( $DebugPreference -eq 'Continue' ) { 13 | $VerbosePreference = 'Continue' 14 | $InformationPreference = 'Continue' 15 | } 16 | 17 | if ( $env:CI -eq $null ) { 18 | throw "Package-Windows.ps1 requires CI environment" 19 | } 20 | 21 | if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) { 22 | throw "Packaging script requires a 64-bit system to build and run." 23 | } 24 | 25 | if ( $PSVersionTable.PSVersion -lt '7.2.0' ) { 26 | Write-Warning 'The packaging script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 27 | exit 2 28 | } 29 | 30 | function Package { 31 | trap { 32 | Write-Error $_ 33 | exit 2 34 | } 35 | 36 | $ScriptHome = $PSScriptRoot 37 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 38 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 39 | 40 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 41 | 42 | foreach( $Utility in $UtilityFunctions ) { 43 | Write-Debug "Loading $($Utility.FullName)" 44 | . $Utility.FullName 45 | } 46 | 47 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 48 | $ProductName = $BuildSpec.name 49 | $ProductVersion = $BuildSpec.version 50 | 51 | $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}" 52 | 53 | $RemoveArgs = @{ 54 | ErrorAction = 'SilentlyContinue' 55 | Path = @( 56 | "${ProjectRoot}/release/${ProductName}-*-windows-*.zip" 57 | "${ProjectRoot}/release/${ProductName}-*-windows-*.exe" 58 | ) 59 | } 60 | 61 | Remove-Item @RemoveArgs 62 | 63 | Log-Group "Archiving ${ProductName}..." 64 | $CompressArgs = @{ 65 | Path = (Get-ChildItem -Path "${ProjectRoot}/release/${Configuration}" -Exclude "${OutputName}*.*") 66 | CompressionLevel = 'Optimal' 67 | DestinationPath = "${ProjectRoot}/release/${OutputName}.zip" 68 | Verbose = ($Env:CI -ne $null) 69 | } 70 | Compress-Archive -Force @CompressArgs 71 | Log-Group 72 | 73 | Log-Group "Archiving Portable ${ProductName}..." 74 | tree /F "${ProjectRoot}/release/${Configuration}/${ProductName}" 75 | Copy-Item -Path "${ProjectRoot}/release/${Configuration}/${ProductName}/data/locale" -Destination "${ProjectRoot}/release-portable/${Configuration}/data/obs-plugins/${ProductName}/locale" -Recurse 76 | Copy-Item -Path "${ProjectRoot}/release/${Configuration}/${ProductName}/bin" -Destination "${ProjectRoot}/release-portable/${Configuration}/obs-plugins" -Recurse 77 | tree /F "${ProjectRoot}/release-portable/${Configuration}" 78 | 79 | $CompressArgs = @{ 80 | Path = (Get-ChildItem -Path "${ProjectRoot}/release-portable/${Configuration}" -Exclude "${OutputName}*.*") 81 | CompressionLevel = 'Optimal' 82 | DestinationPath = "${ProjectRoot}/release-portable/${OutputName}-Portable.zip" 83 | Verbose = ($Env:CI -ne $null) 84 | } 85 | Compress-Archive -Force @CompressArgs 86 | Log-Group 87 | 88 | if ( ( $BuildInstaller ) ) { 89 | $IsccFile = "${ProjectRoot}/build_${Target}/installer-Windows.generated.iss" 90 | 91 | if ( ! ( Test-Path -Path $IsccFile ) ) { 92 | throw 'InnoSetup install script not found. Run the build script or the CMake build and install procedures first.' 93 | } 94 | 95 | Log-Information 'Creating InnoSetup installer...' 96 | Push-Location -Stack BuildTemp 97 | Ensure-Location -Path "${ProjectRoot}/release" 98 | Copy-Item -Path "${Configuration}/${ProductName}" -Destination Package -Recurse 99 | Invoke-External iscc ${IsccFile} /O"${ProjectRoot}/release" /F"${OutputName}-Installer" 100 | Remove-Item -Path Package -Recurse 101 | Pop-Location -Stack BuildTemp 102 | 103 | Log-Group 104 | } 105 | } 106 | 107 | Package 108 | -------------------------------------------------------------------------------- /.github/scripts/build-macos: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | builtin emulate -L zsh 4 | setopt EXTENDED_GLOB 5 | setopt PUSHD_SILENT 6 | setopt ERR_EXIT 7 | setopt ERR_RETURN 8 | setopt NO_UNSET 9 | setopt PIPE_FAIL 10 | setopt NO_AUTO_PUSHD 11 | setopt NO_PUSHD_IGNORE_DUPS 12 | setopt FUNCTION_ARGZERO 13 | 14 | ## Enable for script debugging 15 | # setopt WARN_CREATE_GLOBAL 16 | # setopt WARN_NESTED_VAR 17 | # setopt XTRACE 18 | 19 | if (( ! ${+CI} )) { 20 | print -u2 -PR "%F{1} ✖︎ ${ZSH_ARGZERO:t:r} requires CI environment.%f" 21 | exit 1 22 | } 23 | 24 | autoload -Uz is-at-least && if ! is-at-least 5.9; then 25 | print -u2 -PR "${CI:+::error::}%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade Zsh to fix this issue." 26 | exit 1 27 | fi 28 | 29 | TRAPZERR() { 30 | print -u2 -PR "::error::%F{1} ✖︎ script execution error%f" 31 | print -PR -e " 32 | Callstack: 33 | ${(j:\n :)funcfiletrace} 34 | " 35 | 36 | exit 2 37 | } 38 | 39 | build() { 40 | if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h} 41 | local host_os='macos' 42 | local project_root=${SCRIPT_HOME:A:h:h} 43 | local buildspec_file=${project_root}/buildspec.json 44 | 45 | fpath=("${SCRIPT_HOME}/utils.zsh" ${fpath}) 46 | autoload -Uz log_group log_info log_error log_output check_macos setup_ccache 47 | 48 | if [[ ! -r ${buildspec_file} ]] { 49 | log_error \ 50 | 'No buildspec.json found. Please create a build specification for your project.' 51 | return 2 52 | } 53 | 54 | local -i debug=0 55 | 56 | local config='RelWithDebInfo' 57 | local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel) 58 | local -i codesign=0 59 | 60 | local -a args 61 | while (( # )) { 62 | case ${1} { 63 | -c|--config) 64 | if (( # == 1 )) || [[ ${2:0:1} == '-' ]] { 65 | log_error "Missing value for option %B${1}%b" 66 | log_output ${_usage} 67 | exit 2 68 | } 69 | ;; 70 | } 71 | case ${1} { 72 | --) shift; args+=($@); break ;; 73 | -c|--config) 74 | if (( ! ${_valid_configs[(Ie)${2}]} )) { 75 | log_error "Invalid value %B${2}%b for option %B${1}%b" 76 | exit 2 77 | } 78 | config=${2} 79 | shift 2 80 | ;; 81 | -s|--codesign) codesign=1; shift ;; 82 | --debug) debug=1; shift ;; 83 | *) log_error "Unknown option: %B${1}%b"; exit 2 ;; 84 | } 85 | } 86 | 87 | set -- ${(@)args} 88 | 89 | check_macos 90 | 91 | local product_name 92 | local product_version 93 | read -r product_name product_version <<< \ 94 | "$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})" 95 | 96 | pushd ${project_root} 97 | 98 | local -a cmake_args=() 99 | local -a cmake_build_args=(--build) 100 | local -a cmake_install_args=(--install) 101 | 102 | if (( debug )) cmake_args+=(--debug-output) 103 | 104 | cmake_args+=(--preset 'macos-ci') 105 | 106 | typeset -gx NSUnbufferedIO=YES 107 | 108 | typeset -gx CODESIGN_IDENT="${CODESIGN_IDENT:--}" 109 | if (( codesign )) && [[ -z ${CODESIGN_TEAM} ]] { 110 | typeset -gx CODESIGN_TEAM="$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p')" 111 | } 112 | 113 | log_group "Configuring ${product_name}..." 114 | cmake -S ${project_root} ${cmake_args} 115 | 116 | log_group "Building ${product_name}..." 117 | run_xcodebuild() { 118 | if (( debug )) { 119 | xcodebuild ${@} 120 | } else { 121 | if [[ ${GITHUB_EVENT_NAME} == push ]] { 122 | xcodebuild ${@} 2>&1 | xcbeautify --renderer terminal 123 | } else { 124 | xcodebuild ${@} 2>&1 | xcbeautify --renderer github-actions 125 | } 126 | } 127 | } 128 | 129 | local -a build_args=( 130 | ONLY_ACTIVE_ARCH=NO 131 | -arch arm64 132 | -arch x86_64 133 | -project ${product_name}.xcodeproj 134 | -target ${product_name} 135 | -destination "generic/platform=macOS,name=Any Mac" 136 | -configuration ${config} 137 | -parallelizeTargets 138 | -hideShellScriptEnvironment 139 | build 140 | ) 141 | 142 | pushd build_macos 143 | run_xcodebuild ${build_args} 144 | popd 145 | 146 | log_group "Installing ${product_name}..." 147 | cmake --install build_macos --config ${config} --prefix "${project_root}/release/${config}" 148 | 149 | popd 150 | log_group 151 | } 152 | 153 | build ${@} 154 | -------------------------------------------------------------------------------- /.github/scripts/build-ubuntu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | builtin emulate -L zsh 4 | setopt EXTENDED_GLOB 5 | setopt PUSHD_SILENT 6 | setopt ERR_EXIT 7 | setopt ERR_RETURN 8 | setopt NO_UNSET 9 | setopt PIPE_FAIL 10 | setopt NO_AUTO_PUSHD 11 | setopt NO_PUSHD_IGNORE_DUPS 12 | setopt FUNCTION_ARGZERO 13 | 14 | ## Enable for script debugging 15 | # setopt WARN_CREATE_GLOBAL 16 | # setopt WARN_NESTED_VAR 17 | # setopt XTRACE 18 | 19 | autoload -Uz is-at-least && if ! is-at-least 5.2; then 20 | print -u2 -PR "${CI:+::error::}%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade Zsh to fix this issue." 21 | exit 1 22 | fi 23 | 24 | TRAPZERR() { 25 | if (( ${_loglevel:-3} > 2 )) { 26 | print -u2 -PR "${CI:+::error::}%F{1} ✖︎ script execution error%f" 27 | print -PR -e " 28 | Callstack: 29 | ${(j:\n :)funcfiletrace} 30 | " 31 | } 32 | 33 | exit 2 34 | } 35 | 36 | build() { 37 | if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h} 38 | local host_os='ubuntu' 39 | local project_root=${SCRIPT_HOME:A:h:h} 40 | local buildspec_file=${project_root}/buildspec.json 41 | 42 | fpath=("${SCRIPT_HOME}/utils.zsh" ${fpath}) 43 | autoload -Uz log_group log_info log_error log_output set_loglevel check_ubuntu setup_ccache 44 | 45 | if [[ ! -r ${buildspec_file} ]] { 46 | log_error \ 47 | 'No buildspec.json found. Please create a build specification for your project.' 48 | return 2 49 | } 50 | 51 | local -i debug=0 52 | typeset -g -a skips=() 53 | local -i verbosity=1 54 | local -r _version='2.0.0' 55 | local -r -a _valid_targets=( 56 | ubuntu-x86_64 57 | ) 58 | local target 59 | local config='RelWithDebInfo' 60 | local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel) 61 | local -i codesign=0 62 | 63 | local -r -a _valid_generators=(Ninja 'Unix Makefiles') 64 | local generator='Ninja' 65 | local -r _usage_host=" 66 | %F{yellow} Additional options for Linux builds%f 67 | ----------------------------------------------------------------------------- 68 | %B--generator%b Specify build system to generate 69 | Available generators: 70 | - Ninja 71 | - Unix Makefiles" 72 | 73 | local -i print_config=0 74 | local -r _usage=" 75 | Usage: %B${functrace[1]%:*}%b