Fix Mac dmg build (#138)

* [mac/build] update build steps

* [macos] bundle app

* [macos] build dmg

* [ci] setup macOS CI

* [i18n] TODO: rm WX locale files

* [deps] set main branch to main

* Fix osx-fix-libs.py

Create symbolic links, to make libicu happy
Rewrite the script in python3, as python2 is deprecated

* Add write permission before install_name_tool when doing osx-bundle

Fix wangqr/Aegisub#39

* Handle @loader_path in libboost on macOS

See wangqr/Aegisub#39

* [tools/mac] use python3

* [ci/mac] install & using system deps

* [ci/win] don't build fribidi:docs

* [ci/mac]  trying openal-soft

* [ci/mac] use pulseaudio in CI

* [ci/win] only run aeg's test

* [ci/win] fix CI

* [ci/win] fix CI: not use dict `{}`

* [ci] run ci

* [ci/win] don't build docs

* [ci/win] remove args tail newline

* [ci/win] false->disabled

* Use md title format

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>

* Recover file permissions.

* [ci/win] disable fontconfig

Co-Authored-By: Ryan Lucia <ryan@luciaonline.net>

* [ci/win] disable libass:fontconfig

Co-authored-by: wangqr <wangqr@wangqr.tk>
Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
This commit is contained in:
woclass 2021-10-12 15:37:49 +08:00 committed by GitHub
parent 0dffcec461
commit e58e4a9149
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 218 additions and 137 deletions

View file

@ -14,13 +14,23 @@ jobs:
strategy:
matrix:
config:
- {
name: Windows MSVC Release,
os: windows-latest,
msvc: true,
buildtype: release,
args: '-Ddefault_library=static --force-fallback-for=zlib,harfbuzz,freetype2,fribidi,libpng -Dfreetype2:harfbuzz=disabled -Dharfbuzz:freetype=disabled -Dharfbuzz:cairo=disabled -Dharfbuzz:glib=disabled -Dharfbuzz:gobject=disabled'
}
- name: Windows MSVC Release
os: windows-latest
msvc: true
buildtype: release
args: >-
-Ddefault_library=static
--force-fallback-for=zlib,harfbuzz,freetype2,fribidi,libpng
-Dfreetype2:harfbuzz=disabled
-Dharfbuzz:freetype=disabled
-Dharfbuzz:cairo=disabled
-Dharfbuzz:glib=disabled
-Dharfbuzz:gobject=disabled
-Dharfbuzz:tests=disabled
-Dharfbuzz:docs=disabled
-Dfribidi:tests=false
-Dfribidi:docs=false
-Dlibass:fontconfig=disabled
#- {
# name: Windows MinGW,
# os: windows-latest,
@ -42,13 +52,13 @@ jobs:
name: macOS Debug,
os: macos-latest,
buildtype: debugoptimized,
args: -Ddefault_library=static
args: -Ddefault_library=static -Dbuild_osx_bundle=true -Dlocal_boost=true
}
- {
name: macOS Release,
os: macos-latest,
buildtype: release,
args: -Ddefault_library=static
args: -Ddefault_library=static -Dbuild_osx_bundle=true -Dlocal_boost=true
}
steps:
@ -83,9 +93,12 @@ jobs:
- name: Install dependencies (MacOS)
if: matrix.config.os == 'macos-latest'
run: |
brew install luarocks nasm ninja
brew update
brew install luarocks ninja
luarocks install luafilesystem 1.8.0
luarocks install moonscript --dev
brew install libass zlib ffms2 fftw hunspell
brew install pulseaudio # NO OpenAL in github CI
- name: Install dependencies (Linux)
if: matrix.config.os == 'ubuntu-latest'
@ -100,8 +113,9 @@ jobs:
run: meson compile -C build
- name: Run test
run: meson test -C build --verbose
run: meson test -C build --verbose "gtest main"
# Windows artifacts
- name: Generate Windows installer
if: matrix.config.os == 'windows-latest'
run: meson compile win-installer -C build
@ -123,3 +137,17 @@ jobs:
with:
name: ${{ matrix.config.name }} - portable
path: build/aegisub-portable-64.zip
# macOS artifacts
- name: Generate macOS installer
if: matrix.config.os == 'macos-latest'
run: |
meson compile osx-bundle -C build
meson compile osx-build-dmg -C build
- name: Upload artifacts - macOS dmg
uses: actions/upload-artifact@v2
if: matrix.config.os == 'macos-latest'
with:
name: ${{ matrix.config.name }} - installer
path: build/Aegisub-*.dmg

