Update GHA CI and build scripts #619
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI | |
env: | |
PROJECT_NAME: unison | |
PROJECT_DESC: "`unison` file synchronizer" | |
PROJECT_EXES: "unison unison-fsmonitor" | |
on: | |
- pull_request | |
- push | |
jobs: | |
docs: | |
runs-on: ubuntu-20.04 | |
steps: | |
- run: sudo apt-get update | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Use OCaml | |
uses: ocaml/setup-ocaml@v3 | |
with: | |
ocaml-compiler: 4.14.x | |
- run: sudo apt-get install hevea lynx texlive-latex-base | |
- run: opam exec -- make docs | |
- name: Store user manual for the build jobs | |
uses: actions/upload-artifact@v4 | |
with: | |
name: unison-docs | |
path: | | |
doc/unison-manual.txt | |
doc/unison-manual.html | |
doc/unison-manual.pdf | |
man/unison.1 | |
build: | |
if: ${{ !cancelled() }} # Don't fail if 'docs' failed | |
needs: docs | |
strategy: | |
fail-fast: false | |
matrix: | |
job: | |
- { os: macos-14 , ocaml-version: 5.2.0 } | |
- { os: macos-14 , ocaml-version: 4.14.2 , publish: true , fnsuffix: -macos-arm64 } | |
- { os: macos-13 , ocaml-version: 4.14.2 , publish: true , fnsuffix: -macos-x86_64 } | |
- { os: ubuntu-22.04 , ocaml-version: 5.2.0 } | |
- { os: ubuntu-22.04 , ocaml-version: 4.14.2 } | |
- { os: ubuntu-20.04 , ocaml-version: 4.14.2 } | |
- { os: windows-2022 , ocaml-version: "4.14.2,system-mingw" , publish: true , fnsuffix: -windows-x86_64 } | |
- { os: windows-2019 , ocaml-version: "4.14.2,system-mingw,arch-x86_32" , publish: true , fnsuffix: -windows-i386 } | |
- { os: windows-2022 , ocaml-version: "4.14.2,system-msvc" } | |
- { os: windows-2019 , ocaml-version: "4.14.2,system-msvc,arch-x86_32" } | |
runs-on: ${{ matrix.job.os }} | |
steps: | |
- if: contains(matrix.job.os, 'ubuntu') | |
run: sudo apt-get update | |
- if: runner.os == 'Windows' | |
name: "Windows: Stash away the default MSYS installation" | |
continue-on-error: true | |
shell: cmd | |
# This conflicts with Cygwin installed by setup-ocaml | |
# Adjusting PATH alone does not seem to work | |
run: rename C:\msys64 dmsys64 | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Initialize workflow variables | |
id: vars | |
shell: bash | |
run: | | |
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT ; done; } | |
# normalize to pre-compiled ocaml compiler variants for windows/Cygwin (decreases OCaml install time by 50%) | |
case '${{ matrix.job.ocaml-version }}' in | |
*+*) OCAML_COMPILER='ocaml-variants.${{ matrix.job.ocaml-version }}' ;; | |
*) OCAML_COMPILER='ocaml-base-compiler.${{ matrix.job.ocaml-version }}' ;; | |
esac | |
OCAML_VARIANT='${{ matrix.job.ocaml-version }}' | |
OCAML_VARIANT="${OCAML_VARIANT/+options/}" | |
outputs OCAML_VARIANT OCAML_COMPILER | |
# architecture/platform vars | |
EXE_suffix='' ; case '${{ matrix.job.os }}' in windows-*) EXE_suffix=".exe" ;; esac | |
MinGW_ARCH='x86_64' ; case '${{ matrix.job.ocaml-version }}' in *x86_32*) MinGW_ARCH='i686' ;; *mingw*) MinGW_ARCH='x86_64' ;; esac | |
outputs EXE_suffix MinGW_ARCH | |
MACOSX_DEPLOYMENT_TARGET=10.13 | |
case '${{ matrix.job.os }}' in | |
macos-*) echo "MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" >> $GITHUB_ENV ; | |
echo "XCODEFLAGS=-arch $(uname -m) -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET} MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" >> $GITHUB_ENV ; | |
echo "CFLAGS=${CFLAGS} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" >> $GITHUB_ENV ;; | |
esac | |
# staging environment | |
STAGING_DIR='_staging' | |
outputs STAGING_DIR | |
# parse commit reference info | |
echo GITHUB_REF=${GITHUB_REF} | |
echo GITHUB_SHA=${GITHUB_SHA} | |
REF_NAME="${GITHUB_REF#refs/*/}" | |
unset REF_BRANCH ; case "${GITHUB_REF}" in refs/heads/*) REF_BRANCH="${GITHUB_REF#refs/heads/}" ;; esac; | |
unset REF_TAG ; case "${GITHUB_REF}" in refs/tags/*) REF_TAG="${GITHUB_REF#refs/tags/}" ;; esac; | |
REF_SHAS="${GITHUB_SHA:0:8}" | |
outputs REF_BRANCH REF_NAME REF_SHAS REF_TAG | |
# deployable tag? (ie, leading "vM" or "M"; M == version number) | |
unset DEPLOY ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOY='true' ; fi | |
outputs DEPLOY | |
# package name | |
PKG_suffix='.tar.gz' ; case '${{ matrix.job.os }}' in windows-*) PKG_suffix='.zip' ;; esac; | |
PKG_VER="${REF_TAG:-git_$REF_SHAS}" | |
PKG_VER="${PKG_VER#v}" | |
PKG_BASENAME="${PROJECT_NAME}-${PKG_VER}${{ matrix.job.fnsuffix }}" | |
PKG_NAME="${PKG_BASENAME}${PKG_suffix}" | |
PKG_DIR="${STAGING_DIR}/${PKG_BASENAME}" | |
outputs PKG_VER PKG_BASENAME PKG_DIR PKG_NAME PKG_suffix | |
COMPRESS_CMD='tar czf'; case '${{ matrix.job.os }}' in windows-*) COMPRESS_CMD='7z -y a' ;; esac; | |
outputs COMPRESS_CMD | |
- name: Create/configure any needed build/workspace | |
shell: bash | |
run: | | |
# create build/work space | |
mkdir -p '${{ steps.vars.outputs.STAGING_DIR }}' | |
mkdir -p '${{ steps.vars.outputs.PKG_DIR }}' | |
mkdir -p '${{ steps.vars.outputs.PKG_DIR }}'/bin | |
- name: Use OCaml ${{ matrix.job.ocaml-version }} | |
uses: ocaml/setup-ocaml@v3 | |
with: | |
ocaml-compiler: "${{ steps.vars.outputs.OCAML_COMPILER }}" | |
opam-pin: false | |
# setup-ocaml can prepare the build environment from unison.opam | |
# We're not relying on that capability here, to make sure the builds | |
# also work without using unison.opam | |
## note: at this point, after OCaml installation, windows platforms will use Cygwin bash as the default | |
## ... Cygwin bash cannot handle shell scripts containing CRLF EOLs (which are what is generated by GHA on windows platforms) | |
## ... so, "igncr" must be added to SHELLOPTS | |
- name: Prepare Cygwin environment (Windows) | |
if: runner.os == 'Windows' | |
shell: cmd | |
run: | | |
echo SHELLOPTS=igncr>> %GITHUB_ENV% | |
- shell: bash | |
env: | |
LDFLAGS: ${{ matrix.job.static }} | |
run: | | |
opam exec -- make tui | |
opam exec -- make fsmonitor | |
# stage | |
# * notes: darwin/macos doesn't build `unison-fsmonitor` | |
for file in ${PROJECT_EXES} ; do | |
if [ -f "src/${file}${{ steps.vars.outputs.EXE_suffix }}" ]; then | |
cp "src/${file}${{ steps.vars.outputs.EXE_suffix }}" '${{ steps.vars.outputs.PKG_DIR }}/bin' | |
echo "'src/${file}${{ steps.vars.outputs.EXE_suffix }}' copied to '${{ steps.vars.outputs.PKG_DIR }}/bin'" | |
fi | |
done | |
- run: opam exec -- make test | |
## There is still code to run tests with old ocaml on Windows. | |
## That remains intentionally so that someone could turn it on if | |
## desired. | |
- name: Run self-tests over RPC | |
if: runner.os == 'Windows' && !contains(matrix.job.ocaml-version, '4.14') | |
shell: bash | |
run: | | |
# Separate backup dir must be set for server instance so that the central | |
# backup location of both instances doesn't overlap | |
UNISONBACKUPDIR=./src/testbak2 ./src/unison -socket 55443 & | |
sleep 1 # Wait for the server to be fully started | |
./src/unison -ui text -selftest testr1 socket://127.0.0.1:55443/testr2 -killserver | |
- name: Run self-tests over local socket | |
# Recent Windows versions do support Unix domain sockets | |
# but at least OCaml 4.14 is required to use that support | |
if: runner.os != 'Windows' || contains(matrix.job.ocaml-version, '4.14') | |
shell: bash | |
run: | | |
mkdir localsocket | |
chmod 700 localsocket | |
# Separate backup dir must be set for server instance so that the central | |
# backup location of both instances doesn't overlap | |
UNISONBACKUPDIR=./src/testbak4 ./src/unison -socket ./localsocket/test.sock & | |
sleep 1 # Wait for the server to be fully started | |
${{ runner.os == 'Windows' }} || test -S ./localsocket/test.sock | |
./src/unison -ui text -selftest testr3 socket://{./localsocket/test.sock}/testr4 -killserver | |
- name: Prepare lablgtk install (Windows) | |
if: ${{ runner.os == 'Windows' && contains(matrix.job.ocaml-version, 'mingw') }} | |
shell: bash | |
run: | | |
setup-x86_64.exe --quiet-mode --root "${CYGWIN_ROOT}" --site http://cygwin.mirror.constant.com --symlink-type=sys --packages hicolor-icon-theme,adwaita-icon-theme | |
# [2022-11] This terrible (terrible) hack is here to forcibly skip | |
# building the fontconfig cache because it can take 30-45 minutes | |
# on GHA runners and is never needed anyway. | |
setup-x86_64.exe --quiet-mode --root "${CYGWIN_ROOT}" --site http://cygwin.mirror.constant.com --symlink-type=sys --local-package-dir D:/a --download --packages mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-fontconfig | |
cd 'D:/a/https%3a%2f%2fcygwin.mirror.constant.com%2f/noarch/release/'mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-fontconfig | |
CNAMEXZ=$(ls mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-fontconfig*.tar.xz) | |
CNAME=${CNAMEXZ%.xz} | |
unxz ${CNAMEXZ} | |
tar --delete --file ${CNAME} etc/postinstall/zp_mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-fontconfig_cache.sh | |
xz ${CNAME} | |
sha512sum > sha512.sum | |
CSZ=$(stat -c %s ${CNAMEXZ}) | |
SHASUM=$(sha512sum ${CNAMEXZ}) | |
cd 'D:/a/https%3a%2f%2fcygwin.mirror.constant.com%2f/x86_64' | |
mv setup.ini tsetup.ini | |
rm -f setup* | |
sed -E -e "\|install: noarch/release/mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-fontconfig/${CNAMEXZ}|s/xz .+/xz ${CSZ} ${SHASUM%% *}/" tsetup.ini > setup.ini | |
rm tsetup.ini | |
sha512sum > sha512.sum | |
setup-x86_64.exe --quiet-mode --root "${CYGWIN_ROOT}" --symlink-type=sys --local-install --local-package-dir 'D:/a/https%3a%2f%2fcygwin.mirror.constant.com%2f' --mirror-mode --no-verify --packages mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-fontconfig | |
# [2024-11] Not exactly sure what is happening here, but opam | |
# packages using pkg-config will fail in Windows without this. | |
# /usr/bin/pkg-config is a symlink to /usr/bin/pkgconf | |
# This does not work when opam packages try to execute pkg-config | |
# directly (that is, D:\cygwin\bin\pkg-config). | |
# It looks like this issue is being worked around package-by-package | |
# upstream, but the fix here is universal, so let's keep it for now. | |
cp /usr/bin/pkgconf /usr/bin/pkg-config.exe | |
# [2024-11] opam depext has been broken/disabled by migration to | |
# opam >= 2.2 which can support Windows natively. System dependencies | |
# in Windows must be installed manually (for now, at least). | |
setup-x86_64.exe --quiet-mode --root "${CYGWIN_ROOT}" --site http://cygwin.mirror.constant.com --symlink-type=sys --packages mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-cairo,mingw64-${{ steps.vars.outputs.MinGW_ARCH }}-gtk3 | |
# [2024-11] Setting PKG_CONFIG_LIBDIR like this is required with | |
# opam >= 2.2 which can support Windows natively. Not sure why, | |
# but it's likely due to pkg-conf being executed "outside" Cygwin | |
# environment. | |
echo PKG_CONFIG_LIBDIR=/usr/${{ steps.vars.outputs.MinGW_ARCH }}-w64-mingw32/sys-root/mingw/lib/pkgconfig >> "$GITHUB_ENV" | |
- name: lablgtk install | |
## [2020-09] non-working/unavailable for MSVC or musl OCaml variants ; also, non-working for 32bit OCaml variant (see [GH:garrigue/lablgtk#64](https://github.com/garrigue/lablgtk/issues/64)) | |
if: ${{ ! ( contains(matrix.job.ocaml-version, 'msvc') || contains(matrix.job.ocaml-version, '-musl') || contains(matrix.job.ocaml-version, '-32bit') ) }} | |
run: opam install lablgtk3 ocamlfind | |
- if: ${{ !matrix.job.static && !contains(matrix.job.ocaml-version, 'msvc') }} ## unable to build static gtk/gui | |
shell: bash | |
run: | | |
opam exec -- make gui | |
# stage | |
# * copy only main/first project binary | |
project_exe_stem=${PROJECT_EXES%% *} | |
cp "src/${project_exe_stem}-gui${{ steps.vars.outputs.EXE_suffix }}" "${{ steps.vars.outputs.PKG_DIR }}/bin/" | |
- name: "Build WinOS text+gui hybrid" | |
if: ${{ runner.os == 'Windows' && !matrix.job.static && !contains(matrix.job.ocaml-version, 'msvc') }} ## WinOS, non-static (unable to build static gtk/gui) | |
shell: bash | |
run: | | |
# create and stage text+gui hybrid for Windows | |
# * copy only main/first project binary | |
project_exe_stem=${PROJECT_EXES%% *} | |
# * clean/remove build artifact(s) | |
rm "src/${project_exe_stem}-gui${{ steps.vars.outputs.EXE_suffix }}" ##.or.# opam exec -- make -C src clean #.or.# opam exec -- make clean | |
# * re-create (with hybrid text+gui UI) | |
opam exec -- make gui UI_WINOS=hybrid | |
cp "src/${project_exe_stem}-gui${{ steps.vars.outputs.EXE_suffix }}" "${{ steps.vars.outputs.PKG_DIR }}/bin/${project_exe_stem}-text+gui${{ steps.vars.outputs.EXE_suffix }}" | |
- uses: actions/upload-artifact@v4 | |
if: false ## disable by default; only useful for debugging GHA | |
with: | |
name: unison-${{ steps.vars.outputs.REF_SHAS }}.ocaml-${{ matrix.job.ocaml-version }}.${{ matrix.job.os }} | |
path: ${{ steps.vars.outputs.PKG_DIR }}/bin/* | |
- name: Copy user manual | |
continue-on-error: ${{ !(steps.vars.outputs.DEPLOY && matrix.job.publish) }} | |
uses: actions/download-artifact@v4 | |
with: | |
name: unison-docs | |
path: '${{ steps.vars.outputs.PKG_DIR }}' | |
- name: Prepare package | |
# if: steps.vars.outputs.DEPLOY | |
shell: bash | |
run: | | |
## package artifact(s) | |
PKG_DIR='${{ steps.vars.outputs.PKG_DIR }}' | |
# `strip` binaries | |
strip "${PKG_DIR}/bin"/*'${{ steps.vars.outputs.EXE_suffix }}' | |
# README and LICENSE | |
(shopt -s nullglob; for f in [R]'EADME'{,.*}; do cp $f "${PKG_DIR}"/ ; done) | |
(shopt -s nullglob; for f in [L]'ICENSE'{-*,}{,.*}; do cp $f "${PKG_DIR}"/ ; done) | |
cp INSTALL.md "${PKG_DIR}"/ | |
cp CONTRIBUTING.md "${PKG_DIR}"/ | |
cp NEWS.md "${PKG_DIR}"/ | |
- if: ${{ runner.os == 'Windows' && !contains(matrix.job.ocaml-version, 'msvc') }} | |
name: "Windows: Package gtk" | |
shell: bash | |
run: | | |
# [2023-03] Setting this PATH here (and it has to be right here) is | |
# a workaround for an unknown issue (most likely something with GHA) | |
# causing MinGW binutils not to be found (while other binutils | |
# pre-installed in the GHA images may be found instead), which in turn | |
# causes the DLL extracting functions to silently fail. | |
export PATH="/usr/${{ steps.vars.outputs.MinGW_ARCH }}-w64-mingw32/bin":"/usr/${{ steps.vars.outputs.MinGW_ARCH }}-w64-mingw32/sys-root/mingw/bin":${PATH} | |
## package artifact(s) | |
PKG_DIR='${{ steps.vars.outputs.PKG_DIR }}' | |
# collect any needed dlls/libraries | |
# dlls | |
dll_refs() { eval "$(opam env)" ; objdump -x "$@" | grep -Po "\S+[.]dll$" | xargs -I{} 2>/dev/null which "{}" | sort -u ; } | |
filtered_dll_refs() { list="$(dll_refs "$@" | grep -vF "$(cygpath ${WINDIR})" | perl -lape '$_ = qq/@{[sort @F]}/')" ; echo "$list" ; } | |
recursive_filtered_dll_refs() { list="$(filtered_dll_refs "$@")" ; n=0 ; while [ $n -lt $(echo "$list" | wc -l) ]; do n=$(echo "$list" | wc -l) ; list="$(filtered_dll_refs $list)" ; done ; echo "$list" ; } | |
IFS=$'\n' DLL_list=( "$(recursive_filtered_dll_refs "${PKG_DIR}"/bin/*)" ) | |
for dll in ${DLL_list[@]} ; do cp "${dll}" "${PKG_DIR}"/bin ; done | |
# required gdk support files | |
mkdir "${PKG_DIR}"/lib | |
cp -r /usr/${{ steps.vars.outputs.MinGW_ARCH }}-w64-mingw32/sys-root/mingw/lib/gdk-pixbuf-2.0 "${PKG_DIR}"/lib/ | |
# update loader.cache to point to local relative installation | |
mv "${PKG_DIR}"/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache "${PKG_DIR}"/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache.original | |
cat "${PKG_DIR}"/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache.original | sed -E 's#([^"]*)(lib/gdk-pixbuf-2.0/2.10.0/loaders/[^"]*[.]dll)#../\2#' > "${PKG_DIR}"/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache | |
rm "${PKG_DIR}"/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache.original | |
# required icons | |
mkdir "${PKG_DIR}"/share | |
cp -rL /usr/share/icons "${PKG_DIR}"/share | |
# compile glib settings schema | |
mkdir -p "${PKG_DIR}"/share/glib-2.0 | |
cp -r /usr/${{ steps.vars.outputs.MinGW_ARCH }}-w64-mingw32/sys-root/mingw/share/glib-2.0/schemas "${PKG_DIR}"/share/glib-2.0 | |
glib-compile-schemas "${PKG_DIR}"/share/glib-2.0/schemas | |
# add gtk configuration | |
mkdir -p "${PKG_DIR}"/etc/gtk-3.0 | |
printf "[Settings]\ngtk-button-images=true\ngtk-font-name=Segoe UI 9\n" > "${PKG_DIR}"/etc/gtk-3.0/settings.ini | |
- name: Package | |
shell: bash | |
run: cd '${{ steps.vars.outputs.PKG_DIR }}/' && ${{ steps.vars.outputs.COMPRESS_CMD }} '../${{ steps.vars.outputs.PKG_NAME }}' * | |
- uses: actions/upload-artifact@v4 | |
if: matrix.job.publish | |
with: | |
name: ${{ steps.vars.outputs.PKG_NAME }}___ocaml-${{ matrix.job.ocaml-version }}.${{ matrix.job.os }}-publish | |
path: ${{ steps.vars.outputs.STAGING_DIR }}/${{ steps.vars.outputs.PKG_NAME }} | |
- name: Publish | |
if: steps.vars.outputs.DEPLOY && matrix.job.publish | |
uses: softprops/action-gh-release@v1 | |
with: | |
files: | | |
${{ steps.vars.outputs.STAGING_DIR }}/${{ steps.vars.outputs.PKG_NAME }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- if: runner.os == 'macOS' | |
name: "macOS: Build and package Unison.app" | |
id: macapp | |
run: | | |
opam exec -- make macui | |
# package | |
APP_NAME=Unison-${{ steps.vars.outputs.PKG_VER }}${{ matrix.job.fnsuffix }}.app.tar.gz | |
echo APP_NAME=${APP_NAME} >> $GITHUB_OUTPUT | |
tar czf ${APP_NAME} -C src/uimac/build/Default Unison.app | |
- if: runner.os == 'macOS' && matrix.job.publish | |
name: "macOS: Upload Unison.app artifact" | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ steps.macapp.outputs.APP_NAME }}___ocaml-${{ matrix.job.ocaml-version }}.${{ matrix.job.os }}-publish | |
path: ${{ steps.macapp.outputs.APP_NAME }} | |
- if: runner.os == 'macOS' && steps.vars.outputs.DEPLOY && matrix.job.publish | |
name: "macOS: Publish Unison.app" | |
uses: softprops/action-gh-release@v1 | |
with: | |
files: ${{ steps.macapp.outputs.APP_NAME }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
rpc_abicheck: | |
needs: build | |
strategy: | |
fail-fast: false | |
matrix: | |
# This list is intended to balance good enough coverage and | |
# limited resource usage. | |
job: | |
- { os: ubuntu-22.04 , ocaml-version: 5.2.x , ref: v2.53.5 } | |
- { os: ubuntu-22.04 , ocaml-version: 4.14.x , ref: v2.53.5 } | |
- { os: ubuntu-22.04 , ocaml-version: 4.14.x , ref: v2.52.1 , oldmake: true } | |
- { os: ubuntu-22.04 , ocaml-version: 4.14.x , ref: v2.51.5 , oldmake: true } | |
- { os: ubuntu-22.04 , ocaml-version: 4.08.x , ref: v2.51.5 , oldmake: true } | |
- { os: ubuntu-22.04 , ocaml-version: 4.08.x , ref: v2.51.2 , oldmake: true } | |
- { os: windows-2019 , ocaml-version: ocaml-variants.4.14.0+mingw64c , ref: v2.53.5 } | |
- { os: windows-2019 , ocaml-version: ocaml-variants.4.14.0+mingw64c , ref: v2.52.1 , oldmake: true } | |
- { os: windows-2019 , ocaml-version: ocaml-variants.4.08.1+mingw32c , ref: v2.51.5 , oldmake: true } | |
- { os: macos-13 , ocaml-version: 4.14.x , ref: v2.53.5 } | |
- { os: macos-13 , ocaml-version: 4.14.x , ref: v2.52.1 , oldmake: true } | |
- { os: macos-13 , ocaml-version: 4.08.x , ref: v2.51.2 , oldmake: true } | |
runs-on: ${{ matrix.job.os }} | |
steps: | |
- if: contains(matrix.job.os, 'ubuntu') | |
run: sudo apt-get update | |
- name: Use OCaml ${{ matrix.job.ocaml-version }} | |
uses: ocaml/setup-ocaml@v2 | |
with: | |
ocaml-compiler: "${{ matrix.job.ocaml-version }}" | |
opam-pin: false | |
opam-depext: false | |
# setup-ocaml can prepare the build environment from unison.opam | |
# We're not relying on that capability here, to make sure the builds | |
# also work without using unison.opam | |
- name: Prepare Cygwin environment (Windows) | |
if: runner.os == 'Windows' | |
shell: cmd | |
run: | | |
echo %CYGWIN_ROOT_BIN%>> %GITHUB_PATH% | |
echo %CYGWIN_ROOT_WRAPPERBIN%>> %GITHUB_PATH% | |
echo SHELLOPTS=igncr>> %GITHUB_ENV% | |
- name: Checkout HEAD to _new | |
uses: actions/checkout@v4 | |
with: | |
path: _new | |
- run: cd _new && opam exec -- make tui | |
shell: bash | |
- name: Checkout ${{ matrix.job.ref }} to _prev | |
uses: actions/checkout@v4 | |
with: | |
ref: "${{ matrix.job.ref }}" | |
path: _prev | |
- name: "2.51.0: Patch connection header in _prev" | |
if: contains(matrix.job.ref, '2.51.0') | |
shell: bash | |
run: | | |
# Connection header string was broken in 2.51.0 | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/remote.ml b/src/remote.ml | |
index ddca0d77..7f819eab 100644 | |
--- a/src/remote.ml | |
+++ b/src/remote.ml | |
@@ -920,7 +920,7 @@ let connectionHeader = | |
Scanf.sscanf Sys.ocaml_version "%d.%d.%d" (fun x y z -> (x,y,z)) in | |
let compiler = | |
if major < 4 | |
- || major = 4 && minor <= 2 | |
+ || major = 4 && minor < 2 | |
|| major = 4 && minor = 2 && patchlevel <= 1 | |
then "<= 4.01.1" | |
else ">= 4.01.2" | |
EOF | |
- name: "2.51.{2,3}: Patch bugs in _prev" | |
if: contains(matrix.job.ref, '2.51.2') || contains(matrix.job.ref, '2.51.3') | |
shell: bash | |
run: | | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/bytearray_stubs.c b/src/bytearray_stubs.c | |
index 2b29421a..2850f2d8 100644 | |
--- a/src/bytearray_stubs.c | |
+++ b/src/bytearray_stubs.c | |
@@ -10,7 +10,7 @@ | |
CAMLprim value ml_marshal_to_bigarray(value v, value flags) | |
{ | |
char *buf; | |
- long len; | |
+ intnat len; | |
output_value_to_malloc(v, flags, &buf, &len); | |
return alloc_bigarray(BIGARRAY_UINT8 | BIGARRAY_C_LAYOUT | BIGARRAY_MANAGED, | |
1, buf, &len); | |
diff --git a/src/uicommon.ml b/src/uicommon.ml | |
index 9fa94cf5..65fc37a5 100644 | |
--- a/src/uicommon.ml | |
+++ b/src/uicommon.ml | |
@@ -494,10 +494,11 @@ let promptForRoots getFirstRoot getSecondRoot = | |
(* ---- *) | |
let makeTempDir pattern = | |
- let ic = Unix.open_process_in (Printf.sprintf "(mktemp --tmpdir -d %s.XXXXXX || mktemp -d -t %s) 2>/dev/null" pattern pattern) in | |
- let path = input_line ic in | |
- ignore (Unix.close_process_in ic); | |
- path | |
+ let path = Filename.temp_file pattern "" in | |
+ let fspath = System.fspathFromString path in | |
+ System.unlink fspath; (* Remove file created by [temp_file]... *) | |
+ System.mkdir fspath 0o755; (* ... and create a dir instead. *) | |
+ path ^ Filename.dir_sep | |
(* The first time we load preferences, we also read the command line | |
arguments; if we re-load prefs (because the user selected a new profile) | |
EOF | |
- name: "2.51.2: Patch _prev for mingw compilers" | |
if: contains(matrix.job.ref, '2.51.2') | |
shell: bash | |
run: | | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/Makefile.OCaml b/src/Makefile.OCaml | |
index 7cefa2ec..95b1bec4 100644 | |
--- a/src/Makefile.OCaml | |
+++ b/src/Makefile.OCaml | |
@@ -107,8 +107,8 @@ CAMLFLAGS+=-I system/$(SYSTEM) -I lwt/$(SYSTEM) | |
ifeq ($(OSARCH),win32) | |
# Win32 system | |
EXEC_EXT=.exe | |
- OBJ_EXT=.obj | |
- OUTPUT_SEL=/Fo | |
+ OBJ_EXT=.o | |
+ OUTPUT_SEL=-o | |
CWD=. | |
# Fix suggested by Karl M, Jan 2009: | |
# "The new flexlink wrapper that OCaml 3.11 uses was gagging on the res | |
@@ -120,8 +120,6 @@ ifeq ($(OSARCH),win32) | |
COBJS+=system/system_win_stubs$(OBJ_EXT) lwt/lwt_unix_stubs$(OBJ_EXT) | |
WINOBJS=system/system_win.cmo | |
SYSTEM=win | |
- CLIBS+=-cclib "-link win32rc/unison.res" shell32.lib | |
- STATICLIBS+=-cclib "-link win32rc/unison.res" shell32.lib | |
buildexecutable:: | |
@echo Building for Windows | |
else | |
EOF | |
- name: "2.51.2: Patch _prev for newer compilers/OS" | |
if: contains(matrix.job.ref, '2.51.2') | |
shell: bash | |
run: | | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/Makefile.OCaml b/src/Makefile.OCaml | |
index 7cefa2ec..ffc3ab9e 100644 | |
--- a/src/Makefile.OCaml | |
+++ b/src/Makefile.OCaml | |
@@ -180,7 +180,7 @@ else | |
buildexecutable:: $(NAME)$(EXEC_EXT) | |
endif | |
-MINOSXVERSION=10.5 | |
+MINOSXVERSION=10.7 | |
# XCODEFLAGS=-sdk macosx$(MINOSXVERSION) | |
ifeq ($(OSARCH),osx) | |
CAMLFLAGS+=-ccopt -mmacosx-version-min=$(MINOSXVERSION) | |
diff --git a/src/files.ml b/src/files.ml | |
index 5ff18810..1d1fbcc6 100644 | |
--- a/src/files.ml | |
+++ b/src/files.ml | |
@@ -734,7 +734,7 @@ let get_files_in_directory dir = | |
with End_of_file -> | |
dirh.System.closedir () | |
end; | |
- Sort.list (<) !files | |
+ List.sort String.compare !files | |
let ls dir pattern = | |
Util.convertUnixErrorsToTransient | |
diff --git a/src/recon.ml b/src/recon.ml | |
index 2c619bb8..2412c18e 100644 | |
--- a/src/recon.ml | |
+++ b/src/recon.ml | |
@@ -661,8 +661,8 @@ let rec reconcile | |
(* Sorts the paths so that they will be displayed in order *) | |
let sortPaths pathUpdatesList = | |
- Sort.list | |
- (fun (p1, _) (p2, _) -> Path.compare p1 p2 <= 0) | |
+ List.sort | |
+ Path.compare | |
pathUpdatesList | |
let rec enterPath p1 p2 t = | |
diff --git a/src/system/system_generic.ml b/src/system/system_generic.ml | |
index 453027d0..290851e1 100755 | |
--- a/src/system/system_generic.ml | |
+++ b/src/system/system_generic.ml | |
@@ -47,7 +47,7 @@ let open_out_gen = open_out_gen | |
let chmod = Unix.chmod | |
let chown = Unix.chown | |
let utimes = Unix.utimes | |
-let link = Unix.link | |
+let link x y = Unix.link x y | |
let openfile = Unix.openfile | |
let opendir f = | |
let h = Unix.opendir f in | |
EOF | |
- name: "2.51.0 - 2.52.1: Patch tests in _prev" | |
if: contains(matrix.job.ref, '2.51') || matrix.job.ref == 'v2.52.0' || matrix.job.ref == 'v2.52.1' | |
shell: bash | |
run: | | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/test.ml b/src/test.ml | |
index 3d480409..60ed014d 100644 | |
--- a/src/test.ml | |
+++ b/src/test.ml | |
@@ -542,6 +542,77 @@ let test() = | |
*) | |
end; | |
+ if not bothRootsLocal then | |
+ begin | |
+ let localR, remoteR, localRaw = | |
+ match r1 with | |
+ | Common.Local, _ -> R1, R2, r1 | |
+ | _ -> R2, R1, r2 | |
+ in | |
+ | |
+ (* Test RPC function "fingerprintSubfile" *) | |
+ runtest "RPC: transfer append" [] (fun () -> | |
+ let prefixLen = 1024 * 1024 + 1 in | |
+ let len = prefixLen + 31 in | |
+ let contents = String.make len '.' in | |
+ let fileName = "bigfile" in | |
+ let prefixPath = Path.fromString fileName in | |
+ let (workingDir, _) = Fspath.findWorkingDir (snd localRaw) prefixPath in | |
+ let prefixName = Path.toString (Os.tempPath ~fresh:false workingDir prefixPath) in | |
+ put remoteR (Dir [(fileName, File contents)]); | |
+ put localR (Dir [(prefixName, File (String.sub contents 0 prefixLen))]); | |
+ sync (); | |
+ check "1" localR (Dir [(fileName, File contents)]); | |
+ ); | |
+ | |
+ (* Test RPC function "updateProps" *) | |
+ runtest "RPC: update props" ["times = true"] (fun () -> | |
+ let state = [("a", File "x")] in | |
+ put remoteR (Dir state); | |
+ put localR (Dir []); | |
+ sync (); | |
+ (* Having to sleep here is an unfortunate side-effect of the current | |
+ Windows limitations-inspired time comparison algorithm which is | |
+ designed to work on FAT filesystems (2-second granularity). *) | |
+ Unix.sleep 2; | |
+ put remoteR (Dir state); | |
+ sync (); | |
+ check "1" localR (Dir state); | |
+ ); | |
+ | |
+ (* Test RPC function "replaceArchive" *) | |
+ runtest "RPC: replaceArchive" [] (fun () -> | |
+ put localR (Dir [("n", File "to delete")]); | |
+ put remoteR (Dir []); | |
+ sync (); | |
+ put remoteR (Dir []); | |
+ sync (); | |
+ check "1" localR (Dir []); | |
+ ); | |
+ | |
+ (* Test RPC functions "mkdir" and "setDirProp" *) | |
+ runtest "RPC: mkdir, setDirProp" [] (fun () -> | |
+ let state = [("subd", Dir [])] in | |
+ put localR (Dir state); | |
+ put remoteR (Dir []); | |
+ sync (); | |
+ check "1" remoteR (Dir state); | |
+ ); | |
+ | |
+ (* Test RPC function "setupTargetPaths" *) | |
+ runtest "RPC: merge" ["merge = Name ma -> echo x> NEW"; "backupcurr = Name ma"] (fun () -> | |
+ let result = match Sys.os_type with | |
+ | "Win32" -> ("ma", File "x\r\n") | |
+ | _ -> ("ma", File "x\n") | |
+ in | |
+ put localR (Dir [("ma", File "a")]); | |
+ put remoteR (Dir [("ma", File "b")]); | |
+ sync (); | |
+ check "1" localR (Dir [result]); | |
+ check "2" remoteR (Dir [result]); | |
+ ); | |
+ end; | |
+ | |
if !failures = 0 then | |
Util.msg "Success :-)\n" | |
else | |
EOF | |
- name: "2.52.0: Patch _prev for newer compilers" | |
if: contains(matrix.job.ref, '2.52.0') | |
shell: bash | |
run: | | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/system/system_win_stubs.c b/src/system/system_win_stubs.c | |
index 50ea663f..57940d98 100644 | |
--- a/src/system/system_win_stubs.c | |
+++ b/src/system/system_win_stubs.c | |
@@ -373,7 +373,7 @@ typedef enum _FILE_INFORMATION_CLASS { | |
#include <caml/version.h> /* Available since OCaml 4.02 */ | |
#endif | |
-#if !defined(OCAML_VERSION) || OCAML_VERSION < 40300 | |
+#if !defined(OCAML_VERSION) || OCAML_VERSION < 40300 || OCAML_VERSION >= 41400 | |
typedef struct _REPARSE_DATA_BUFFER { | |
ULONG ReparseTag; | |
EOF | |
- name: "2.52 - 2.53.3: Patch _prev for newer compilers" | |
if: contains(matrix.job.ref, '2.52') || matrix.job.ref == 'v2.53.0' || matrix.job.ref == 'v2.53.1' || matrix.job.ref == 'v2.53.2' || matrix.job.ref == 'v2.53.3' | |
shell: bash | |
run: | | |
cd _prev && git apply - <<"EOF" | |
diff --git a/src/lwt/lwt_unix_stubs.c b/src/lwt/lwt_unix_stubs.c | |
index 37154710..51caabff 100644 | |
--- a/src/lwt/lwt_unix_stubs.c | |
+++ b/src/lwt/lwt_unix_stubs.c | |
@@ -392,8 +392,8 @@ CAMLprim value win_check_connection (value socket, value kind, value h) { | |
static HANDLE dummyEvent; | |
-CAMLprim value init_lwt (value callback) { | |
- CAMLparam1 (callback); | |
+CAMLprim value init_lwt (value callb) { | |
+ CAMLparam1 (callb); | |
// GUID GuidConnectEx = WSAID_CONNECTEX; | |
// SOCKET s; | |
// DWORD l; | |
@@ -401,7 +401,7 @@ CAMLprim value init_lwt (value callback) { | |
D(printf("Init...\n")); | |
caml_register_global_root(&completionCallback); | |
- completionCallback = callback; | |
+ completionCallback = callb; | |
dummyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Dummy event | |
EOF | |
- run: cd _prev && opam exec -- make src UISTYLE=text OSTYPE=$OSTYPE | |
shell: bash | |
if: matrix.job.oldmake | |
- run: cd _prev && opam exec -- make tui | |
shell: bash | |
if: ${{ !matrix.job.oldmake }} | |
# IMPORTANT! These tests do not exercise the entire RPC API. Yet, they | |
# should work fine as a smoke test. | |
- name: Run self-tests over RPC - new client to prev server | |
env: | |
UNISON: test1 | |
shell: bash | |
run: | | |
# Separate backup dir must be set for server instance so that the central | |
# backup location of both instances doesn't overlap | |
UNISONBACKUPDIR=./_prev/src/testbak_s _prev/src/unison -socket 55443 & | |
sleep 1 # Wait for the server to be fully started | |
_new/src/unison -ui text -selftest testr_c socket://127.0.0.1:55443/testr_s -killserver | |
- name: Run self-tests over RPC - prev client to new server | |
env: | |
UNISON: test2 | |
shell: bash | |
run: | | |
# Separate backup dir must be set for server instance so that the central | |
# backup location of both instances doesn't overlap | |
UNISONBACKUPDIR=./_new/src/testbak_s _new/src/unison -socket 55443 & | |
sleep 1 # Wait for the server to be fully started | |
_prev/src/unison -ui text -selftest testr_c socket://127.0.0.1:55443/testr_s -killserver | |
## We know the code is ok with various ocaml versions, so this is | |
## just checking the dune build process. Therefore build each OS | |
## family just once. Pick a different ocaml version because that's | |
## better coverage without adding a build. | |
opam_dune_build: | |
strategy: | |
fail-fast: false | |
matrix: | |
job: | |
- { os: ubuntu-22.04 , ocaml-compiler: 4.12.x } | |
- { os: macos-14 , ocaml-compiler: 4.14.x } | |
runs-on: ${{ matrix.job.os }} | |
steps: | |
- if: contains(matrix.job.os, 'ubuntu') | |
run: sudo apt-get update | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Use OCaml ${{ matrix.job.ocaml-compiler }} | |
uses: ocaml/setup-ocaml@v3 | |
with: | |
ocaml-compiler: "${{ matrix.job.ocaml-compiler }}" | |
- run: opam install . --deps-only | |
- run: opam exec -- dune build && cp -L ./_build/install/default/bin/unison* ./src/ | |
# - run: opam exec -- make test | |
bytecode_build: | |
strategy: | |
fail-fast: false | |
matrix: | |
job: | |
- { os: ubuntu-22.04 , ocaml-compiler: 4.14.x } | |
runs-on: ${{ matrix.job.os }} | |
steps: | |
- if: contains(matrix.job.os, 'ubuntu') | |
run: sudo apt-get update | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Use OCaml ${{ matrix.job.ocaml-compiler }} | |
uses: ocaml/setup-ocaml@v3 | |
with: | |
ocaml-compiler: "${{ matrix.job.ocaml-compiler }}" | |
opam-pin: false | |
- run: opam exec -- make tui NATIVE=false | |
- run: opam exec -- make test | |
build_compat: | |
if: ${{ !cancelled() }} # Don't fail if 'docs' failed | |
needs: docs | |
strategy: | |
fail-fast: false | |
matrix: | |
job: | |
- { ocaml-version: 4.14.x, publish: true, fnsuffix: -ubuntu-x86_64 } | |
- { ocaml-version: "ocaml-variants.4.14.2+options,ocaml-option-musl,ocaml-option-static,ocaml-option-flambda", publish: true, fnsuffix: -ubuntu-x86_64-static, static: -static } | |
- { ocaml-version: 4.12.x } | |
- { ocaml-version: 4.08.x } | |
runs-on: ubuntu-20.04 | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Use OCaml ${{ matrix.job.ocaml-version }} | |
uses: ocaml/setup-ocaml@v2 | |
with: | |
ocaml-compiler: ${{ matrix.job.ocaml-version }} | |
opam-disable-sandboxing: true | |
opam-pin: false | |
opam-depext: false | |
- name: Build text UI | |
env: | |
LDFLAGS: ${{ matrix.job.static }} | |
run: | | |
opam exec -- make | |
mkdir -p pkg/bin | |
cp src/unison pkg/bin/ | |
cp src/unison-fsmonitor pkg/bin/ | |
- name: Run local tests | |
run: opam exec -- make test | |
- name: Run remote tests | |
run: | | |
mkdir localsocket | |
chmod 700 localsocket | |
# Separate backup dir must be set for server instance so that the central | |
# backup location of both instances doesn't overlap | |
UNISONBACKUPDIR=./src/testbak4 ./src/unison -socket ./localsocket/test.sock & | |
sleep 1 # Wait for the server to be fully started | |
test -S ./localsocket/test.sock | |
./src/unison -ui text -selftest testr3 socket://{./localsocket/test.sock}/testr4 -killserver | |
- name: Build GUI | |
if: ${{ !contains(matrix.job.ocaml-version, '-musl') }} | |
run: | | |
opam depext --install --verbose --yes lablgtk3 && opam install ocamlfind | |
opam exec -- make gui | |
cp src/unison-gui pkg/bin/ | |
- name: Initialize packaging variables | |
id: vars | |
run: | | |
REF_SHAS=$(echo '${{ github.sha }}' | awk '{ print substr($0, 1, 8) }') | |
unset REF_TAG ; case "${GITHUB_REF}" in refs/tags/*) REF_TAG="${GITHUB_REF#refs/tags/}" ;; esac; | |
PKG_VER="${REF_TAG:-git_$REF_SHAS}" | |
PKG_VER="${PKG_VER#v}" | |
echo PKG_NAME="${PROJECT_NAME}-${PKG_VER}${{ matrix.job.fnsuffix }}.tar.gz" >> $GITHUB_OUTPUT | |
echo REF_SHAS=${REF_SHAS} >> $GITHUB_OUTPUT | |
- uses: actions/upload-artifact@v4 | |
if: false ## disable by default; only useful for debugging GHA | |
with: | |
name: unison-${{ steps.vars.outputs.REF_SHAS }}.ocaml-${{ matrix.job.ocaml-version }}.ubuntu.x86_64 | |
path: pkg/bin/* | |
- name: Copy user manual | |
if: matrix.job.publish | |
continue-on-error: ${{ !(github.ref_type == 'tag' && startsWith(github.ref_name, 'v') && matrix.job.publish) }} | |
uses: actions/download-artifact@v4 | |
with: | |
name: unison-docs | |
path: pkg | |
- name: Prepare package | |
if: matrix.job.publish | |
run: | | |
strip pkg/bin/* | |
cp README* pkg/ | |
cp LICENSE* pkg/ | |
cp INSTALL.md pkg/ | |
cp CONTRIBUTING.md pkg/ | |
cp NEWS.md pkg/ | |
- name: Package | |
if: matrix.job.publish | |
run: cd pkg && tar czf '${{ steps.vars.outputs.PKG_NAME }}' * | |
- uses: actions/upload-artifact@v4 | |
if: matrix.job.publish | |
with: | |
name: ${{ steps.vars.outputs.PKG_NAME }}___ocaml-${{ matrix.job.ocaml-version }}.ubuntu_compat-publish | |
path: pkg/${{ steps.vars.outputs.PKG_NAME }} | |
- name: Publish | |
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v') && matrix.job.publish | |
uses: softprops/action-gh-release@v1 | |
with: | |
files: pkg/${{ steps.vars.outputs.PKG_NAME }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |