Skip to content

Commit

Permalink
GUACAMOLE-1841: Implement MinGW build for libguac.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmuehlner committed Jan 25, 2024
1 parent a575af6 commit 48b6baf
Show file tree
Hide file tree
Showing 88 changed files with 3,592 additions and 398 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ doc/*/doxygen-output

# IDE metadata
nbproject/

# Crash Reports
**/*.stackdump
33 changes: 33 additions & 0 deletions Dockerfile.windows
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM ubuntu:22.04

RUN apt-get -y update
RUN apt-get -y install software-properties-common

RUN apt-get -y install \
autoconf \
autotools-dev \
cmake \
git \
libtool \
make \
meson \
pkg-config \
wget \
zstd \
gpg \
wine \
gcc-mingw-w64-x86-64-win32 \
gcc-mingw-w64-x86-64-win32-runtime

COPY scripts/install-msys2.sh /install-msys2.sh
RUN bash /install-msys2.sh

COPY scripts/fix-links.sh /fix-links.sh
RUN bash /fix-links.sh

RUN mkdir /guacamole-server
COPY . /guacamole-server

COPY /scripts/build-guac.sh /build-guac.sh
RUN bash /build-guac.sh

127 changes: 127 additions & 0 deletions README-windows-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Building guacamole-server for Windows
The `guacamole-server` Windows build relies on compatibility features provided by Cygwin (most notably, a `fork()` implmentation), and therefore _must_ be built using Cygwin tools. Since no Cygwin cross-compilation environment exists, this means that `guacamole-server` can only be built for Windows using the Windows OS. This document describes a build that produces a working `guacd.exe`, as well as shared libraries for every supported protocol.

## Build Specifics
In this example, `guacamole-server` was built under Cygwin, on a Windows Server 2022 x86_64 build node. Dependencies were installed using packages from Cygwin and MSYS2 (and built from source where no suitable package is available, in the case of `libtelnet` and `libfreerdp2`).

