๐Ÿ“ฆ LizardByte / Sunshine

๐Ÿ“„ sunshine.rb ยท 304 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304require "language/node"

class Sunshine < Formula
  CUDA_VERSION = "13.1".freeze
  CUDA_FORMULA = "cuda@#{CUDA_VERSION}".freeze
  GCC_VERSION = "14".freeze
  GCC_FORMULA = "gcc@#{GCC_VERSION}".freeze
  IS_UPSTREAM_REPO = ENV.fetch("GITHUB_REPOSITORY", "") == "LizardByte/Sunshine"

  desc "@PROJECT_DESCRIPTION@"
  homepage "@PROJECT_HOMEPAGE_URL@"
  url "@GITHUB_CLONE_URL@",
    tag: "@GITHUB_TAG@"
  version "@BUILD_VERSION@"
  license all_of: ["GPL-3.0-only"]
  head "@GITHUB_CLONE_URL@", branch: "@GITHUB_DEFAULT_BRANCH@"

  # https://docs.brew.sh/Brew-Livecheck#githublatest-strategy-block
  livecheck do
    url :stable
    regex(/^v?(\d+\.\d+\.\d+)$/i)
    strategy :github_latest do |json, regex|
      match = json["tag_name"]&.match(regex)
      next if match.blank?

      match[1]
    end
  end

  bottle do
    root_url "https://ghcr.io/v2/lizardbyte/homebrew"
    sha256 arm64_tahoe:   "0000000000000000000000000000000000000000000000000000000000000000"
    sha256 arm64_sequoia: "0000000000000000000000000000000000000000000000000000000000000000"
    sha256 arm64_sonoma:  "0000000000000000000000000000000000000000000000000000000000000000"
    sha256 x86_64_linux:  "0000000000000000000000000000000000000000000000000000000000000000"
  end

  option "with-static-boost", "Enable static link of Boost libraries"
  option "without-static-boost", "Disable static link of Boost libraries" # default option

  depends_on "cmake" => :build
  depends_on "doxygen" => [:build, :recommended]
  depends_on "graphviz" => :build if build.with? "doxygen"
  depends_on "node" => :build
  depends_on "pkgconf" => :build
  depends_on "gcovr" => :test
  depends_on "boost"
  depends_on "curl"
  depends_on "icu4c@78"
  depends_on "miniupnpc"
  depends_on "openssl@3"
  depends_on "opus"

  on_macos do
    depends_on "llvm" => [:build, :test]
  end

  on_linux do
    depends_on GCC_FORMULA => [:build, :test]
    depends_on "lizardbyte/homebrew/#{CUDA_FORMULA}" => [:build, :recommended]
    depends_on "at-spi2-core"
    depends_on "avahi"
    depends_on "ayatana-ido"
    depends_on "cairo"
    depends_on "gdk-pixbuf"
    depends_on "glib"
    depends_on "gnu-which"
    depends_on "gtk+3"
    depends_on "harfbuzz"
    depends_on "libayatana-appindicator"
    depends_on "libayatana-indicator"
    depends_on "libcap"
    depends_on "libdbusmenu"
    depends_on "libdrm"
    depends_on "libice"
    depends_on "libnotify"
    depends_on "libsm"
    depends_on "libva"
    depends_on "libx11"
    depends_on "libxcb"
    depends_on "libxcursor"
    depends_on "libxext"
    depends_on "libxfixes"
    depends_on "libxi"
    depends_on "libxinerama"
    depends_on "libxrandr"
    depends_on "libxtst"
    depends_on "mesa"
    depends_on "numactl"
    depends_on "pango"
    depends_on "pulseaudio"
    depends_on "systemd"
    depends_on "wayland"
  end

  conflicts_with "sunshine-beta", because: "sunshine and sunshine-beta cannot be installed at the same time"
  fails_with :clang do
    build 1400
    cause "Requires C++23 support"
  end

  fails_with :gcc do
    version "12" # fails with GCC 12.x and earlier
    cause "Requires C++23 support"
  end

  def setup_build_environment
    ENV["BRANCH"] = "@GITHUB_BRANCH@"
    ENV["BUILD_VERSION"] = "@BUILD_VERSION@"
    ENV["COMMIT"] = "@GITHUB_COMMIT@"

    setup_linux_gcc_environment if OS.linux?
  end

  def setup_linux_gcc_environment
    # Use GCC because gcov from llvm cannot handle our paths
    gcc_path = Formula[GCC_FORMULA]
    ENV["CC"] = "#{gcc_path.opt_bin}/gcc-#{GCC_VERSION}"
    ENV["CXX"] = "#{gcc_path.opt_bin}/g++-#{GCC_VERSION}"
  end

  def base_cmake_args
    %W[
      -DBUILD_WERROR=ON
      -DCMAKE_CXX_STANDARD=23
      -DCMAKE_INSTALL_PREFIX=#{prefix}
      -DHOMEBREW_ALLOW_FETCHCONTENT=ON
      -DOPENSSL_ROOT_DIR=#{Formula["openssl"].opt_prefix}
      -DSUNSHINE_ASSETS_DIR=sunshine/assets
      -DSUNSHINE_BUILD_HOMEBREW=ON
      -DSUNSHINE_PUBLISHER_NAME='LizardByte'
      -DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev'
      -DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
    ]
  end

  def add_test_args(args)
    if IS_UPSTREAM_REPO
      args << "-DBUILD_TESTS=ON"
      ohai "Building tests: enabled"
    else
      args << "-DBUILD_TESTS=OFF"
      ohai "Building tests: disabled"
    end
  end

  def add_docs_args(args)
    if build.with? "doxygen"
      ohai "Building docs: enabled"
      args << "-DBUILD_DOCS=ON"
    else
      ohai "Building docs: disabled"
      args << "-DBUILD_DOCS=OFF"
    end
  end

  def add_boost_args(args)
    if build.without? "static-boost"
      args << "-DBOOST_USE_STATIC=OFF"
      ohai "Disabled statically linking Boost libraries"
    else
      configure_static_boost(args)
    end
  end

  def configure_static_boost(args)
    args << "-DBOOST_USE_STATIC=ON"
    ohai "Enabled statically linking Boost libraries"

    unless Formula["icu4c"].any_version_installed?
      odie <<~EOS
        icu4c must be installed to link against static Boost libraries,
        either install icu4c or use brew install sunshine --with-static-boost instead
      EOS
    end
    ENV.append "CXXFLAGS", "-I#{Formula["icu4c"].opt_include}"
    icu4c_lib_path = Formula["icu4c"].opt_lib.to_s
    ENV.append "LDFLAGS", "-L#{icu4c_lib_path}"
    ENV["LIBRARY_PATH"] = icu4c_lib_path
    ohai "Linking against ICU libraries at: #{icu4c_lib_path}"
  end

  def add_cuda_args(args)
    return unless OS.linux?

    if build.with?(CUDA_FORMULA)
      configure_cuda(args)
    else
      args << "-DSUNSHINE_ENABLE_CUDA=OFF"
      ohai "CUDA disabled"
    end
  end

  def configure_cuda(args)
    cuda_path = Formula["lizardbyte/homebrew/#{CUDA_FORMULA}"]
    nvcc_path = "#{cuda_path.opt_bin}/nvcc"
    gcc_path = Formula[GCC_FORMULA]

    args << "-DSUNSHINE_ENABLE_CUDA=ON"
    args << "-DCMAKE_CUDA_COMPILER:PATH=#{nvcc_path}"
    args << "-DCMAKE_CUDA_HOST_COMPILER=#{gcc_path.opt_bin}/gcc-#{GCC_VERSION}"
    ohai "CUDA enabled with nvcc at: #{nvcc_path}"
  end

  def build_cmake_args
    args = base_cmake_args
    add_test_args(args)
    add_docs_args(args)
    add_boost_args(args)
    add_cuda_args(args)
    args
  end

  def build_and_install_project
    system "cmake", "-S", ".", "-B", "build", "-G", "Unix Makefiles",
            *std_cmake_args,
            *build_cmake_args

    system "make", "-C", "build"
    system "make", "-C", "build", "install"
  end

  def install_platform_specific_files
    bin.install "build/tests/test_sunshine" if IS_UPSTREAM_REPO

    # codesign the binary on intel macs
    system "codesign", "-s", "-", "--force", "--deep", bin/"sunshine" if OS.mac? && Hardware::CPU.intel?

    bin.install "src_assets/linux/misc/postinst" if OS.linux?
  end

  def install
    setup_build_environment
    build_and_install_project
    install_platform_specific_files
  end

  service do
    run [opt_bin/"sunshine", "~/.config/sunshine/sunshine.conf"]
  end

  def post_install
    if OS.linux?
      opoo <<~EOS
        ATTENTION: To complete installation, you must run the following command:
        `sudo #{bin}/postinst`
      EOS
    end

    if OS.mac?
      opoo <<~EOS
        Sunshine can only access microphones on macOS due to system limitations.
        To stream system audio use "Soundflower" or "BlackHole".

        Gamepads are not currently supported on macOS.
      EOS
    end
  end

  def caveats
    <<~EOS
      Thanks for installing @PROJECT_NAME@!

      To get started, review the documentation at:
        https://docs.lizardbyte.dev/projects/sunshine
    EOS
  end

  test do
    # test that the binary runs at all
    system bin/"sunshine", "--version"

    if IS_UPSTREAM_REPO && ENV.fetch("HOMEBREW_BOTTLE_BUILD", "false") != "true"
      # run the test suite
      system bin/"test_sunshine", "--gtest_color=yes", "--gtest_output=xml:tests/test_results.xml"
      assert_path_exists File.join(testpath, "tests", "test_results.xml")

      # create gcovr report
      buildpath = ENV.fetch("HOMEBREW_BUILDPATH", "")
      unless buildpath.empty?
        # Change to the source directory for gcovr to work properly
        cd "#{buildpath}/build" do
          # Use GCC version to match what was used during compilation
          if OS.linux?
            gcc_path = Formula[GCC_FORMULA]
            gcov_executable = "#{gcc_path.opt_bin}/gcov-#{GCC_VERSION}"

            system "gcovr", ".",
              "-r", "../src",
              "--gcov-executable", gcov_executable,
              "--exclude-noncode-lines",
              "--exclude-throw-branches",
              "--exclude-unreachable-branches",
              "--xml-pretty",
              "-o=#{testpath}/coverage.xml"

            assert_path_exists File.join(testpath, "coverage.xml")
          end
        end
      end
    end
  end
end