Skip to content

Commit

Permalink
fix: macOS nightly-release (#105)
Browse files Browse the repository at this point in the history
* Add script to package libs

* Install gstreamer on macOS CI

* Add cargo packager to macOS

* Install cargo-packager on macos

* Update version env variable

* Move step order

* Update artifact path

* Add develop installer

* Use fallback path instead

* Move env variable to build steps instead

* Manually export instead

* Move dylib to framework instead

* Try ad-hoc codesign

* Try frameworks

* Test custom package script

* Update dmg version

* Add rpath in build script

* Add entitlements

* Cleanup

* Revert CI condition for packaging
  • Loading branch information
wusyong authored Aug 5, 2024
1 parent b25660f commit 2da3ed2
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 8 deletions.
36 changes: 31 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ jobs:
uses: dsherret/rust-toolchain-file@v1

- name: Install Rust toolchain for packager
if: ${{ github.event_name == 'schedule' }}
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
Expand All @@ -148,6 +149,7 @@ jobs:
uses: Mozilla-Actions/[email protected]

- name: Install Cargo Packager
if: ${{ github.event_name == 'schedule' }}
run: cargo +stable install cargo-packager

- name: Build
Expand Down Expand Up @@ -196,37 +198,61 @@ jobs:
- name: Install Rust
uses: dsherret/rust-toolchain-file@v1

- name: Install Rust toolchain for packager
if: ${{ github.event_name == 'schedule' }}
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install dependencies
run: |
brew install cmake pkg-config
brew install cmake
python -m pip install mako
curl https://gstreamer.freedesktop.org/data/pkg/osx/1.24.6/gstreamer-1.0-1.24.6-universal.pkg -o runtime.pkg
sudo installer -pkg runtime.pkg -target /
curl https://gstreamer.freedesktop.org/data/pkg/osx/1.24.6/gstreamer-1.0-devel-1.24.6-universal.pkg -o develop.pkg
sudo installer -pkg develop.pkg -target /
- name: Run sccache-cache
uses: Mozilla-Actions/[email protected]

- name: Install Cargo Packager
if: ${{ github.event_name == 'schedule' }}
run: cargo +stable install cargo-packager

- name: Build
run: |
export PATH="/Library/Frameworks/GStreamer.framework/Versions/1.0/bin${PATH:+:$PATH}"
export DYLD_LIBRARY_PATH="/Library/Frameworks/GStreamer.framework/Versions/1.0/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"
cargo build --release
# - name: Test
# run: |
# cargo test --release

- name: Tar Binary
- name: Bundle
if: ${{ github.event_name == 'schedule' }}
run: |
export PATH="/Library/Frameworks/GStreamer.framework/Versions/1.0/bin${PATH:+:$PATH}"
export DYLD_LIBRARY_PATH="/Library/Frameworks/GStreamer.framework/Versions/1.0/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"
cargo packager --release
- name: Fetch Verso version
if: ${{ github.event_name == 'schedule' }}
run: tar -czvf verso-${{ matrix.platform.target }}.tar.gz -C ./target/release/ verso
run: |
echo "VERSO_VERSION=$(cargo metadata --format-version=1 --no-deps | jq -r '.packages[] | select(.name == "verso") | .version')" >> $GITHUB_ENV
- name: Upload artifact
if: ${{ github.event_name == 'schedule' }}
uses: actions/upload-artifact@v4
with:
name: verso-${{ matrix.platform.target }}
path: verso-${{ matrix.platform.target }}.tar.gz
name: verso_${{ env.VERSO_VERSION }}_${{ matrix.platform.arch }}
path: target/release/verso_${{ env.VERSO_VERSION }}_${{ matrix.platform.arch }}.dmg

build-result:
name: Build Result
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ name = "verso"
product-name = "verso"
identifier = "org.versotile.verso"
version = "0.0.1"
before-each-package-command = "cargo build --release --features packager"
before-each-package-command = "python etc/package_libs.py"
resources = [
"resources",
"icons",
"target/release/build/**/libEGL.dll",
"target/release/build/**/libGLESv2.dll",
"target/release/lib",
]
icons = ["icons/icon256x256.png", "icons/icon.ico"]

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ But please understand we don't triage any build issue without flatpak or nix set

Nightly releases built with CrabNebula Cloud can be found at [releases](https://web.crabnebula.cloud/verso/verso-nightly/releases).

> Packages are unsigned currently. If you have problem opening the app on macOS, try `xattr -d com.apple.quarantine /Applications/verso.app` after installation.
## Future Work

- Multiwindow support.
- Enable multiprocess mode.
- Enable sandobx in all platforms.
- Enable `Gstreamer` feature and remove `brew install harfbuzz` in README.
- Enable sandbox in all platforms.
- Enable `Gstreamer` feature.
3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ fn main() {
apple: { any(target_os = "ios", target_os = "macos") },
linux: { all(unix, not(apple), not(android)) },
}

#[cfg(all(feature = "packager", target_os = "macos"))]
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Resources/lib");
}
103 changes: 103 additions & 0 deletions etc/package_libs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import os
import os.path as path
import shutil
import subprocess
import sys

def otool(s):
o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE)
for line in map(lambda s: s.decode('ascii'), o.stdout):
if line[0] == '\t':
yield line.split(' ', 1)[0][1:]