### Build Steps
1. Install Cygwin (version 2.926 used here)
2. Install MSYS2 (version 20230718 used here)
3. Install Cygwin packages:
* autoconf
* automake
* cmake
* git
* gcc-core
* libtool
* libuuid-devel
* make
* pkg-config
4. Install MSYS2 packages:
* autoconf-wrapper
* automake-wrapper
* diffutils
* make
* mingw-w64-x86_64-gcc
* mingw-w64-x86_64-cairo
* mingw-w64-x86_64-pango
* mingw-w64-x86_64-libwebsockets
* mingw-w64-x86_64-libvncserver
* mingw-w64-x86_64-libssh2
* mingw-w64-x86_64-libtool
* mingw-w64-x86_64-libmariadbclient
* mingw-w64-x86_64-postgresql
* mingw-w64-x86_64-libpng
* mingw-w64-x86_64-libjpeg
* mingw-w64-x86_64-dlfcn
* mingw-w64-x86_64-pkg-config
* mingw-w64-x86_64-pulseaudio
* mingw-w64-x86_64-libvorbis
* mingw-w64-x86_64-libwebp
* mingw-w64-x86_64-zlib
5. Build `libtelnet` from source using MSYS2 bash shell
```
curl -s -L https://github.com/seanmiddleditch/libtelnet/releases/download/0.23/libtelnet-0.23.tar.gz | tar xz
cd libtelnet-0.23
autoreconf -fi
./configure --disable-static --disable-util LDFLAGS="-Wl,-no-undefined"
make LDFLAGS="-no-undefined"
make install
# Required for the Windows Build to understand how to link against this DLL
ln -s /usr/bin/msys-telnet-2.dll /usr/bin/libtelnet.dll
```
5. Build `libfreerdp2` from source using Cygwin bash shell
```
curl -s -L https://github.com/FreeRDP/FreeRDP/archive/2.10.0/FreeRDP-2.10.0.tar.gz | tar xz
cd FreeRDP-2.10.0
cmake \
-DWITH_ALSA=OFF \
-DWITH_CUPS=OFF \
-DWITH_CHANNELS=ON \
-DBUILTIN_CHANNELS=OFF \
-DCHANNEL_URBDRC=OFF \
-DWITH_CLIENT=ON \
-DWITH_DIRECTFB=OFF \
-DWITH_FFMPEG=OFF \
-DWITH_GSM=OFF \
-DWITH_GSSAPI=OFF \
-DWITH_GSTREAMER_1_0=OFF \
-DWITH_GSTREAMER_0_10=OFF \
-DWITH_IPP=OFF \
-DWITH_JPEG=ON \
-DWITH_MANPAGES=ON \
-DWITH_OPENH264=OFF \
-DWITH_OPENSSL=ON \
-DWITH_PCSC=OFF \
-DWITH_PULSE=OFF \
-DWITH_SERVER=OFF \
-DWITH_SERVER_INTERFACE=OFF \
-DWITH_SHADOW_X11=OFF \
-DWITH_SHADOW_MAC=OFF \
-DWITH_SSE2=$SSE2_SETTING \
-DWITH_WAYLAND=OFF \
-DWITH_X11=OFF \
-DWITH_X264=OFF \
-DWITH_XCURSOR=ON \
-DWITH_XEXT=ON \
-DWITH_XKBFILE=ON \
-DWITH_XI=OFF \
-DWITH_XINERAMA=OFF \
-DWITH_XRENDER=OFF \
-DWITH_XTEST=OFF \
-DWITH_XV=OFF \
-DWITH_ZLIB=ON \
.
cmake . -G"Unix Makefiles"
make
make install
```
7. Build `guacamole-server` from source using Cygwin bash shell
```
# FIXME: Update this to check out master once this PR is ready for merge
git clone https://github.com/jmuehlner/guacamole-server.git
cd guacamole-server
git checkout GUACAMOLE-1841-cygwin-build
autoreconf -fi
export LDFLAGS="-L/usr/bin/ -L/usr/lib/ -L/usr/local/lib -L/usr/local/bin -L/cygdrive/c/msys64/mingw64/bin -L/cygdrive/c/msys64/mingw64/lib -L/cygdrive/c/msys64/usr/bin"
export CFLAGS="-idirafter /cygdrive/c/msys64/mingw64/include/winpr2 -idirafter /usr/include/ -idirafter /cygdrive/c/msys64/mingw64/include -idirafter /cygdrive/c/msys64/mingw64/include/pango-1.0 -idirafter /cygdrive/c/msys64/mingw64/include/cairo -idirafter /usr/local/include/freerdp2 -idirafter /cygdrive/c/msys64/mingw64/include/glib-2.0 -idirafter /cygdrive/c/msys64/mingw64/include/harfbuzz -idirafter /cygdrive/c/msys64/mingw64/lib/glib-2.0/include -idirafter /cygdrive/c/msys64/usr/include"
export PKG_CONFIG_PATH="/cygdrive/c/msys64/mingw64/lib/pkgconfig/:/usr/local/lib/pkgconfig"
./configure --with-windows || cat config.log
make
```

## Closing Notes
The generated `guacd.exe` will run as expected when invoked from the Cygwin bash shell, but when run outside of a Cygwin environment (e.g. using powershell), connections using text-based protocols (ssh, telnet, kubernetes) will fail with `Fontconfig error: Cannot load default config file`.

FIXME: Update this document to explain how to configure fonts under Windows once I figure it out.

In addition, to enable running `guacd.exe` outside of a Cygwin environment, all (non-system) DLLs that it depends on should be copied into the same directory as the executable. These can be found by running `ldd` in the Cygwin bash shell - e.g.
```
ldd /usr/local/sbin/guacd.exe
find /usr -name '*guac*.dll' | xargs ldd
```
110 changes: 82 additions & 28 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,35 @@ AC_PROG_LIBTOOL
# Headers
AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/socket.h time.h sys/time.h syslog.h unistd.h cairo/cairo.h pngstruct.h])

# windows build
GENERAL_LDFLAGS=
GENERAL_CFLAGS=-Werror -Wall -pedantic
AC_ARG_WITH([windows],
[AS_HELP_STRING([--with-windows],
[build under/for Windows @<:@default=no@:>@])],
[with_windows=yes], [with_windows=no])

AM_CONDITIONAL([WINDOWS_BUILD], [test "x${with_windows}" = "xyes"])

