Rebuilding Spice client for macOS from scratch

Friday, December 1, 2017

Here is a quick recipe to rebuild a macOS SPICE client from scratch. There are two methods, one using autotools, one using c3d/build. The autotools approach is presently complicated on macOS due to a bug in autotools and to differences between Apple and GNU tools.

Building with make and c3d/build

This approach is still experimental (as in "published today"), but it is significantly faster than autotools (as in about 16 times faster).

  1. Install Homebrew as follows:
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    
  2. Install the required dependencies for building:
    brew install pkg-config gettext intltool pixman gtk+3 gtk-doc gstreamer gstreamermm libjpeg-turbo gst-plugins-good gst-plugins-bad gst-libav
    

    brew cask install xquartz

    Note that installing XQuartz is not necessary for the SPICE client, only for the SPICE streaming agent, but this ensures that all SPICE components build successfully. Also, this step is only used as a quick way to install the /usr/X11 directory. If you have that directory on your system, you can skip the brew cask install xquartz step.
  3. Clone the top-level directory for SPICE and go to that directory:
    git clone https://github.com/c3d/spice
    cd spice
    
  4. Tell pkg-config where to find packages installed by brew:
    export PKG_CONFIG_PATH=/usr/local/opt/jpeg-turbo/lib/pkgconfig:/usr/local/Cellar/openssl/1.0.2m/lib/pkgconfig:$PKG_CONFIG_PATH
    
    Make sure that you adjust the path for openssl to match what brew actually installed. The OpenSSL package is not seen by default by pkg-config because it conflicts with Apple's own versions.
  5. Run the actual build:
    make -j
    
  6. If there is no error, you should be able to run spicy:
    ./spicy -h some-host -p 5900
    
You can switch this top-level directory to autotools by running the ./autogen.sh script from the top-level, and restore to c3d/build by running make gitclean.

Building using autotools

The steps are somewhat similar, but there is an additional requirement to patch the current version of autotools. This is also more likely to run into relatively mysterious errors, so make sure to check the Possible errors section below if you have a problem.

  1. Install Homebrew as follows:
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    
  2. Install the required dependencies for building:
    brew install pkg-config autoconf automake gettext intltool pixman gtk+3 gtk-doc gstreamer gstreamermm libjpeg-turbo gst-plugins-good gst-plugins-bad gst-libav
    

    brew cask install xquartz

  3. After verifying the version for OpenSSL installed by brew, setup the environment for building as follows:
       export PKG_CONFIG_PATH=/usr/local/opt/jpeg-turbo/lib/pkgconfig:/usr/local/Cellar/openssl/1.0.2m/lib/pkgconfig:$PKG_CONFIG_PATH
       export PATH=$PATH:/usr/local/Cellar/gettext/0.19.8.1/bin:/usr/local/Cellar/automake/1.15.1/bin
       export CFLAGS='-ObjC'
    
  4. Clone the spice-protocol repository and go there:
    cd ~/my-build-directory
    git clone git://anongit.freedesktop.org/spice/spice-protocol
    cd spice-protocol
    
  5. Run auto-configuration in spice-protocol (it may be a good time to take some coffee):
    ./autogen.sh
    
  6. Build and install spice-protocol:
    make install
    
  7. Clone the top-level directory for the SPICE GTK viewer, and go to that directory:
    cd ~/my-build-directory
    git clone git://anongit.freedesktop.org/spice/spice-gtk
    cd spice-gtk
    
  8. Run the auto-configuration for spice-gtk, and go grab another coffee:
    ./autogen.sh
    
  9. You may need to run configure manually, because the default configuration is