View file

@ -48,15 +48,24 @@ A vaguely recent version of Xcode and the corresponding command-line tools are r
For personal usage, you can use pip and homebrew to install almost all of Aegisub's dependencies:
brew install libass nasm ninja boost zlib icu4c pkg-config ffms2 fftw hunspell gettext cmake
brew link --force gettext
export LDFLAGS="-L/usr/local/opt/icu4c/lib"
export CPPFLAGS="-I/usr/local/opt/icu4c/include"
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig"
pip install meson
pip3 install meson
brew install cmake ninja pkg-config libass boost zlib ffms2 fftw hunspell
export LDFLAGS="-L/usr/local/opt/icu4c/lib"
export CPPFLAGS="-I/usr/local/opt/icu4c/include"
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig"
Once the dependencies are installed, build Aegisub with `meson build && meson compile -C build`.
#### Build dmg
```bash
meson build_static -Ddefault_library=static -Dbuildtype=debugoptimized -Dbuild_osx_bundle=true -Dlocal_boost=true
meson compile -C build_static
meson test -C build_static --verbose
meson compile osx-bundle -C build_static
meson compile osx-build-dmg -C build_static
```
## Updating Moonscript
From within the Moonscript repository, run `bin/moon bin/splat.moon -l moonscript moonscript/ > bin/moonscript.lua`.

View file