if test "x$with_windows" = "xyes"
then
GENERAL_LDFLAGS=-no-undefined
GENERAL_CFLAGS=-Werror -Wall -Wno-error=expansion-to-defined
AC_DEFINE([WINDOWS_BUILD],,[Build against windows on Windows])

AC_CHECK_LIB([systre], [regcomp], [SYSTRE_LIBS=-lsystre],
AC_MSG_ERROR("libsystre is required for regex functionality"))
else
SYSTRE_LIBS=
fi

# Source characteristics
AC_DEFINE([_XOPEN_SOURCE], [700], [Uses X/Open and POSIX APIs])
AC_DEFINE([__BSD_VISIBLE], [1], [Uses BSD-specific APIs (if available)])

if test "x$with_windows" = "xno"
then
AC_DEFINE([__BSD_VISIBLE], [1], [Uses BSD-specific APIs (if available)])
fi

# Check for whether math library is required
AC_CHECK_LIB([m], [cos],
Expand All @@ -68,13 +94,17 @@ AC_CHECK_LIB([pthread], [pthread_create], [PTHREAD_LIBS=-lpthread
AC_DEFINE([HAVE_LIBPTHREAD],,
[Whether libpthread was found])])

# Windows provides its own timer API, and does not user timer_create
if test "x$with_windows" = "xno"
then
# librt
AC_CHECK_FUNC([timer_create], [AC_MSG_RESULT([timer_create was found without librt.])],
[AC_CHECK_LIB([rt], [timer_create],
[AC_MSG_RESULT([timer_create was found in librt.])
RT_LIBS=-lrt],
[AC_MSG_ERROR([timer_create could not be found.])])
])
fi

# Include libdl for dlopen() if necessary
AC_CHECK_LIB([dl], [dlopen],
Expand All @@ -84,48 +114,67 @@ AC_CHECK_LIB([dl], [dlopen],
[#include <dlfcn.h>])])

#
# libuuid
# UUID library
# First, check librpcrt4 (Windows), then libuuid, and finally OSSP UUID
#

AC_ARG_WITH([librpcrt4],
[AS_HELP_STRING([--with-librpcrt4],
[use librpcrt4 to generate unique identifiers @<:@default=check@:>@])],
[],
[with_librpcrt4=check])

# First, check for librpcrt4 (Windows)
have_librpcrt4=disabled
if test "x$with_librpcrt4" != "xno"
then
have_librpcrt4=yes
AC_CHECK_LIB([rpcrt4], [UuidCreate],
[UUID_LIBS=-lrpcrt4]
[AC_DEFINE([HAVE_LIBRPCRT4],, [Whether librpcrt4 is available])],
[have_librpcrt4=no])
fi

# Next, look for libuuid
have_libuuid=disabled
AC_ARG_WITH([libuuid],
[AS_HELP_STRING([--with-libuuid],
[use libuuid to generate unique identifiers @<:@default=check@:>@])],
[],
[with_libuuid=check])

if test "x$with_libuuid" != "xno"
if test "x$with_libuuid" != "xno" -a "x$have_librpcrt4" != "xyes"
then
have_libuuid=yes
AC_CHECK_LIB([uuid], [uuid_generate],
[UUID_LIBS=-luuid]
[AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])],
[have_libuuid=no])
[UUID_LIBS=-luuid]
[AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])],
[have_libuuid=no])
fi

# OSSP UUID (if libuuid is unavilable)
if test "x${have_libuuid}" != "xyes"
# OSSP UUID (if librpcrt4 and libuuid are unavilable)
if test "x$have_librpcrt4" != "xyes" -a "x$have_libuuid" != "xyes"
then

AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
AC_MSG_ERROR([
--------------------------------------------
Unable to find libuuid or the OSSP UUID library.
Either libuuid (from util-linux) or the OSSP UUID library is required for
guacamole-server to be built.
--------------------------------------------])))
AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
AC_MSG_ERROR([
--------------------------------------------
Unable to find librpcrt4 or libuuid or the OSSP UUID library.
Either librpcrt4 (Windows) or libuuid (from util-linux) or the
OSSP UUID library is required for guacamole-server to be built.
--------------------------------------------])))