Possible errors

  • Dependency on Objective-C headers: The CFLAGS='-ObjC' is required because one of the keyboard mapping files includes a header that uses Objective-C syntax. Without it, you will get something like:
    In file included from vncdisplaykeymap.c:95:
    

    In file included from /usr/local/Cellar/gtk+3/3.22.24/include/gtk-3.0/gdk/gdkquartz.h:23: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/AppKit.framework/Headers/AppKit.h:10: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:8: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:506:9: error: unknown type name 'NSString'; did you mean 'GString'? typedef NSString * NSRunLoopMode NS_EXTENSIBLE_STRING_ENUM; ^

  • Autoconf bug in error reporting: While running auto-configuration, you may end up with a helpful error message that looks something like that:
    autoreconf: running: aclocal --force -I m4
    Use of uninitialized value $msg in concatenation (.) or string at /usr/local/Cellar/autoconf/2.69/bin/autom4te line 1026.
    Use of uninitialized value $stacktrace in pattern match (m//) at /usr/local/Cellar/autoconf/2.69/bin/autom4te line 1026.
    unknown channel m4trace: -1- AS_VAR_APPEND(ac_configure_args, " '$ac_arg'")
     at /usr/local/Cellar/autoconf/2.69/share/autoconf/Autom4te/Channels.pm line 638.
    	Autom4te::Channels::msg('m4trace: -1- AS_VAR_APPEND(ac_configure_args, " '$ac_arg'")x{a}', undef, 'warning: ', 'partial', 0) called at /usr/local/Cellar/autoconf/2.69/bin/autom4te line 1026
    aclocal: error: echo failed with exit status: 1
    autoreconf: aclocal failed with exit status: 1
    
    If you see this, then you need to patch autom4te as explained in this comment. You can for example run:
    sudo vi /usr/local/Cellar/autoconf/2.69/bin/autom4te
    
    then search for the message # Trace with arguments, and insert the following text just above it:
          # Traces without file/line
          next if (m{^m4trace: -(d+)- ([^(]+)((.*)$});
    
    In other words, you need to apply the following patch:
    diff --git a/usr/local/Cellar/autoconf/2.69/bin/autom4te.old b/usr/local/Cellar/autoconf/2.69/bin/autom4te
    --- a/usr/local/Cellar/autoconf/2.69/bin/autom4te.old
    +++ b/usr/local/Cellar/autoconf/2.69/bin/autom4te
    @@ -821,6 +821,8 @@ EOF
       my $traces = new Autom4te::XFile ("< " . open_quote ($tcache . $req->id));
       while ($_ = $traces->getline)
         {
    +      # Traces without file/line
    +      next if (m{^m4trace: -(d+)- ([^(]+)((.*)$});
           # Trace with arguments, as the example above.  We don't try
           # to match the trailing parenthesis as it might be on a
           # separate line.
    
    Once you have applied the patch, you will see the actual error that the autoconf bug was trying to report but failed to, for example:
    configure: error: Package requirements (spice-protocol >= 0.12.13) were not met:
    No package 'spice-protocol' found
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    Alternatively, you may set the environment variables SPICE_PROTOCOL_CFLAGS
    and SPICE_PROTOCOL_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

  • Configuration errors. For some reason, autogen.sh may leave you with an invalid configuration if you don't run it manually. In that case, you get this:
    make install
    

    echo 0.34.23-0381-dirty > .version-t && mv .version-t .version /Library/Developer/CommandLineTools/usr/bin/make install-recursive Making install in spice-common make[2]: *** No rule to make target `install'. Stop. make[1]: *** [install-recursive] Error 1 make: *** [install] Error 2

    If you see this, simply run ./configure manually.
  • Forced errors for warnings: You may see an error like:
    channel-display-mjpeg.c:117:2: error: "You should consider building with
          libjpeg-turbo" [-Werror,-W#warnings]
    #warning "You should consider building with libjpeg-turbo"
     ^
    1 error generated.
    
    There were apparently recent changes in the Homebrew-installed jpeg-turbo, which make the version incompatible with the regular JPEG version. For now, simply remove the #warning.

In the end...

Let me conclude with a personal opinion...

On my machine, the complete build with c3d/build takes about 15 seconds for a debug build and about 30 seconds for an optimized build. With autotools, it takes roughly 3 minutes, that's about 16 times slower.

The description of what's required to build SPICE with c3d/build is presently about 800 lines of standard Makefile. By contrast, with autotools, it takes about 5x as much, 1303 lines for configure.ac files, 2428 lines of Makefile.am (granted, at present, building very slightly more, e.g. documentation)

Autotools: Complex non-solutions to simple non-problems.

As far as I know, there is nothing autotools makes that make cannot do as well and faster. QED.