xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r16133  
    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
     72import struct
     73BITS = struct.calcsize("P")*8
     74
     75
     76if "pkg-info" in sys.argv:
     77    with open("PKG-INFO", "wb") as f:
     78        pkg_info_values = setup_options.copy()
     79        pkg_info_values.update({
     80                                "metadata_version"  : "1.1",
     81                                "summary"           :  description,
     82                                "home_page"         : url,
     83                                })
     84        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     85                  "Author", "Author-email", "License", "Download-URL", "Description"):
     86            v = pkg_info_values[k.lower().replace("-", "_")]
     87            f.write(b"%s: %s\n" % (k, v))
     88    sys.exit(0)
     89
     90
     91print("Xpra version %s" % XPRA_VERSION)
    3092#*******************************************************************************
    3193# Most of the options below can be modified on the command line
     
    3395# only the default values are specified here:
    3496#*******************************************************************************
    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 
     97from xpra.os_util import get_status_output
     98
     99PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     100has_pkg_config = False
     101#we don't support building with "pkg-config" on win32 with python2:
     102if PKG_CONFIG:
     103    v = get_status_output([PKG_CONFIG, "--version"])
     104    has_pkg_config = v[0]==0 and v[1]
     105    if has_pkg_config:
     106        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     107
     108for arg in list(sys.argv):
     109    if arg.startswith("--pkg-config-path="):
     110        pcp = arg[len("--pkg-config-path="):]
     111        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     112        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     113        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     114        sys.argv.remove(arg)
     115
     116def pkg_config_ok(*args, **kwargs):
     117    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     118    return get_status_output(cmd)[0]==0
     119
     120def pkg_config_version(req_version, pkgname, **kwargs):
     121    cmd = [PKG_CONFIG, "--modversion", pkgname]
     122    r, out, _ = get_status_output(cmd)
     123    if r!=0 or not out:
     124        return False
     125    from distutils.version import LooseVersion
     126    return LooseVersion(out)>=LooseVersion(req_version)
     127
     128def is_RH():
     129    try:
     130        with open("/etc/redhat-release", mode='rb') as f:
     131            data = f.read()
     132        return data.startswith("CentOS") or data.startswith("RedHat")
     133    except:
     134        pass
     135    return False
     136
     137DEFAULT = True
     138if "--minimal" in sys.argv:
     139    sys.argv.remove("--minimal")
     140    DEFAULT = False
     141
     142from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     143shadow_ENABLED = SHADOW_SUPPORTED and not (PYTHON3 and LINUX) and DEFAULT       #shadow servers use some GTK2 code..
     144server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and not (PYTHON3 and LINUX) and DEFAULT
     145service_ENABLED = LINUX and server_ENABLED
     146sd_listen_ENABLED = pkg_config_ok("--exists", "libsystemd")
     147proxy_ENABLED  = DEFAULT
     148client_ENABLED = DEFAULT
     149
     150x11_ENABLED = DEFAULT and not WIN32 and not OSX
     151xinput_ENABLED = x11_ENABLED
     152dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     153gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     154gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     155gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     156opengl_ENABLED = DEFAULT and client_ENABLED
     157html5_ENABLED = DEFAULT
     158html5_gzip_ENABLED = DEFAULT
     159html5_brotli_ENABLED = DEFAULT
     160minify_ENABLED = html5_ENABLED
     161pam_ENABLED = DEFAULT and (server_ENABLED or proxy_ENABLED) and os.name=="posix" and not OSX and (os.path.exists("/usr/include/pam/pam_misc.h") or os.path.exists("/usr/include/security/pam_misc.h"))
     162
     163vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     164bencode_ENABLED         = DEFAULT
     165cython_bencode_ENABLED  = DEFAULT
     166clipboard_ENABLED       = DEFAULT and not PYTHON3
     167Xdummy_ENABLED          = None          #None means auto-detect
     168Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     169if WIN32 or OSX:
     170    Xdummy_ENABLED = False
     171sound_ENABLED           = DEFAULT
     172printing_ENABLED        = DEFAULT
     173crypto_ENABLED          = DEFAULT
     174mdns_ENABLED            = DEFAULT
     175
     176enc_proxy_ENABLED       = DEFAULT
     177enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     178enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     179pillow_ENABLED          = DEFAULT
     180jpeg_ENABLED            = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     181vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     182enc_ffmpeg_ENABLED      = DEFAULT and pkg_config_version("56", "libavcodec")
     183webcam_ENABLED          = DEFAULT and not OSX
     184v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD)
     185#ffmpeg 3.1 or later is required
     186dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     187csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     188nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("7", "nvenc")
     189nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     190cuda_kernels_ENABLED    = DEFAULT
     191cuda_rebuild_ENABLED    = DEFAULT
     192csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     193
     194#Cython / gcc / packaging build options:
     195annotate_ENABLED        = True
    86196warn_ENABLED            = True
    87197strict_ENABLED          = True
    88 PIC_ENABLED             = True
     198PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89199debug_ENABLED           = False
    90200verbose_ENABLED         = False
    91201bundle_tests_ENABLED    = False
     202tests_ENABLED           = False
     203rebuild_ENABLED         = True
    92204
    93205#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",
     206SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     207            "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     208            "vpx", "pillow", "jpeg",
     209            "v4l2",
     210            "dec_avcodec2", "csc_swscale",
     211            "csc_libyuv",
     212            "bencode", "cython_bencode", "vsock", "mdns",
    104213            "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")
     214            "server", "client", "dbus", "x11", "xinput", "sd_listen",
     215            "gtk_x11", "service",
     216            "gtk2", "gtk3",
     217            "html5", "minify", "html5_gzip", "html5_brotli",
     218            "pam",
     219            "sound", "opengl", "printing", "webcam",
     220            "rebuild",
     221            "annotate", "warn", "strict",
     222            "shadow", "proxy",
     223            "debug", "PIC",
     224            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     225if WIN32:
     226    SWITCHES.append("zip")
     227    zip_ENABLED = True
    109228HELP = "-h" in sys.argv or "--help" in sys.argv
    110229if HELP:
     
    120239            default_str = "auto-detect"
    121240        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     241    print("  --pkg-config-path=PATH")
     242    print("  --rpath=PATH")
    122243    sys.exit(0)
    123244
     245install = "dist"
     246rpath = None
     247ssl_cert = None
     248ssl_key = None
    124249filtered_args = []
    125250for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     251    matched = False
     252    for x in ("rpath", "ssl-cert", "ssl-key", "install"):
     253        varg = "--%s=" % x
     254        if arg.startswith(varg):
     255            value = arg[len(varg):]
     256            globals()[x.replace("-", "_")] = value
     257            #remove these arguments from sys.argv,
     258            #except for --install=PATH
     259            matched = x!="install"
     260            break
     261    if matched:
    129262        continue
    130     matched = False
    131263    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     264        with_str = "--with-%s" % x
     265        without_str = "--without-%s" % x
     266        if arg.startswith(with_str+"="):
     267            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     268            matched = True
     269            break
     270        elif arg==with_str:
    133271            vars()["%s_ENABLED" % x] = True
    134272            matched = True
    135273            break
    136         elif arg=="--without-%s" % x:
     274        elif arg==without_str:
    137275            vars()["%s_ENABLED" % x] = False
    138276            matched = True
     
    145283    for x in SWITCHES:
    146284        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)
     285    print("build switches:")
     286    for k in SWITCHES:
     287        v = switches_info[k]
     288        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150289
    151290    #sanity check the flags:
     
    153292        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154293        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
    158294    if shadow_ENABLED and not server_ENABLED:
    159295        print("Warning: shadow requires server to be enabled!")
    160296        shadow_ENABLED = False
    161     if cymaths_ENABLED and not server_ENABLED:
    162         print("Warning: cymaths requires server to be enabled!")
    163         cymaths_ENABLED = False
    164297    if x11_ENABLED and WIN32:
    165298        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:
     299    if gtk_x11_ENABLED and not x11_ENABLED:
     300        print("Error: you must enable x11 to support gtk_x11!")
     301        exit(1)
     302    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167303        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)
     304    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     305        print("Warning: you probably want to build at least the client or server!")
     306    if DEFAULT and not pillow_ENABLED:
     307        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     308    if minify_ENABLED:
     309        r = get_status_output(["uglifyjs", "--version"])[0]
     310        if r==0:
     311            minifier = "uglifyjs"
     312        else:
     313            print("Warning: uglifyjs failed and return %i" % r)
     314            try:
     315                import yuicompressor
     316                assert yuicompressor
     317                minifier = "yuicompressor"
     318            except ImportError as e:
     319                print("Warning: yuicompressor module not found, cannot minify")
     320                minify_ENABLED = False
     321    if not enc_x264_ENABLED and not vpx_ENABLED:
     322        print("Warning: no x264 and no vpx support!")
     323        print(" you should enable at least one of these two video encodings")
    174324
    175325
    176326#*******************************************************************************
    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",
     327# default sets:
     328
     329external_includes = ["hashlib",
    204330                     "ctypes", "platform"]
     331
     332
     333if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     334    external_includes += ["gi"]
     335elif gtk2_ENABLED or x11_ENABLED:
     336    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205337
    206338external_excludes = [
     
    212344                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213345                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
     346                    "curses", "pdb",
     347                    "urllib2", "tty",
     348                    "cookielib", "ftplib", "httplib", "fileinput",
    218349                    "distutils", "setuptools", "doctest"
    219350                    ]
    220 
     351if not html5_ENABLED and not crypto_ENABLED:
     352    external_excludes += ["ssl", "_ssl"]
     353if not html5_ENABLED:
     354    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     355
     356if not client_ENABLED and not server_ENABLED:
     357    excludes += ["PIL"]
     358if not dbus_ENABLED:
     359    excludes += ["dbus"]
    221360
    222361
    223362#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     363#for distutils / py2app and cx_freeze
    225364#use the following functions, which should get the right
    226365#data in the global variables "packages", "modules" and "excludes"
     
    253392
    254393def add_modules(*mods):
     394    def add(v):
     395        global modules
     396        if v not in modules:
     397            modules.append(v)
     398    do_add_modules(add, *mods)
     399
     400def do_add_modules(op, *mods):
    255401    """ adds the packages and any .py module found in the packages to the "modules" list
    256402    """
    257403    global modules
    258404    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     405        #ugly path stripping:
     406        if x.startswith("./"):
     407            x = x[2:]
     408        if x.endswith(".py"):
     409            x = x[:-3]
     410            x = x.replace("/", ".") #.replace("\\", ".")
    261411        pathname = os.path.sep.join(x.split("."))
     412        #is this a file module?
     413        f = "%s.py" % pathname
     414        if os.path.exists(f) and os.path.isfile(f):
     415            op(x)
    262416        if os.path.exists(pathname) and os.path.isdir(pathname):
    263417            #add all file modules found in this directory
    264418            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     419                #make sure we only include python files,
     420                #and ignore eclipse copies
     421                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266422                    fname = os.path.join(pathname, f)
    267423                    if os.path.isfile(fname):
    268424                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     425                        op(modname)
    270426
    271427def toggle_packages(enabled, *module_names):
     
    275431        remove_packages(*module_names)
    276432
     433def toggle_modules(enabled, *module_names):
     434    if enabled:
     435        def op(v):
     436            global modules
     437            if v not in modules:
     438                modules.append(v)
     439        do_add_modules(op, *module_names)
     440    else:
     441        remove_packages(*module_names)
     442
     443
    277444#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 
     445add_modules("xpra", "xpra.platform", "xpra.net")
     446add_modules("xpra.scripts.main")
     447
     448
     449def add_data_files(target_dir, files):
     450    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     451    assert type(target_dir)==str
     452    assert type(files) in (list, tuple)
     453    data_files.append((target_dir, files))
     454
     455
     456def check_md5sums(md5sums):
     457    print("Verifying md5sums:")
     458    for filename, md5sum in md5sums.items():
     459        if not os.path.exists(filename) or not os.path.isfile(filename):
     460            sys.exit("ERROR: file %s is missing or not a file!" % filename)
     461        sys.stdout.write("* %s: " % str(filename).ljust(52))
     462        with open(filename, mode='rb') as f:
     463            data = f.read()
     464        m = md5()
     465        m.update(data)
     466        digest = m.hexdigest()
     467        assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (filename, md5sum, digest)
     468        sys.stdout.write("OK\n")
     469        sys.stdout.flush()
     470
     471#for pretty printing of options:
     472def print_option(prefix, k, v):
     473    if type(v)==dict:
     474        print("%s* %s:" % (prefix, k))
     475        for kk,vv in v.items():
     476            print_option(" "+prefix, kk, vv)
     477    else:
     478        print("%s* %s=%s" % (prefix, k, v))
    284479
    285480#*******************************************************************************
     
    288483    try:
    289484        from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
     485    except ImportError as e:
    291486        sys.exit("ERROR: Cannot find Cython: %s" % e)
    292487    from distutils.version import LooseVersion
     
    297492                 % (cython_version, ".".join([str(part) for part in min_version])))
    298493
    299 def cython_add(extension, min_version=(0, 14, 0)):
     494def cython_add(extension, min_version=(0, 19)):
    300495    #gentoo does weird things, calls --no-compile with build *and* install
    301496    #then expects to find the cython modules!? ie:
     
    303498    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304499        return
    305     global ext_modules, cmdclass
    306500    cython_version_check(min_version)
    307501    from Cython.Distutils import build_ext
    308502    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     503    global cmdclass
     504    cmdclass['build_ext'] = build_ext
     505
     506def insert_into_keywords(kw, key, *args):
     507    values = kw.setdefault(key, [])
     508    for arg in args:
     509        values.insert(0, arg)
    310510
    311511def add_to_keywords(kw, key, *args):
     
    319519
    320520
     521def checkdirs(*dirs):
     522    for d in dirs:
     523        if not os.path.exists(d) or not os.path.isdir(d):
     524            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     525
    321526PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
    322527
     
    326531    if len(GCC_VERSION)==0:
    327532        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:
     533        r, _, err = get_status_output(cmd)
     534        if r==0:
    332535            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     536            for line in err.splitlines():
    334537                if line.startswith(V_LINE):
    335538                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343546    return GCC_VERSION
    344547
    345 def make_constants_pxi(constants_path, pxi_path):
     548def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346549    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):
     550    with open(constants_path) as f:
     551        for line in f:
     552            data = line.split("#", 1)[0].strip()
     553            # data can be empty ''...
     554            if not data:
     555                continue
     556            # or a pair like 'cFoo "Foo"'...
     557            elif len(data.split()) == 2:
     558                (pyname, cname) = data.split()
     559                constants.append((pyname, cname))
     560            # or just a simple token 'Foo'
     561            else:
     562                constants.append(data)
     563
     564    with open(pxi_path, "w") as out:
     565        if constants:
     566            out.write("cdef extern from *:\n")
     567            ### Apparently you can't use | on enum's?!
     568            # out.write("    enum MagicNumbers:\n")
     569            # for const in constants:
     570            #     if isinstance(const, tuple):
     571            #         out.write('        %s %s\n' % const)
     572            #     else:
     573            #         out.write('        %s\n' % (const,))
     574            for const in constants:
     575                if isinstance(const, tuple):
     576                    out.write('    unsigned int %s %s\n' % const)
     577                else:
     578                    out.write('    unsigned int %s\n' % (const,))
     579
     580            out.write("constants = {\n")
     581            for const in constants:
     582                if isinstance(const, tuple):
     583                    pyname = const[0]
     584                else:
     585                    pyname = const
     586                out.write('    "%s": %s,\n' % (pyname, pyname))
     587            out.write("}\n")
     588            if kwargs:
     589                out.write("\n\n")
     590
     591        if kwargs:
     592            for k, v in kwargs.items():
     593                out.write('DEF %s = %s\n' % (k, v))
     594
     595
     596def should_rebuild(src_file, bin_file):
     597    if not os.path.exists(bin_file):
     598        return "no file"
     599    elif rebuild_ENABLED:
     600        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     601            return "binary file out of date"
     602        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     603            return "newer build file"
     604    return None
     605
     606def make_constants(*paths, **kwargs):
    384607    base = os.path.join(os.getcwd(), *paths)
    385608    constants_file = "%s.txt" % base
    386609    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"
     610    reason = should_rebuild(constants_file, pxi_file)
    394611    if reason:
    395612        if verbose_ENABLED:
    396613            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
     614        make_constants_pxi(constants_file, pxi_file, **kwargs)
     615
    414616
    415617# 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 
     618def exec_pkgconfig(*pkgs_options, **ekw):
    423619    kw = dict(ekw)
     620    if "optimize" in kw:
     621        optimize = kw["optimize"]
     622        del kw["optimize"]
     623        if type(optimize)==bool:
     624            optimize = int(optimize)*3
     625        if not debug_ENABLED:
     626            add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     627    ignored_flags = []
     628    if kw.get("ignored_flags"):
     629        ignored_flags = kw.get("ignored_flags")
     630        del kw["ignored_flags"]
     631
    424632    if len(pkgs_options)>0:
    425633        package_names = []
     
    438646            for option in options:
    439647                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:
     648                r, _, _ = get_status_output(cmd)
     649                if r==0:
    443650                    valid_option = option
    444651                    break
    445652            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     653                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)")))
    447654            package_names.append(valid_option)
    448655        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     656            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450657        flag_map = {'-I': 'include_dirs',
    451658                    '-L': 'library_dirs',
    452659                    '-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))
     660        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     661        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     662        if r!=0:
     663            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     664        env_cflags = os.environ.get("CFLAGS")       #["dpkg-buildflags", "--get", "CFLAGS"]
     665        env_ldflags = os.environ.get("LDFLAGS")     #["dpkg-buildflags", "--get", "LDFLAGS"]
     666        for s in (pkg_config_out, env_cflags, env_ldflags):
     667            if not s:
     668                continue
     669            for token in s.split():
     670                if token[:2] in ignored_flags:
     671                    pass
     672                elif token[:2] in flag_map:
     673                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     674                elif token.startswith("-W"):
     675                    add_to_keywords(kw, 'extra_compile_args', token)
     676                else:# throw others to extra_link_args
     677                    add_to_keywords(kw, 'extra_link_args', token)
    468678    if warn_ENABLED:
    469679        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470680        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471681    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"
     682        if os.environ.get("CC", "").find("clang")>=0:
     683            #clang emits too many warnings with cython code,
     684            #so we can't enable Werror
     685            eifd = ["-Werror",
     686                    "-Wno-unneeded-internal-declaration",
     687                    "-Wno-unknown-attributes",
     688                    "-Wno-unused-function",
     689                    "-Wno-self-assign",
     690                    "-Wno-sometimes-uninitialized"]
     691        elif get_gcc_version()>=[4, 4]:
     692            eifd = ["-Werror",
     693                    #CentOS 6.x gives us some invalid warnings in nvenc, ignore those:
     694                    #"-Wno-error=uninitialized",
     695                    ]
     696            from xpra.os_util import is_Ubuntu, is_Debian, is_Raspbian
     697            if is_Debian() or is_Ubuntu() or is_Raspbian():
     698                #needed on Debian and Ubuntu to avoid this error:
     699                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     700                eifd.append("-Wno-error=strict-prototypes")
     701            if NETBSD:
     702                #see: http://trac.cython.org/ticket/395
     703                eifd += ["-fno-strict-aliasing"]
     704            elif FREEBSD:
     705                eifd += ["-Wno-error=unused-function"]
    475706        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     707            #older versions of OSX ship an old gcc,
     708            #not much we can do with this:
     709            eifd = []
     710        for eif in eifd:
     711            add_to_keywords(kw, 'extra_compile_args', eif)
    478712    if PIC_ENABLED:
    479713        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481715        add_to_keywords(kw, 'extra_compile_args', '-g')
    482716        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     717        if get_gcc_version()>=[4, 8]:
    485718            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486719            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     720    if rpath:
     721        insert_into_keywords(kw, "library_dirs", rpath)
     722        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
    487723    #add_to_keywords(kw, 'include_dirs', '.')
    488724    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     725        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490726    return kw
     727pkgconfig = exec_pkgconfig
    491728
    492729
    493730#*******************************************************************************
    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()
     731
     732
     733def get_base_conf_dir(install_dir, stripbuildroot=True):
     734    #in some cases we want to strip the buildroot (to generate paths in the config file)
     735    #but in other cases we want the buildroot path (when writing out the config files)
     736    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     737    #this is a bit hackish, but I can't think of a better way of detecting it
     738    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     739    dirs = (install_dir or sys.prefix).split(os.path.sep)
     740    if install_dir and stripbuildroot:
     741        pkgdir = os.environ.get("pkgdir")
     742        if "debian" in dirs and "tmp" in dirs:
     743            #ugly fix for stripping the debian tmp dir:
     744            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     745            while "tmp" in dirs:
     746                dirs = dirs[dirs.index("tmp")+1:]
     747        elif "debian" in dirs:
     748            #same for recent debian versions:
     749            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     750            i = dirs.index("debian")
     751            if dirs[i+1] == "xpra":
     752                dirs = dirs[i+2:]
     753        elif "BUILDROOT" in dirs:
     754            #strip rpm style build root:
     755            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     756            dirs = dirs[dirs.index("BUILDROOT")+2:]
     757        elif pkgdir and install_dir.startswith(pkgdir):
     758            #arch build dir:
     759            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     760        elif "usr" in dirs:
     761            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     762            #assume "/usr" or "/usr/local" is the build root
     763            while "usr" in dirs[1:]:
     764                dirs = dirs[dirs[1:].index("usr")+1:]
     765        elif "image" in dirs:
     766            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     767            while "image" in dirs:
     768                dirs = dirs[dirs.index("image")+2:]
     769    #now deal with the fact that "/etc" is used for the "/usr" prefix
     770    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     771    if dirs and dirs[-1]=="usr":
     772        dirs = dirs[:-1]
     773    #is this an absolute path?
     774    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     775        #ie: ["/", "usr"] or ["/", "usr", "local"]
     776        dirs.insert(0, os.path.sep)
     777    return dirs
     778
     779def get_conf_dir(install_dir, stripbuildroot=True):
     780    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     781    dirs.append("etc")
     782    dirs.append("xpra")
     783    return os.path.join(*dirs)
     784
     785def detect_xorg_setup(install_dir=None):
     786    from xpra.scripts import config
     787    config.debug = config.warn
     788    conf_dir = get_conf_dir(install_dir)
     789    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     790
     791def build_xpra_conf(install_dir):
     792    #generates an actual config file from the template
     793    xvfb_command = detect_xorg_setup(install_dir)
     794    from xpra.platform.features import DEFAULT_ENV
     795    def bstr(b):
     796        if b is None:
     797            return "auto"
     798        return ["no", "yes"][int(b)]
     799    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     800    conf_dir = get_conf_dir(install_dir)
     801    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     802    from xpra.platform.paths import get_socket_dirs
     803    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     804    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     805    socket_dirs = get_socket_dirs()
     806    if WIN32:
     807        bind = "Main"
    533808    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()
     809        if os.getuid()>0:
     810            #remove any paths containing the uid,
     811            #osx uses /var/tmp/$UID-Xpra,
     812            #but this should not be included in the default config for all users!
     813            #(the buildbot's uid!)
     814            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     815        bind = "auto"
     816    #FIXME: we should probably get these values from the default config instead
     817    pdf, postscript = "", ""
     818    if os.name=="posix" and printing_ENABLED:
     819        try:
     820            if "/usr/sbin" not in sys.path:
     821                sys.path.append("/usr/sbin")
     822            from xpra.platform.pycups_printing import get_printer_definition
     823            print("probing cups printer definitions")
     824            pdf = get_printer_definition("pdf")
     825            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     826            print("pdf=%s, postscript=%s" % (pdf, postscript))
     827        except Exception as e:
     828            print("could not probe for pdf/postscript printers: %s" % e)
     829    def pretty_cmd(cmd):
     830        return " ".join(cmd)
     831    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     832    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     833    from xpra.os_util import getUbuntuVersion
     834    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     835    #no python-avahi on RH / CentOS, need dbus module on *nix:
     836    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     837    SUBS = {
     838            'xvfb_command'          : pretty_cmd(xvfb_command),
     839            'ssh_command'           : DEFAULT_SSH_COMMAND,
     840            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     841            'remote_logging'        : "both",
     842            'start_env'             : start_env,
     843            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     844            'pulseaudio_command'    : pretty_cmd(DEFAULT_PULSEAUDIO_COMMAND),
     845            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     846            'conf_dir'              : conf_dir,
     847            'bind'                  : bind,
     848            'ssl_cert'              : ssl_cert or "",
     849            'ssl_key'               : ssl_key or "",
     850            'systemd_run'           : get_default_systemd_run(),
     851            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     852            'log_dir'               : "auto",
     853            'mdns'                  : bstr(mdns),
     854            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     855            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     856            'pdf_printer'           : pdf,
     857            'postscript_printer'    : postscript,
     858            'webcam'                : ["no", "auto"][webcam],
     859            'mousewheel'            : "on",
     860            'printing'              : printing_ENABLED,
     861            'dbus_control'          : bstr(dbus_ENABLED),
     862            'mmap'                  : bstr(True),
     863            }
     864    def convert_templates(subdirs=[]):
     865        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     866        #get conf dir for install, without stripping the build root
     867        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     868        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     869        if not os.path.exists(target_dir):
     870            try:
     871                os.makedirs(target_dir)
     872            except Exception as e:
     873                print("cannot create target dir '%s': %s" % (target_dir, e))
     874        for f in sorted(os.listdir(dirname)):
     875            if f.endswith("osx.conf.in") and not OSX:
     876                continue
     877            filename = os.path.join(dirname, f)
     878            if os.path.isdir(filename):
     879                convert_templates(subdirs+[f])
     880                continue
     881            if not f.endswith(".in"):
     882                continue
     883            with open(filename, "r") as f_in:
     884                template  = f_in.read()
     885            target_file = os.path.join(target_dir, f[:-len(".in")])
     886            print("generating %s from %s" % (target_file, f))
     887            with open(target_file, "w") as f_out:
     888                config_data = template % SUBS
     889                f_out.write(config_data)
     890    convert_templates()
    576891
    577892
     
    586901    #ensure we remove the files we generate:
    587902    CLEAN_FILES = [
     903                   "xpra/build_info.py",
     904                   "xpra/monotonic_time.c",
    588905                   "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",
     906                   "xpra/x11/gtk2/constants.pxi",
     907                   "xpra/x11/gtk2/gdk_bindings.c",
     908                   "xpra/x11/gtk2/gdk_display_source.c",
     909                   "xpra/x11/gtk3/gdk_display_source.c",
    592910                   "xpra/x11/bindings/constants.pxi",
    593911                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597915                   "xpra/x11/bindings/randr_bindings.c",
    598916                   "xpra/x11/bindings/core_bindings.c",
     917                   "xpra/x11/bindings/posix_display_source.c",
    599918                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     919                   "xpra/x11/bindings/xi2_bindings.c",
     920                   "xpra/platform/win32/propsys.cpp",
     921                   "xpra/platform/darwin/gdk_bindings.c",
     922                   "xpra/net/bencode/cython_bencode.c",
     923                   "xpra/net/vsock.c",
     924                   "xpra/buffers/membuf.c",
    601925                   "xpra/codecs/vpx/encoder.c",
    602926                   "xpra/codecs/vpx/decoder.c",
    603927                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     928                   "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     929                   "xpra/codecs/cuda_common/BGRA_to_U.fatbin",
     930                   "xpra/codecs/cuda_common/BGRA_to_V.fatbin",
     931                   "xpra/codecs/cuda_common/BGRA_to_Y.fatbin",
     932                   "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
    605933                   "xpra/codecs/enc_x264/encoder.c",
    606934                   "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",
     935                   "xpra/codecs/jpeg/encoder.c",
     936                   "xpra/codecs/jpeg/decoder.c",
     937                   "xpra/codecs/enc_ffmpeg/encoder.c",
     938                   "xpra/codecs/v4l2/constants.pxi",
     939                   "xpra/codecs/v4l2/pusher.c",
     940                   "xpra/codecs/libav_common/av_log.c",
    610941                   "xpra/codecs/dec_avcodec2/decoder.c",
     942                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611943                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614944                   "xpra/codecs/xor/cyxor.c",
    615945                   "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")
     946                   "xpra/codecs/nvapi_version.c",
     947                   "xpra/client/gtk3/cairo_workaround.c",
     948                   "xpra/server/cystats.c",
     949                   "xpra/server/window/region.c",
     950                   "xpra/server/window/motion.c",
     951                   "xpra/server/pam.c",
     952                   "xpra/server/sd_listen.c",
     953                   "etc/xpra/xpra.conf",
     954                   #special case for the generated xpra conf files in build (see #891):
     955                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     956    for x in CLEAN_FILES:
     957        p, ext = os.path.splitext(x)
     958        if ext in (".c", ".cpp", ".pxi"):
     959            #clean the Cython annotated html files:
     960            CLEAN_FILES.append(p+".html")
     961            if WIN32 and ext!=".pxi":
     962                #on win32, the build creates ".pyd" files, clean those too:
     963                CLEAN_FILES.append(p+".pyd")
    623964    if 'clean' in sys.argv:
    624965        CLEAN_FILES.append("xpra/build_info.py")
     
    630971            os.unlink(filename)
    631972
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     973from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    633974
    634975if "clean" not in sys.argv:
    635976    # Add build info to build_info.py file:
    636977    record_build_info()
     978    # ensure it is included in the module list if it didn't exist before
     979    add_modules(BUILD_INFO_FILE)
    637980
    638981if "sdist" in sys.argv:
    639982    record_src_info()
    640983
    641 if "install" in sys.argv:
     984if "install" in sys.argv or "build" in sys.argv:
    642985    #if installing from source tree rather than
    643986    #from a source snapshot, we may not have a "src_info" file
     
    645988    if not has_src_info():
    646989        record_src_info()
     990        # ensure it is now included in the module list
     991        add_modules(SRC_INFO_FILE)
    647992
    648993
     
    6631008    return m
    6641009
     1010
     1011def install_html5(install_dir="www"):
     1012    if minify_ENABLED:
     1013        print("minifying html5 client to %s using %s" % (install_dir, minifier))
     1014    else:
     1015        print("copying html5 client to %s" % (install_dir, ))
     1016    symlinks = {
     1017        "jquery.js"     : [
     1018            "/usr/share/javascript/jquery/jquery.js",
     1019            "/usr/share/javascript/jquery/3/jquery.js",
     1020            ],
     1021        }
     1022    for k,files in glob_recurse("html5").items():
     1023        if (k!=""):
     1024            k = os.sep+k
     1025        for f in files:
     1026            src = os.path.join(os.getcwd(), f)
     1027            parts = f.split(os.path.sep)
     1028            if parts[-1] in ("AUTHORS", "LICENSE"):
     1029                continue
     1030            if parts[0]=="html5":
     1031                f = os.path.join(*parts[1:])
     1032            if install_dir==".":
     1033                install_dir = os.getcwd()
     1034            dst = os.path.join(install_dir, f)
     1035            if os.path.exists(dst):
     1036                os.unlink(dst)
     1037            #try to find an existing installed library and symlink it:
     1038            symlink_options = symlinks.get(os.path.basename(f), [])
     1039            for symlink_option in symlink_options:
     1040                if os.path.exists(symlink_option):
     1041                    os.symlink(symlink_option, dst)
     1042                    break
     1043            if os.path.exists(dst):
     1044                #we've created a symlink, skip minification and compression
     1045                continue
     1046            ddir = os.path.split(dst)[0]
     1047            if ddir and not os.path.exists(ddir):
     1048                os.makedirs(ddir, 0o755)
     1049            ftype = os.path.splitext(f)[1].lstrip(".")
     1050            if minify_ENABLED and ftype=="js":
     1051                if minifier=="uglifyjs":
     1052                    minify_cmd = ["uglifyjs",
     1053                                  "--screw-ie8",
     1054                                  src,
     1055                                  "-o", dst,
     1056                                  "--compress",
     1057                                  ]
     1058                else:
     1059                    assert minifier=="yuicompressor"
     1060                    assert yuicompressor
     1061                    jar = yuicompressor.get_jar_filename()
     1062                    java_cmd = os.environ.get("JAVA", "java")
     1063                    minify_cmd = [java_cmd, "-jar", jar,
     1064                                  src,
     1065                                  "--nomunge",
     1066                                  "--line-break", "400",
     1067                                  "--type", ftype,
     1068                                  "-o", dst,
     1069                                  ]
     1070                r = get_status_output(minify_cmd)[0]
     1071                if r!=0:
     1072                    print("Error: failed to minify '%s', command returned error %i" % (f, r))
     1073                    if verbose_ENABLED:
     1074                        print(" command: %s" % (minify_cmd,))
     1075                else:
     1076                    print("minified %s" % (f, ))
     1077            else:
     1078                r = -1
     1079            if r!=0:
     1080                shutil.copyfile(src, dst)
     1081                os.chmod(dst, 0o644)
     1082            if ftype not in ("png", ):
     1083                if html5_gzip_ENABLED:
     1084                    gzip_dst = "%s.gz" % dst
     1085                    if os.path.exists(gzip_dst):
     1086                        os.unlink(gzip_dst)
     1087                    cmd = ["gzip", "-f", "-n", "-9", "-k", dst]
     1088                    get_status_output(cmd)
     1089                    if os.path.exists(gzip_dst):
     1090                        os.chmod(gzip_dst, 0o644)
     1091                if html5_brotli_ENABLED:
     1092                    br_dst = "%s.br" % dst
     1093                    if os.path.exists(br_dst):
     1094                        os.unlink(br_dst)
     1095                    cmd = ["bro", "--input", dst, "--output", br_dst]
     1096                    get_status_output(cmd)
     1097                    if os.path.exists(br_dst):
     1098                        os.chmod(br_dst, 0o644)
     1099
     1100
    6651101#*******************************************************************************
    6661102if 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"]
     1103    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1104    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821105    add_packages("xpra.platform.win32")
    8831106    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"]
     1107
     1108    #this is where the win32 gi installer will put things:
     1109    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1110
     1111    #only add the cx_freeze specific options
     1112    #if we aren't just building the Cython bits with "build_ext":
     1113    if "build_ext" not in sys.argv:
     1114        #with cx_freeze, we don't use py_modules
     1115        del setup_options["py_modules"]
     1116        import cx_Freeze                            #@UnresolvedImport
     1117        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1118        CX5 = cx_Freeze.version>="5"
     1119        if CX5 and not hasattr(sys, "base_prefix"):
     1120            #workaround for broken sqlite hook with python 2.7, see:
     1121            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1122            sys.base_prefix = sys.prefix
     1123
     1124        #cx_freeze doesn't use "data_files"...
     1125        del setup_options["data_files"]
     1126        #it wants source files first, then where they are placed...
     1127        #one item at a time (no lists)
     1128        #all in its own structure called "include_files" instead of "data_files"...
     1129        def add_data_files(target_dir, files):
     1130            if verbose_ENABLED:
     1131                print("add_data_files(%s, %s)" % (target_dir, files))
     1132            assert type(target_dir)==str
     1133            assert type(files) in (list, tuple)
     1134            for f in files:
     1135                target_file = os.path.join(target_dir, os.path.basename(f))
     1136                data_files.append((f, target_file))
     1137
     1138        #pass a potentially nested dictionary representing the tree
     1139        #of files and directories we do want to include
     1140        #relative to gnome_include_path
     1141        def add_dir(base, defs):
     1142            if verbose_ENABLED:
     1143                print("add_dir(%s, %s)" % (base, defs))
     1144            if type(defs) in (list, tuple):
     1145                for sub in defs:
     1146                    if type(sub)==dict:
     1147                        add_dir(base, sub)
     1148                    else:
     1149                        assert type(sub)==str
     1150                        filename = os.path.join(gnome_include_path, base, sub)
     1151                        if os.path.exists(filename):
     1152                            add_data_files(base, [filename])
     1153                        else:
     1154                            print("Warning: missing '%s'" % filename)
     1155            else:
     1156                assert type(defs)==dict
     1157                for d, sub in defs.items():
     1158                    assert type(sub) in (dict, list, tuple)
     1159                    #recurse down:
     1160                    add_dir(os.path.join(base, d), sub)
     1161
     1162        #convenience method for adding GI libs and "typelib" and "gir":
     1163        def add_gi(*libs):
     1164            if verbose_ENABLED:
     1165                print("add_gi(%s)" % str(libs))
     1166            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1167            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1168
     1169        def add_DLLs(*dll_names):
     1170            try:
     1171                do_add_DLLs(*dll_names)
     1172            except Exception as e:
     1173                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1174                print(" %s" % e)
     1175                sys.exit(1)
     1176
     1177        def do_add_DLLs(*dll_names):
     1178            dll_names = list(dll_names)
     1179            dll_files = []
     1180            import re
     1181            version_re = re.compile("\-[0-9\.\-]+$")
     1182            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1183            if os.path.exists(gnome_include_path):
     1184                dirs.insert(0, gnome_include_path)
     1185            if verbose_ENABLED:
     1186                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1187            for d in dirs:
     1188                if not os.path.exists(d):
     1189                    continue
     1190                for x in os.listdir(d):
     1191                    dll_path = os.path.join(d, x)
     1192                    x = x.lower()
     1193                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1194                        continue
     1195                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1196                    if verbose_ENABLED:
     1197                        print("checking %s: %s" % (x, nameversion))
     1198                    m = version_re.search(nameversion)          #look for version part of filename
     1199                    if m:
     1200                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1201                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1202                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1203                    else:
     1204                        dll_version = ""                        #no version
     1205                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1206                    if dll_name in dll_names:
     1207                        #this DLL is on our list
     1208                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1209                        dll_files.append(dll_path)
     1210                        dll_names.remove(dll_name)
     1211            if len(dll_names)>0:
     1212                print("some DLLs could not be found:")
     1213                for x in dll_names:
     1214                    print(" - lib%s*.dll" % x)
     1215            add_data_files("", dll_files)
     1216
     1217        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1218        #(ie: "libatk-1.0-0.dll" -> "atk")
     1219        if sound_ENABLED or gtk3_ENABLED:
     1220            add_DLLs('gio', 'girepository', 'glib',
     1221                     'gnutls', 'gobject', 'gthread',
     1222                     'orc', 'stdc++',
     1223                     'winpthread',
     1224                     )
     1225        if gtk3_ENABLED:
     1226            add_DLLs('atk',
     1227                     'dbus', 'dbus-glib',
     1228                     'gdk', 'gdk_pixbuf', 'gtk',
     1229                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1230                     'harfbuzz', 'harfbuzz-gobject',
     1231                     'jasper', 'epoxy',
     1232                     'intl',
     1233                     'p11-kit',
     1234                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1235            #these are missing in newer aio installers (sigh):
     1236            do_add_DLLs('javascriptcoregtk')
     1237            if opengl_ENABLED:
     1238                do_add_DLLs('gdkglext', 'gtkglext')
     1239
     1240        if gtk3_ENABLED:
     1241            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1242            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1243                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1244            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1245                              "icons", "p11-kit", "xml",
     1246                              {"locale" : ["en"]},
     1247                              {"themes" : ["Default"]}
     1248                             ])
     1249        if gtk3_ENABLED or sound_ENABLED:
     1250            add_dir('lib', ["gio"])
     1251            packages.append("gi")
     1252            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1253                   "GObject-2.0")
     1254        if gtk3_ENABLED:
     1255            add_gi("Atk-1.0",
     1256                   "fontconfig-2.0", "freetype2-2.0",
     1257                   "GDesktopEnums-3.0", "Soup-2.4",
     1258                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0"
     1259                   "HarfBuzz-0.0",
     1260                   "Libproxy-1.0", "libxml2-2.0",
     1261                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1262                   "Rsvg-2.0",
     1263                   "win32-1.0")
     1264            if opengl_ENABLED:
     1265                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1266            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1267        if gtk3_ENABLED or server_ENABLED:
     1268            add_DLLs('sqlite3')
     1269
     1270        if gtk2_ENABLED:
     1271            add_dir('lib',      {
     1272                "gdk-pixbuf-2.0":    {
     1273                    "2.10.0"    :   {
     1274                        "loaders"   :
     1275                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp")]
     1276                        },
     1277                    },
     1278                })
     1279
     1280        if sound_ENABLED:
     1281            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1282            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1283                   "GstTag-1.0")
     1284            add_DLLs('gstreamer', 'orc-test')
     1285            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1286                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1287                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1288                      "basecamerabinsrc", "mpegts", "photography",
     1289                      ):
     1290                add_DLLs('gst%s' % p)
     1291            #DLLs needed by the plugins:
     1292            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1293            #add the gstreamer plugins we need:
     1294            GST_PLUGINS = ("app",
     1295                           #muxers:
     1296                           "gdp", "matroska", "ogg", "isomp4",
     1297                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1298                           "coreelements", "directsoundsink", "directsoundsrc", "wasapi",
     1299                           #codecs:
     1300                           "opus", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1301                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1302                           #untested: a52dec, voaacenc
     1303                           )
     1304            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1305            #END OF SOUND
     1306
     1307        if server_ENABLED:
     1308            #used by proxy server:
     1309            external_includes += ["multiprocessing", "setproctitle"]
     1310
     1311        external_includes += ["encodings"]
     1312        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1313        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1314        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1315        cx_freeze_options = {
     1316                            "includes"          : external_includes,
     1317                            "packages"          : packages,
     1318                            "include_files"     : data_files,
     1319                            "excludes"          : excludes,
     1320                            "include_msvcr"     : True,
     1321                            "bin_excludes"      : bin_excludes,
     1322                            }
     1323        if not CX5:
     1324            cx_freeze_options.update({
     1325                            "compressed"        : True,
     1326                            "create_shared_zip" : zip_ENABLED,
     1327                            })
     1328        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1329        executables = []
     1330        setup_options["executables"] = executables
     1331
     1332        def add_exe(script, icon, base_name, base="Console"):
     1333            kwargs = {}
     1334            if not CX5:
     1335                kwargs = {
     1336                    "compress"              : True,
     1337                    "copyDependentFiles"    : True,
     1338                    "appendScriptToExe"     : False,
     1339                    "appendScriptToLibrary" : True,
     1340                    }
     1341            executables.append(Executable(
     1342                        script                  = script,
     1343                        initScript              = None,
     1344                        #targetDir               = "dist",
     1345                        icon                    = "win32/%s" % icon,
     1346                        targetName              = "%s.exe" % base_name,
     1347                        base                    = base,
     1348                        **kwargs))
     1349
     1350        def add_console_exe(script, icon, base_name):
     1351            add_exe(script, icon, base_name)
     1352        def add_gui_exe(script, icon, base_name):
     1353            add_exe(script, icon, base_name, base="Win32GUI")
     1354        def add_service_exe(script, icon, base_name):
     1355            add_exe(script, icon, base_name, base="Win32Service")
     1356
     1357        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1358        if client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1359            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1360            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1361            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1362            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1363            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1364        if gtk2_ENABLED:
     1365            #these need porting..
     1366            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1367        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1368            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1369        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1370        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1371        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1372        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1373        if gtk2_ENABLED or gtk3_ENABLED:
     1374            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1375            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1376            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1377        if client_ENABLED or server_ENABLED:
     1378            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1379            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1380        if server_ENABLED:
     1381            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1382            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1383            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1384            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1385        if client_ENABLED:
     1386            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1387            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1388            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1389        if client_ENABLED:
     1390            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1391            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1392        if sound_ENABLED:
     1393            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1394            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1395            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1396            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1397            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1398        if opengl_ENABLED:
     1399            add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1400        if webcam_ENABLED:
     1401            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1402            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1403        if printing_ENABLED:
     1404            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1405            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1406            add_DLLs("pdfium")  #libpdfium.dll
     1407            if os.path.exists("C:\\Program Files\\Ghostgum\\gsview"):
     1408                GSVIEW = "C:\\Program Files\\Ghostgum\\gsview"
     1409            else:
     1410                GSVIEW = "C:\\Program Files (x86)\\Ghostgum\\gsview"
     1411            if os.path.exists("C:\\Program Files\\gs"):
     1412                GHOSTSCRIPT_PARENT_DIR = "C:\\Program Files\\gs"
     1413            else:
     1414                GHOSTSCRIPT_PARENT_DIR = "C:\\Program Files (x86)\\gs"
     1415            GHOSTSCRIPT = None
     1416            for x in reversed(sorted(os.listdir(GHOSTSCRIPT_PARENT_DIR))):
     1417                f = os.path.join(GHOSTSCRIPT_PARENT_DIR, x)
     1418                if os.path.isdir(f):
     1419                    GHOSTSCRIPT = os.path.join(f, "bin")
     1420                    print("found ghostscript: %s" % GHOSTSCRIPT)
     1421                    break
     1422            assert GHOSTSCRIPT is not None, "cannot find ghostscript installation directory in %s" % GHOSTSCRIPT_PARENT_DIR
     1423            add_data_files('gsview', glob.glob(GSVIEW+'\\*.*'))
     1424            add_data_files('gsview', glob.glob(GHOSTSCRIPT+'\\*.*'))
     1425        if nvenc_ENABLED:
     1426            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1427        if nvfbc_ENABLED:
     1428            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1429        if nvfbc_ENABLED or nvenc_ENABLED:
     1430            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1431
     1432        #FIXME: how do we figure out what target directory to use?
     1433        print("calling build_xpra_conf in-place")
     1434        #building etc files in-place:
     1435        build_xpra_conf(".")
     1436        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1437        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1438        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1439        #build minified html5 client in temporary build dir:
     1440        if "clean" not in sys.argv and html5_ENABLED:
     1441            install_html5(os.path.join(install, "www"))
     1442            for k,v in glob_recurse("build/www").items():
     1443                if (k!=""):
     1444                    k = os.sep+k
     1445                add_data_files('www'+k, v)
     1446
     1447    if client_ENABLED or server_ENABLED:
     1448        add_data_files('',      ['COPYING', 'README', 'win32/website.url'])
     1449        add_data_files('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*'))
     1450
     1451    if webcam_ENABLED:
     1452        add_data_files('',      ['win32\\DirectShow.tlb'])
     1453        add_modules("comtypes.gen.stdole", "comtypes.gen.DirectShowLib")
     1454
    9071455    remove_packages(*external_excludes)
     1456    external_includes.append("mmap")
    9081457    remove_packages(#not used on win32:
    909                     "mmap",
    9101458                    #we handle GL separately below:
    9111459                    "OpenGL", "OpenGL_accelerate",
     
    9131461                    "ctypes.macholib")
    9141462
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1463    if webcam_ENABLED:
     1464        external_includes.append("cv2")
     1465
     1466    if opengl_ENABLED:
    9161467        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1468        external_includes.append("numpy")
    9181469    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1470        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211471                        "pydoc")
    9221472
    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:
     1473    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1474    remove_packages("pygst", "gst", "gst.extend")
     1475
     1476    #add subset of PyOpenGL modules (only when installing):
     1477    if opengl_ENABLED and ("install_exe" in sys.argv or "install" in sys.argv):
    9291478        #for this hack to work, you must add "." to the sys.path
    9301479        #so python can load OpenGL from the install directory
    9311480        #(further complicated by the fact that "." is the "frozen" path...)
     1481        #but we re-add those two directories to the library.zip as part of the build script
    9321482        import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
     1483        print("*** copying PyOpenGL modules to %s ***" % install)
    9351484        for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
    9361485            module_dir = os.path.dirname(module.__file__ )
    9371486            try:
    9381487                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1488                    module_dir, os.path.join(install, module_name),
     1489                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411490                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1491            except Exception as e:
     1492                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441493                    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 
     1494
     1495    add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1496
     1497    #END OF win32
    9831498#*******************************************************************************
    9841499else:
    9851500    #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"]))
     1501    scripts += ["scripts/xpra", "scripts/xpra_launcher", "scripts/xpra_browser"]
     1502    add_data_files("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1", "man/xpra_browser.1"])
     1503    add_data_files("share/xpra",          ["README", "COPYING"])
     1504    add_data_files("share/xpra/icons",    glob.glob("icons/*"))
     1505    add_data_files("share/applications",  ["xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1506    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1507    add_data_files("share/icons",         ["xdg/xpra.png"])
     1508    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1509
     1510    #here, we override build and install so we can
     1511    #generate our /etc/xpra/xpra.conf
     1512    class build_override(build):
     1513        def run(self):
     1514            build.run(self)
     1515            self.run_command("build_conf")
     1516
     1517    class build_conf(build):
     1518        def run(self):
     1519            try:
     1520                build_base = self.distribution.command_obj['build'].build_base
     1521            except:
     1522                build_base = self.build_base
     1523            build_xpra_conf(build_base)
     1524
     1525    class install_data_override(install_data):
     1526        def run(self):
     1527            print("install_data_override: install_dir=%s" % self.install_dir)
     1528            if html5_ENABLED:
     1529                install_html5(os.path.join(self.install_dir, "share/xpra/www"))
     1530            install_data.run(self)
     1531
     1532            root_prefix = self.install_dir.rstrip("/")
     1533            if root_prefix.endswith("/usr"):
     1534                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1535            build_xpra_conf(root_prefix)
     1536
     1537            def copytodir(src, dst_dir, chmod=0o644):
     1538                #convert absolute paths:
     1539                if dst_dir.startswith("/"):
     1540                    dst_dir = root_prefix+dst_dir
     1541                else:
     1542                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1543                #make sure the target directory exists:
     1544                self.mkpath(dst_dir)
     1545                #generate the target filename:
     1546                filename = os.path.basename(src)
     1547                dst_file = os.path.join(dst_dir, filename)
     1548                #copy it
     1549                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1550                shutil.copyfile(src, dst_file)
     1551                if chmod:
     1552                    os.chmod(dst_file, chmod)
     1553
     1554            if printing_ENABLED and os.name=="posix":
     1555                #install "/usr/lib/cups/backend" with 0700 permissions:
     1556                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1557
     1558            if x11_ENABLED:
     1559                #install xpra_Xdummy if we need it:
     1560                xvfb_command = detect_xorg_setup()
     1561                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1562                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1563                #install xorg.conf, cuda.conf and nvenc.keys:
     1564                etc_xpra_files = ["xorg.conf"]
     1565                if nvenc_ENABLED:
     1566                    etc_xpra_files += ["cuda.conf", "nvenc.keys"]
     1567                for x in etc_xpra_files:
     1568                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1569
     1570            if pam_ENABLED:
     1571                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1572
     1573            #fedora and centos can use just "/lib/systemd/system" here
     1574            #but debian gets totally confused if we do...
     1575            systemd_dir = "/usr/lib/systemd/system"
     1576            if service_ENABLED:
     1577                #Linux init service:
     1578                if os.path.exists("/bin/systemctl"):
     1579                    copytodir("service/xpra.service", systemd_dir)
     1580                else:
     1581                    copytodir("service/xpra", "/etc/init.d")
     1582                if os.path.exists("/etc/sysconfig"):
     1583                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1584                elif os.path.exists("/etc/default"):
     1585                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1586            if sd_listen_ENABLED:
     1587                copytodir("service/xpra.socket", systemd_dir)
     1588
     1589
     1590    # add build_conf to build step
     1591    cmdclass.update({
     1592             'build'        : build_override,
     1593             'build_conf'   : build_conf,
     1594             'install_data' : install_data_override,
     1595             })
    9971596
    9981597    if OSX:
     1598        #pyobjc needs email.parser
     1599        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
    9991600        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001601        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011602        add_packages("xpra.platform.darwin")
     1603        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1604        #to support GStreamer 1.x we need this:
     1605        modules.append("importlib")
     1606        modules.append("xpra.scripts.gtk_info")
     1607        modules.append("xpra.scripts.show_webcam")
    10021608    else:
    10031609        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041610        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")
     1611        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1612        #not supported by all distros, but doesn't hurt to install them anyway:
     1613        for x in ("tmpfiles.d", "sysusers.d"):
     1614            add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
    10081615
    10091616    #gentoo does weird things, calls --no-compile with build *and* install
     
    10141621        def pkgconfig(*pkgs_options, **ekw):
    10151622            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))
    10331623
    10341624    if OSX and "py2app" in sys.argv:
     
    10451635        remove_packages(*external_excludes)
    10461636
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1637        try:
     1638            from xpra.src_info import REVISION
     1639        except:
     1640            REVISION = "unknown"
     1641        Plist = {
     1642            "CFBundleDocumentTypes" : {
     1643                "CFBundleTypeExtensions"    : ["Xpra"],
     1644                "CFBundleTypeName"          : "Xpra Session Config File",
     1645                "CFBundleName"              : "Xpra",
     1646                "CFBundleTypeRole"          : "Viewer",
     1647                },
     1648            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2017 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1649            "CFBundleIdentifier"            : "org.xpra.xpra",
     1650            }
    10531651        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541652        #so the make-app.sh script still has to hack around this problem.
     
    10651663            }
    10661664        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1665        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1666
     1667    if OSX:
     1668        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1669        #so instead we have to query each package seperately and merge the results:
     1670        def osx_pkgconfig(*pkgs_options, **ekw):
     1671            kw = dict(ekw)
     1672            for pkg in pkgs_options:
     1673                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1674                if pkg.lower().startswith("x"):
     1675                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1676                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1677                kw = exec_pkgconfig(pkg, **kw)
     1678                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1679            return kw
     1680
     1681        pkgconfig = osx_pkgconfig
    10681682
    10691683
    10701684if 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 
     1685    if WIN32 or OSX:
     1686        external_includes.append("websockify")
     1687        external_includes.append("numpy")
     1688        external_includes.append("ssl")
     1689        external_includes.append("_ssl")
     1690        if not PYTHON3:
     1691            external_includes.append("mimetypes")
     1692            external_includes.append("mimetools")
     1693            external_includes.append("BaseHTTPServer")
     1694
     1695
     1696if annotate_ENABLED:
     1697    from Cython.Compiler import Options
     1698    Options.annotate = True
    10761699
    10771700
    10781701#*******************************************************************************
    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")
     1702buffers_c = "xpra/buffers/buffers.c"
     1703memalign_c = "xpra/buffers/memalign.c"
     1704xxhash_c = "xpra/buffers/xxhash.c"
     1705membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1706
     1707add_packages("xpra.buffers")
     1708buffers_pkgconfig = pkgconfig(optimize=3)
     1709cython_add(Extension("xpra.buffers.membuf",
     1710            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1711
     1712
     1713toggle_packages(dbus_ENABLED, "xpra.dbus")
     1714toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1715toggle_packages(server_ENABLED or proxy_ENABLED or shadow_ENABLED, "xpra.server", "xpra.server.auth")
     1716toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1717toggle_packages(server_ENABLED, "xpra.server.window")
     1718toggle_packages(server_ENABLED and shadow_ENABLED, "xpra.server.shadow")
     1719toggle_packages(server_ENABLED or (client_ENABLED and gtk2_ENABLED), "xpra.clipboard")
     1720toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1721
     1722#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1723if dbus_ENABLED and server_ENABLED:
     1724    add_packages("xpra.server.dbus")
     1725
     1726if OSX:
     1727    quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1728    add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1729                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h",
     1730                    '-ObjC',
     1731                    '-mmacosx-version-min=10.10')
     1732    add_to_keywords(quartz_pkgconfig, 'extra_link_args',
     1733                    '-framework', 'Foundation',
     1734                    '-framework', 'AppKit',
     1735                    )
     1736    cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1737                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1738                language="objc",
     1739                **quartz_pkgconfig
     1740                ))
     1741
     1742monotonic_time_pkgconfig = pkgconfig()
     1743if not OSX and not WIN32:
     1744    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1745cython_add(Extension("xpra.monotonic_time",
     1746            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1747            **monotonic_time_pkgconfig
     1748            ))
     1749
     1750
     1751toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841752if x11_ENABLED:
    10851753    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1754    make_constants("xpra", "x11", "gtk2", "constants")
    10871755
    10881756    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981766                **pkgconfig("x11")
    10991767                ))
     1768    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1769                ["xpra/x11/bindings/posix_display_source.pyx"],
     1770                **pkgconfig("x11")
     1771                ))
     1772
    11001773    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011774                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041777    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051778                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1779                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071780                ))
    11081781
    11091782    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101783                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1784                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121785                ))
    11131786    cython_add(Extension("xpra.x11.bindings.ximage",
    11141787                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1788                **pkgconfig("x11", "xcomposite", "xdamage", "xext")
    11161789                ))
    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)
     1790if xinput_ENABLED:
     1791    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1792                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1793                **pkgconfig("x11", "xi")
    11221794                ))
    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)
     1795
     1796toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1797if gtk_x11_ENABLED:
     1798    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1799    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1800    if PYTHON3:
     1801        #GTK3 display source:
     1802        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1803                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1804                    **pkgconfig("gtk+-3.0")
     1805                    ))
     1806    else:
     1807        #below uses gtk/gdk:
     1808        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1809                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1810                    **pkgconfig(*PYGTK_PACKAGES)
     1811                    ))
     1812        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1813        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1814                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1815                    **pkgconfig(*GDK_BINDINGS_PACKAGES)
     1816                    ))
     1817
     1818if client_ENABLED and gtk3_ENABLED:
     1819    #cairo workaround:
     1820    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1821                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1822                **pkgconfig("pycairo")
    11271823                ))
    11281824
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1825if client_ENABLED or server_ENABLED:
     1826    add_packages("xpra.codecs.argb")
     1827    argb_pkgconfig = pkgconfig(optimize=3)
    11321828    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1829                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1830
     1831
     1832#build tests, but don't install them:
     1833toggle_packages(tests_ENABLED, "unit")
    11341834
    11351835
    11361836if bundle_tests_ENABLED:
    11371837    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1838    for k,v in glob_recurse("unit").items():
    11391839        if (k!=""):
    11401840            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:
     1841        add_data_files("unit"+k, v)
     1842
     1843#python-cryptography needs workarounds for bundling:
     1844if crypto_ENABLED and (OSX or WIN32):
     1845    external_includes.append("_ssl")
     1846    external_includes.append("cffi")
     1847    external_includes.append("_cffi_backend")
     1848    external_includes.append("cryptography")
     1849    external_includes.append("pkg_resources._vendor.packaging")
     1850    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1851    external_includes.append("pkg_resources._vendor.pyparsing")
     1852    add_modules("cryptography.hazmat.bindings._openssl")
     1853    add_modules("cryptography.hazmat.bindings._constant_time")
     1854    add_modules("cryptography.hazmat.bindings._padding")
     1855    add_modules("cryptography.hazmat.backends.openssl")
     1856    add_modules("cryptography.fernet")
     1857    if WIN32:
     1858        external_includes.append("appdirs")
     1859
     1860#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441861if client_ENABLED:
    11451862    add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1863toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471864toggle_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")
     1865toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1866toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501867toggle_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")
     1868toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1869toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1870if client_ENABLED and WIN32 and MINGW_PREFIX:
     1871    propsys_pkgconfig = pkgconfig()
     1872    if debug_ENABLED:
     1873        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1874    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1875    cython_add(Extension("xpra.platform.win32.propsys",
     1876                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1877                language="c++",
     1878                **propsys_pkgconfig))
     1879
     1880if client_ENABLED or server_ENABLED:
     1881    add_modules("xpra.codecs")
     1882toggle_packages(client_ENABLED or server_ENABLED, "xpra.keyboard")
     1883if client_ENABLED or server_ENABLED:
     1884    add_modules("xpra.scripts.config", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1885if server_ENABLED or proxy_ENABLED:
     1886    add_modules("xpra.scripts.server")
     1887if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1888    add_modules("xpra.scripts.gtk_info")
     1889
     1890toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1891#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1892#and try to include both gtk3 and gtk2, and fail hard..
     1893for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gtk_compat"):
     1894    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1895
     1896toggle_modules(sound_ENABLED, "xpra.sound")
     1897toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541898
    11551899toggle_packages(clipboard_ENABLED, "xpra.clipboard")
     
    11601904                ))
    11611905
    1162 if cyxor_ENABLED:
     1906toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1907if client_ENABLED or server_ENABLED:
    11631908    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641909                ["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")
     1910                **pkgconfig(optimize=3)))
     1911
     1912if server_ENABLED:
     1913    O3_pkgconfig = pkgconfig(optimize=3)
     1914    cython_add(Extension("xpra.server.cystats",
     1915                ["xpra/server/cystats.pyx"],
     1916                **O3_pkgconfig))
     1917    cython_add(Extension("xpra.server.window.region",
     1918                ["xpra/server/window/region.pyx"],
     1919                **O3_pkgconfig))
     1920    cython_add(Extension("xpra.server.window.motion",
     1921                ["xpra/server/window/motion.pyx"],
     1922                **O3_pkgconfig))
     1923
     1924if sd_listen_ENABLED:
     1925    sdp = pkgconfig("libsystemd")
     1926    cython_add(Extension("xpra.server.sd_listen",
     1927                ["xpra/server/sd_listen.pyx"],
     1928                **sdp))
     1929
     1930
    11781931toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11791932
     1933toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     1934if nvfbc_ENABLED:
     1935    nvfbc_pkgconfig = pkgconfig("nvfbc")
     1936    #add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     1937    platform = sys.platform.rstrip("0123456789")
     1938    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     1939                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     1940                         language="c++",
     1941                         **nvfbc_pkgconfig))
     1942
    11801943toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     1944toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     1945toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     1946
     1947if nvenc_ENABLED and cuda_kernels_ENABLED:
     1948    #find nvcc:
     1949    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     1950    if WIN32:
     1951        nvcc_exe = "nvcc.exe"
     1952        path_options = [
     1953                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\bin",
     1954                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\bin",
     1955                         ] + path_options
     1956    else:
     1957        nvcc_exe = "nvcc"
     1958        for v in ("-7.5", "-8.0", ""):
     1959            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     1960    options = [os.path.join(x, nvcc_exe) for x in path_options]
     1961    def which(cmd):
     1962        try:
     1963            code, out, _ = get_status_output(["which", cmd])
     1964            if code==0:
     1965                return out
     1966        except:
     1967            pass
     1968    #prefer the one we find on the $PATH, if any:
     1969    try:
     1970        v = which(nvcc_exe)
     1971        if v and (v not in options):
     1972            options.insert(0, v)
     1973    except:
     1974        pass
     1975    nvcc_versions = {}
     1976    for filename in options:
     1977        if not os.path.exists(filename):
     1978            continue
     1979        code, out, err = get_status_output([filename, "--version"])
     1980        if code==0:
     1981            vpos = out.rfind(", V")
     1982            if vpos>0:
     1983                version = out[vpos+3:].strip("\n")
     1984                version_str = " version %s" % version
     1985            else:
     1986                version = "0"
     1987                version_str = " unknown version!"
     1988            print("found CUDA compiler: %s%s" % (filename, version_str))
     1989            nvcc_versions[version] = filename
     1990    assert nvcc_versions, "cannot find nvcc compiler!"
     1991    #choose the most recent one:
     1992    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     1993    if len(nvcc_versions)>1:
     1994        print(" using version %s from %s" % (version, nvcc))
     1995    if WIN32:
     1996        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     1997        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     1998    #first compile the cuda kernels
     1999    #(using the same cuda SDK for both nvenc modules for now..)
     2000    #TODO:
     2001    # * compile directly to output directory instead of using data files?
     2002    # * detect which arches we want to build for? (does it really matter much?)
     2003    kernels = ("BGRA_to_NV12", "BGRA_to_YUV444")
     2004    for kernel in kernels:
     2005        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     2006        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     2007        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     2008            continue
     2009        reason = should_rebuild(cuda_src, cuda_bin)
     2010        if not reason:
     2011            continue
     2012        cmd = [nvcc,
     2013               '-fatbin',
     2014               #"-cubin",
     2015               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     2016               #"-gencode=arch=compute_50,code=sm_50",
     2017               #"-gencode=arch=compute_52,code=sm_52",
     2018               #"-gencode=arch=compute_52,code=compute_52",
     2019               "-c", cuda_src,
     2020               "-o", cuda_bin]
     2021        #GCC 6 uses C++11 by default:
     2022        if get_gcc_version()>=[6, 0]:
     2023            cmd.append("-std=c++11")
     2024        CL_VERSION = os.environ.get("CL_VERSION")
     2025        if CL_VERSION:
     2026            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     2027            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     2028            cmd += ["--machine", "32"]
     2029        if WIN32:
     2030            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     2031            #cmd += ["--input-drive-prefix", "/"]
     2032            #cmd += ["--dependency-drive-prefix", "/"]
     2033            cmd += ["-I%s" % os.path.abspath("win32")]
     2034        comp_code_options = [(30, 30), (35, 35)]
     2035        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     2036        if version!="0" and version<"7.5":
     2037            print("CUDA version %s is very unlikely to work")
     2038            print("try upgrading to version 7.5 or later")
     2039        if version>="7.5":
     2040            comp_code_options.append((50, 50))
     2041            comp_code_options.append((52, 52))
     2042            comp_code_options.append((53, 53))
     2043        if version>="8.0":
     2044            comp_code_options.append((60, 60))
     2045            comp_code_options.append((61, 61))
     2046            comp_code_options.append((62, 62))
     2047        for arch, code in comp_code_options:
     2048            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2049        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2050        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2051        c, stdout, stderr = get_status_output(cmd)
     2052        if c!=0:
     2053            print("Error: failed to compile CUDA kernel %s" % kernel)
     2054            print(stdout or "")
     2055            print(stderr or "")
     2056            sys.exit(1)
     2057    CUDA_BIN = "share/xpra/cuda"
     2058    if WIN32:
     2059        CUDA_BIN = "CUDA"
     2060    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2061
    11812062if 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))
     2063    nvencmodule = "nvenc"
     2064    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2065    #don't link against libnvidia-encode, we load it dynamically:
     2066    libraries = nvenc_pkgconfig.get("libraries", [])
     2067    if "nvidia-encode" in libraries:
     2068        libraries.remove("nvidia-encode")
     2069    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2070        #with gcc 6.2 on Fedora:
     2071        #xpra/codecs/nvenc/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2072        #xpra/codecs/nvenc/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2073        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2074        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2075    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2076                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2077                         **nvenc_pkgconfig))
    11872078
    11882079toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892080if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2081    x264_pkgconfig = pkgconfig("x264")
     2082    if get_gcc_version()>=[6, 0]:
     2083        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912084    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922085                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2086                **x264_pkgconfig))
    11942087
    11952088toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962089if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2090    x265_pkgconfig = pkgconfig("x265")
    11982091    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992092                ["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))
     2093                **x265_pkgconfig))
     2094
     2095toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2096if pillow_ENABLED:
     2097    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
     2098
     2099toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2100if jpeg_ENABLED:
     2101    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2102    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2103                ["xpra/codecs/jpeg/encoder.pyx"],
     2104                **jpeg_pkgconfig))
     2105    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2106                ["xpra/codecs/jpeg/decoder.pyx"],
     2107                **jpeg_pkgconfig))
     2108
     2109#swscale and avcodec2 use libav_common/av_log:
     2110libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2111toggle_packages(libav_common, "xpra.codecs.libav_common")
     2112if libav_common:
     2113    avutil_pkgconfig = pkgconfig("avutil")
     2114    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2115                ["xpra/codecs/libav_common/av_log.pyx"],
     2116                **avutil_pkgconfig))
     2117
    12162118
    12172119toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182120if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2121    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil")
    12202122    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 
     2123                ["xpra/codecs/dec_avcodec2/decoder.pyx"],
     2124                **avcodec2_pkgconfig))
     2125
     2126
     2127toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2128if csc_libyuv_ENABLED:
     2129    libyuv_pkgconfig = pkgconfig("libyuv")
     2130    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2131                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2132                language="c++",
     2133                **libyuv_pkgconfig))
    12242134
    12252135toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262136if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2137    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292138    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))
     2139                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2140                **swscale_pkgconfig))
     2141
    12392142
    12402143toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412144if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2145    vpx_pkgconfig = pkgconfig("vpx")
    12432146    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))
     2147                ["xpra/codecs/vpx/encoder.pyx"],
     2148                **vpx_pkgconfig))
    12462149    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))
     2150                ["xpra/codecs/vpx/decoder.pyx"],
     2151                **vpx_pkgconfig))
     2152
     2153toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2154if enc_ffmpeg_ENABLED:
     2155    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2156    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2157                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2158                **ffmpeg_pkgconfig))
     2159
     2160toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2161if v4l2_ENABLED:
     2162    v4l2_pkgconfig = pkgconfig()
     2163    #fuly warning: cython makes this difficult,
     2164    #we have to figure out if "device_caps" exists in the headers:
     2165    ENABLE_DEVICE_CAPS = False
     2166    if os.path.exists("/usr/include/linux/videodev2.h"):
     2167        hdata = open("/usr/include/linux/videodev2.h").read()
     2168        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2169    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2170    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2171    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2172                ["xpra/codecs/v4l2/pusher.pyx"],
     2173                **v4l2_pkgconfig))
    12622174
    12632175
    12642176toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2177toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652178if 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")
     2179    bencode_pkgconfig = pkgconfig(optimize=not debug_ENABLED)
    12722180    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732181                ["xpra/net/bencode/cython_bencode.pyx"],
    12742182                **bencode_pkgconfig))
    12752183
     2184if vsock_ENABLED:
     2185    vsock_pkgconfig = pkgconfig()
     2186    cython_add(Extension("xpra.net.vsock",
     2187                ["xpra/net/vsock.pyx"],
     2188                **vsock_pkgconfig))
     2189
     2190if pam_ENABLED:
     2191    pam_pkgconfig = pkgconfig()
     2192    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2193    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2194    cython_add(Extension("xpra.server.pam",
     2195                ["xpra/server/pam.pyx"],
     2196                **pam_pkgconfig))
     2197
    12762198
    12772199if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2200    from Cython.Build import cythonize
     2201    #this causes Cython to fall over itself:
     2202    #gdb_debug=debug_ENABLED
     2203    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792204if cmdclass:
    12802205    setup_options["cmdclass"] = cmdclass
     
    12832208
    12842209
    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 
    12932210def main():
    12942211    if OSX or WIN32 or debug_ENABLED:
     2212        print()
    12952213        print("setup options:")
     2214        if verbose_ENABLED:
     2215            print("setup_options=%s" % (setup_options,))
     2216        try:
     2217            from xpra.util import repr_ellipsized as pv
     2218        except:
     2219            def pv(v):
     2220                return str(v)
    12962221        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2222            print_option("", k, pv(v))
    12982223        print("")
    12992224
Note: See TracChangeset for help on using the changeset viewer.