# Check for and validate OSSP uuid.h header
AC_CHECK_HEADERS([ossp/uuid.h])
AC_CHECK_DECL([uuid_make],,
AC_MSG_ERROR("No OSSP uuid.h found in include path"),
[#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#else
#include <uuid.h>
#endif
])
AC_MSG_ERROR("No OSSP uuid.h found in include path"),
[#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#else
#include <uuid.h>
#endif
])
fi

# cunit
Expand All @@ -140,6 +189,9 @@ AC_SUBST(RT_LIBS)
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(UUID_LIBS)
AC_SUBST(CUNIT_LIBS)
AC_SUBST(GENERAL_LDFLAGS)
AC_SUBST(GENERAL_CFLAGS)
AC_SUBST(SYSTRE_LIBS)

# Library functions
AC_CHECK_FUNCS([clock_gettime gettimeofday memmove memset select strdup nanosleep])
Expand Down Expand Up @@ -366,6 +418,7 @@ AC_SUBST(SSL_LIBS)

have_winsock=disabled
WINSOCK_LIBS=

AC_ARG_WITH([winsock],
[AS_HELP_STRING([--with-winsock],
[support Windows Sockets API @<:@default=check@:>@])],
Expand All @@ -376,10 +429,10 @@ if test "x$with_winsock" != "xno"
then
have_winsock=yes
AC_CHECK_LIB([wsock32], [main],
[WINSOCK_LIBS="-lwsock32"]
[AC_DEFINE([ENABLE_WINSOCK],,
[WINSOCK_LIBS="-lwsock32"]
[AC_DEFINE([ENABLE_WINSOCK],,
[Whether Windows Socket API support is enabled])],
[have_winsock=no])
[have_winsock=no])
fi

AM_CONDITIONAL([ENABLE_WINSOCK], [test "x${have_winsock}" = "xyes"])
Expand Down Expand Up @@ -522,7 +575,7 @@ then

# Whether libvncserver was built against libgcrypt
AC_CHECK_DECL([LIBVNCSERVER_WITH_CLIENT_GCRYPT],
[AC_CHECK_HEADER(gcrypt.h,,
[AC_CHECK_HEADER(gcrypt.h, [VNC_LIBS="$VNC_LIBS -lgcrypt"],
[AC_MSG_WARN([
--------------------------------------------
libvncserver appears to be built against
Expand Down Expand Up @@ -671,7 +724,7 @@ if test "x$with_rdp" != "xno"
then
have_freerdp2=yes
PKG_CHECK_MODULES([RDP], [freerdp2 freerdp-client2 winpr2],
[CPPFLAGS="${RDP_CFLAGS} -Werror $CPPFLAGS"]
[CPPFLAGS="${RDP_CFLAGS} -Werror -Wexpansion-to-defined $CPPFLAGS"]
[AS_IF([test "x${FREERDP2_PLUGIN_DIR}" = "x"],
[FREERDP2_PLUGIN_DIR="`$PKG_CONFIG --variable=libdir freerdp2`/freerdp2"])],
[AC_MSG_WARN([
Expand Down Expand Up @@ -949,7 +1002,7 @@ then
# Whether libssh2 was built against libgcrypt
AC_CHECK_LIB([ssh2], [gcry_control],
[AC_CHECK_HEADER(gcrypt.h,
[AC_DEFINE([LIBSSH2_USES_GCRYPT],,
[AC_DEFINE([LIBSSH2_USES_GCRYPT],[SSH_LIBS="$SSH_LIBS -lgcrypt"],
[Whether libssh2 was built against libgcrypt])],
[AC_MSG_WARN([
--------------------------------------------
Expand Down Expand Up @@ -1196,6 +1249,7 @@ AC_CONFIG_FILES([Makefile
src/protocols/kubernetes/Makefile
src/protocols/kubernetes/tests/Makefile
src/protocols/rdp/Makefile
src/protocols/rdp/guacxpstopdf/Makefile
src/protocols/rdp/tests/Makefile
src/protocols/ssh/Makefile
src/protocols/telnet/Makefile
Expand Down
Loading

0 comments on commit 48b6baf

Please sign in to comment.