def install_name_tool(binary, *args):
try:
subprocess.check_call(['install_name_tool', *args, binary])
except subprocess.CalledProcessError as e:
print("install_name_tool exited with return value %d" % e.returncode)


def change_link_name(binary, old, new):
install_name_tool(binary, '-change', old, f"@executable_path/{new}")


def is_system_library(lib):
return lib.startswith("/System/Library") or lib.startswith("/usr/lib") or ".asan." in lib


def is_relocatable_library(lib):
return lib.startswith("@rpath/")


def change_non_system_libraries_path(libraries, relative_path, binary):
for lib in libraries:
if is_system_library(lib) or is_relocatable_library(lib):
continue
new_path = path.join(relative_path, path.basename(lib))
change_link_name(binary, lib, new_path)


def resolve_rpath(lib, rpath_root):
if not is_relocatable_library(lib):
return lib

rpaths = ['', '../', 'gstreamer-1.0/']
for rpath in rpaths:
full_path = rpath_root + lib.replace('@rpath/', rpath)
if path.exists(full_path):
return path.normpath(full_path)

raise Exception("Unable to satisfy rpath dependency: " + lib)


def copy_dependencies(binary_path, lib_path, gst_lib_dir):
relative_path = path.relpath(lib_path, path.dirname(binary_path)) + "/"

# Update binary libraries
binary_dependencies = set(otool(binary_path))
change_non_system_libraries_path(binary_dependencies, relative_path, binary_path)

# Update dependencies libraries
need_checked = binary_dependencies
checked = set()
while need_checked:
checking = set(need_checked)
need_checked = set()
for f in checking:
# No need to check these for their dylibs
if is_system_library(f):
continue
full_path = resolve_rpath(f, gst_lib_dir)
need_relinked = set(otool(full_path))
new_path = path.join(lib_path, path.basename(full_path))
if not path.exists(new_path):
shutil.copyfile(full_path, new_path)
change_non_system_libraries_path(need_relinked, relative_path, new_path)
need_checked.update(need_relinked)
checked.update(checking)
need_checked.difference_update(checked)

def package_gstreamer_dylibs(bin):
gst_root = "/Library/Frameworks/GStreamer.framework/Versions/1.0"
lib_dir = path.join(path.dirname(bin), "lib")
if os.path.exists(lib_dir):
shutil.rmtree(lib_dir)
os.mkdir(lib_dir)
try:
copy_dependencies(bin, lib_dir, path.join(gst_root, 'lib', ''))
except Exception as e:
print("ERROR: could not package required dylibs")
print(e)
return False
return True

if __name__ == '__main__':
try:
subprocess.check_call(['cargo', 'build', '--release', '--features', 'packager'])
except subprocess.CalledProcessError as e:
print("cargo build exited with return value %d" % e.returncode)

if sys.platform == "darwin":
binary = "./target/release/verso"
package_gstreamer_dylibs(binary)

0 comments on commit 2da3ed2

Please sign in to comment.