xpra icon
Bug tracker and wiki

Changes in trunk/src/setup.py [6000:16705] in xpra


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r16705  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2017 Antoine Martin <antoine@devloop.org.uk>
    55# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <njs@pobox.com>
    66# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     
    1111# does the make_constants hack.)
    1212
    13 import commands
    1413import glob
    1514from distutils.core import setup
    1615from distutils.extension import Extension
    17 import subprocess, sys, traceback
     16import sys
    1817import os.path
    19 import stat
     18from distutils.command.build import build
     19from distutils.command.install_data import install_data
     20import shutil
     21
     22if sys.version<'2.7':
     23    raise Exception("xpra no longer supports Python 2 versions older than 2.7")
     24if sys.version[0]=='3' and sys.version<'3.4':
     25    raise Exception("xpra no longer supports Python 3 versions older than 3.4")
     26#we don't support versions of Python without the new ssl code:
     27import ssl
     28assert ssl.SSLContext, "xpra requires a Python version with ssl.SSLContext support"
     29
     30from hashlib import md5
    2031
    2132print(" ".join(sys.argv))
    2233
     34#*******************************************************************************
     35# build options, these may get modified further down..
     36#
    2337import xpra
    24 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
    25 
    26 WIN32 = sys.platform.startswith("win")
     38data_files = []
     39modules = []
     40packages = []       #used by py2app
     41excludes = []       #only used by cx_freeze on win32
     42ext_modules = []
     43cmdclass = {}
     44scripts = []
     45description = "multi-platform screen and application forwarding system"
     46long_description = "Xpra is a multi platform persistent remote display server and client for " + \
     47            "forwarding applications and desktop screens. Also known as 'screen for X11'."
     48url = "http://xpra.org/"
     49
     50
     51XPRA_VERSION = xpra.__version__         #@UndefinedVariable
     52setup_options = {
     53                 "name"             : "xpra",
     54                 "version"          : XPRA_VERSION,
     55                 "license"          : "GPLv2+",
     56                 "author"           : "Antoine Martin",
     57                 "author_email"     : "antoine@devloop.org.uk",
     58                 "url"              : url,
     59                 "download_url"     : "http://xpra.org/src/",
     60                 "description"      : description,
     61                 "long_description" : long_description,
     62                 "data_files"       : data_files,
     63                 "py_modules"       : modules,
     64                 }
     65
     66WIN32 = sys.platform.startswith("win") or sys.platform.startswith("msys")
    2767OSX = sys.platform.startswith("darwin")
    28 
    29 
     68LINUX = sys.platform.startswith("linux")
     69NETBSD = sys.platform.startswith("netbsd")
     70FREEBSD = sys.platform.startswith("freebsd")
     71PYTHON3 = sys.version_info[0] == 3
     72POSIX = os.name=="posix"
     73import struct
     74BITS = struct.calcsize("P")*8
     75
     76
     77if "pkg-info" in sys.argv:
     78    with open("PKG-INFO", "wb") as f:
     79        pkg_info_values = setup_options.copy()
     80        pkg_info_values.update({
     81                                "metadata_version"  : "1.1",
     82                                "summary"           :  description,
     83                                "home_page"         : url,
     84                                })
     85        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     86                  "Author", "Author-email", "License", "Download-URL", "Description"):
     87            v = pkg_info_values[k.lower().replace("-", "_")]
     88            f.write(b"%s: %s\n" % (k, v))
     89    sys.exit(0)
     90
     91
     92print("Xpra version %s" % XPRA_VERSION)
    3093#*******************************************************************************
    3194# Most of the options below can be modified on the command line
     
    3396# only the default values are specified here:
    3497#*******************************************************************************
    35 def pkg_config_ok(*args):
    36     return commands.getstatusoutput("pkg-config %s" % (" ".join(args)))[0]==0
    37 
    38 shadow_ENABLED = SHADOW_SUPPORTED
    39 server_ENABLED = LOCAL_SERVERS_SUPPORTED or shadow_ENABLED
    40 client_ENABLED = True
    41 
    42 x11_ENABLED = not WIN32 and not OSX
    43 argb_ENABLED = True
    44 gtk2_ENABLED = client_ENABLED
    45 gtk3_ENABLED = False
    46 qt4_ENABLED = False
    47 opengl_ENABLED = client_ENABLED
    48 html5_ENABLED = not WIN32 and not OSX
    49 
    50 bencode_ENABLED         = True
    51 cython_bencode_ENABLED  = True
    52 rencode_ENABLED         = True
    53 cymaths_ENABLED         = True
    54 cyxor_ENABLED           = True
    55 clipboard_ENABLED       = True
    56 Xdummy_ENABLED          = None          #none means auto-detect
    57 sound_ENABLED           = True
    58 
    59 enc_proxy_ENABLED       = True
    60 enc_x264_ENABLED        = True          #too important to detect
    61 enc_x265_ENABLED        = pkg_config_ok("--exists", "x265")
    62 webp_ENABLED            = WIN32 or pkg_config_ok("--atleast-version=0.3", "libwebp")
    63 x264_static_ENABLED     = False
    64 x265_static_ENABLED     = False
    65 vpx_ENABLED             = WIN32 or pkg_config_ok("--atleast-version=1.0", "vpx") or pkg_config_ok("--atleast-version=1.0", "libvpx")
    66 vpx_static_ENABLED      = False
    67 #ffmpeg 1.x and libav:
    68 dec_avcodec_ENABLED     = not WIN32 and pkg_config_ok("--max-version=55", "libavcodec")
    69 #ffmpeg 2 onwards:
    70 dec_avcodec2_ENABLED    = WIN32 or pkg_config_ok("--atleast-version=55", "libavcodec")
    71 # some version strings I found:
    72 # Fedora 19: 54.92.100
    73 # Fedora 20: 55.39.101
    74 # Debian sid and jessie: 54.35.0
    75 # Debian wheezy: 53.35
    76 avcodec_static_ENABLED  = False
    77 avcodec2_static_ENABLED = False
    78 csc_swscale_ENABLED     = WIN32 or pkg_config_ok("--exists", "libswscale")
    79 swscale_static_ENABLED  = False
    80 csc_cython_ENABLED      = True
    81 webm_ENABLED            = True
    82 nvenc_ENABLED           = pkg_config_ok("--exists", "nvenc3")
    83 csc_nvcuda_ENABLED      = pkg_config_ok("--exists", "cuda")
    84 csc_opencl_ENABLED      = pkg_config_ok("--exists", "OpenCL")
    85 
     98from xpra.os_util import get_status_output
     99
     100PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     101has_pkg_config = False
     102#we don't support building with "pkg-config" on win32 with python2:
     103if PKG_CONFIG:
     104    v = get_status_output([PKG_CONFIG, "--version"])
     105    has_pkg_config = v[0]==0 and v[1]
     106    if has_pkg_config:
     107        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     108
     109from Cython.Compiler.Version import version as cython_version
     110
     111for arg in list(sys.argv):
     112    if arg.startswith("--pkg-config-path="):
     113        pcp = arg[len("--pkg-config-path="):]
     114        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     115        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     116        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     117        sys.argv.remove(arg)
     118
     119def pkg_config_ok(*args, **kwargs):
     120    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     121    return get_status_output(cmd)[0]==0
     122
     123def pkg_config_version(req_version, pkgname, **kwargs):
     124    cmd = [PKG_CONFIG, "--modversion", pkgname]
     125    r, out, _ = get_status_output(cmd)
     126    if r!=0 or not out:
     127        return False
     128    from distutils.version import LooseVersion
     129    return LooseVersion(out)>=LooseVersion(req_version)
     130
     131def is_RH():
     132    try:
     133        with open("/etc/redhat-release", mode='rb') as f:
     134            data = f.read()
     135        return data.startswith("CentOS") or data.startswith("RedHat")
     136    except:
     137        pass
     138    return False
     139
     140DEFAULT = True
     141if "--minimal" in sys.argv:
     142    sys.argv.remove("--minimal")
     143    DEFAULT = False
     144
     145from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     146shadow_ENABLED = SHADOW_SUPPORTED and not (PYTHON3 and LINUX) and DEFAULT       #shadow servers use some GTK2 code..
     147server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and not (PYTHON3 and LINUX) and DEFAULT
     148service_ENABLED = LINUX and server_ENABLED
     149sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd")
     150proxy_ENABLED  = DEFAULT
     151client_ENABLED = DEFAULT
     152
     153x11_ENABLED = DEFAULT and not WIN32 and not OSX
     154xinput_ENABLED = x11_ENABLED
     155uinput_ENABLED = x11_ENABLED
     156dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     157gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     158gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     159gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     160opengl_ENABLED = DEFAULT and client_ENABLED
     161html5_ENABLED = DEFAULT
     162html5_gzip_ENABLED = DEFAULT
     163html5_brotli_ENABLED = DEFAULT
     164minify_ENABLED = html5_ENABLED
     165pam_ENABLED = DEFAULT and (server_ENABLED or proxy_ENABLED) and POSIX and not OSX and (os.path.exists("/usr/include/pam/pam_misc.h") or os.path.exists("/usr/include/security/pam_misc.h"))
     166
     167vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     168bencode_ENABLED         = DEFAULT
     169cython_bencode_ENABLED  = DEFAULT
     170clipboard_ENABLED       = DEFAULT and not PYTHON3
     171Xdummy_ENABLED          = None          #None means auto-detect
     172Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     173if WIN32 or OSX:
     174    Xdummy_ENABLED = False
     175sound_ENABLED           = DEFAULT
     176printing_ENABLED        = DEFAULT
     177crypto_ENABLED          = DEFAULT
     178mdns_ENABLED            = DEFAULT
     179
     180enc_proxy_ENABLED       = DEFAULT
     181enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     182enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     183pillow_ENABLED          = DEFAULT
     184jpeg_ENABLED            = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     185vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     186enc_ffmpeg_ENABLED      = False
     187webcam_ENABLED          = DEFAULT and not OSX
     188v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD)
     189#ffmpeg 3.1 or later is required
     190dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     191csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     192nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("7", "nvenc")
     193nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     194cuda_kernels_ENABLED    = DEFAULT
     195cuda_rebuild_ENABLED    = DEFAULT
     196csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     197example_ENABLED         = DEFAULT
     198
     199#Cython / gcc / packaging build options:
     200annotate_ENABLED        = True
    86201warn_ENABLED            = True
    87202strict_ENABLED          = True
    88 PIC_ENABLED             = True
     203PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89204debug_ENABLED           = False
    90205verbose_ENABLED         = False
    91206bundle_tests_ENABLED    = False
     207tests_ENABLED           = False
     208rebuild_ENABLED         = True
    92209
    93210#allow some of these flags to be modified on the command line:
    94 SWITCHES = ("enc_x264", "x264_static",
    95             "enc_x265", "x265_static",
    96             "nvenc",
    97             "dec_avcodec", "avcodec_static",
    98             "dec_avcodec2", "avcodec2_static",
    99             "csc_swscale", "swscale_static",
    100             "csc_nvcuda", "csc_opencl", "csc_cython",
    101             "vpx", "vpx_static",
    102             "webp", "webm",
    103             "rencode", "bencode", "cython_bencode",
     211SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     212            "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     213            "vpx", "pillow", "jpeg",
     214            "v4l2",
     215            "dec_avcodec2", "csc_swscale",
     216            "csc_libyuv",
     217            "bencode", "cython_bencode", "vsock", "mdns",
    104218            "clipboard",
    105             "server", "client", "x11",
    106             "gtk2", "gtk3", "qt4", "html5",
    107             "sound", "cyxor", "cymaths", "opengl", "argb",
    108             "warn", "strict", "shadow", "debug", "PIC", "Xdummy", "verbose", "bundle_tests")
     219            "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     220            "gtk_x11", "service",
     221            "gtk2", "gtk3", "example",
     222            "html5", "minify", "html5_gzip", "html5_brotli",
     223            "pam",
     224            "sound", "opengl", "printing", "webcam",
     225            "rebuild",
     226            "annotate", "warn", "strict",
     227            "shadow", "proxy",
     228            "debug", "PIC",
     229            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     230if WIN32:
     231    SWITCHES.append("zip")
     232    zip_ENABLED = True
    109233HELP = "-h" in sys.argv or "--help" in sys.argv
    110234if HELP:
     
    120244            default_str = "auto-detect"
    121245        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     246    print("  --pkg-config-path=PATH")
     247    print("  --rpath=PATH")
    122248    sys.exit(0)
    123249
     250install = "dist"
     251rpath = None
     252ssl_cert = None
     253ssl_key = None
     254minifier = None
    124255filtered_args = []
    125256for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     257    matched = False
     258    for x in ("rpath", "ssl-cert", "ssl-key", "install"):
     259        varg = "--%s=" % x
     260        if arg.startswith(varg):
     261            value = arg[len(varg):]
     262            globals()[x.replace("-", "_")] = value
     263            #remove these arguments from sys.argv,
     264            #except for --install=PATH
     265            matched = x!="install"
     266            break
     267    if matched:
    129268        continue
    130     matched = False
    131269    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     270        with_str = "--with-%s" % x
     271        without_str = "--without-%s" % x
     272        if arg.startswith(with_str+"="):
     273            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     274            matched = True
     275            break
     276        elif arg==with_str:
    133277            vars()["%s_ENABLED" % x] = True
    134278            matched = True
    135279            break
    136         elif arg=="--without-%s" % x:
     280        elif arg==without_str:
    137281            vars()["%s_ENABLED" % x] = False
    138282            matched = True
     
    145289    for x in SWITCHES:
    146290        switches_info[x] = vars()["%s_ENABLED" % x]
    147     print("build switches: %s" % switches_info)
    148     if LOCAL_SERVERS_SUPPORTED:
    149         print("Xdummy build flag: %s" % Xdummy_ENABLED)
     291    print("build switches:")
     292    for k in sorted(SWITCHES):
     293        v = switches_info[k]
     294        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150295
    151296    #sanity check the flags:
     
    153298        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154299        clipboard_ENABLED = False
    155     if opengl_ENABLED and not gtk2_ENABLED:
    156         print("Warning: opengl can only be used with the gtk2 clients")
    157         opengl_ENABLED = False
    158300    if shadow_ENABLED and not server_ENABLED:
    159301        print("Warning: shadow requires server to be enabled!")
    160302        shadow_ENABLED = False
    161     if cymaths_ENABLED and not server_ENABLED:
    162         print("Warning: cymaths requires server to be enabled!")
    163         cymaths_ENABLED = False
    164303    if x11_ENABLED and WIN32:
    165304        print("Warning: enabling x11 on MS Windows is unlikely to work!")
    166     if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED and not qt4_ENABLED:
     305    if gtk_x11_ENABLED and not x11_ENABLED:
     306        print("Error: you must enable x11 to support gtk_x11!")
     307        exit(1)
     308    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167309        print("Warning: client is enabled but none of the client toolkits are!?")
    168     if not argb_ENABLED and (x11_ENABLED or OSX):
    169         print("Error: argb is required for x11 and osx builds!")
    170         exit(1)
    171     if not client_ENABLED and not server_ENABLED:
    172         print("Error: you must build at least the client or server!")
    173         exit(1)
     310    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     311        print("Warning: you probably want to build at least the client or server!")
     312    if DEFAULT and not pillow_ENABLED:
     313        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     314    if minify_ENABLED:
     315        r = get_status_output(["uglifyjs", "--version"])[0]
     316        if r==0:
     317            minifier = "uglifyjs"
     318        else:
     319            print("Warning: uglifyjs failed and return %i" % r)
     320            try:
     321                import yuicompressor
     322                assert yuicompressor
     323                minifier = "yuicompressor"
     324            except ImportError as e:
     325                print("Warning: yuicompressor module not found, cannot minify")
     326                minify_ENABLED = False
     327    if not enc_x264_ENABLED and not vpx_ENABLED:
     328        print("Warning: no x264 and no vpx support!")
     329        print(" you should enable at least one of these two video encodings")
    174330
    175331
    176332#*******************************************************************************
    177 # build options, these may get modified further down..
    178 #
    179 setup_options = {}
    180 setup_options["name"] = "xpra"
    181 setup_options["author"] = "Antoine Martin"
    182 setup_options["author_email"] = "antoine@devloop.org.uk"
    183 setup_options["version"] = xpra.__version__
    184 setup_options["url"] = "http://xpra.org/"
    185 setup_options["download_url"] = "http://xpra.org/src/"
    186 setup_options["description"] = "Xpra: 'screen for X' utility"
    187 
    188 xpra_desc = "'screen for X' -- a tool to detach/reattach running X programs"
    189 setup_options["long_description"] = xpra_desc
    190 data_files = []
    191 setup_options["data_files"] = data_files
    192 modules = []
    193 setup_options["py_modules"] = modules
    194 packages = []       #used by py2app and py2exe
    195 excludes = []       #only used by py2exe on win32
    196 ext_modules = []
    197 cmdclass = {}
    198 scripts = []
    199 
    200 external_includes = ["cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms",
    201                      "Crypto", "Crypto.Cipher",
    202                      "hashlib",
    203                      "PIL", "PIL.Image",
     333# default sets:
     334
     335external_includes = ["hashlib",
    204336                     "ctypes", "platform"]
     337
     338
     339if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     340    external_includes += ["gi"]
     341elif gtk2_ENABLED or x11_ENABLED:
     342    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205343
    206344external_excludes = [
     
    212350                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213351                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
     352                    "curses", "pdb",
     353                    "urllib2", "tty",
     354                    "cookielib", "ftplib", "httplib", "fileinput",
    218355                    "distutils", "setuptools", "doctest"
    219356                    ]
    220 
     357if not html5_ENABLED and not crypto_ENABLED:
     358    external_excludes += ["ssl", "_ssl"]
     359if not html5_ENABLED:
     360    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     361
     362if not client_ENABLED and not server_ENABLED:
     363    excludes += ["PIL"]
     364if not dbus_ENABLED:
     365    excludes += ["dbus"]
    221366
    222367
    223368#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     369#for distutils / py2app and cx_freeze
    225370#use the following functions, which should get the right
    226371#data in the global variables "packages", "modules" and "excludes"
     
    253398
    254399def add_modules(*mods):
     400    def add(v):
     401        global modules
     402        if v not in modules:
     403            modules.append(v)
     404    do_add_modules(add, *mods)
     405
     406def do_add_modules(op, *mods):
    255407    """ adds the packages and any .py module found in the packages to the "modules" list
    256408    """
    257409    global modules
    258410    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     411        #ugly path stripping:
     412        if x.startswith("./"):
     413            x = x[2:]
     414        if x.endswith(".py"):
     415            x = x[:-3]
     416            x = x.replace("/", ".") #.replace("\\", ".")
    261417        pathname = os.path.sep.join(x.split("."))
     418        #is this a file module?
     419        f = "%s.py" % pathname
     420        if os.path.exists(f) and os.path.isfile(f):
     421            op(x)
    262422        if os.path.exists(pathname) and os.path.isdir(pathname):
    263423            #add all file modules found in this directory
    264424            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     425                #make sure we only include python files,
     426                #and ignore eclipse copies
     427                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266428                    fname = os.path.join(pathname, f)
    267429                    if os.path.isfile(fname):
    268430                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     431                        op(modname)
    270432
    271433def toggle_packages(enabled, *module_names):
     
    275437        remove_packages(*module_names)
    276438
     439def toggle_modules(enabled, *module_names):
     440    if enabled:
     441        def op(v):
     442            global modules
     443            if v not in modules:
     444                modules.append(v)
     445        do_add_modules(op, *module_names)
     446    else:
     447        remove_packages(*module_names)
     448
     449
    277450#always included:
    278 add_modules("xpra",
    279             "xpra.platform",
    280             "xpra.codecs",
    281             "xpra.codecs.xor")
    282 add_packages("xpra.scripts", "xpra.keyboard", "xpra.net")
    283 
     451add_modules("xpra", "xpra.platform", "xpra.net")
     452add_modules("xpra.scripts.main")
     453
     454
     455def add_data_files(target_dir, files):
     456    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     457    assert type(target_dir)==str
     458    assert type(files) in (list, tuple)
     459    data_files.append((target_dir, files))
     460
     461
     462def check_md5sums(md5sums):
     463    print("Verifying md5sums:")
     464    for filename, md5sum in md5sums.items():
     465        if not os.path.exists(filename) or not os.path.isfile(filename):
     466            sys.exit("ERROR: file %s is missing or not a file!" % filename)
     467        sys.stdout.write("* %s: " % str(filename).ljust(52))
     468        with open(filename, mode='rb') as f:
     469            data = f.read()
     470        m = md5()
     471        m.update(data)
     472        digest = m.hexdigest()
     473        assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (filename, md5sum, digest)
     474        sys.stdout.write("OK\n")
     475        sys.stdout.flush()
     476
     477#for pretty printing of options:
     478def print_option(prefix, k, v):
     479    if type(v)==dict:
     480        print("%s* %s:" % (prefix, k))
     481        for kk,vv in v.items():
     482            print_option(" "+prefix, kk, vv)
     483    else:
     484        print("%s* %s=%s" % (prefix, k, v))
    284485
    285486#*******************************************************************************
    286487# Utility methods for building with Cython
    287488def cython_version_check(min_version):
    288     try:
    289         from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
    291         sys.exit("ERROR: Cannot find Cython: %s" % e)
    292489    from distutils.version import LooseVersion
    293490    if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
     
    297494                 % (cython_version, ".".join([str(part) for part in min_version])))
    298495
    299 def cython_add(extension, min_version=(0, 14, 0)):
     496def cython_add(extension, min_version=(0, 19)):
    300497    #gentoo does weird things, calls --no-compile with build *and* install
    301498    #then expects to find the cython modules!? ie:
     
    303500    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304501        return
    305     global ext_modules, cmdclass
    306502    cython_version_check(min_version)
    307503    from Cython.Distutils import build_ext
    308504    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     505    global cmdclass
     506    cmdclass['build_ext'] = build_ext
     507
     508def insert_into_keywords(kw, key, *args):
     509    values = kw.setdefault(key, [])
     510    for arg in args:
     511        values.insert(0, arg)
    310512
    311513def add_to_keywords(kw, key, *args):
     
    319521
    320522
     523def checkdirs(*dirs):
     524    for d in dirs:
     525        if not os.path.exists(d) or not os.path.isdir(d):
     526            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     527
    321528PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
     529#override the pkgconfig file,
     530#we don't need to link against any of these:
     531gtk2_ignored_tokens=[("-l%s" % x) for x in
     532                     ["fontconfig", "freetype", "cairo",
     533                      "atk-1.0", "pangoft2-1.0", "pango-1.0", "pangocairo-1.0",
     534                      "gio-2.0", "gdk_pixbuf-2.0"]]
    322535
    323536GCC_VERSION = []
     
    326539    if len(GCC_VERSION)==0:
    327540        cmd = [os.environ.get("CC", "gcc"), "-v"]
    328         proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    329         output, _ = proc.communicate()
    330         status = proc.wait()
    331         if status==0:
     541        r, _, err = get_status_output(cmd)
     542        if r==0:
    332543            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     544            for line in err.splitlines():
    334545                if line.startswith(V_LINE):
    335546                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343554    return GCC_VERSION
    344555
    345 def make_constants_pxi(constants_path, pxi_path):
     556def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346557    constants = []
    347     for line in open(constants_path):
    348         data = line.split("#", 1)[0].strip()
    349         # data can be empty ''...
    350         if not data:
    351             continue
    352         # or a pair like 'cFoo "Foo"'...
    353         elif len(data.split()) == 2:
    354             (pyname, cname) = data.split()
    355             constants.append((pyname, cname))
    356         # or just a simple token 'Foo'
    357         else:
    358             constants.append(data)
    359     out = open(pxi_path, "w")
    360     out.write("cdef extern from *:\n")
    361     ### Apparently you can't use | on enum's?!
    362     # out.write("    enum MagicNumbers:\n")
    363     # for const in constants:
    364     #     if isinstance(const, tuple):
    365     #         out.write('        %s %s\n' % const)
    366     #     else:
    367     #         out.write('        %s\n' % (const,))
    368     for const in constants:
    369         if isinstance(const, tuple):
    370             out.write('    unsigned int %s %s\n' % const)
    371         else:
    372             out.write('    unsigned int %s\n' % (const,))
    373 
    374     out.write("constants = {\n")
    375     for const in constants:
    376         if isinstance(const, tuple):
    377             pyname = const[0]
    378         else:
    379             pyname = const
    380         out.write('    "%s": %s,\n' % (pyname, pyname))
    381     out.write("}\n")
    382 
    383 def make_constants(*paths):
     558    with open(constants_path) as f:
     559        for line in f:
     560            data = line.split("#", 1)[0].strip()
     561            # data can be empty ''...
     562            if not data:
     563                continue
     564            # or a pair like 'cFoo "Foo"'...
     565            elif len(data.split()) == 2:
     566                (pyname, cname) = data.split()
     567                constants.append((pyname, cname))
     568            # or just a simple token 'Foo'
     569            else:
     570                constants.append(data)
     571
     572    with open(pxi_path, "w") as out:
     573        if constants:
     574            out.write("cdef extern from *:\n")
     575            ### Apparently you can't use | on enum's?!
     576            # out.write("    enum MagicNumbers:\n")
     577            # for const in constants:
     578            #     if isinstance(const, tuple):
     579            #         out.write('        %s %s\n' % const)
     580            #     else:
     581            #         out.write('        %s\n' % (const,))
     582            for const in constants:
     583                if isinstance(const, tuple):
     584                    out.write('    unsigned int %s %s\n' % const)
     585                else:
     586                    out.write('    unsigned int %s\n' % (const,))
     587
     588            out.write("constants = {\n")
     589            for const in constants:
     590                if isinstance(const, tuple):
     591                    pyname = const[0]
     592                else:
     593                    pyname = const
     594                out.write('    "%s": %s,\n' % (pyname, pyname))
     595            out.write("}\n")
     596            if kwargs:
     597                out.write("\n\n")
     598
     599        if kwargs:
     600            for k, v in kwargs.items():
     601                out.write('DEF %s = %s\n' % (k, v))
     602
     603
     604def should_rebuild(src_file, bin_file):
     605    if not os.path.exists(bin_file):
     606        return "no file"
     607    elif rebuild_ENABLED:
     608        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     609            return "binary file out of date"
     610        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     611            return "newer build file"
     612    return None
     613
     614def make_constants(*paths, **kwargs):
    384615    base = os.path.join(os.getcwd(), *paths)
    385616    constants_file = "%s.txt" % base
    386617    pxi_file = "%s.pxi" % base
    387     reason = None
    388     if not os.path.exists(pxi_file):
    389         reason = "no pxi file"
    390     elif os.path.getctime(pxi_file)<os.path.getctime(constants_file):
    391         reason = "pxi file out of date"
    392     elif os.path.getctime(pxi_file)<os.path.getctime(__file__):
    393         reason = "newer build file"
     618    reason = should_rebuild(constants_file, pxi_file)
    394619    if reason:
    395620        if verbose_ENABLED:
    396621            print("(re)generating %s (%s):" % (pxi_file, reason))
    397         make_constants_pxi(constants_file, pxi_file)
    398 
    399 
    400 def static_link_args(*libnames):
    401     return ["-Wl,-Bstatic"] + ["-l%s" % x for x in libnames] + ["-Wl,-Bsymbolic", "-Wl,-Bdynamic"]
    402 
    403 def get_static_pkgconfig(*libnames):
    404     defs = pkgconfig()
    405     remove_from_keywords(defs, 'extra_compile_args', '-fsanitize=address')
    406     if os.name=="posix":
    407         if debug_ENABLED:
    408             add_to_keywords(defs, 'extra_link_args', "-Wl,--verbose")
    409         defs.update({'include_dirs': ["/usr/local/include"],
    410                      'library_dirs': ["/usr/local/lib", "/usr/local/lib64"]})
    411     if len(libnames)>0:
    412         add_to_keywords(defs,  'extra_link_args', *static_link_args(*libnames))
    413     return defs
     622        make_constants_pxi(constants_file, pxi_file, **kwargs)
     623
    414624
    415625# Tweaked from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/502261
    416 def pkgconfig(*pkgs_options, **ekw):
    417     static = ekw.get("static", None)
    418     if static is not None:
    419         del ekw["static"]
    420         if static:
    421             return get_static_pkgconfig(*pkgs_options)
    422 
     626def exec_pkgconfig(*pkgs_options, **ekw):
    423627    kw = dict(ekw)
     628    if "optimize" in kw:
     629        optimize = kw["optimize"]
     630        del kw["optimize"]
     631        if type(optimize)==bool:
     632            optimize = int(optimize)*3
     633        if not debug_ENABLED:
     634            add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     635    ignored_flags = kw.pop("ignored_flags", [])
     636    ignored_tokens = kw.pop("ignored_tokens", [])
     637
    424638    if len(pkgs_options)>0:
    425639        package_names = []
     
    438652            for option in options:
    439653                cmd = ["pkg-config", "--exists", option]
    440                 proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    441                 status = proc.wait()
    442                 if status==0:
     654                r, _, _ = get_status_output(cmd)
     655                if r==0:
    443656                    valid_option = option
    444657                    break
    445658            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     659                raise Exception("ERROR: cannot find a valid pkg-config entry for %s using PKG_CONFIG_PATH=%s" % (" or ".join(options), os.environ.get("PKG_CONFIG_PATH", "(empty)")))
    447660            package_names.append(valid_option)
    448661        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     662            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450663        flag_map = {'-I': 'include_dirs',
    451664                    '-L': 'library_dirs',
    452665                    '-l': 'libraries'}
    453         cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
    454         proc = subprocess.Popen(cmd, env=os.environ, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    455         (output, _) = proc.communicate()
    456         status = proc.wait()
    457         if status!=0:
    458             sys.exit("ERROR: call to pkg-config ('%s') failed" % " ".join(cmd))
    459         if sys.version>='3':
    460             output = output.decode('utf-8')
    461         for token in output.split():
    462             if token[:2] in flag_map:
    463                 add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
    464             else: # throw others to extra_link_args
    465                 add_to_keywords(kw, 'extra_link_args', token)
    466             for k, v in kw.items(): # remove duplicates
    467                 kw[k] = list(set(v))
     666        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     667        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     668        if r!=0:
     669            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     670        env_cflags = os.environ.get("CFLAGS")       #["dpkg-buildflags", "--get", "CFLAGS"]
     671        env_ldflags = os.environ.get("LDFLAGS")     #["dpkg-buildflags", "--get", "LDFLAGS"]
     672        for s in (pkg_config_out, env_cflags, env_ldflags):
     673            if not s:
     674                continue
     675            for token in s.split():
     676                if token in ignored_tokens:
     677                    pass
     678                elif token[:2] in ignored_flags:
     679                    pass
     680                elif token[:2] in flag_map:
     681                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     682                elif token.startswith("-W"):
     683                    add_to_keywords(kw, 'extra_compile_args', token)
     684                else:# throw others to extra_link_args
     685                    add_to_keywords(kw, 'extra_link_args', token)
    468686    if warn_ENABLED:
    469687        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470688        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471689    if strict_ENABLED:
    472         #these are almost certainly real errors since our code is "clean":
    473         if get_gcc_version()>=[4, 4]:
    474             eifd = "-Werror=implicit-function-declaration"
     690        if os.environ.get("CC", "").find("clang")>=0:
     691            #clang emits too many warnings with cython code,
     692            #so we can't enable Werror without turning off some warnings:
     693            #this list of flags should allow clang to build the whole source tree,
     694            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     695            #(un)commenting other switches.
     696            eifd = ["-Werror",
     697                    #"-Wno-unneeded-internal-declaration",
     698                    #"-Wno-unknown-attributes",
     699                    #"-Wno-unused-function",
     700                    #"-Wno-self-assign",
     701                    #"-Wno-sometimes-uninitialized",
     702                    #cython adds rpath to the compilation command??
     703                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     704                    "-Wno-unused-command-line-argument",
     705                    ]
     706        elif get_gcc_version()>=[4, 4]:
     707            eifd = ["-Werror"]
     708            from xpra.os_util import is_Ubuntu, is_Debian, is_Raspbian
     709            if is_Debian() or is_Ubuntu() or is_Raspbian():
     710                #needed on Debian and Ubuntu to avoid this error:
     711                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     712                eifd.append("-Wno-error=strict-prototypes")
     713            if NETBSD:
     714                #see: http://trac.cython.org/ticket/395
     715                eifd += ["-fno-strict-aliasing"]
     716            elif FREEBSD:
     717                eifd += ["-Wno-error=unused-function"]
    475718        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     719            #older versions of OSX ship an old gcc,
     720            #not much we can do with this:
     721            eifd = []
     722        for eif in eifd:
     723            add_to_keywords(kw, 'extra_compile_args', eif)
    478724    if PIC_ENABLED:
    479725        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481727        add_to_keywords(kw, 'extra_compile_args', '-g')
    482728        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     729        if get_gcc_version()>=[4, 8]:
    485730            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486731            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     732    if rpath and kw.get("libraries"):
     733        insert_into_keywords(kw, "library_dirs", rpath)
     734        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
    487735    #add_to_keywords(kw, 'include_dirs', '.')
    488736    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     737        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490738    return kw
     739pkgconfig = exec_pkgconfig
    491740
    492741
    493742#*******************************************************************************
    494 def get_xorg_conf_and_script():
    495     if not server_ENABLED:
    496         return "etc/xpra/client-only/xpra.conf", False
    497 
    498     def Xvfb():
    499         return "etc/xpra/Xvfb/xpra.conf", False
    500 
    501     if sys.platform.find("bsd")>=0:
    502         print("Warning: sorry, no support for Xdummy on %s" % sys.platform)
    503         return Xvfb()
    504 
    505     XORG_BIN = None
    506     PATHS = os.environ.get("PATH").split(os.pathsep)
    507     for x in PATHS:
    508         xorg = os.path.join(x, "Xorg")
    509         if os.path.isfile(xorg):
    510             XORG_BIN = xorg
    511             break
    512     if not XORG_BIN:
    513         print("Xorg not found, cannot detect version or Xdummy support")
    514         return Xvfb()
    515 
    516     def Xorg_suid_check():
    517         xorg_stat = os.stat(XORG_BIN)
    518         if (xorg_stat.st_mode & stat.S_ISUID)!=0:
    519             if (xorg_stat.st_mode & stat.S_IROTH)==0:
    520                 print("Xorg is suid and not readable, Xdummy support unavailable")
    521                 return Xvfb()
    522             print("%s is suid and readable, using the xpra_Xdummy wrapper" % XORG_BIN)
    523             return "etc/xpra/xpra_Xdummy/xpra.conf", True
    524         else:
    525             print("using Xdummy config file")
    526             return "etc/xpra/Xdummy/xpra.conf", False
    527 
    528     if Xdummy_ENABLED is False:
    529         return Xvfb()
    530     elif Xdummy_ENABLED is True:
    531         print("Xdummy support specified as 'enabled', will detect suid mode")
    532         return Xorg_suid_check()
     743
     744
     745def get_base_conf_dir(install_dir, stripbuildroot=True):
     746    #in some cases we want to strip the buildroot (to generate paths in the config file)
     747    #but in other cases we want the buildroot path (when writing out the config files)
     748    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     749    #this is a bit hackish, but I can't think of a better way of detecting it
     750    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     751    dirs = (install_dir or sys.prefix).split(os.path.sep)
     752    if install_dir and stripbuildroot:
     753        pkgdir = os.environ.get("pkgdir")
     754        if "debian" in dirs and "tmp" in dirs:
     755            #ugly fix for stripping the debian tmp dir:
     756            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     757            while "tmp" in dirs:
     758                dirs = dirs[dirs.index("tmp")+1:]
     759        elif "debian" in dirs:
     760            #same for recent debian versions:
     761            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     762            i = dirs.index("debian")
     763            if dirs[i+1] == "xpra":
     764                dirs = dirs[i+2:]
     765        elif "BUILDROOT" in dirs:
     766            #strip rpm style build root:
     767            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     768            dirs = dirs[dirs.index("BUILDROOT")+2:]
     769        elif pkgdir and install_dir.startswith(pkgdir):
     770            #arch build dir:
     771            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     772        elif "usr" in dirs:
     773            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     774            #assume "/usr" or "/usr/local" is the build root
     775            while "usr" in dirs[1:]:
     776                dirs = dirs[dirs[1:].index("usr")+1:]
     777        elif "image" in dirs:
     778            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     779            while "image" in dirs:
     780                dirs = dirs[dirs.index("image")+2:]
     781    #now deal with the fact that "/etc" is used for the "/usr" prefix
     782    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     783    if dirs and dirs[-1]=="usr":
     784        dirs = dirs[:-1]
     785    #is this an absolute path?
     786    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     787        #ie: ["/", "usr"] or ["/", "usr", "local"]
     788        dirs.insert(0, os.path.sep)
     789    return dirs
     790
     791def get_conf_dir(install_dir, stripbuildroot=True):
     792    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     793    dirs.append("etc")
     794    dirs.append("xpra")
     795    return os.path.join(*dirs)
     796
     797def detect_xorg_setup(install_dir=None):
     798    from xpra.scripts import config
     799    config.debug = config.warn
     800    conf_dir = get_conf_dir(install_dir)
     801    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     802
     803def build_xpra_conf(install_dir):
     804    #generates an actual config file from the template
     805    xvfb_command = detect_xorg_setup(install_dir)
     806    from xpra.platform.features import DEFAULT_ENV
     807    def bstr(b):
     808        if b is None:
     809            return "auto"
     810        return ["no", "yes"][int(b)]
     811    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     812    conf_dir = get_conf_dir(install_dir)
     813    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     814    from xpra.platform.paths import get_socket_dirs
     815    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     816    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     817    socket_dirs = get_socket_dirs()
     818    if WIN32:
     819        bind = "Main"
    533820    else:
    534         print("Xdummy support unspecified, will try to detect")
    535 
    536     cmd = ["lsb_release", "-cs"]
    537     try:
    538         proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    539         out, _ = proc.communicate()
    540         release = out.replace("\n", "")
    541         print("Found OS release: %s" % release)
    542         if release in ("raring", "saucy"):
    543             #yet another instance of Ubuntu breaking something
    544             print("Warning: Ubuntu '%s' breaks Xorg/Xdummy usage - using Xvfb fallback" % release)
    545             return  Xvfb()
    546     except Exception, e:
    547         print("failed to detect OS release using %s: %s" % (" ".join(cmd), e))
    548 
    549     #do live detection
    550     cmd = ["Xorg", "-version"]
    551     if verbose_ENABLED:
    552         print("detecting Xorg version using: %s" % str(cmd))
    553     try:
    554         proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    555         out, _ = proc.communicate()
    556         V_LINE = "X.Org X Server "
    557         xorg_version = None
    558         for line in out.decode("utf8").splitlines():
    559             if line.startswith(V_LINE):
    560                 v_str = line[len(V_LINE):]
    561                 xorg_version = [int(x) for x in v_str.split(".")[:2]]
    562                 break
    563         if not xorg_version:
    564             print("Xorg version could not be detected, Xdummy support unavailable")
    565             return Xvfb()
    566         if xorg_version<[1, 12]:
    567             print("Xorg version %s is too old (1.12 or later required), Xdummy support not available" % v_str)
    568             return Xvfb()
    569         print("found valid recent version of Xorg server: %s" % v_str)
    570         return Xorg_suid_check()
    571     except Exception, e:
    572         print("failed to detect Xorg version: %s" % e)
    573         print("not installing Xdummy support")
    574         traceback.print_exc()
    575         return  Xvfb()
     821        if os.getuid()>0:
     822            #remove any paths containing the uid,
     823            #osx uses /var/tmp/$UID-Xpra,
     824            #but this should not be included in the default config for all users!
     825            #(the buildbot's uid!)
     826            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     827        bind = "auto"
     828    #FIXME: we should probably get these values from the default config instead
     829    pdf, postscript = "", ""
     830    if POSIX and printing_ENABLED:
     831        try:
     832            if "/usr/sbin" not in sys.path:
     833                sys.path.append("/usr/sbin")
     834            from xpra.platform.pycups_printing import get_printer_definition
     835            print("probing cups printer definitions")
     836            pdf = get_printer_definition("pdf")
     837            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     838            print("pdf=%s, postscript=%s" % (pdf, postscript))
     839        except Exception as e:
     840            print("could not probe for pdf/postscript printers: %s" % e)
     841    def pretty_cmd(cmd):
     842        return " ".join(cmd)
     843    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     844    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     845    from xpra.os_util import getUbuntuVersion
     846    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     847    #no python-avahi on RH / CentOS, need dbus module on *nix:
     848    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     849    SUBS = {
     850            'xvfb_command'          : pretty_cmd(xvfb_command),
     851            'ssh_command'           : DEFAULT_SSH_COMMAND,
     852            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     853            'remote_logging'        : "both",
     854            'start_env'             : start_env,
     855            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     856            'pulseaudio_command'    : pretty_cmd(DEFAULT_PULSEAUDIO_COMMAND),
     857            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     858            'conf_dir'              : conf_dir,
     859            'bind'                  : bind,
     860            'ssl_cert'              : ssl_cert or "",
     861            'ssl_key'               : ssl_key or "",
     862            'systemd_run'           : get_default_systemd_run(),
     863            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     864            'log_dir'               : "auto",
     865            'mdns'                  : bstr(mdns),
     866            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     867            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     868            'pdf_printer'           : pdf,
     869            'postscript_printer'    : postscript,
     870            'webcam'                : ["no", "auto"][webcam],
     871            'mousewheel'            : "on",
     872            'printing'              : printing_ENABLED,
     873            'dbus_control'          : bstr(dbus_ENABLED),
     874            'mmap'                  : bstr(True),
     875            }
     876    def convert_templates(subdirs=[]):
     877        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     878        #get conf dir for install, without stripping the build root
     879        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     880        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     881        if not os.path.exists(target_dir):
     882            try:
     883                os.makedirs(target_dir)
     884            except Exception as e:
     885                print("cannot create target dir '%s': %s" % (target_dir, e))
     886        for f in sorted(os.listdir(dirname)):
     887            if f.endswith("osx.conf.in") and not OSX:
     888                continue
     889            filename = os.path.join(dirname, f)
     890            if os.path.isdir(filename):
     891                convert_templates(subdirs+[f])
     892                continue
     893            if not f.endswith(".in"):
     894                continue
     895            with open(filename, "r") as f_in:
     896                template  = f_in.read()
     897            target_file = os.path.join(target_dir, f[:-len(".in")])
     898            print("generating %s from %s" % (target_file, f))
     899            with open(target_file, "w") as f_out:
     900                config_data = template % SUBS
     901                f_out.write(config_data)
     902    convert_templates()
    576903
    577904
     
    586913    #ensure we remove the files we generate:
    587914    CLEAN_FILES = [
    588                    "xpra/gtk_common/gdk_atoms.c",
    589                    "xpra/x11/gtk_x11/constants.pxi",
    590                    "xpra/x11/gtk_x11/gdk_bindings.c",
    591                    "xpra/x11/gtk_x11/gdk_display_source.c",
     915                   "xpra/build_info.py",
     916                   "xpra/monotonic_time.c",
     917                   "xpra/gtk_common/gtk2/gdk_atoms.c",
     918                   "xpra/gtk_common/gtk2/gdk_bindings.c",
     919                   "xpra/x11/gtk2/constants.pxi",
     920                   "xpra/x11/gtk2/gdk_bindings.c",
     921                   "xpra/x11/gtk2/gdk_display_source.c",
     922                   "xpra/x11/gtk3/gdk_display_source.c",
    592923                   "xpra/x11/bindings/constants.pxi",
    593924                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597928                   "xpra/x11/bindings/randr_bindings.c",
    598929                   "xpra/x11/bindings/core_bindings.c",
     930                   "xpra/x11/bindings/posix_display_source.c",
    599931                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     932                   "xpra/x11/bindings/xi2_bindings.c",
     933                   "xpra/platform/win32/propsys.cpp",
     934                   "xpra/platform/darwin/gdk_bindings.c",
     935                   "xpra/platform/xposix/sd_listen.c",
     936                   "xpra/net/bencode/cython_bencode.c",
     937                   "xpra/net/vsock.c",
     938                   "xpra/buffers/membuf.c",
    601939                   "xpra/codecs/vpx/encoder.c",
    602940                   "xpra/codecs/vpx/decoder.c",
    603941                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     942                   "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     943                   "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     944                   "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     945                   "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
    605946                   "xpra/codecs/enc_x264/encoder.c",
    606947                   "xpra/codecs/enc_x265/encoder.c",
    607                    "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     948                   "xpra/codecs/jpeg/encoder.c",
     949                   "xpra/codecs/jpeg/decoder.c",
     950                   "xpra/codecs/enc_ffmpeg/encoder.c",
     951                   "xpra/codecs/v4l2/constants.pxi",
     952                   "xpra/codecs/v4l2/pusher.c",
     953                   "xpra/codecs/libav_common/av_log.c",
    610954                   "xpra/codecs/dec_avcodec2/decoder.c",
     955                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611956                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614957                   "xpra/codecs/xor/cyxor.c",
    615958                   "xpra/codecs/argb/argb.c",
    616                    "xpra/server/stats/cymaths.c",
    617                    "etc/xpra/xpra.conf"]
    618     if sys.platform.startswith("win"):
    619         #on win32, the build creates ".pyd" files, clean those too:
    620         for x in list(CLEAN_FILES):
    621             if x.endswith(".c"):
    622                 CLEAN_FILES.append(x[:-2]+".pyd")
     959                   "xpra/codecs/nvapi_version.c",
     960                   "xpra/client/gtk3/cairo_workaround.c",
     961                   "xpra/server/cystats.c",
     962                   "xpra/server/window/region.c",
     963                   "xpra/server/window/motion.c",
     964                   "xpra/server/pam.c",
     965                   "etc/xpra/xpra.conf",
     966                   #special case for the generated xpra conf files in build (see #891):
     967                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     968    for x in CLEAN_FILES:
     969        p, ext = os.path.splitext(x)
     970        if ext in (".c", ".cpp", ".pxi"):
     971            #clean the Cython annotated html files:
     972            CLEAN_FILES.append(p+".html")
     973            if WIN32 and ext!=".pxi":
     974                #on win32, the build creates ".pyd" files, clean those too:
     975                CLEAN_FILES.append(p+".pyd")
     976                #when building with python3, we need to clean files named like:
     977                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     978                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     979                CLEAN_FILES += glob.glob(filename)
    623980    if 'clean' in sys.argv:
    624981        CLEAN_FILES.append("xpra/build_info.py")
     
    630987            os.unlink(filename)
    631988
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     989from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    633990
    634991if "clean" not in sys.argv:
    635992    # Add build info to build_info.py file:
    636993    record_build_info()
     994    # ensure it is included in the module list if it didn't exist before
     995    add_modules(BUILD_INFO_FILE)
    637996
    638997if "sdist" in sys.argv:
    639998    record_src_info()
    640999
    641 if "install" in sys.argv:
     1000if "install" in sys.argv or "build" in sys.argv:
    6421001    #if installing from source tree rather than
    6431002    #from a source snapshot, we may not have a "src_info" file
     
    6451004    if not has_src_info():
    6461005        record_src_info()
     1006        # ensure it is now included in the module list
     1007        add_modules(SRC_INFO_FILE)
    6471008
    6481009
     
    6631024    return m
    6641025
     1026
     1027def install_html5(install_dir="www"):
     1028    from setup_html5 import install_html5 as do_install_html5
     1029    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1030
     1031
    6651032#*******************************************************************************
    6661033if WIN32:
    667     # The Microsoft C library DLLs:
    668     # Unfortunately, these files cannot be re-distributed legally :(
    669     # So here is the md5sum so you can find the right version:
    670     # (you can find them in various packages, including Visual Studio 2008,
    671     # pywin32, etc...)
    672     import md5
    673     md5sums = {"Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest" : "37f44d535dcc8bf7a826dfa4f5fa319b",
    674                "Microsoft.VC90.CRT/msvcm90.dll"                 : "4a8bc195abdc93f0db5dab7f5093c52f",
    675                "Microsoft.VC90.CRT/msvcp90.dll"                 : "6de5c66e434a9c1729575763d891c6c2",
    676                "Microsoft.VC90.CRT/msvcr90.dll"                 : "e7d91d008fe76423962b91c43c88e4eb",
    677                "Microsoft.VC90.CRT/vcomp90.dll"                 : "f6a85f3b0e30c96c993c69da6da6079e",
    678                "Microsoft.VC90.MFC/Microsoft.VC90.MFC.manifest" : "17683bda76942b55361049b226324be9",
    679                "Microsoft.VC90.MFC/mfc90.dll"                   : "462ddcc5eb88f34aed991416f8e354b2",
    680                "Microsoft.VC90.MFC/mfc90u.dll"                  : "b9030d821e099c79de1c9125b790e2da",
    681                "Microsoft.VC90.MFC/mfcm90.dll"                  : "d4e7c1546cf3131b7d84b39f8da9e321",
    682                "Microsoft.VC90.MFC/mfcm90u.dll"                 : "371226b8346f29011137c7aa9e93f2f6",
    683                }
    684     # This is where I keep them, you will obviously need to change this value:
    685     C_DLLs = "C:\\"
    686     for dll_file, md5sum in md5sums.items():
    687         filename = os.path.join(C_DLLs, *dll_file.split("/"))
    688         if not os.path.exists(filename) or not os.path.isfile(filename):
    689             sys.exit("ERROR: DLL file %s is missing or not a file!" % filename)
    690         sys.stdout.write("* verifying md5sum for %s: " % filename)
    691         f = open(filename, mode='rb')
    692         data = f.read()
    693         f.close()
    694         m = md5.new()
    695         m.update(data)
    696         digest = m.hexdigest()
    697         assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (dll_file, md5sum, digest)
    698         sys.stdout.write("OK\n")
    699         sys.stdout.flush()
    700     #this should all be done with pkgconfig...
    701     #but until someone figures this out, the ugly path code below works
    702     #as long as you install in the same place or tweak the paths.
    703 
    704     #first some header crap so codecs can find the inttypes.h
    705     #and stdint.h:
    706     win32_include_dir = os.path.join(os.getcwd(), "win32")
    707 
    708     #cuda:
    709     cuda_path = "C:\\NVIDIA\CUDA\CUDAToolkit"
    710     cuda_include_dir   = os.path.join(cuda_path, "include")
    711     cuda_bin_dir       = os.path.join(cuda_path, "bin")
    712 
    713     #ffmpeg is needed for both swscale and x264:
    714     libffmpeg_path = None
    715     if dec_avcodec_ENABLED:
    716         assert not dec_avcodec2_ENABLED, "cannot enable both dec_avcodec and dec_avcodec2"
    717         libffmpeg_path = "C:\\ffmpeg-win32-bin"
    718     elif dec_avcodec2_ENABLED:
    719         assert not dec_avcodec_ENABLED, "cannot enable both dec_avcodec and dec_avcodec2"
    720         libffmpeg_path = "C:\\ffmpeg2-win32-bin"
    721     else:
    722         if csc_swscale_ENABLED:
    723             for p in ("C:\\ffmpeg2-win32-bin", "C:\\ffmpeg-win32-bin"):
    724                 if os.path.exists(p):
    725                     libffmpeg_path = p
    726             assert libffmpeg_path is not None, "no ffmpeg found, cannot use csc_swscale"
    727     libffmpeg_include_dir   = os.path.join(libffmpeg_path, "include")
    728     libffmpeg_lib_dir       = os.path.join(libffmpeg_path, "lib")
    729     libffmpeg_bin_dir       = os.path.join(libffmpeg_path, "bin")
    730     #x265
    731     x265_path ="C:\\x265"
    732     x265_include_dir    = x265_path
    733     x265_lib_dir        = x265_path
    734     x265_bin_dir        = x265_path
    735     #x264 (direct from build dir.. yuk - sorry!):
    736     x264_path ="C:\\x264"
    737     x264_include_dir    = x264_path
    738     x264_lib_dir        = x264_path
    739     x264_bin_dir        = x264_path
    740     # Same for vpx:
    741     # http://code.google.com/p/webm/downloads/list
    742     #the path after installing may look like this:
    743     #vpx_PATH="C:\\vpx-vp8-debug-src-x86-win32mt-vs9-v1.1.0"
    744     #but we use something more generic, without the version numbers:
    745     vpx_path = ""
    746     for p in ("C:\\vpx-1.3", "C:\\vpx-1.2", "C:\\vpx-1.1", "C:\\vpx-vp8"):
    747         if os.path.exists(p) and os.path.isdir(p):
    748             vpx_path = p
    749             break
    750     vpx_include_dir     = os.path.join(vpx_path, "include")
    751     vpx_lib_dir         = os.path.join(vpx_path, "lib", "Win32")
    752     if os.path.exists(os.path.join(vpx_lib_dir, "vpx.lib")):
    753         vpx_lib_names = ["vpx"]               #for libvpx 1.3.0
    754     elif os.path.exists(os.path.join(vpx_lib_dir, "vpxmd.lib")):
    755         vpx_lib_names = ["vpxmd"]             #for libvpx 1.2.0
    756     else:
    757         vpx_lib_names = ["vpxmt", "vpxmtd"]   #for libvpx 1.1.0
    758     #webp:
    759     webp_path = "C:\\libwebp-windows-x86"
    760     webp_include_dir    = webp_path+"\\include"
    761     webp_lib_dir        = webp_path+"\\lib"
    762     webp_bin_dir        = webp_path+"\\bin"
    763     webp_lib_names      = ["libwebp"]
    764 
    765     # Same for PyGTK:
    766     # http://www.pygtk.org/downloads.html
    767     gtk2_path = "C:\\Python27\\Lib\\site-packages\\gtk-2.0"
    768     python_include_path = "C:\\Python27\\include"
    769     gtk2runtime_path        = os.path.join(gtk2_path, "runtime")
    770     gtk2_lib_dir            = os.path.join(gtk2runtime_path, "bin")
    771     gtk2_base_include_dir   = os.path.join(gtk2runtime_path, "include")
    772 
    773     pygtk_include_dir       = os.path.join(python_include_path, "pygtk-2.0")
    774     atk_include_dir         = os.path.join(gtk2_base_include_dir, "atk-1.0")
    775     gtk2_include_dir        = os.path.join(gtk2_base_include_dir, "gtk-2.0")
    776     gdkpixbuf_include_dir   = os.path.join(gtk2_base_include_dir, "gdk-pixbuf-2.0")
    777     glib_include_dir        = os.path.join(gtk2_base_include_dir, "glib-2.0")
    778     cairo_include_dir       = os.path.join(gtk2_base_include_dir, "cairo")
    779     pango_include_dir       = os.path.join(gtk2_base_include_dir, "pango-1.0")
    780     gdkconfig_include_dir   = os.path.join(gtk2runtime_path, "lib", "gtk-2.0", "include")
    781     glibconfig_include_dir  = os.path.join(gtk2runtime_path, "lib", "glib-2.0", "include")
    782 
    783     def checkdirs(*dirs):
    784         for d in dirs:
    785             if not os.path.exists(d) or not os.path.isdir(d):
    786                 raise Exception("cannot find a directory which is required for building: %s" % d)
    787 
    788     def pkgconfig(*pkgs_options, **ekw):
    789         kw = dict(ekw)
    790         #remove static flag on win32..
    791         static = kw.get("static", None)
    792         if static is not None:
    793             del kw["static"]
    794         #always add the win32 include dirs, everyone needs that:
    795         add_to_keywords(kw, 'include_dirs', win32_include_dir)
    796         if len(pkgs_options)==0:
    797             return kw
    798 
    799         def add_to_PATH(bindir):
    800             if os.environ['PATH'].find(bindir)<0:
    801                 os.environ['PATH'] = bindir + ';' + os.environ['PATH']
    802             if bindir not in sys.path:
    803                 sys.path.append(bindir)
    804         if "avcodec" in pkgs_options[0]:
    805             add_to_PATH(libffmpeg_bin_dir)
    806             add_to_keywords(kw, 'include_dirs', libffmpeg_include_dir)
    807             add_to_keywords(kw, 'libraries', "avcodec", "avutil")
    808             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_lib_dir)
    809             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_bin_dir)
    810             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    811             checkdirs(libffmpeg_include_dir, libffmpeg_lib_dir, libffmpeg_bin_dir)
    812         elif "swscale" in pkgs_options[0]:
    813             add_to_PATH(libffmpeg_bin_dir)
    814             add_to_keywords(kw, 'include_dirs', libffmpeg_include_dir)
    815             add_to_keywords(kw, 'libraries', "swscale", "avutil")
    816             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_lib_dir)
    817             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_bin_dir)
    818             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    819             checkdirs(libffmpeg_include_dir, libffmpeg_lib_dir, libffmpeg_bin_dir)
    820         elif "x264" in pkgs_options[0]:
    821             add_to_PATH(libffmpeg_bin_dir)
    822             add_to_PATH(x264_bin_dir)
    823             add_to_keywords(kw, 'include_dirs', x264_include_dir)
    824             add_to_keywords(kw, 'libraries', "libx264")
    825             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % x264_lib_dir)
    826             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    827             checkdirs(x264_include_dir, x264_lib_dir)
    828         elif "x265" in pkgs_options[0]:
    829             add_to_PATH(libffmpeg_bin_dir)
    830             add_to_PATH(x265_bin_dir)
    831             add_to_keywords(kw, 'include_dirs', x265_include_dir)
    832             add_to_keywords(kw, 'libraries', "libx265")
    833             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % x265_lib_dir)
    834             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    835             checkdirs(x265_include_dir, x265_lib_dir)
    836         elif "vpx" in pkgs_options[0]:
    837             add_to_PATH(libffmpeg_bin_dir)
    838             add_to_keywords(kw, 'include_dirs', vpx_include_dir)
    839             add_to_keywords(kw, 'libraries', *vpx_lib_names)
    840             add_to_keywords(kw, 'extra_link_args', "/NODEFAULTLIB:LIBCMT")
    841             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % vpx_lib_dir)
    842             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    843             checkdirs(vpx_include_dir, vpx_lib_dir)
    844         elif "webp" in pkgs_options[0]:
    845             add_to_PATH(webp_bin_dir)
    846             add_to_keywords(kw, 'include_dirs', webp_include_dir)
    847             add_to_keywords(kw, 'libraries', *webp_lib_names)
    848             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % webp_lib_dir)
    849             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    850             checkdirs(webp_include_dir, webp_lib_dir, webp_bin_dir)
    851         elif "pygobject-2.0" in pkgs_options[0]:
    852             dirs = (python_include_path,
    853                     pygtk_include_dir, atk_include_dir, gtk2_include_dir,
    854                     gtk2_base_include_dir, gdkconfig_include_dir, gdkpixbuf_include_dir,
    855                     glib_include_dir, glibconfig_include_dir,
    856                     cairo_include_dir, pango_include_dir)
    857             add_to_keywords(kw, 'include_dirs', *dirs)
    858             checkdirs(*dirs)
    859         elif "cuda" in pkgs_options[0]:
    860             add_to_keywords(kw, 'include_dirs', cuda_include_dir)
    861             checkdirs(cuda_include_dir)
    862             data_files.append(('.', glob.glob("%s/*32*.dll" % cuda_bin_dir)))
    863         else:
    864             sys.exit("ERROR: unknown package config: %s" % str(pkgs_options))
    865         if debug_ENABLED:
    866             #Od will override whatever may be specified elsewhere
    867             #and allows us to use the debug switches,
    868             #at the cost of a warning...
    869             for flag in ('/Od', '/Zi', '/DEBUG', '/RTC1', '/GS'):
    870                 add_to_keywords(kw, 'extra_compile_args', flag)
    871             add_to_keywords(kw, 'extra_link_args', "/DEBUG")
    872             kw['cython_gdb'] = True
    873         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    874         return kw
    875 
    876     import py2exe    #@UnresolvedImport
    877     assert py2exe is not None
    878 
    879     #with py2exe, we don't use py_modules, we use "packages"... sigh
    880     #(and it is a little bit different too - see below)
    881     del setup_options["py_modules"]
     1034    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1035    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821036    add_packages("xpra.platform.win32")
    8831037    remove_packages("xpra.platform.darwin", "xpra.platform.xposix")
    884     #UI applications (detached from shell: no text output if ran from cmd.exe)
    885     setup_options["windows"] = [
    886                     {'script': 'scripts/xpra',                          'icon_resources': [(1, "win32/xpra_txt.ico")],  "dest_base": "Xpra",},
    887                     {'script': 'scripts/xpra_launcher',                 'icon_resources': [(1, "win32/xpra.ico")],      "dest_base": "Xpra-Launcher",},
    888                     {'script': 'xpra/gtk_common/gtk_view_keyboard.py',  'icon_resources': [(1, "win32/keyboard.ico")],  "dest_base": "GTK_Keyboard_Test",},
    889                     {'script': 'xpra/gtk_common/gtk_view_clipboard.py', 'icon_resources': [(1, "win32/clipboard.ico")], "dest_base": "GTK_Clipboard_Test",},
    890               ]
    891     #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
    892     console = [
    893                     {'script': 'scripts/xpra',                          'icon_resources': [(1, "win32/xpra_txt.ico")],  "dest_base": "Xpra_cmd",},
    894                     {'script': 'win32/python_execfile.py',              'icon_resources': [(1, "win32/python.ico")],    "dest_base": "Python_execfile",},
    895                     {'script': 'xpra/platform/win32/gui.py',            'icon_resources': [(1, "win32/loop.ico")],      "dest_base": "Events_Test",},
    896                     {'script': 'xpra/codecs/loader.py',                 'icon_resources': [(1, "win32/encoding.ico")],  "dest_base": "Encoding_info",},
    897                     {'script': 'xpra/sound/gstreamer_util.py',          'icon_resources': [(1, "win32/gstreamer.ico")], "dest_base": "GStreamer_info",},
    898                     {'script': 'xpra/sound/src.py',                     'icon_resources': [(1, "win32/microphone.ico")],"dest_base": "Sound_Record",},
    899                     {'script': 'xpra/sound/sink.py',                    'icon_resources': [(1, "win32/speaker.ico")],   "dest_base": "Sound_Play",},
    900               ]
    901     if opengl_ENABLED:
    902         console.append({'script': 'xpra/client/gl/gl_check.py',            'icon_resources': [(1, "win32/opengl.ico")],    "dest_base": "OpenGL_check",})
    903     setup_options["console"] = console
    904 
    905     py2exe_includes = external_includes + ["win32con", "win32gui", "win32process", "win32api"]
    906     dll_excludes = ["w9xpopen.exe","tcl85.dll", "tk85.dll"]
     1038
     1039    #this is where the win32 gi installer will put things:
     1040    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1041
     1042    #only add the cx_freeze specific options
     1043    #if we aren't just building the Cython bits with "build_ext":
     1044    if "build_ext" not in sys.argv:
     1045        #with cx_freeze, we don't use py_modules
     1046        del setup_options["py_modules"]
     1047        import cx_Freeze                            #@UnresolvedImport
     1048        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1049        CX5 = cx_Freeze.version>="5"
     1050        if CX5 and not hasattr(sys, "base_prefix"):
     1051            #workaround for broken sqlite hook with python 2.7, see:
     1052            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1053            sys.base_prefix = sys.prefix
     1054
     1055        #cx_freeze doesn't use "data_files"...
     1056        del setup_options["data_files"]
     1057        #it wants source files first, then where they are placed...
     1058        #one item at a time (no lists)
     1059        #all in its own structure called "include_files" instead of "data_files"...
     1060        def add_data_files(target_dir, files):
     1061            if verbose_ENABLED:
     1062                print("add_data_files(%s, %s)" % (target_dir, files))
     1063            assert type(target_dir)==str
     1064            assert type(files) in (list, tuple)
     1065            for f in files:
     1066                target_file = os.path.join(target_dir, os.path.basename(f))
     1067                data_files.append((f, target_file))
     1068
     1069        #pass a potentially nested dictionary representing the tree
     1070        #of files and directories we do want to include
     1071        #relative to gnome_include_path
     1072        def add_dir(base, defs):
     1073            if verbose_ENABLED:
     1074                print("add_dir(%s, %s)" % (base, defs))
     1075            if type(defs) in (list, tuple):
     1076                for sub in defs:
     1077                    if type(sub)==dict:
     1078                        add_dir(base, sub)
     1079                    else:
     1080                        assert type(sub)==str
     1081                        filename = os.path.join(gnome_include_path, base, sub)
     1082                        if os.path.exists(filename):
     1083                            add_data_files(base, [filename])
     1084                        else:
     1085                            print("Warning: missing '%s'" % filename)
     1086            else:
     1087                assert type(defs)==dict
     1088                for d, sub in defs.items():
     1089                    assert type(sub) in (dict, list, tuple)
     1090                    #recurse down:
     1091                    add_dir(os.path.join(base, d), sub)
     1092
     1093        #convenience method for adding GI libs and "typelib" and "gir":
     1094        def add_gi(*libs):
     1095            if verbose_ENABLED:
     1096                print("add_gi(%s)" % str(libs))
     1097            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1098            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1099
     1100        def add_DLLs(*dll_names):
     1101            try:
     1102                do_add_DLLs(*dll_names)
     1103            except Exception as e:
     1104                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1105                print(" %s" % e)
     1106                sys.exit(1)
     1107
     1108        def do_add_DLLs(*dll_names):
     1109            dll_names = list(dll_names)
     1110            dll_files = []
     1111            import re
     1112            version_re = re.compile("\-[0-9\.\-]+$")
     1113            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1114            if os.path.exists(gnome_include_path):
     1115                dirs.insert(0, gnome_include_path)
     1116            if verbose_ENABLED:
     1117                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1118            for d in dirs:
     1119                if not os.path.exists(d):
     1120                    continue
     1121                for x in os.listdir(d):
     1122                    dll_path = os.path.join(d, x)
     1123                    x = x.lower()
     1124                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1125                        continue
     1126                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1127                    if verbose_ENABLED:
     1128                        print("checking %s: %s" % (x, nameversion))
     1129                    m = version_re.search(nameversion)          #look for version part of filename
     1130                    if m:
     1131                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1132                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1133                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1134                    else:
     1135                        dll_version = ""                        #no version
     1136                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1137                    if dll_name in dll_names:
     1138                        #this DLL is on our list
     1139                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1140                        dll_files.append(dll_path)
     1141                        dll_names.remove(dll_name)
     1142            if len(dll_names)>0:
     1143                print("some DLLs could not be found:")
     1144                for x in dll_names:
     1145                    print(" - lib%s*.dll" % x)
     1146            add_data_files("", dll_files)
     1147
     1148        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1149        #(ie: "libatk-1.0-0.dll" -> "atk")
     1150        if sound_ENABLED or gtk3_ENABLED:
     1151            add_DLLs('gio', 'girepository', 'glib',
     1152                     'gnutls', 'gobject', 'gthread',
     1153                     'orc', 'stdc++',
     1154                     'winpthread',
     1155                     )
     1156        if gtk3_ENABLED:
     1157            add_DLLs('atk',
     1158                     'dbus', 'dbus-glib',
     1159                     'gdk', 'gdk_pixbuf', 'gtk',
     1160                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1161                     'harfbuzz', 'harfbuzz-gobject',
     1162                     'jasper', 'epoxy',
     1163                     'intl',
     1164                     'p11-kit',
     1165                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1166            #these are missing in newer aio installers (sigh):
     1167            do_add_DLLs('javascriptcoregtk')
     1168            if opengl_ENABLED:
     1169                do_add_DLLs('gdkglext', 'gtkglext')
     1170
     1171        if gtk3_ENABLED:
     1172            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1173            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1174                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1175            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1176                              "icons", "p11-kit", "xml",
     1177                              {"locale" : ["en"]},
     1178                              {"themes" : ["Default"]}
     1179                             ])
     1180        if gtk3_ENABLED or sound_ENABLED:
     1181            add_dir('lib', ["gio"])
     1182            packages.append("gi")
     1183            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1184                   "GObject-2.0")
     1185        if gtk3_ENABLED:
     1186            add_gi("Atk-1.0",
     1187                   "fontconfig-2.0", "freetype2-2.0",
     1188                   "GDesktopEnums-3.0", "Soup-2.4",
     1189                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0"
     1190                   "HarfBuzz-0.0",
     1191                   "Libproxy-1.0", "libxml2-2.0",
     1192                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1193                   "Rsvg-2.0",
     1194                   "win32-1.0")
     1195            if opengl_ENABLED:
     1196                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1197            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1198        if gtk3_ENABLED or server_ENABLED:
     1199            add_DLLs('sqlite3')
     1200
     1201        if gtk2_ENABLED:
     1202            add_dir('lib',      {
     1203                "gdk-pixbuf-2.0":    {
     1204                    "2.10.0"    :   {
     1205                        "loaders"   :
     1206                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp")]
     1207                        },
     1208                    },
     1209                })
     1210
     1211        if sound_ENABLED:
     1212            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1213            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1214                   "GstTag-1.0")
     1215            add_DLLs('gstreamer', 'orc-test')
     1216            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1217                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1218                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1219                      "basecamerabinsrc", "mpegts", "photography",
     1220                      ):
     1221                add_DLLs('gst%s' % p)
     1222            #DLLs needed by the plugins:
     1223            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1224            #add the gstreamer plugins we need:
     1225            GST_PLUGINS = ("app",
     1226                           #muxers:
     1227                           "gdp", "matroska", "ogg", "isomp4",
     1228                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1229                           "coreelements", "directsoundsink", "directsoundsrc", "wasapi",
     1230                           #codecs:
     1231                           "opus", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1232                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1233                           #untested: a52dec, voaacenc
     1234                           )
     1235            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1236            #END OF SOUND
     1237
     1238        if server_ENABLED:
     1239            #used by proxy server:
     1240            external_includes += ["multiprocessing", "setproctitle"]
     1241
     1242        external_includes += ["encodings"]
     1243        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1244        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1245        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1246        cx_freeze_options = {
     1247                            "includes"          : external_includes,
     1248                            "packages"          : packages,
     1249                            "include_files"     : data_files,
     1250                            "excludes"          : excludes,
     1251                            "include_msvcr"     : True,
     1252                            "bin_excludes"      : bin_excludes,
     1253                            }
     1254        if not CX5:
     1255            cx_freeze_options.update({
     1256                            "compressed"        : True,
     1257                            "create_shared_zip" : zip_ENABLED,
     1258                            })
     1259        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1260        executables = []
     1261        setup_options["executables"] = executables
     1262
     1263        def add_exe(script, icon, base_name, base="Console"):
     1264            kwargs = {}
     1265            if not CX5:
     1266                kwargs = {
     1267                    "compress"              : True,
     1268                    "copyDependentFiles"    : True,
     1269                    "appendScriptToExe"     : False,
     1270                    "appendScriptToLibrary" : True,
     1271                    }
     1272            executables.append(Executable(
     1273                        script                  = script,
     1274                        initScript              = None,
     1275                        #targetDir               = "dist",
     1276                        icon                    = "win32/%s" % icon,
     1277                        targetName              = "%s.exe" % base_name,
     1278                        base                    = base,
     1279                        **kwargs))
     1280
     1281        def add_console_exe(script, icon, base_name):
     1282            add_exe(script, icon, base_name)
     1283        def add_gui_exe(script, icon, base_name):
     1284            add_exe(script, icon, base_name, base="Win32GUI")
     1285        def add_service_exe(script, icon, base_name):
     1286            add_exe(script, icon, base_name, base="Win32Service")
     1287
     1288        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1289        if client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1290            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1291            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1292            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1293            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1294            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1295        if gtk2_ENABLED:
     1296            #these need porting..
     1297            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1298        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1299            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1300        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1301        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1302        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1303        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1304        if gtk2_ENABLED or gtk3_ENABLED:
     1305            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1306            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1307            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1308        if client_ENABLED or server_ENABLED:
     1309            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1310            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1311        if server_ENABLED:
     1312            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1313            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1314            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1315            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1316        if client_ENABLED:
     1317            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1318            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1319            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1320        if client_ENABLED:
     1321            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1322            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1323        if sound_ENABLED:
     1324            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1325            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1326            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1327            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1328            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1329        if opengl_ENABLED:
     1330            add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1331        if webcam_ENABLED:
     1332            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1333            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1334        if printing_ENABLED:
     1335            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1336            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1337            add_DLLs("pdfium")  #libpdfium.dll
     1338        if nvenc_ENABLED:
     1339            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1340        if nvfbc_ENABLED:
     1341            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1342        if nvfbc_ENABLED or nvenc_ENABLED:
     1343            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1344
     1345        if example_ENABLED:
     1346            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1347            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1348            add_gui_exe("xpra/client/gtk_base/example/gl_colors_gradient.py",   "encoding.ico",     "OpenGL-Colors-Gradient")
     1349            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1350            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1351            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1352            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1353            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1354
     1355        #FIXME: how do we figure out what target directory to use?
     1356        print("calling build_xpra_conf in-place")
     1357        #building etc files in-place:
     1358        build_xpra_conf(".")
     1359        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1360        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1361        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1362        #build minified html5 client in temporary build dir:
     1363        if "clean" not in sys.argv and html5_ENABLED:
     1364            install_html5(os.path.join(install, "www"), )
     1365            for k,v in glob_recurse("build/www").items():
     1366                if (k!=""):
     1367                    k = os.sep+k
     1368                add_data_files('www'+k, v)
     1369
     1370    if client_ENABLED or server_ENABLED:
     1371        add_data_files('',      ['COPYING', 'README', 'win32/website.url'])
     1372        add_data_files('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*'))
     1373
     1374    if webcam_ENABLED:
     1375        add_data_files('',      ['win32\\DirectShow.tlb'])
     1376        add_modules("comtypes.gen.stdole", "comtypes.gen.DirectShowLib")
     1377
    9071378    remove_packages(*external_excludes)
     1379    external_includes.append("mmap")
    9081380    remove_packages(#not used on win32:
    909                     "mmap",
    9101381                    #we handle GL separately below:
    9111382                    "OpenGL", "OpenGL_accelerate",
     
    9131384                    "ctypes.macholib")
    9141385
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1386    if webcam_ENABLED:
     1387        external_includes.append("cv2")
     1388
     1389    if opengl_ENABLED:
    9161390        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1391        external_includes.append("numpy")
    9181392    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1393        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211394                        "pydoc")
    9221395
    923     if sound_ENABLED:
    924         py2exe_includes += ["pygst", "gst", "gst.extend"]
    925     else:
    926         remove_packages("pygst", "gst", "gst.extend")
    927 
    928     if opengl_ENABLED:
     1396    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1397    remove_packages("pygst", "gst", "gst.extend")
     1398
     1399    #add subset of PyOpenGL modules (only when installing):
     1400    if opengl_ENABLED and ("install_exe" in sys.argv or "install" in sys.argv):
    9291401        #for this hack to work, you must add "." to the sys.path
    9301402        #so python can load OpenGL from the install directory
    9311403        #(further complicated by the fact that "." is the "frozen" path...)
     1404        #but we re-add those two directories to the library.zip as part of the build script
    9321405        import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
     1406        print("*** copying PyOpenGL modules to %s ***" % install)
    9351407        for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
    9361408            module_dir = os.path.dirname(module.__file__ )
    9371409            try:
    9381410                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1411                    module_dir, os.path.join(install, module_name),
     1412                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411413                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1414            except Exception as e:
     1415                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441416                    raise
    945     py2exe_options = {
    946                       "skip_archive"   : False,
    947                       "optimize"       : 0,    #WARNING: do not change - causes crashes
    948                       "unbuffered"     : True,
    949                       "compressed"     : True,
    950                       "skip_archive"   : False,
    951                       "packages"       : packages,
    952                       "includes"       : py2exe_includes,
    953                       "excludes"       : excludes,
    954                       "dll_excludes"   : dll_excludes,
    955                      }
    956     setup_options["options"] = {"py2exe" : py2exe_options}
    957     data_files += [
    958                    ('', ['COPYING', 'README',
    959                          'win32/website.url',
    960                          'etc/xpra/client-only/xpra.conf'] +
    961                          glob.glob('%s\\bin\\*.dll' % libffmpeg_path)),
    962                    ('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*')),
    963                    ('Microsoft.VC90.CRT', glob.glob('%s\\Microsoft.VC90.CRT\\*.*' % C_DLLs)),
    964                    ('Microsoft.VC90.MFC', glob.glob('%s\\Microsoft.VC90.MFC\\*.*' % C_DLLs)),
    965                    ]
    966     if enc_x264_ENABLED:
    967         data_files.append(('', ['%s\\libx264.dll' % x264_bin_dir]))
    968     html5_dir = ''
    969 
    970     if webm_ENABLED or webp_ENABLED:
    971         #Note: confusingly, the python bindings are called webm...
    972         #add the webp DLL to the output:
    973         #And since 0.2.1, you have to compile the DLL yourself..
    974         #the path after installing may look like this:
    975         #webp_DLL = "C:\\libwebp-0.3.1-windows-x86\\bin\\libwebp.dll"
    976         #but we use something more generic, without the version numbers:
    977         webp_DLL = webp_bin_dir+"\\libwebp.dll"
    978         data_files.append(('', [webp_DLL]))
    979         #and its license:
    980         data_files.append(('webm', ["xpra/codecs/webm/LICENSE"]))
    981 
    982 
     1417
     1418    add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1419    add_data_files('', ["bell.wav"])
     1420
     1421    #END OF win32
    9831422#*******************************************************************************
    9841423else:
    9851424    #OSX and *nix:
    986     scripts += ["scripts/xpra", "scripts/xpra_launcher"]
    987     data_files += [
    988                     ("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1"]),
    989                     ("share/xpra",          ["README", "COPYING"]),
    990                     ("share/xpra/icons",    glob.glob("icons/*")),
    991                     ("share/applications",  ["xdg/xpra_launcher.desktop", "xdg/xpra.desktop"]),
    992                     ("share/icons",         ["xdg/xpra.png"])
    993                   ]
    994     html5_dir = "share/xpra/www"
    995     if webm_ENABLED:
    996         data_files.append(('share/xpra/webm', ["xpra/codecs/webm/LICENSE"]))
     1425    scripts += ["scripts/xpra", "scripts/xpra_launcher", "scripts/xpra_browser"]
     1426    add_data_files("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1", "man/xpra_browser.1"])
     1427    add_data_files("share/xpra",          ["README", "COPYING"])
     1428    add_data_files("share/xpra/icons",    glob.glob("icons/*"))
     1429    add_data_files("share/applications",  ["xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1430    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1431    add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png"])
     1432    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1433    add_data_files('share/xpra/',         ["bell.wav"])
     1434
     1435    #here, we override build and install so we can
     1436    #generate our /etc/xpra/xpra.conf
     1437    class build_override(build):
     1438        def run(self):
     1439            build.run(self)
     1440            self.run_command("build_conf")
     1441
     1442    class build_conf(build):
     1443        def run(self):
     1444            try:
     1445                build_base = self.distribution.command_obj['build'].build_base
     1446            except:
     1447                build_base = self.build_base
     1448            build_xpra_conf(build_base)
     1449
     1450    class install_data_override(install_data):
     1451        def run(self):
     1452            print("install_data_override: install_dir=%s" % self.install_dir)
     1453            if html5_ENABLED:
     1454                install_html5(os.path.join(self.install_dir, "share/xpra/www"))
     1455            install_data.run(self)
     1456
     1457            root_prefix = self.install_dir.rstrip("/")
     1458            if root_prefix.endswith("/usr"):
     1459                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1460            build_xpra_conf(root_prefix)
     1461
     1462            def copytodir(src, dst_dir, chmod=0o644):
     1463                #convert absolute paths:
     1464                if dst_dir.startswith("/"):
     1465                    dst_dir = root_prefix+dst_dir
     1466                else:
     1467                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1468                #make sure the target directory exists:
     1469                self.mkpath(dst_dir)
     1470                #generate the target filename:
     1471                filename = os.path.basename(src)
     1472                dst_file = os.path.join(dst_dir, filename)
     1473                #copy it
     1474                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1475                shutil.copyfile(src, dst_file)
     1476                if chmod:
     1477                    os.chmod(dst_file, chmod)
     1478
     1479            if printing_ENABLED and POSIX:
     1480                #install "/usr/lib/cups/backend" with 0700 permissions:
     1481                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1482
     1483            if x11_ENABLED:
     1484                #install xpra_Xdummy if we need it:
     1485                xvfb_command = detect_xorg_setup()
     1486                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1487                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1488                #install xorg*.conf, cuda.conf and nvenc.keys:
     1489                etc_xpra_files = ["xorg.conf"]
     1490                if uinput_ENABLED:
     1491                    etc_xpra_files.append("xorg-uinput.conf")
     1492                if nvenc_ENABLED:
     1493                    etc_xpra_files += ["cuda.conf", "nvenc.keys"]
     1494                for x in etc_xpra_files:
     1495                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1496                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1497
     1498            if pam_ENABLED:
     1499                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1500
     1501            #fedora and centos can use just "/lib/systemd/system" here
     1502            #but debian gets totally confused if we do...
     1503            systemd_dir = "/usr/lib/systemd/system"
     1504            if service_ENABLED:
     1505                #Linux init service:
     1506                if os.path.exists("/bin/systemctl"):
     1507                    copytodir("service/xpra.service", systemd_dir)
     1508                else:
     1509                    copytodir("service/xpra", "/etc/init.d")
     1510                if os.path.exists("/etc/sysconfig"):
     1511                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1512                elif os.path.exists("/etc/default"):
     1513                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1514            if sd_listen_ENABLED:
     1515                copytodir("service/xpra.socket", systemd_dir)
     1516
     1517
     1518    # add build_conf to build step
     1519    cmdclass.update({
     1520             'build'        : build_override,
     1521             'build_conf'   : build_conf,
     1522             'install_data' : install_data_override,
     1523             })
    9971524
    9981525    if OSX:
     1526        #pyobjc needs email.parser
     1527        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
    9991528        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001529        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011530        add_packages("xpra.platform.darwin")
     1531        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1532        #to support GStreamer 1.x we need this:
     1533        modules.append("importlib")
     1534        modules.append("xpra.scripts.gtk_info")
     1535        modules.append("xpra.scripts.show_webcam")
    10021536    else:
    10031537        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041538        add_packages("xpra.platform.xposix")
    1005         #always include the wrapper in case we need it later:
    1006         #(we remove it during the 'install' step below if it isn't actually needed)
    1007         scripts.append("scripts/xpra_Xdummy")
     1539        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1540        #not supported by all distros, but doesn't hurt to install them anyway:
     1541        for x in ("tmpfiles.d", "sysusers.d"):
     1542            add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1543        if uinput_ENABLED:
     1544            add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081545
    10091546    #gentoo does weird things, calls --no-compile with build *and* install
     
    10141551        def pkgconfig(*pkgs_options, **ekw):
    10151552            return {}
    1016     if "install" in sys.argv:
    1017         #prepare default [/usr/local]/etc configuration files:
    1018         if sys.prefix == '/usr':
    1019             etc_prefix = '/etc/xpra'
    1020         else:
    1021             etc_prefix = sys.prefix + '/etc/xpra'
    1022 
    1023         etc_files = []
    1024         if server_ENABLED and x11_ENABLED:
    1025             etc_files = ["etc/xpra/xorg.conf"]
    1026             #figure out the version of the Xorg server:
    1027             xorg_conf, use_Xdummy_wrapper = get_xorg_conf_and_script()
    1028             if not use_Xdummy_wrapper and "scripts/xpra_Xdummy" in scripts:
    1029                 #if we're not using the wrapper, don't install it
    1030                 scripts.remove("scripts/xpra_Xdummy")
    1031             etc_files.append(xorg_conf)
    1032         data_files.append((etc_prefix, etc_files))
    10331553
    10341554    if OSX and "py2app" in sys.argv:
     
    10451565        remove_packages(*external_excludes)
    10461566
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1567        try:
     1568            from xpra.src_info import REVISION
     1569        except:
     1570            REVISION = "unknown"
     1571        Plist = {
     1572            "CFBundleDocumentTypes" : {
     1573                "CFBundleTypeExtensions"    : ["Xpra"],
     1574                "CFBundleTypeName"          : "Xpra Session Config File",
     1575                "CFBundleName"              : "Xpra",
     1576                "CFBundleTypeRole"          : "Viewer",
     1577                },
     1578            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2017 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1579            "CFBundleIdentifier"            : "org.xpra.xpra",
     1580            }
    10531581        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541582        #so the make-app.sh script still has to hack around this problem.
     
    10651593            }
    10661594        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1595        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1596
     1597    if OSX:
     1598        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1599        #so instead we have to query each package seperately and merge the results:
     1600        def osx_pkgconfig(*pkgs_options, **ekw):
     1601            kw = dict(ekw)
     1602            for pkg in pkgs_options:
     1603                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1604                if pkg.lower().startswith("x"):
     1605                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1606                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1607                kw = exec_pkgconfig(pkg, **kw)
     1608                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1609            return kw
     1610
     1611        pkgconfig = osx_pkgconfig
    10681612
    10691613
    10701614if html5_ENABLED:
    1071     for k,v in glob_recurse("html5").items():
    1072         if (k!=""):
    1073             k = os.sep+k
    1074         data_files.append((html5_dir+k, v))
    1075 
     1615    if WIN32 or OSX:
     1616        external_includes.append("websockify")
     1617        external_includes.append("numpy")
     1618        external_includes.append("ssl")
     1619        external_includes.append("_ssl")
     1620        if not PYTHON3:
     1621            external_includes.append("mimetypes")
     1622            external_includes.append("mimetools")
     1623            external_includes.append("BaseHTTPServer")
     1624
     1625
     1626if annotate_ENABLED:
     1627    from Cython.Compiler import Options
     1628    Options.annotate = True
    10761629
    10771630
    10781631#*******************************************************************************
    1079 toggle_packages(server_ENABLED, "xpra.server", "xpra.server.stats", "xpra.server.auth")
    1080 toggle_packages(server_ENABLED or gtk2_ENABLED or gtk3_ENABLED, "xpra.gtk_common", "xpra.clipboard")
    1081 
    1082 
    1083 toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.gtk_x11", "xpra.x11.bindings")
     1632buffers_c = "xpra/buffers/buffers.c"
     1633memalign_c = "xpra/buffers/memalign.c"
     1634xxhash_c = "xpra/buffers/xxhash.c"
     1635membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1636
     1637add_packages("xpra.buffers")
     1638buffers_pkgconfig = pkgconfig(optimize=3)
     1639cython_add(Extension("xpra.buffers.membuf",
     1640            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1641
     1642
     1643toggle_packages(dbus_ENABLED, "xpra.dbus")
     1644toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1645toggle_packages(server_ENABLED or proxy_ENABLED or shadow_ENABLED, "xpra.server", "xpra.server.auth")
     1646toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1647toggle_packages(server_ENABLED, "xpra.server.window")
     1648toggle_packages(server_ENABLED and shadow_ENABLED, "xpra.server.shadow")
     1649toggle_packages(server_ENABLED or (client_ENABLED and gtk2_ENABLED), "xpra.clipboard")
     1650toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1651
     1652#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1653if dbus_ENABLED and server_ENABLED:
     1654    add_packages("xpra.server.dbus")
     1655
     1656if OSX:
     1657    quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1658    add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1659                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h",
     1660                    '-ObjC',
     1661                    '-mmacosx-version-min=10.10')
     1662    add_to_keywords(quartz_pkgconfig, 'extra_link_args',
     1663                    '-framework', 'Foundation',
     1664                    '-framework', 'AppKit',
     1665                    )
     1666    cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1667                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1668                language="objc",
     1669                **quartz_pkgconfig
     1670                ))
     1671
     1672monotonic_time_pkgconfig = pkgconfig()
     1673if not OSX and not WIN32:
     1674    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1675cython_add(Extension("xpra.monotonic_time",
     1676            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1677            **monotonic_time_pkgconfig
     1678            ))
     1679
     1680
     1681toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841682if x11_ENABLED:
    10851683    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1684    make_constants("xpra", "x11", "gtk2", "constants")
    10871685
    10881686    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981696                **pkgconfig("x11")
    10991697                ))
     1698    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1699                ["xpra/x11/bindings/posix_display_source.pyx"],
     1700                **pkgconfig("x11")
     1701                ))
     1702
    11001703    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011704                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041707    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051708                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1709                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071710                ))
    11081711
    11091712    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101713                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1714                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121715                ))
    11131716    cython_add(Extension("xpra.x11.bindings.ximage",
    11141717                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1718                **pkgconfig("x11", "xext", "xcomposite")
    11161719                ))
    1117 
    1118     #below uses gtk/gdk:
    1119     cython_add(Extension("xpra.x11.gtk_x11.gdk_display_source",
    1120                 ["xpra/x11/gtk_x11/gdk_display_source.pyx"],
    1121                 **pkgconfig(*PYGTK_PACKAGES)
     1720if xinput_ENABLED:
     1721    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1722                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1723                **pkgconfig("x11", "xi")
    11221724                ))
    1123     GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["xfixes", "xdamage"]
    1124     cython_add(Extension("xpra.x11.gtk_x11.gdk_bindings",
    1125                 ["xpra/x11/gtk_x11/gdk_bindings.pyx"],
    1126                 **pkgconfig(*GDK_BINDINGS_PACKAGES)
     1725
     1726toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1727if gtk_x11_ENABLED:
     1728    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1729    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1730    if PYTHON3:
     1731        #GTK3 display source:
     1732        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1733                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1734                    **pkgconfig("gdk-3.0")
     1735                    ))
     1736    else:
     1737        #GTK2:
     1738        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1739                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1740                    **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1741                    ))
     1742        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1743        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1744                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1745                    **pkgconfig(*GDK_BINDINGS_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1746                    ))
     1747
     1748toggle_packages(not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED), "xpra.gtk_common.gtk2")
     1749if not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED):
     1750    cython_add(Extension("xpra.gtk_common.gtk2.gdk_bindings",
     1751                ["xpra/gtk_common/gtk2/gdk_bindings.pyx"],
     1752                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11271753                ))
    11281754
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1755if client_ENABLED and gtk3_ENABLED:
     1756    #cairo workaround:
     1757    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1758                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1759                **pkgconfig("pycairo")
     1760                ))
     1761
     1762if client_ENABLED or server_ENABLED:
     1763    add_packages("xpra.codecs.argb")
     1764    argb_pkgconfig = pkgconfig(optimize=3)
    11321765    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1766                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1767
     1768
     1769#build tests, but don't install them:
     1770toggle_packages(tests_ENABLED, "unit")
    11341771
    11351772
    11361773if bundle_tests_ENABLED:
    11371774    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1775    for k,v in glob_recurse("unit").items():
    11391776        if (k!=""):
    11401777            k = os.sep+k
    1141         data_files.append(("tests"+k, v))
    1142 
    1143 #special case for client: cannot use toggle_packages which would include gtk3, qt, etc:
     1778        add_data_files("unit"+k, v)
     1779
     1780#python-cryptography needs workarounds for bundling:
     1781if crypto_ENABLED and (OSX or WIN32):
     1782    external_includes.append("_ssl")
     1783    external_includes.append("cffi")
     1784    external_includes.append("_cffi_backend")
     1785    external_includes.append("cryptography")
     1786    external_includes.append("pkg_resources._vendor.packaging")
     1787    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1788    external_includes.append("pkg_resources._vendor.pyparsing")
     1789    add_modules("cryptography.hazmat.bindings._openssl")
     1790    add_modules("cryptography.hazmat.bindings._constant_time")
     1791    add_modules("cryptography.hazmat.bindings._padding")
     1792    add_modules("cryptography.hazmat.backends.openssl")
     1793    add_modules("cryptography.fernet")
     1794    if WIN32:
     1795        external_includes.append("appdirs")
     1796
     1797#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441798if client_ENABLED:
    11451799    add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1800toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471801toggle_packages(client_ENABLED and gtk2_ENABLED, "xpra.client.gtk2")
    1148 toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3", "gi")
    1149 toggle_packages(client_ENABLED and qt4_ENABLED, "xpra.client.qt4", "PyQt4")
     1802toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1803toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501804toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gtk_base")
    1151 toggle_packages(sound_ENABLED, "xpra.sound")
    1152 toggle_packages(webm_ENABLED, "xpra.codecs.webm")
    1153 toggle_packages(client_ENABLED and gtk2_ENABLED and opengl_ENABLED, "xpra.client.gl")
     1805toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1806toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1807toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED) and example_ENABLED, "xpra.client.gtk_base.example")
     1808if client_ENABLED and WIN32 and MINGW_PREFIX:
     1809    propsys_pkgconfig = pkgconfig()
     1810    if debug_ENABLED:
     1811        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1812    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1813    cython_add(Extension("xpra.platform.win32.propsys",
     1814                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1815                language="c++",
     1816                **propsys_pkgconfig))
     1817
     1818if client_ENABLED or server_ENABLED:
     1819    add_modules("xpra.codecs")
     1820toggle_packages(client_ENABLED or server_ENABLED, "xpra.keyboard")
     1821if client_ENABLED or server_ENABLED:
     1822    add_modules("xpra.scripts.config", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1823if server_ENABLED or proxy_ENABLED:
     1824    add_modules("xpra.scripts.server")
     1825if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1826    add_modules("xpra.scripts.gtk_info")
     1827
     1828toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1829#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1830#and try to include both gtk3 and gtk2, and fail hard..
     1831for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gtk_compat"):
     1832    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1833
     1834toggle_modules(sound_ENABLED, "xpra.sound")
     1835toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541836
    11551837toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11561838if clipboard_ENABLED:
    1157     cython_add(Extension("xpra.gtk_common.gdk_atoms",
    1158                 ["xpra/gtk_common/gdk_atoms.pyx"],
    1159                 **pkgconfig(*PYGTK_PACKAGES)
     1839    cython_add(Extension("xpra.gtk_common.gtk2.gdk_atoms",
     1840                ["xpra/gtk_common/gtk2/gdk_atoms.pyx"],
     1841                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11601842                ))
    11611843
    1162 if cyxor_ENABLED:
     1844toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1845if client_ENABLED or server_ENABLED:
    11631846    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641847                ["xpra/codecs/xor/cyxor.pyx"],
    1165                 **pkgconfig()))
    1166 
    1167 if cymaths_ENABLED:
    1168     cython_add(Extension("xpra.server.stats.cymaths",
    1169                 ["xpra/server/stats/cymaths.pyx"],
    1170                 **pkgconfig()))
    1171 
    1172 
    1173 #needed for both nvenc and csc_cuda:
    1174 toggle_packages(csc_nvcuda_ENABLED or nvenc_ENABLED, "xpra.codecs.cuda_common")
    1175 
    1176 toggle_packages(csc_opencl_ENABLED, "xpra.codecs.csc_opencl")
    1177 toggle_packages(csc_nvcuda_ENABLED, "xpra.codecs.csc_nvcuda")
     1848                **pkgconfig(optimize=3)))
     1849
     1850if server_ENABLED:
     1851    O3_pkgconfig = pkgconfig(optimize=3)
     1852    cython_add(Extension("xpra.server.cystats",
     1853                ["xpra/server/cystats.pyx"],
     1854                **O3_pkgconfig))
     1855    cython_add(Extension("xpra.server.window.region",
     1856                ["xpra/server/window/region.pyx"],
     1857                **O3_pkgconfig))
     1858    cython_add(Extension("xpra.server.window.motion",
     1859                ["xpra/server/window/motion.pyx"],
     1860                **O3_pkgconfig))
     1861
     1862if sd_listen_ENABLED:
     1863    sdp = pkgconfig("libsystemd")
     1864    cython_add(Extension("xpra.platform.xposix.sd_listen",
     1865                ["xpra/platform/xposix/sd_listen.pyx"],
     1866                **sdp))
     1867
     1868
    11781869toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11791870
     1871toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     1872if nvfbc_ENABLED:
     1873    nvfbc_pkgconfig = pkgconfig("nvfbc")
     1874    #add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     1875    platform = sys.platform.rstrip("0123456789")
     1876    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     1877                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     1878                         language="c++",
     1879                         **nvfbc_pkgconfig))
     1880
    11801881toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     1882toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     1883toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     1884
     1885if nvenc_ENABLED and cuda_kernels_ENABLED:
     1886    #find nvcc:
     1887    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     1888    if WIN32:
     1889        nvcc_exe = "nvcc.exe"
     1890        path_options = [
     1891                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\bin",
     1892                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\bin",
     1893                         ] + path_options
     1894    else:
     1895        nvcc_exe = "nvcc"
     1896        for v in ("-7.5", "-8.0", ""):
     1897            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     1898    options = [os.path.join(x, nvcc_exe) for x in path_options]
     1899    def which(cmd):
     1900        try:
     1901            code, out, _ = get_status_output(["which", cmd])
     1902            if code==0:
     1903                return out
     1904        except:
     1905            pass
     1906    #prefer the one we find on the $PATH, if any:
     1907    try:
     1908        v = which(nvcc_exe)
     1909        if v and (v not in options):
     1910            options.insert(0, v)
     1911    except:
     1912        pass
     1913    nvcc_versions = {}
     1914    for filename in options:
     1915        if not os.path.exists(filename):
     1916            continue
     1917        code, out, err = get_status_output([filename, "--version"])
     1918        if code==0:
     1919            vpos = out.rfind(", V")
     1920            if vpos>0:
     1921                version = out[vpos+3:].strip("\n")
     1922                version_str = " version %s" % version
     1923            else:
     1924                version = "0"
     1925                version_str = " unknown version!"
     1926            print("found CUDA compiler: %s%s" % (filename, version_str))
     1927            nvcc_versions[version] = filename
     1928    assert nvcc_versions, "cannot find nvcc compiler!"
     1929    #choose the most recent one:
     1930    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     1931    if len(nvcc_versions)>1:
     1932        print(" using version %s from %s" % (version, nvcc))
     1933    if WIN32:
     1934        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     1935        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     1936    #first compile the cuda kernels
     1937    #(using the same cuda SDK for both nvenc modules for now..)
     1938    #TODO:
     1939    # * compile directly to output directory instead of using data files?
     1940    # * detect which arches we want to build for? (does it really matter much?)
     1941    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     1942    for kernel in kernels:
     1943        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     1944        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     1945        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     1946            continue
     1947        reason = should_rebuild(cuda_src, cuda_bin)
     1948        if not reason:
     1949            continue
     1950        cmd = [nvcc,
     1951               '-fatbin',
     1952               #"-cubin",
     1953               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     1954               #"-gencode=arch=compute_50,code=sm_50",
     1955               #"-gencode=arch=compute_52,code=sm_52",
     1956               #"-gencode=arch=compute_52,code=compute_52",
     1957               "-c", cuda_src,
     1958               "-o", cuda_bin]
     1959        #GCC 6 uses C++11 by default:
     1960        if get_gcc_version()>=[6, 0]:
     1961            cmd.append("-std=c++11")
     1962        CL_VERSION = os.environ.get("CL_VERSION")
     1963        if CL_VERSION:
     1964            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     1965            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     1966            cmd += ["--machine", "32"]
     1967        if WIN32:
     1968            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     1969            #cmd += ["--input-drive-prefix", "/"]
     1970            #cmd += ["--dependency-drive-prefix", "/"]
     1971            cmd += ["-I%s" % os.path.abspath("win32")]
     1972        comp_code_options = [(30, 30), (35, 35)]
     1973        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     1974        if version!="0" and version<"7.5":
     1975            print("CUDA version %s is very unlikely to work")
     1976            print("try upgrading to version 7.5 or later")
     1977        if version>="7.5":
     1978            comp_code_options.append((50, 50))
     1979            comp_code_options.append((52, 52))
     1980            comp_code_options.append((53, 53))
     1981        if version>="8.0":
     1982            comp_code_options.append((60, 60))
     1983            comp_code_options.append((61, 61))
     1984            comp_code_options.append((62, 62))
     1985        if version>="9.0":
     1986            comp_code_options.append((70, 70))
     1987        for arch, code in comp_code_options:
     1988            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     1989        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     1990        print(" %s" % " ".join("'%s'" % x for x in cmd))
     1991        c, stdout, stderr = get_status_output(cmd)
     1992        if c!=0:
     1993            print("Error: failed to compile CUDA kernel %s" % kernel)
     1994            print(stdout or "")
     1995            print(stderr or "")
     1996            sys.exit(1)
     1997    CUDA_BIN = "share/xpra/cuda"
     1998    if WIN32:
     1999        CUDA_BIN = "CUDA"
     2000    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2001
    11812002if nvenc_ENABLED:
    1182     make_constants("xpra", "codecs", "nvenc", "constants")
    1183     nvenc_pkgconfig = pkgconfig("nvenc3", "cuda")
    1184     cython_add(Extension("xpra.codecs.nvenc.encoder",
    1185                          ["xpra/codecs/nvenc/encoder.pyx"],
    1186                          **nvenc_pkgconfig), min_version=(0, 16))
     2003    nvencmodule = "nvenc"
     2004    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2005    #don't link against libnvidia-encode, we load it dynamically:
     2006    libraries = nvenc_pkgconfig.get("libraries", [])
     2007    if "nvidia-encode" in libraries:
     2008        libraries.remove("nvidia-encode")
     2009    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2010        #with gcc 6.2 on Fedora:
     2011        #xpra/codecs/nvenc/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2012        #xpra/codecs/nvenc/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2013        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2014        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2015    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2016                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2017                         **nvenc_pkgconfig))
    11872018
    11882019toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892020if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2021    x264_pkgconfig = pkgconfig("x264")
     2022    if get_gcc_version()>=[6, 0]:
     2023        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912024    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922025                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2026                **x264_pkgconfig))
    11942027
    11952028toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962029if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2030    x265_pkgconfig = pkgconfig("x265")
    11982031    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992032                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
    1201 
    1202 toggle_packages(webp_ENABLED, "xpra.codecs.webp")
    1203 if webp_ENABLED:
    1204     webp_pkgconfig = pkgconfig("webp")
    1205     cython_add(Extension("xpra.codecs.webp.encode",
    1206                 ["xpra/codecs/webp/encode.pyx"],
    1207                 **webp_pkgconfig), min_version=(0, 16))
    1208 
    1209 toggle_packages(dec_avcodec_ENABLED, "xpra.codecs.dec_avcodec")
    1210 if dec_avcodec_ENABLED:
    1211     make_constants("xpra", "codecs", "dec_avcodec", "constants")
    1212     avcodec_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec_static_ENABLED)
    1213     cython_add(Extension("xpra.codecs.dec_avcodec.decoder",
    1214                 ["xpra/codecs/dec_avcodec/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1215                 **avcodec_pkgconfig), min_version=(0, 19))
     2033                **x265_pkgconfig))
     2034
     2035toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2036if pillow_ENABLED:
     2037    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
     2038
     2039toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2040if jpeg_ENABLED:
     2041    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2042    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2043                ["xpra/codecs/jpeg/encoder.pyx"],
     2044                **jpeg_pkgconfig))
     2045    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2046                ["xpra/codecs/jpeg/decoder.pyx"],
     2047                **jpeg_pkgconfig))
     2048
     2049#swscale and avcodec2 use libav_common/av_log:
     2050libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2051toggle_packages(libav_common, "xpra.codecs.libav_common")
     2052if libav_common:
     2053    avutil_pkgconfig = pkgconfig("avutil")
     2054    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2055                ["xpra/codecs/libav_common/av_log.pyx"],
     2056                **avutil_pkgconfig))
     2057
    12162058
    12172059toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182060if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2061    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil")
    12202062    cython_add(Extension("xpra.codecs.dec_avcodec2.decoder",
    1221                 ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1222                 **avcodec2_pkgconfig), min_version=(0, 19))
    1223 
     2063                ["xpra/codecs/dec_avcodec2/decoder.pyx"],
     2064                **avcodec2_pkgconfig))
     2065
     2066
     2067toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2068if csc_libyuv_ENABLED:
     2069    libyuv_pkgconfig = pkgconfig("libyuv")
     2070    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2071                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2072                language="c++",
     2073                **libyuv_pkgconfig))
    12242074
    12252075toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262076if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2077    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292078    cython_add(Extension("xpra.codecs.csc_swscale.colorspace_converter",
    1230                 ["xpra/codecs/csc_swscale/colorspace_converter.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1231                 **swscale_pkgconfig), min_version=(0, 19))
    1232 
    1233 toggle_packages(csc_cython_ENABLED, "xpra.codecs.csc_cython")
    1234 if csc_cython_ENABLED:
    1235     csc_cython_pkgconfig = pkgconfig()
    1236     cython_add(Extension("xpra.codecs.csc_cython.colorspace_converter",
    1237                 ["xpra/codecs/csc_cython/colorspace_converter.pyx", "xpra/codecs/memalign/memalign.c"],
    1238                 **csc_cython_pkgconfig), min_version=(0, 15))
     2079                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2080                **swscale_pkgconfig))
     2081
    12392082
    12402083toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412084if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2085    vpx_pkgconfig = pkgconfig("vpx")
    12432086    cython_add(Extension("xpra.codecs.vpx.encoder",
    1244                 ["xpra/codecs/vpx/encoder.pyx", "xpra/codecs/vpx/vpxlib.c", "xpra/codecs/memalign/memalign.c"],
    1245                 **vpx_pkgconfig), min_version=(0, 16))
     2087                ["xpra/codecs/vpx/encoder.pyx"],
     2088                **vpx_pkgconfig))
    12462089    cython_add(Extension("xpra.codecs.vpx.decoder",
    1247                 ["xpra/codecs/vpx/decoder.pyx", "xpra/codecs/memalign/memalign.c"],
    1248                 **vpx_pkgconfig), min_version=(0, 16))
    1249 
    1250 
    1251 toggle_packages(rencode_ENABLED, "xpra.net.rencode")
    1252 if rencode_ENABLED:
    1253     rencode_pkgconfig = pkgconfig()
    1254     if not debug_ENABLED:
    1255         if WIN32:
    1256             add_to_keywords(rencode_pkgconfig, 'extra_compile_args', "/Ox")
    1257         else:
    1258             add_to_keywords(rencode_pkgconfig, 'extra_compile_args', "-O3")
    1259     cython_add(Extension("xpra.net.rencode._rencode",
    1260                 ["xpra/net/rencode/rencode.pyx"],
    1261                 **rencode_pkgconfig))
     2090                ["xpra/codecs/vpx/decoder.pyx"],
     2091                **vpx_pkgconfig))
     2092
     2093toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2094if enc_ffmpeg_ENABLED:
     2095    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2096    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2097                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2098                **ffmpeg_pkgconfig))
     2099
     2100toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2101if v4l2_ENABLED:
     2102    v4l2_pkgconfig = pkgconfig()
     2103    #fuly warning: cython makes this difficult,
     2104    #we have to figure out if "device_caps" exists in the headers:
     2105    ENABLE_DEVICE_CAPS = False
     2106    if os.path.exists("/usr/include/linux/videodev2.h"):
     2107        hdata = open("/usr/include/linux/videodev2.h").read()
     2108        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2109    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2110    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2111    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2112                ["xpra/codecs/v4l2/pusher.pyx"],
     2113                **v4l2_pkgconfig))
    12622114
    12632115
    12642116toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2117toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652118if cython_bencode_ENABLED:
    1266     bencode_pkgconfig = pkgconfig()
    1267     if not debug_ENABLED:
    1268         if WIN32:
    1269             add_to_keywords(bencode_pkgconfig, 'extra_compile_args', "/Ox")
    1270         else:
    1271             add_to_keywords(bencode_pkgconfig, 'extra_compile_args', "-O3")
     2119    bencode_pkgconfig = pkgconfig(optimize=not debug_ENABLED)
    12722120    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732121                ["xpra/net/bencode/cython_bencode.pyx"],
    12742122                **bencode_pkgconfig))
    12752123
     2124if vsock_ENABLED:
     2125    vsock_pkgconfig = pkgconfig()
     2126    cython_add(Extension("xpra.net.vsock",
     2127                ["xpra/net/vsock.pyx"],
     2128                **vsock_pkgconfig))
     2129
     2130if pam_ENABLED:
     2131    pam_pkgconfig = pkgconfig()
     2132    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2133    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2134    cython_add(Extension("xpra.server.pam",
     2135                ["xpra/server/pam.pyx"],
     2136                **pam_pkgconfig))
     2137
    12762138
    12772139if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2140    from Cython.Build import cythonize
     2141    #this causes Cython to fall over itself:
     2142    #gdb_debug=debug_ENABLED
     2143    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792144if cmdclass:
    12802145    setup_options["cmdclass"] = cmdclass
     
    12832148
    12842149
    1285 def print_option(prefix, k, v):
    1286     if type(v)==dict:
    1287         print("%s* %s:" % (prefix, k))
    1288         for kk,vv in v.items():
    1289             print_option(" "+prefix, kk, vv)
    1290     else:
    1291         print("%s* %s=%s" % (prefix, k, v))
    1292 
    12932150def main():
    12942151    if OSX or WIN32 or debug_ENABLED:
     2152        print()
    12952153        print("setup options:")
     2154        if verbose_ENABLED:
     2155            print("setup_options=%s" % (setup_options,))
     2156        try:
     2157            from xpra.util import repr_ellipsized as pv
     2158        except:
     2159            def pv(v):
     2160                return str(v)
    12962161        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2162            print_option("", k, pv(v))
    12982163        print("")
    12992164
Note: See TracChangeset for help on using the changeset viewer.