diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70f1abd6e..f53bbf84b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/README.md b/README.md index bab339076..892782993 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/packages/meson.build b/packages/meson.build index 39be5aae7..1e13be54a 100644 --- a/packages/meson.build +++ b/packages/meson.build @@ -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') diff --git a/packages/win_installer/fragment_translations.iss b/packages/win_installer/fragment_translations.iss index c54ef3010..5817470e1 100644 --- a/packages/win_installer/fragment_translations.iss +++ b/packages/win_installer/fragment_translations.iss @@ -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 diff --git a/src/subtitles_provider.cpp b/src/subtitles_provider.cpp index 057bbbdbd..6d4801324 100644 --- a/src/subtitles_provider.cpp +++ b/src/subtitles_provider.cpp @@ -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. diff --git a/subprojects/harfbuzz.wrap b/subprojects/harfbuzz.wrap index 08bfb0850..10caa4b4a 100644 --- a/subprojects/harfbuzz.wrap +++ b/subprojects/harfbuzz.wrap @@ -1,7 +1,7 @@ [wrap-git] directory = harfbuzz url = https://github.com/harfbuzz/harfbuzz -revision = master +revision = main [provide] harfbuzz = libharfbuzz_dep diff --git a/tools/osx-bundle.sh b/tools/osx-bundle.sh index dbec657d8..f8d4a682e 100755 --- a/tools/osx-bundle.sh +++ b/tools/osx-bundle.sh @@ -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}\"" diff --git a/tools/osx-dmg.sh b/tools/osx-dmg.sh index 0f7b916b6..24a9ec64b 100755 --- a/tools/osx-dmg.sh +++ b/tools/osx-dmg.sh @@ -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!" diff --git a/tools/osx-fix-libs.py b/tools/osx-fix-libs.py index 845a56b97..e29c8180f 100755 --- a/tools/osx-fix-libs.py +++ b/tools/osx-fix-libs.py @@ -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!")