Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUACAMOLE-1841: Create a Cygwin build option. #454

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
30 changes: 30 additions & 0 deletions Dockerfile.windows
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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

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 Cygwin 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-cygwin || 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
```
142 changes: 86 additions & 56 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,30 @@ 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])

# Cygwin build
CYWGIN_LDFLAGS=
CYGWIN_CFLAGS=-Werror -Wall -pedantic
AC_ARG_WITH([cygwin],
[AS_HELP_STRING([--with-cygwin],
[use Cygwin to build under Windows @<:@default=no@:>@])],
[with_cygwin=yes], [with_cygwin=no])

AM_CONDITIONAL([CYGWIN_BUILD], [test "x${with_cygwin}" = "xyes"])

if test "x$with_cygwin" = "xyes"
then
CYWGIN_LDFLAGS=-no-undefined
CYGWIN_CFLAGS=-Werror -Wall -Wno-error=expansion-to-defined
AC_DEFINE([CYGWIN_BUILD],,[Build against Cygwin on Windows])
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_cygwin" = "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 Down Expand Up @@ -75,49 +96,52 @@ AC_CHECK_LIB([dl], [dlopen],
AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"),
[#include <dlfcn.h>])])

#
# 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_cygwin" = "xno"
then
have_libuuid=yes
AC_CHECK_LIB([uuid], [uuid_generate],
[UUID_LIBS=-luuid]
[AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])],
[have_libuuid=no])
fi
#
# 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"
then
have_libuuid=yes
AC_CHECK_LIB([uuid], [uuid_generate],
[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"
then
# OSSP UUID (if libuuid is unavilable)
if test "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.
--------------------------------------------])))

# 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_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.
--------------------------------------------])))

# 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
])
fi
fi

# cunit
Expand All @@ -131,6 +155,8 @@ AC_SUBST(CAIRO_LIBS)
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(UUID_LIBS)
AC_SUBST(CUNIT_LIBS)
AC_SUBST(CYWGIN_LDFLAGS)
AC_SUBST(CYGWIN_CFLAGS)

# Library functions
AC_CHECK_FUNCS([clock_gettime gettimeofday memmove memset select strdup nanosleep])
Expand Down Expand Up @@ -357,20 +383,24 @@ AC_SUBST(SSL_LIBS)

have_winsock=disabled
WINSOCK_LIBS=
AC_ARG_WITH([winsock],
[AS_HELP_STRING([--with-winsock],
[support Windows Sockets API @<:@default=check@:>@])],
[],
[with_winsock=check])

if test "x$with_winsock" != "xno"
if test "x$with_cygwin" = "xno"
Copy link
Contributor Author

@jmuehlner jmuehlner Aug 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Winsock support will be detected on Cygwin, but will induce compilation errors if an attempt is made to actually build with it:

In file included from /usr/include/w32api/winsock2.h:56,
                 from socket-fd.c:35:
/usr/include/w32api/psdk_inc/_fd_types.h:100:2: error: #warning "fd_set and associated macros have been defined in sys/types.      This can cause runtime problems with W32 sockets" [-Werror=cpp]
  100 | #warning "fd_set and associated macros have been defined in sys/types.  \
      |  ^~~~~~~
In file included from socket-fd.c:35:
/usr/include/w32api/winsock2.h:1031:34: error: conflicting types for 'select'; have 'int(int,  fd_set *, fd_set *, fd_set *, const TIMEVAL *)'
 1031 |   WINSOCK_API_LINKAGE int WSAAPI select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,const TIMEVAL *timeout);
      |                                  ^~~~~~
In file included from /usr/include/sys/time.h:49,
                 from socket-fd.c:31:
/usr/include/sys/select.h:80:5: note: previous declaration of 'select' with type 'int(int,  fd_set *, fd_set *, fd_set *, struct timeval *)'
   80 | int select __P ((int __n, fd_set *__readfds, fd_set *__writefds,
      |     ^~~~~~
In file included from socket-fd.c:35:
/usr/include/w32api/winsock2.h:1040:34: error: conflicting types for 'gethostname'; have 'int(char *, int)'
 1040 |   WINSOCK_API_LINKAGE int WSAAPI gethostname(char *name,int namelen);
      |                                  ^~~~~~~~~~~
In file included from /usr/include/unistd.h:4,
                 from guacamole/socket-fntypes.h:31,
                 from guacamole/socket.h:31,
                 from socket-fd.c:23:
/usr/include/sys/unistd.h:286:9: note: previous declaration of 'gethostname' with type 'int(char *, size_t)' {aka 'int(char *, long unsigned int)'}
  286 |  int    gethostname (char *__name, size_t __len);
      |         ^~~~~~~~~~~

then
have_winsock=yes
AC_CHECK_LIB([wsock32], [main],
[WINSOCK_LIBS="-lwsock32"]
[AC_DEFINE([ENABLE_WINSOCK],,
[Whether Windows Socket API support is enabled])],
[have_winsock=no])
AC_ARG_WITH([winsock],
[AS_HELP_STRING([--with-winsock],
[support Windows Sockets API @<:@default=check@:>@])],
[],
[with_winsock=check])

if test "x$with_winsock" != "xno"
then
have_winsock=yes
AC_CHECK_LIB([wsock32], [main],
[WINSOCK_LIBS="-lwsock32"]
[AC_DEFINE([ENABLE_WINSOCK],,
[Whether Windows Socket API support is enabled])],
[have_winsock=no])
fi
fi

AM_CONDITIONAL([ENABLE_WINSOCK], [test "x${have_winsock}" = "xyes"])
Expand Down Expand Up @@ -513,7 +543,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 @@ -662,7 +692,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 @@ -940,7 +970,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
18 changes: 18 additions & 0 deletions scripts/build-cairo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
set -e

wget -qO- https://www.cairographics.org/releases/pixman-0.42.2.tar.gz | tar xz
pushd pixman-0.42.2

./configure --host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32 \
CPPFLAGS="-I/usr/x86_64-w64-mingw32/include" LDFLAGS="-L/usr/x86_64-w64-mingw32/lib/"
make
make install
popd

wget -qO- https://www.cairographics.org/releases/cairo-1.18.0.tar.xz | tar xJ
pushd cairo-1.18.0

meson setup --cross-file /x86_64-w64-mingw32.txt build-mingw
meson compile -C build-mingw
meson install
16 changes: 16 additions & 0 deletions scripts/build-guac.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
#set -e

cd guacamole-server

export PKG_CONFIG_PATH="/quasi-msys2/root/ucrt64/lib/pkgconfig/"
export LDFLAGS="-L/quasi-msys2/root/ucrt64/bin/"
export CFLAGS="-I/quasi-msys2/root/ucrt64/include/ -I/usr/x86_64-w64-mingw32/include"

autoreconf -fi
./configure --host=x86_64-w64-mingw32 --with-cygwin --with-telnet=no --with-ssh=no --with-rdp=no --with-vnc=no --disable-kubernetes --disable-guacenc --disable-guacd --disable-guaclog

cat config.log

make
make install
Loading