@ -8,9 +8,13 @@ if host_machine.system() == 'windows'
run_target('win-portable', command: [portable_setup, meson.project_build_root(), meson.project_source_root()])
elif host_machine.system() == 'darwin'
fontconfig_conf = run_command('pkg-config', '--variable=confdir', 'fontconfig').stdout().strip()
bundle_app_sh = find_program(meson.project_source_root() / 'tools/osx-bundle.sh')
run_target('osx-bundle',
command: ['../tools/osx-bundle.sh', meson.project_source_root(), meson.project_build_root(), 'wx-config', fontconfig_conf, '',
command: [bundle_app_sh, meson.project_source_root(), meson.project_build_root(), 'wx-config', fontconfig_conf, '',
get_option('build_osx_bundle') ? 'TRUE' : 'FALSE'])
build_dmg_sh = find_program(meson.project_source_root() / 'tools/osx-dmg.sh')
run_target('osx-build-dmg',
command: [build_dmg_sh, meson.project_source_root(), meson.project_build_root(), meson.project_version()])
else
conf_pkg.set('AEGISUB_COMMAND', 'aegisub')

View file

@ -34,7 +34,8 @@ Source: {#BUILD_ROOT}\po\zh_TW.gmo; DestDir: {app}\locale\zh_TW; DestName:
#endif
; END ENABLE_TRANSLATIONS
;; TODO: rm those lines
;; xref: [Update and review translations · Issue #132 · TypesettingTools/Aegisub](https://github.com/TypesettingTools/Aegisub/issues/132)
#ifdef ENABLE_WX_TRANSLATIONS
; wxWidgets localization (commented out ones are out of date; some don't have wxstd.mo)
Source: src\mo\wxstd-ar.mo; DestDir: {app}\locale\ar; DestName: wxstd.mo; Flags: ignoreversion; Components: translations

View file

@ -88,7 +88,7 @@ void SubtitlesProvider::LoadSubtitles(AssFile *subs, int time) {
push_line(line.GetEntryData());
if (!subs->Attachments.empty()) {
// TODO: some scripts may have a lot of attachments,
// TODO: some scripts may have a lot of attachments,
// so ideally we'd want to write only those actually used on the requested video frame,
// but this would require some pre-parsing of the attached font files with FreeType,
// which isn't probably trivial.

View file

@ -1,7 +1,7 @@
[wrap-git]
directory = harfbuzz
url = https://github.com/harfbuzz/harfbuzz
revision = master
revision = main
[provide]
harfbuzz = libharfbuzz_dep

View file

@ -4,7 +4,7 @@ set -e
SRC_DIR="${1}"
BUILD_DIR="${2}"
WX_PREFIX=`${3} --prefix`
WX_PREFIX=""
FONTCONFIG_CONF_DIR="${4}"
DICT_DIR="${5}"
MESON_BUILD_OSX_BUNDLE="${6}"
@ -75,26 +75,28 @@ mkdir -vp "${PKG_DIR}/Contents/Resources/en.lproj"
#mv "${PKG_DIR}/Contents/Resources/sr_RS.lproj" "${PKG_DIR}/Contents/Resources/sr_YU.lproj"
#mv "${PKG_DIR}/Contents/Resources/sr_RS@latin.lproj" "${PKG_DIR}/Contents/Resources/sr_YU@latin.lproj"
echo
echo "---- Copying WX locale files ----"
for i in `ls -1 ${SRC_DIR}/po/*.mo|sed "s|po/\(.*\).mo|\1|"`; do
WX_MO="${WX_PREFIX}/share/locale/${i}/LC_MESSAGES/wxstd.mo"
if ! test -f "${WX_MO}"; then
WX_MO="${HOME_DIR}/wxstd/${i}.mo"
fi
if test -f "${WX_MO}"; then
cp -v "${WX_MO}" "${PKG_DIR}/Contents/Resources/${i}.lproj/"
else
echo "WARNING: \"$i\" locale in aegisub but no WX catalog found!"
fi
done
## TODO: rm those lines
## xref: [Update and review translations · Issue #132 · TypesettingTools/Aegisub](https://github.com/TypesettingTools/Aegisub/issues/132)
# echo
# echo "---- Copying WX locale files ----"
#
# for i in `ls -1 ${SRC_DIR}/po/*.mo|sed "s|po/\(.*\).mo|\1|"`; do
# WX_MO="${WX_PREFIX}/share/locale/${i}/LC_MESSAGES/wxstd.mo"
#
# if ! test -f "${WX_MO}"; then
# WX_MO="${HOME_DIR}/wxstd/${i}.mo"
# fi
#
# if test -f "${WX_MO}"; then
# cp -v "${WX_MO}" "${PKG_DIR}/Contents/Resources/${i}.lproj/"
# else
# echo "WARNING: \"$i\" locale in aegisub but no WX catalog found!"
# fi
# done
echo
echo "---- Fixing libraries ----"
sudo python "${SRC_DIR}/tools/osx-fix-libs.py" "${PKG_DIR}/Contents/MacOS/aegisub" || exit $?
sudo python3 "${SRC_DIR}/tools/osx-fix-libs.py" "${PKG_DIR}/Contents/MacOS/aegisub" || exit $?
echo
echo "Done creating \"${PKG_DIR}\""

View file

@ -15,37 +15,46 @@
set -e
TMP_DMG="temp_dmg"
PKG_DIR="Aegisub.app"
PKG_NAME="Aegisub-${1}"
PKG_NAME_RW="Aegisub-${1}_rw.dmg"
PKG_NAME_VOLUME="Aegisub-${1}"
SRC_DIR="${1}"
BUILD_DIR="${2}"
AEGI_VER="${3}"
PKG_NAME="Aegisub-${AEGI_VER}"
PKG_NAME_VOLUME="${PKG_NAME}"
PKG_DIR="${BUILD_DIR}/Aegisub.app"
DMG_TMP_DIR="${BUILD_DIR}/temp_dmg"
DMG_PATH="${BUILD_DIR}/${PKG_NAME}.dmg"
DMG_RW_PATH="${BUILD_DIR}/${PKG_NAME}_rw.dmg"
if ! test -d "${PKG_DIR}"; then
echo "\"${PKG_DIR}\" does not exist, please run 'make osx-bundle'"
exit 1;
fi
rm -rf "${TMP_DMG}" "${PKG_NAME}.dmg"
mkdir -v "${TMP_DMG}"
echo
echo "---- Copying ${1} into ${TMP_DMG}/ ----"
cp -R "${PKG_DIR}" "${TMP_DMG}"
echo "---- Removing old \"${DMG_TMP_DIR}\", \"${DMG_PATH}\", \"${DMG_RW_PATH}\" ----"
rm -rf "${DMG_TMP_DIR}" "${DMG_PATH}" "${DMG_RW_PATH}"
mkdir -v "${DMG_TMP_DIR}"
echo
echo "---- Copying ${AEGI_VER} into ${DMG_TMP_DIR}/ ----"
cp -R "${PKG_DIR}" "${DMG_TMP_DIR}"
echo
echo "---- Setting up ----"
ln -vsf /Applications "${TMP_DMG}"
mkdir -v "${TMP_DMG}/.background"
cp -v packages/osx_dmg/dmg_background.png "${TMP_DMG}/.background/background.png"
cp -v packages/osx_bundle/Contents/Resources/Aegisub.icns "${TMP_DMG}/.VolumeIcon.icns"
ln -vsf /Applications "${DMG_TMP_DIR}"
mkdir -v "${DMG_TMP_DIR}/.background"
cp -v "${SRC_DIR}/packages/osx_dmg/dmg_background.png" "${DMG_TMP_DIR}/.background/background.png"
cp -v "${SRC_DIR}/packages/osx_bundle/Contents/Resources/Aegisub.icns" "${DMG_TMP_DIR}/.VolumeIcon.icns"
echo
echo "---- Creating image ----"
/usr/bin/hdiutil create -srcfolder "${TMP_DMG}" -volname "${PKG_NAME}" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW "${PKG_NAME_RW}"
/usr/bin/hdiutil create -srcfolder "${DMG_TMP_DIR}" -volname "${PKG_NAME}" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW "${DMG_RW_PATH}"
echo
echo "---- Mounting image ----"
DEV_NAME=`/usr/bin/hdiutil attach -readwrite -noverify -noautoopen "${PKG_NAME_RW}" |awk '/GUID_partition_scheme/ {print $1}'`
DEV_NAME=`/usr/bin/hdiutil attach -readwrite -noverify -noautoopen "${DMG_RW_PATH}" |awk '/GUID_partition_scheme/ {print $1}'`
echo "Device name: ${DEV_NAME}"
echo
@ -61,7 +70,7 @@ if test -n "${SET_STYLE}"; then
echo "---- Running AppleScript to set style ----"
SCRIPT_TMP=`mktemp /tmp/aegisub_dmg_as.XXX`
sed -f scripts/osx-bundle.sed packages/osx_dmg/dmg_set_style.applescript > ${SCRIPT_TMP}
sed -f "${SRC_DIR}/scripts/osx-bundle.sed" "${SRC_DIR}/packages/osx_dmg/dmg_set_style.applescript" > ${SCRIPT_TMP}
/usr/bin/osacompile -o ${SCRIPT_TMP}.scpt ${SCRIPT_TMP}
@ -79,18 +88,18 @@ if test -n "${SET_STYLE}"; then
hdiutil detach "${DEV_NAME}"
DEV_NAME=`/usr/bin/hdiutil attach -readwrite -noverify -noautoopen "${PKG_NAME_RW}" |awk '/GUID_partition_scheme/ {print $1}'`
DEV_NAME=`/usr/bin/hdiutil attach -readwrite -noverify -noautoopen "${DMG_RW_PATH}" |awk '/GUID_partition_scheme/ {print $1}'`
echo "Device name: ${DEV_NAME}"
cp -v "/Volumes/${PKG_NAME_VOLUME}/.DS_Store" packages/osx_dmg/DS_Store
SetFile -a v packages/osx_dmg/DS_Store
cp -v "/Volumes/${PKG_NAME_VOLUME}/.DS_Store" "${SRC_DIR}/packages/osx_dmg/DS_Store"
SetFile -a v "${SRC_DIR}/packages/osx_dmg/DS_Store"
hdiutil detach "${DEV_NAME}"
rm -rf "${TMP_DMG}" "${PKG_NAME_RW}" ${SCRIPT_TMP}.scpt ${SCRIPT_TMP}
rm -rf "${DMG_TMP_DIR}" "${DMG_RW_PATH}" ${SCRIPT_TMP}.scpt ${SCRIPT_TMP}
exit 0
else
echo "---- Installing DS_Store ----"
cp -v packages/osx_dmg/DS_Store "/Volumes/${PKG_NAME_VOLUME}/.DS_Store"
cp -v "${SRC_DIR}/packages/osx_dmg/DS_Store" "/Volumes/${PKG_NAME_VOLUME}/.DS_Store"
fi
echo
@ -100,11 +109,10 @@ echo /usr/bin/hdiutil detach "${DEV_NAME}" -force
echo
echo "---- Compressing ----"
/usr/bin/hdiutil convert "${PKG_NAME_RW}" -format UDBZ -imagekey bzip2-level=9 -o "${PKG_NAME}.dmg"
/usr/bin/hdiutil convert "${DMG_RW_PATH}" -format UDBZ -imagekey bzip2-level=9 -o "${DMG_PATH}"
echo
echo "---- Removing \"${TMP_DMG}\", \"${PKG_NAME_RW}\" ----"
rm -rf "${TMP_DMG}" "${PKG_NAME_RW}"
echo "---- Removing temp dmg \"${DMG_RW_PATH}\" ----"
rm -rf "${DMG_RW_PATH}"
echo
echo "Done!"

View file

@ -5,99 +5,128 @@ import sys
import os
import shutil
import stat
import subprocess
is_bad_lib = re.compile(r'(/usr/local|/opt)').match
is_sys_lib = re.compile(r'(/usr|/System)').match
otool_libname_extract = re.compile(r'\s+(/.*?)[\(\s:]').search
otool_libname_extract = re.compile(r'\s+(/.*?)[(\s:]').search
otool_loader_path_extract = re.compile(r'\s+@loader_path/(.*?)[(\s:]').search
goodlist = []
badlist = []
link_map = {}
def otool(cmdline):
pipe = os.popen("otool " + cmdline, 'r')
output = pipe.readlines()
pipe.close()
return output
with subprocess.Popen(['otool'] + cmdline, stdout=subprocess.PIPE,
encoding='utf-8') as p:
return p.stdout.readlines()
def collectlibs(lib, masterlist, targetdir):
global goodlist, link_map
liblist = otool("-L '" + lib + "'")
locallist = []
global goodlist, link_map
liblist = otool(['-L', lib])
locallist = []
for l in liblist:
lr = otool_libname_extract(l)
if not lr: continue
l = lr.group(1)
if is_bad_lib(l) and not l in badlist:
badlist.append(l)
if ((not is_sys_lib(l)) or is_bad_lib(l)) and not l in masterlist:
locallist.append(l)
print("found %s:" % l)
for l in liblist:
lr = otool_libname_extract(l)
if lr:
l = lr.group(1)
else:
lr = otool_loader_path_extract(l)
if lr:
l = os.path.join(os.path.dirname(lib), lr.group(1))
else:
continue
if is_bad_lib(l) and l not in badlist:
badlist.append(l)
if ((not is_sys_lib(l)) or is_bad_lib(l)) and l not in masterlist:
locallist.append(l)
print("found %s:" % l)
check = l
link_list = []
while check:
if os.path.isfile(check) and not os.path.islink(check):
os.system("cp '%s' '%s'" % (check, targetdir))
print(" FILE %s ... copied to target" % check)
if link_list:
for link in link_list:
link_map[link] = os.path.basename(check)
break
if os.path.islink(check):
print(" LINK %s" % check)
link_list.append(os.path.basename(check))
check = os.path.dirname(check) + "/" + os.readlink(check)
check = l
link_list = []
while check:
basename = os.path.basename(check)
target = os.path.join(targetdir, basename)
elif not l in goodlist and not l in masterlist:
goodlist.append(l)
masterlist.extend(locallist)
if os.path.isfile(check) and not os.path.islink(check):
try:
shutil.copy(check, target)
except PermissionError:
print(" FILE %s ... skipped" % check)
break
print(" FILE %s ... copied to target" % check)
if link_list:
for link in link_list:
link_map[link] = basename
break
for l in locallist:
collectlibs(l, masterlist, targetdir)
if os.path.islink(check):
link_dst = os.readlink(check)
try:
os.symlink(link_dst, target)
except FileExistsError:
print(" LINK %s ... existed" % check)
break
print(" LINK %s ... copied to target" % check)
link_list.append(basename)
check = os.path.join(os.path.dirname(check), link_dst)
exit;
binname = sys.argv[1]
targetdir = os.path.dirname(binname)
print("Searching for libraries in ", binname, "...")
libs = [binname]
collectlibs(sys.argv[1], libs, targetdir)
elif l not in goodlist and l not in masterlist:
goodlist.append(l)
masterlist.extend(locallist)
for l in locallist:
collectlibs(l, masterlist, targetdir)
print()
print("System libraries used...")
goodlist.sort()
for l in goodlist:
print(l)
if __name__ == '__main__':
binname = sys.argv[1]
targetdir = os.path.dirname(binname)
print("Searching for libraries in", binname, "...")
libs = [binname]
collectlibs(binname, libs, targetdir)
print()
print("System libraries used...")
goodlist.sort()
for l in goodlist:
print(l)
print()
print("Fixing library install names...")
in_tool_cmdline = "install_name_tool "
for lib in libs:
libbase = os.path.basename(lib)
if libbase in link_map:
libbase = link_map[libbase]
in_tool_cmdline = in_tool_cmdline + ("-change '%s' '@executable_path/%s' " % (lib, libbase))
for lib in libs:
libbase = os.path.basename(lib)
print()
print("Fixing library install names...")
in_tool_cmdline = ['install_name_tool']
for lib in libs:
libbase = os.path.basename(lib)
if libbase in link_map:
libbase = link_map[libbase]
in_tool_cmdline = in_tool_cmdline + ['-change', lib,
'@executable_path/' + libbase]
for lib in libs:
libbase = os.path.basename(lib)
if libbase in link_map:
libbase = link_map[libbase]
print("%s -> @executable_path/%s (REMAPPED)" % (lib, libbase))
else:
print("%s -> @executable_path/%s" % (lib, libbase))
if libbase in link_map:
libbase = link_map[libbase]
print("%s -> @executable_path/%s (REMAPPED)" % (lib, libbase))
else:
print("%s -> @executable_path/%s" % (lib, libbase))
os.system("%s -id '@executable_path/%s' '%s/%s'" % (in_tool_cmdline, libbase, targetdir, libbase))
sys.stdout.flush()
targetlib = targetdir + '/' + libbase
orig_permission = os.stat(targetlib).st_mode
if not(orig_permission & stat.S_IWUSR):
os.chmod(targetlib, orig_permission | stat.S_IWUSR)
subprocess.run(in_tool_cmdline + ['-id', '@executable_path/' + libbase,
targetlib])
if not(orig_permission & stat.S_IWUSR):
os.chmod(targetlib, orig_permission)
if badlist:
print()
print("WARNING: The following libraries have blacklisted paths:")
for lib in sorted(badlist):
print(lib)
print("These paths normally have files from a package manager, which means that end result may not work if copied to another machine.")
if badlist:
print()
print("WARNING: The following libraries have blacklisted paths:")
for lib in sorted(badlist):
print(lib)
print(
"These paths normally have files from a package manager, which means that end result may not work if copied to another machine.")
print()
print("All done!")
print()
print("All done!")