xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r19666  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2018 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"
    2029
    2130print(" ".join(sys.argv))
    2231
     32#*******************************************************************************
     33# build options, these may get modified further down..
     34#
    2335import xpra
    24 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
    25 
    26 WIN32 = sys.platform.startswith("win")
     36data_files = []
     37modules = []
     38packages = []       #used by py2app
     39excludes = []       #only used by cx_freeze on win32
     40ext_modules = []
     41cmdclass = {}
     42scripts = []
     43description = "multi-platform screen and application forwarding system"
     44long_description = "Xpra is a multi platform persistent remote display server and client for " + \
     45            "forwarding applications and desktop screens. Also known as 'screen for X11'."
     46url = "http://xpra.org/"
     47
     48
     49XPRA_VERSION = xpra.__version__         #@UndefinedVariable
     50setup_options = {
     51                 "name"             : "xpra",
     52                 "version"          : XPRA_VERSION,
     53                 "license"          : "GPLv2+",
     54                 "author"           : "Antoine Martin",
     55                 "author_email"     : "antoine@devloop.org.uk",
     56                 "url"              : url,
     57                 "download_url"     : "http://xpra.org/src/",
     58                 "description"      : description,
     59                 "long_description" : long_description,
     60                 "data_files"       : data_files,
     61                 "py_modules"       : modules,
     62                 }
     63
     64WIN32 = sys.platform.startswith("win") or sys.platform.startswith("msys")
    2765OSX = sys.platform.startswith("darwin")
    28 
    29 
     66LINUX = sys.platform.startswith("linux")
     67NETBSD = sys.platform.startswith("netbsd")
     68FREEBSD = sys.platform.startswith("freebsd")
     69OPENBSD = sys.platform.startswith("openbsd")
     70
     71PYTHON3 = sys.version_info[0] == 3
     72POSIX = os.name=="posix"
     73
     74
     75if "pkg-info" in sys.argv:
     76    with open("PKG-INFO", "wb") as f:
     77        pkg_info_values = setup_options.copy()
     78        pkg_info_values.update({
     79                                "metadata_version"  : "1.1",
     80                                "summary"           :  description,
     81                                "home_page"         : url,
     82                                })
     83        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     84                  "Author", "Author-email", "License", "Download-URL", "Description"):
     85            v = pkg_info_values[k.lower().replace("-", "_")]
     86            f.write(b"%s: %s\n" % (k, v))
     87    sys.exit(0)
     88
     89
     90print("Xpra version %s" % XPRA_VERSION)
    3091#*******************************************************************************
    3192# Most of the options below can be modified on the command line
     
    3394# only the default values are specified here:
    3495#*******************************************************************************
     96from xpra.os_util import get_status_output, getUbuntuVersion, PYTHON3, BITS, \
     97    is_Ubuntu, is_Debian, is_Raspbian, is_Fedora, is_CentOS
     98
     99PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     100has_pkg_config = False
     101if PKG_CONFIG:
     102    v = get_status_output([PKG_CONFIG, "--version"])
     103    has_pkg_config = v[0]==0 and v[1]
     104    if has_pkg_config:
     105        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     106    else:
     107        print("WARNING: pkg-config not found!")
     108
     109from Cython.Compiler.Version import version as cython_version
     110
     111for arg in list(sys.argv):
     112    if arg.startswith("--pkg-config-path="):
     113        pcp = arg[len("--pkg-config-path="):]
     114        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     115        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     116        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     117        sys.argv.remove(arg)
     118
     119def no_pkgconfig(*_pkgs_options, **_ekw):
     120    return {}
     121
    35122def 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 
     123    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     124    return get_status_output(cmd)[0]==0
     125
     126def pkg_config_version(req_version, pkgname):
     127    cmd = [PKG_CONFIG, "--modversion", pkgname]
     128    r, out, _ = get_status_output(cmd)
     129    if r!=0 or not out:
     130        return False
     131    from distutils.version import LooseVersion
     132    return LooseVersion(out)>=LooseVersion(req_version)
     133
     134def is_RH():
     135    try:
     136        with open("/etc/redhat-release", mode='rb') as f:
     137            data = f.read()
     138        return data.startswith("CentOS") or data.startswith("RedHat")
     139    except:
     140        pass
     141    return False
     142
     143DEFAULT = True
     144if "--minimal" in sys.argv:
     145    sys.argv.remove("--minimal")
     146    DEFAULT = False
     147
     148from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     149shadow_ENABLED = SHADOW_SUPPORTED and DEFAULT
     150server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and DEFAULT
     151rfb_ENABLED = server_ENABLED
     152service_ENABLED = LINUX and server_ENABLED
     153sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd") and (not is_Ubuntu() or getUbuntuVersion()>[16, 4])
     154proxy_ENABLED  = DEFAULT
     155client_ENABLED = DEFAULT
     156
     157x11_ENABLED = DEFAULT and not WIN32 and not OSX
     158xinput_ENABLED = x11_ENABLED
     159uinput_ENABLED = x11_ENABLED
     160dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     161gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     162gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     163gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     164opengl_ENABLED = DEFAULT and client_ENABLED
     165html5_ENABLED = DEFAULT
     166html5_gzip_ENABLED = DEFAULT
     167html5_brotli_ENABLED = DEFAULT
     168minify_ENABLED = html5_ENABLED
     169pam_ENABLED = DEFAULT and (server_ENABLED or proxy_ENABLED) and POSIX and not OSX and (os.path.exists("/usr/include/pam/pam_misc.h") or os.path.exists("/usr/include/security/pam_misc.h"))
     170
     171xdg_open_ENABLED        = LINUX and DEFAULT
     172netdev_ENABLED          = LINUX and DEFAULT
     173vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     174bencode_ENABLED         = DEFAULT
     175cython_bencode_ENABLED  = DEFAULT
     176clipboard_ENABLED       = DEFAULT
     177Xdummy_ENABLED          = None          #None means auto-detect
     178Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     179if WIN32 or OSX:
     180    Xdummy_ENABLED = False
     181sound_ENABLED           = DEFAULT
     182printing_ENABLED        = DEFAULT
     183crypto_ENABLED          = DEFAULT
     184mdns_ENABLED            = DEFAULT
     185
     186enc_proxy_ENABLED       = DEFAULT
     187enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     188enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     189pillow_ENABLED          = DEFAULT
     190webp_ENABLED            = DEFAULT and pkg_config_version("0.5", "libwebp")
     191jpeg_ENABLED            = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     192vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     193enc_ffmpeg_ENABLED      = pkg_config_version("58.18", "libavcodec")
     194#opencv currently broken on 32-bit windows (crashes on load):
     195webcam_ENABLED          = DEFAULT and not OSX and (not WIN32 or BITS==64)
     196v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD and not OPENBSD)
     197#ffmpeg 3.1 or later is required
     198dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     199csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     200nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("7", "nvenc")
     201nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     202cuda_kernels_ENABLED    = DEFAULT
     203cuda_rebuild_ENABLED    = DEFAULT
     204csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     205example_ENABLED         = DEFAULT
     206
     207#Cython / gcc / packaging build options:
     208annotate_ENABLED        = True
    86209warn_ENABLED            = True
    87210strict_ENABLED          = True
    88 PIC_ENABLED             = True
     211PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89212debug_ENABLED           = False
    90213verbose_ENABLED         = False
    91214bundle_tests_ENABLED    = False
     215tests_ENABLED           = False
     216rebuild_ENABLED         = True
    92217
    93218#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",
     219SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     220            "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     221            "vpx", "webp", "pillow", "jpeg",
     222            "v4l2",
     223            "dec_avcodec2", "csc_swscale",
     224            "csc_libyuv",
     225            "bencode", "cython_bencode", "vsock", "netdev", "mdns",
    104226            "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")
     227            "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     228            "gtk_x11", "service",
     229            "gtk2", "gtk3", "example",
     230            "html5", "minify", "html5_gzip", "html5_brotli",
     231            "pam", "xdg_open",
     232            "sound", "opengl", "printing", "webcam",
     233            "rebuild",
     234            "annotate", "warn", "strict",
     235            "shadow", "proxy", "rfb",
     236            "debug", "PIC",
     237            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     238if WIN32:
     239    SWITCHES.append("zip")
     240    zip_ENABLED = True
    109241HELP = "-h" in sys.argv or "--help" in sys.argv
    110242if HELP:
     
    120252            default_str = "auto-detect"
    121253        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     254    print("  --pkg-config-path=PATH")
     255    print("  --rpath=PATH")
    122256    sys.exit(0)
    123257
     258install = "dist"
     259rpath = None
     260ssl_cert = None
     261ssl_key = None
     262minifier = None
    124263filtered_args = []
    125264for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     265    matched = False
     266    for x in ("rpath", "ssl-cert", "ssl-key", "install"):
     267        varg = "--%s=" % x
     268        if arg.startswith(varg):
     269            value = arg[len(varg):]
     270            globals()[x.replace("-", "_")] = value
     271            #remove these arguments from sys.argv,
     272            #except for --install=PATH
     273            matched = x!="install"
     274            break
     275    if matched:
    129276        continue
    130     matched = False
    131277    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     278        with_str = "--with-%s" % x
     279        without_str = "--without-%s" % x
     280        if arg.startswith(with_str+"="):
     281            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     282            matched = True
     283            break
     284        elif arg==with_str:
    133285            vars()["%s_ENABLED" % x] = True
    134286            matched = True
    135287            break
    136         elif arg=="--without-%s" % x:
     288        elif arg==without_str:
    137289            vars()["%s_ENABLED" % x] = False
    138290            matched = True
     
    145297    for x in SWITCHES:
    146298        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)
     299    print("build switches:")
     300    for k in sorted(SWITCHES):
     301        v = switches_info[k]
     302        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150303
    151304    #sanity check the flags:
     
    153306        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154307        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
    158     if shadow_ENABLED and not server_ENABLED:
    159         print("Warning: shadow requires server to be enabled!")
    160         shadow_ENABLED = False
    161     if cymaths_ENABLED and not server_ENABLED:
    162         print("Warning: cymaths requires server to be enabled!")
    163         cymaths_ENABLED = False
    164308    if x11_ENABLED and WIN32:
    165309        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:
     310    if gtk_x11_ENABLED and not x11_ENABLED:
     311        print("Error: you must enable x11 to support gtk_x11!")
     312        exit(1)
     313    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167314        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)
     315    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     316        print("Warning: you probably want to build at least the client or server!")
     317    if DEFAULT and not pillow_ENABLED:
     318        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     319    if minify_ENABLED:
     320        r = get_status_output(["uglifyjs", "--version"])[0]
     321        if r==0:
     322            minifier = "uglifyjs"
     323        else:
     324            print("Warning: uglifyjs failed and return %i" % r)
     325            try:
     326                import yuicompressor
     327                assert yuicompressor
     328                minifier = "yuicompressor"
     329            except ImportError as e:
     330                print("Warning: yuicompressor module not found, cannot minify")
     331                minify_ENABLED = False
     332    if not enc_x264_ENABLED and not vpx_ENABLED:
     333        print("Warning: no x264 and no vpx support!")
     334        print(" you should enable at least one of these two video encodings")
    174335
    175336
    176337#*******************************************************************************
    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",
     338# default sets:
     339
     340external_includes = ["hashlib",
    204341                     "ctypes", "platform"]
     342
     343
     344if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     345    external_includes += ["gi"]
     346elif gtk2_ENABLED or x11_ENABLED:
     347    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205348
    206349external_excludes = [
     
    212355                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213356                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
    218                     "distutils", "setuptools", "doctest"
     357                    "curses", "pdb",
     358                    "tty",
     359                    "setuptools", "doctest"
    219360                    ]
    220 
     361if not html5_ENABLED and not crypto_ENABLED:
     362    external_excludes += ["ssl", "_ssl"]
     363if not html5_ENABLED:
     364    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     365
     366if not client_ENABLED and not server_ENABLED:
     367    excludes += ["PIL"]
     368if not dbus_ENABLED:
     369    excludes += ["dbus"]
    221370
    222371
    223372#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     373#for distutils / py2app and cx_freeze
    225374#use the following functions, which should get the right
    226375#data in the global variables "packages", "modules" and "excludes"
     
    253402
    254403def add_modules(*mods):
     404    def add(v):
     405        global modules
     406        if v not in modules:
     407            modules.append(v)
     408    do_add_modules(add, *mods)
     409
     410def do_add_modules(op, *mods):
    255411    """ adds the packages and any .py module found in the packages to the "modules" list
    256412    """
    257413    global modules
    258414    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     415        #ugly path stripping:
     416        if x.startswith("./"):
     417            x = x[2:]
     418        if x.endswith(".py"):
     419            x = x[:-3]
     420            x = x.replace("/", ".") #.replace("\\", ".")
    261421        pathname = os.path.sep.join(x.split("."))
     422        #is this a file module?
     423        f = "%s.py" % pathname
     424        if os.path.exists(f) and os.path.isfile(f):
     425            op(x)
    262426        if os.path.exists(pathname) and os.path.isdir(pathname):
    263427            #add all file modules found in this directory
    264428            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     429                #make sure we only include python files,
     430                #and ignore eclipse copies
     431                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266432                    fname = os.path.join(pathname, f)
    267433                    if os.path.isfile(fname):
    268434                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     435                        op(modname)
    270436
    271437def toggle_packages(enabled, *module_names):
     
    275441        remove_packages(*module_names)
    276442
     443def toggle_modules(enabled, *module_names):
     444    if enabled:
     445        def op(v):
     446            global modules
     447            if v not in modules:
     448                modules.append(v)
     449        do_add_modules(op, *module_names)
     450    else:
     451        remove_packages(*module_names)
     452
     453
    277454#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 
     455add_modules("xpra", "xpra.platform", "xpra.net")
     456add_modules("xpra.scripts.main")
     457
     458
     459def add_data_files(target_dir, files):
     460    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     461    assert type(target_dir)==str
     462    assert type(files) in (list, tuple)
     463    data_files.append((target_dir, files))
     464
     465
     466#for pretty printing of options:
     467def print_option(prefix, k, v):
     468    if type(v)==dict:
     469        print("%s* %s:" % (prefix, k))
     470        for kk,vv in v.items():
     471            print_option(" "+prefix, kk, vv)
     472    else:
     473        print("%s* %s=%s" % (prefix, k, v))
    284474
    285475#*******************************************************************************
    286476# Utility methods for building with Cython
    287477def cython_version_check(min_version):
    288     try:
    289         from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
    291         sys.exit("ERROR: Cannot find Cython: %s" % e)
    292478    from distutils.version import LooseVersion
    293479    if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
     
    297483                 % (cython_version, ".".join([str(part) for part in min_version])))
    298484
    299 def cython_add(extension, min_version=(0, 14, 0)):
     485def cython_add(extension, min_version=(0, 19)):
    300486    #gentoo does weird things, calls --no-compile with build *and* install
    301487    #then expects to find the cython modules!? ie:
     
    303489    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304490        return
    305     global ext_modules, cmdclass
    306491    cython_version_check(min_version)
    307492    from Cython.Distutils import build_ext
    308493    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     494    global cmdclass
     495    cmdclass['build_ext'] = build_ext
     496
     497def insert_into_keywords(kw, key, *args):
     498    values = kw.setdefault(key, [])
     499    for arg in args:
     500        values.insert(0, arg)
    310501
    311502def add_to_keywords(kw, key, *args):
     
    319510
    320511
     512def checkdirs(*dirs):
     513    for d in dirs:
     514        if not os.path.exists(d) or not os.path.isdir(d):
     515            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     516
    321517PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
     518#override the pkgconfig file,
     519#we don't need to link against any of these:
     520gtk2_ignored_tokens=[("-l%s" % x) for x in
     521                     ["fontconfig", "freetype", "cairo",
     522                      "atk-1.0", "pangoft2-1.0", "pango-1.0", "pangocairo-1.0",
     523                      "gio-2.0", "gdk_pixbuf-2.0"]]
    322524
    323525GCC_VERSION = []
     
    326528    if len(GCC_VERSION)==0:
    327529        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:
     530        r, _, err = get_status_output(cmd)
     531        if r==0:
    332532            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     533            for line in err.splitlines():
    334534                if line.startswith(V_LINE):
    335535                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343543    return GCC_VERSION
    344544
    345 def make_constants_pxi(constants_path, pxi_path):
     545def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346546    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):
     547    with open(constants_path) as f:
     548        for line in f:
     549            data = line.split("#", 1)[0].strip()
     550            # data can be empty ''...
     551            if not data:
     552                continue
     553            # or a pair like 'cFoo "Foo"'...
     554            elif len(data.split()) == 2:
     555                (pyname, cname) = data.split()
     556                constants.append((pyname, cname))
     557            # or just a simple token 'Foo'
     558            else:
     559                constants.append(data)
     560
     561    with open(pxi_path, "w") as out:
     562        if constants:
     563            out.write("cdef extern from *:\n")
     564            ### Apparently you can't use | on enum's?!
     565            # out.write("    enum MagicNumbers:\n")
     566            # for const in constants:
     567            #     if isinstance(const, tuple):
     568            #         out.write('        %s %s\n' % const)
     569            #     else:
     570            #         out.write('        %s\n' % (const,))
     571            for const in constants:
     572                if isinstance(const, tuple):
     573                    out.write('    unsigned int %s %s\n' % const)
     574                else:
     575                    out.write('    unsigned int %s\n' % (const,))
     576
     577            out.write("constants = {\n")
     578            for const in constants:
     579                if isinstance(const, tuple):
     580                    pyname = const[0]
     581                else:
     582                    pyname = const
     583                out.write('    "%s": %s,\n' % (pyname, pyname))
     584            out.write("}\n")
     585            if kwargs:
     586                out.write("\n\n")
     587
     588        if kwargs:
     589            for k, v in kwargs.items():
     590                out.write('DEF %s = %s\n' % (k, v))
     591
     592
     593def should_rebuild(src_file, bin_file):
     594    if not os.path.exists(bin_file):
     595        return "no file"
     596    elif rebuild_ENABLED:
     597        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     598            return "binary file out of date"
     599        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     600            return "newer build file"
     601    return None
     602
     603def make_constants(*paths, **kwargs):
    384604    base = os.path.join(os.getcwd(), *paths)
    385605    constants_file = "%s.txt" % base
    386     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"
     606    try:
     607        pxi_file = kwargs.pop("pxi_file")
     608    except KeyError:
     609        pxi_file = "%s.pxi" % base
     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    optimize = kw.pop("optimize", None)
     621    if optimize and not debug_ENABLED:
     622        if type(optimize)==bool:
     623            optimize = int(optimize)*3
     624        add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     625    ignored_flags = kw.pop("ignored_flags", [])
     626    ignored_tokens = kw.pop("ignored_tokens", [])
     627
     628    #for distros that don't patch distutils,
     629    #we have to add the python cflags:
     630    if not (is_Fedora() or is_Debian() or is_CentOS()):
     631        import shlex
     632        import sysconfig
     633        for x in shlex.split(sysconfig.get_config_var('CFLAGS') or ''):
     634            add_to_keywords(kw, 'extra_compile_args', x)
     635
     636    def add_tokens(s, extra="extra_link_args", extra_map={"-W" : "extra_compile_args"}):
     637        if not s:
     638            return
     639        flag_map = {'-I': 'include_dirs',
     640                    '-L': 'library_dirs',
     641                    '-l': 'libraries'}
     642        for token in s.split():
     643            if token in ignored_tokens:
     644                pass
     645            elif token[:2] in ignored_flags:
     646                pass
     647            elif token[:2] in flag_map:
     648                if len(token)>2:
     649                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     650                else:
     651                    print("Warning: invalid token '%s'" % token)
     652            else:
     653                extra_name = extra_map.get(token, extra)
     654                add_to_keywords(kw, extra_name, token)
     655
    424656    if len(pkgs_options)>0:
    425657        package_names = []
     
    438670            for option in options:
    439671                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:
     672                r, _, _ = get_status_output(cmd)
     673                if r==0:
    443674                    valid_option = option
    444675                    break
    445676            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     677                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)")))
    447678            package_names.append(valid_option)
    448679        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450         flag_map = {'-I': 'include_dirs',
    451                     '-L': 'library_dirs',
    452                     '-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))
     680            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     681        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     682        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     683        if r!=0:
     684            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     685        add_tokens(pkg_config_out)
    468686    if warn_ENABLED:
    469687        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470688        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471689    if strict_ENABLED:
    472         #these are almost certainly real errors since our code is "clean":
    473         if get_gcc_version()>=[4, 4]:
    474             eifd = "-Werror=implicit-function-declaration"
     690        if os.environ.get("CC", "").find("clang")>=0:
     691            #clang emits too many warnings with cython code,
     692            #so we can't enable Werror without turning off some warnings:
     693            #this list of flags should allow clang to build the whole source tree,
     694            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     695            #(un)commenting other switches.
     696            eifd = ["-Werror",
     697                    #"-Wno-unneeded-internal-declaration",
     698                    #"-Wno-unknown-attributes",
     699                    #"-Wno-unused-function",
     700                    #"-Wno-self-assign",
     701                    #"-Wno-sometimes-uninitialized",
     702                    #cython adds rpath to the compilation command??
     703                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     704                    "-Wno-deprecated-register",
     705                    "-Wno-unused-command-line-argument",
     706                    ]
     707        elif get_gcc_version()>=[4, 4]:
     708            eifd = ["-Werror"]
     709            if is_Debian() or is_Ubuntu() or is_Raspbian():
     710                #needed on Debian and Ubuntu to avoid this error:
     711                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     712                eifd.append("-Wno-error=strict-prototypes")
     713            if NETBSD:
     714                #see: http://trac.cython.org/ticket/395
     715                eifd += ["-fno-strict-aliasing"]
     716            elif FREEBSD:
     717                eifd += ["-Wno-error=unused-function"]
    475718        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     719            #older versions of OSX ship an old gcc,
     720            #not much we can do with this:
     721            eifd = []
     722        for eif in eifd:
     723            add_to_keywords(kw, 'extra_compile_args', eif)
    478724    if PIC_ENABLED:
    479725        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481727        add_to_keywords(kw, 'extra_compile_args', '-g')
    482728        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     729        if get_gcc_version()>=[4, 8]:
    485730            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486731            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     732    if rpath and kw.get("libraries"):
     733        insert_into_keywords(kw, "library_dirs", rpath)
     734        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
     735    add_tokens(os.environ.get("CFLAGS"), "extra_compile_args", {})
     736    add_tokens(os.environ.get("LDFLAGS"), "extra_link_args", {})
    487737    #add_to_keywords(kw, 'include_dirs', '.')
    488738    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     739        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490740    return kw
     741pkgconfig = exec_pkgconfig
    491742
    492743
    493744#*******************************************************************************
    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()
     745
     746
     747def get_base_conf_dir(install_dir, stripbuildroot=True):
     748    #in some cases we want to strip the buildroot (to generate paths in the config file)
     749    #but in other cases we want the buildroot path (when writing out the config files)
     750    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     751    #this is a bit hackish, but I can't think of a better way of detecting it
     752    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     753    dirs = (install_dir or sys.prefix).split(os.path.sep)
     754    if install_dir and stripbuildroot:
     755        pkgdir = os.environ.get("pkgdir")
     756        if "debian" in dirs and "tmp" in dirs:
     757            #ugly fix for stripping the debian tmp dir:
     758            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     759            while "tmp" in dirs:
     760                dirs = dirs[dirs.index("tmp")+1:]
     761        elif "debian" in dirs:
     762            #same for recent debian versions:
     763            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     764            i = dirs.index("debian")
     765            if dirs[i+1] == "xpra":
     766                dirs = dirs[i+2:]
     767        elif "BUILDROOT" in dirs:
     768            #strip rpm style build root:
     769            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     770            dirs = dirs[dirs.index("BUILDROOT")+2:]
     771        elif pkgdir and install_dir.startswith(pkgdir):
     772            #arch build dir:
     773            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     774        elif "usr" in dirs:
     775            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     776            #assume "/usr" or "/usr/local" is the build root
     777            while "usr" in dirs[1:]:
     778                dirs = dirs[dirs[1:].index("usr")+1:]
     779        elif "image" in dirs:
     780            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     781            while "image" in dirs:
     782                dirs = dirs[dirs.index("image")+2:]
     783    #now deal with the fact that "/etc" is used for the "/usr" prefix
     784    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     785    if dirs and dirs[-1]=="usr":
     786        dirs = dirs[:-1]
     787    #is this an absolute path?
     788    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     789        #ie: ["/", "usr"] or ["/", "usr", "local"]
     790        dirs.insert(0, os.path.sep)
     791    return dirs
     792
     793def get_conf_dir(install_dir, stripbuildroot=True):
     794    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     795    dirs.append("etc")
     796    dirs.append("xpra")
     797    return os.path.join(*dirs)
     798
     799def detect_xorg_setup(install_dir=None):
     800    from xpra.scripts import config
     801    config.debug = config.warn
     802    conf_dir = get_conf_dir(install_dir)
     803    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     804
     805def build_xpra_conf(install_dir):
     806    #generates an actual config file from the template
     807    xvfb_command = detect_xorg_setup(install_dir)
     808    from xpra.platform.features import DEFAULT_ENV
     809    def bstr(b):
     810        if b is None:
     811            return "auto"
     812        return ["no", "yes"][int(b)]
     813    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     814    conf_dir = get_conf_dir(install_dir)
     815    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     816    from xpra.platform.paths import get_socket_dirs
     817    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, get_default_pulseaudio_command, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     818    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     819    socket_dirs = get_socket_dirs()
     820    if WIN32:
     821        bind = "Main"
    533822    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()
     823        if os.getuid()>0:
     824            #remove any paths containing the uid,
     825            #osx uses /var/tmp/$UID-Xpra,
     826            #but this should not be included in the default config for all users!
     827            #(the buildbot's uid!)
     828            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     829        bind = "auto"
     830    #FIXME: we should probably get these values from the default config instead
     831    pdf, postscript = "", ""
     832    if POSIX and printing_ENABLED:
     833        try:
     834            if "/usr/sbin" not in sys.path:
     835                sys.path.append("/usr/sbin")
     836            from xpra.platform.pycups_printing import get_printer_definition
     837            print("probing cups printer definitions")
     838            pdf = get_printer_definition("pdf")
     839            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     840            print("pdf=%s, postscript=%s" % (pdf, postscript))
     841        except Exception as e:
     842            print("could not probe for pdf/postscript printers: %s" % e)
     843    def pretty_cmd(cmd):
     844        return " ".join(cmd)
     845    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     846    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     847    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     848    #no python-avahi on RH / CentOS, need dbus module on *nix:
     849    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     850    SUBS = {
     851            'xvfb_command'          : pretty_cmd(xvfb_command),
     852            'ssh_command'           : DEFAULT_SSH_COMMAND,
     853            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     854            'remote_logging'        : "both",
     855            'start_env'             : start_env,
     856            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     857            'pulseaudio_command'    : pretty_cmd(get_default_pulseaudio_command()),
     858            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     859            'conf_dir'              : conf_dir,
     860            'bind'                  : bind,
     861            'ssl_cert'              : ssl_cert or "",
     862            'ssl_key'               : ssl_key or "",
     863            'systemd_run'           : get_default_systemd_run(),
     864            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     865            'log_dir'               : "auto",
     866            'mdns'                  : bstr(mdns),
     867            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     868            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     869            'pdf_printer'           : pdf,
     870            'postscript_printer'    : postscript,
     871            'webcam'                : ["no", "auto"][webcam],
     872            'mousewheel'            : "on",
     873            'printing'              : bstr(printing_ENABLED),
     874            'dbus_control'          : bstr(dbus_ENABLED),
     875            'mmap'                  : bstr(True),
     876            }
     877    def convert_templates(subdirs=[]):
     878        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     879        #get conf dir for install, without stripping the build root
     880        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     881        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     882        if not os.path.exists(target_dir):
     883            try:
     884                os.makedirs(target_dir)
     885            except Exception as e:
     886                print("cannot create target dir '%s': %s" % (target_dir, e))
     887        for f in sorted(os.listdir(dirname)):
     888            if f.endswith("osx.conf.in") and not OSX:
     889                continue
     890            filename = os.path.join(dirname, f)
     891            if os.path.isdir(filename):
     892                convert_templates(subdirs+[f])
     893                continue
     894            if not f.endswith(".in"):
     895                continue
     896            with open(filename, "r") as f_in:
     897                template  = f_in.read()
     898            target_file = os.path.join(target_dir, f[:-len(".in")])
     899            print("generating %s from %s" % (target_file, f))
     900            with open(target_file, "w") as f_out:
     901                config_data = template % SUBS
     902                f_out.write(config_data)
     903    convert_templates()
    576904
    577905
     
    580908    #clean and sdist don't actually use cython,
    581909    #so skip this (and avoid errors)
    582     def pkgconfig(*pkgs_options, **ekw):
    583         return {}
     910    pkgconfig = no_pkgconfig
    584911    #always include everything in this case:
    585912    add_packages("xpra")
    586913    #ensure we remove the files we generate:
    587914    CLEAN_FILES = [
    588                    "xpra/gtk_common/gdk_atoms.c",
    589                    "xpra/x11/gtk_x11/constants.pxi",
    590                    "xpra/x11/gtk_x11/gdk_bindings.c",
    591                    "xpra/x11/gtk_x11/gdk_display_source.c",
     915                   "xpra/build_info.py",
     916                   "xpra/monotonic_time.c",
     917                   "xpra/gtk_common/gtk2/gdk_atoms.c",
     918                   "xpra/gtk_common/gtk2/gdk_bindings.c",
     919                   "xpra/gtk_common/gtk3/gdk_atoms.c",
     920                   "xpra/gtk_common/gtk3/gdk_bindings.c",
     921                   "xpra/x11/gtk2/constants.pxi",
     922                   "xpra/x11/gtk2/gdk_bindings.c",
     923                   "xpra/x11/gtk2/gdk_display_source.c",
     924                   "xpra/x11/gtk3/gdk_bindings.c",
     925                   "xpra/x11/gtk3/gdk_display_source.c",
    592926                   "xpra/x11/bindings/constants.pxi",
    593927                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597931                   "xpra/x11/bindings/randr_bindings.c",
    598932                   "xpra/x11/bindings/core_bindings.c",
     933                   "xpra/x11/bindings/posix_display_source.c",
    599934                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     935                   "xpra/x11/bindings/xi2_bindings.c",
     936                   "xpra/platform/win32/propsys.cpp",
     937                   "xpra/platform/darwin/gdk_bindings.c",
     938                   "xpra/platform/xposix/sd_listen.c",
     939                   "xpra/platform/xposix/netdev_query.c",
     940                   "xpra/net/bencode/cython_bencode.c",
     941                   "xpra/net/vsock.c",
     942                   "xpra/buffers/membuf.c",
    601943                   "xpra/codecs/vpx/encoder.c",
    602944                   "xpra/codecs/vpx/decoder.c",
    603945                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     946                   "xpra/codecs/nvfbc/fbc_capture_linux.cpp",
     947                   "xpra/codecs/nvfbc/fbc_capture_win.cpp",
    605948                   "xpra/codecs/enc_x264/encoder.c",
    606949                   "xpra/codecs/enc_x265/encoder.c",
     950                   "xpra/codecs/jpeg/encoder.c",
     951                   "xpra/codecs/jpeg/decoder.c",
     952                   "xpra/codecs/enc_ffmpeg/encoder.c",
     953                   "xpra/codecs/v4l2/constants.pxi",
     954                   "xpra/codecs/v4l2/pusher.c",
     955                   "xpra/codecs/libav_common/av_log.c",
    607956                   "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     957                   "xpra/codecs/webp/decode.c",
    610958                   "xpra/codecs/dec_avcodec2/decoder.c",
     959                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611960                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614961                   "xpra/codecs/xor/cyxor.c",
    615962                   "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")
     963                   "xpra/codecs/nvapi_version.c",
     964                   "xpra/gtk_common/gdk_atoms.c",
     965                   "xpra/client/gtk3/cairo_workaround.c",
     966                   "xpra/server/cystats.c",
     967                   "xpra/server/window/region.c",
     968                   "xpra/server/window/motion.c",
     969                   "xpra/server/pam.c",
     970                   "etc/xpra/xpra.conf",
     971                   #special case for the generated xpra conf files in build (see #891):
     972                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     973    if cuda_rebuild_ENABLED:
     974        CLEAN_FILES += [
     975            "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     976            "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     977            "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     978            "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
     979            ]
     980    for x in CLEAN_FILES:
     981        p, ext = os.path.splitext(x)
     982        if ext in (".c", ".cpp", ".pxi"):
     983            #clean the Cython annotated html files:
     984            CLEAN_FILES.append(p+".html")
     985            if WIN32 and ext!=".pxi":
     986                #on win32, the build creates ".pyd" files, clean those too:
     987                CLEAN_FILES.append(p+".pyd")
     988                #when building with python3, we need to clean files named like:
     989                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     990                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     991                CLEAN_FILES += glob.glob(filename)
    623992    if 'clean' in sys.argv:
    624993        CLEAN_FILES.append("xpra/build_info.py")
     
    630999            os.unlink(filename)
    6311000
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     1001from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    6331002
    6341003if "clean" not in sys.argv:
    6351004    # Add build info to build_info.py file:
    6361005    record_build_info()
     1006    # ensure it is included in the module list if it didn't exist before
     1007    add_modules(BUILD_INFO_FILE)
    6371008
    6381009if "sdist" in sys.argv:
    6391010    record_src_info()
    6401011
    641 if "install" in sys.argv:
     1012if "install" in sys.argv or "build" in sys.argv:
    6421013    #if installing from source tree rather than
    6431014    #from a source snapshot, we may not have a "src_info" file
     
    6451016    if not has_src_info():
    6461017        record_src_info()
     1018        # ensure it is now included in the module list
     1019        add_modules(SRC_INFO_FILE)
    6471020
    6481021
     
    6631036    return m
    6641037
     1038
     1039def install_html5(install_dir="www"):
     1040    from setup_html5 import install_html5 as do_install_html5
     1041    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1042
     1043
    6651044#*******************************************************************************
    6661045if 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"]
     1046    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1047    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821048    add_packages("xpra.platform.win32")
    8831049    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"]
     1050
     1051    #this is where the win32 gi installer will put things:
     1052    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1053
     1054    #only add the cx_freeze specific options
     1055    #if we aren't just building the Cython bits with "build_ext":
     1056    if "build_ext" not in sys.argv:
     1057        #with cx_freeze, we don't use py_modules
     1058        del setup_options["py_modules"]
     1059        import cx_Freeze                            #@UnresolvedImport
     1060        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1061        CX5 = cx_Freeze.version>="5"
     1062        if CX5 and not hasattr(sys, "base_prefix"):
     1063            #workaround for broken sqlite hook with python 2.7, see:
     1064            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1065            sys.base_prefix = sys.prefix
     1066
     1067        #cx_freeze doesn't use "data_files"...
     1068        del setup_options["data_files"]
     1069        #it wants source files first, then where they are placed...
     1070        #one item at a time (no lists)
     1071        #all in its own structure called "include_files" instead of "data_files"...
     1072        def add_data_files(target_dir, files):
     1073            if verbose_ENABLED:
     1074                print("add_data_files(%s, %s)" % (target_dir, files))
     1075            assert type(target_dir)==str
     1076            assert type(files) in (list, tuple)
     1077            for f in files:
     1078                target_file = os.path.join(target_dir, os.path.basename(f))
     1079                data_files.append((f, target_file))
     1080
     1081        #pass a potentially nested dictionary representing the tree
     1082        #of files and directories we do want to include
     1083        #relative to gnome_include_path
     1084        def add_dir(base, defs):
     1085            if verbose_ENABLED:
     1086                print("add_dir(%s, %s)" % (base, defs))
     1087            if type(defs) in (list, tuple):
     1088                for sub in defs:
     1089                    if type(sub)==dict:
     1090                        add_dir(base, sub)
     1091                    else:
     1092                        assert type(sub)==str
     1093                        filename = os.path.join(gnome_include_path, base, sub)
     1094                        if os.path.exists(filename):
     1095                            add_data_files(base, [filename])
     1096                        else:
     1097                            print("Warning: missing '%s'" % filename)
     1098            else:
     1099                assert type(defs)==dict
     1100                for d, sub in defs.items():
     1101                    assert type(sub) in (dict, list, tuple)
     1102                    #recurse down:
     1103                    add_dir(os.path.join(base, d), sub)
     1104
     1105        #convenience method for adding GI libs and "typelib" and "gir":
     1106        def add_gi(*libs):
     1107            if verbose_ENABLED:
     1108                print("add_gi(%s)" % str(libs))
     1109            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1110            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1111
     1112        def add_DLLs(*dll_names):
     1113            try:
     1114                do_add_DLLs(*dll_names)
     1115            except Exception as e:
     1116                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1117                print(" %s" % e)
     1118                sys.exit(1)
     1119
     1120        def do_add_DLLs(*dll_names):
     1121            dll_names = list(dll_names)
     1122            dll_files = []
     1123            import re
     1124            version_re = re.compile("\-[0-9\.\-]+$")
     1125            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1126            if os.path.exists(gnome_include_path):
     1127                dirs.insert(0, gnome_include_path)
     1128            if verbose_ENABLED:
     1129                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1130            for d in dirs:
     1131                if not os.path.exists(d):
     1132                    continue
     1133                for x in os.listdir(d):
     1134                    dll_path = os.path.join(d, x)
     1135                    x = x.lower()
     1136                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1137                        continue
     1138                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1139                    if verbose_ENABLED:
     1140                        print("checking %s: %s" % (x, nameversion))
     1141                    m = version_re.search(nameversion)          #look for version part of filename
     1142                    if m:
     1143                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1144                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1145                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1146                    else:
     1147                        dll_version = ""                        #no version
     1148                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1149                    if dll_name in dll_names:
     1150                        #this DLL is on our list
     1151                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1152                        dll_files.append(dll_path)
     1153                        dll_names.remove(dll_name)
     1154            if len(dll_names)>0:
     1155                print("some DLLs could not be found:")
     1156                for x in dll_names:
     1157                    print(" - lib%s*.dll" % x)
     1158            add_data_files("", dll_files)
     1159
     1160        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1161        #(ie: "libatk-1.0-0.dll" -> "atk")
     1162        if sound_ENABLED or gtk3_ENABLED:
     1163            add_DLLs('gio', 'girepository', 'glib',
     1164                     'gnutls', 'gobject', 'gthread',
     1165                     'orc', 'stdc++',
     1166                     'winpthread',
     1167                     )
     1168        if gtk3_ENABLED:
     1169            add_DLLs('atk',
     1170                     'dbus', 'dbus-glib',
     1171                     'gdk', 'gdk_pixbuf', 'gtk',
     1172                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1173                     'harfbuzz', 'harfbuzz-gobject',
     1174                     'jasper', 'epoxy',
     1175                     'intl',
     1176                     'p11-kit',
     1177                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1178            #these are missing in newer aio installers (sigh):
     1179            do_add_DLLs('javascriptcoregtk')
     1180            if opengl_ENABLED:
     1181                do_add_DLLs('gdkglext', 'gtkglext')
     1182
     1183        if gtk3_ENABLED:
     1184            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1185            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1186                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1187            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1188                              "p11-kit", "xml",
     1189                              {"icons"  : ["hicolor"]},
     1190                              {"locale" : ["en"]},
     1191                              {"themes" : ["Default"]}
     1192                             ])
     1193        if gtk3_ENABLED or sound_ENABLED:
     1194            add_dir('lib', ["gio"])
     1195            packages.append("gi")
     1196            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1197                   "GObject-2.0")
     1198        if gtk3_ENABLED:
     1199            add_gi("Atk-1.0",
     1200                   "Notify-0.7",
     1201                   "fontconfig-2.0", "freetype2-2.0",
     1202                   "GDesktopEnums-3.0", "Soup-2.4",
     1203                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0",
     1204                   "HarfBuzz-0.0",
     1205                   "Libproxy-1.0", "libxml2-2.0",
     1206                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1207                   "Rsvg-2.0",
     1208                   "win32-1.0")
     1209            if opengl_ENABLED:
     1210                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1211            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1212        if server_ENABLED and not PYTHON3:
     1213            add_DLLs('sqlite3')
     1214
     1215        if gtk2_ENABLED:
     1216            add_dir('lib',      {
     1217                "gdk-pixbuf-2.0":    {
     1218                    "2.10.0"    :   {
     1219                        "loaders"   :
     1220                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp", "png",)]
     1221                        },
     1222                    },
     1223                })
     1224
     1225        if sound_ENABLED:
     1226            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1227            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1228                   "GstTag-1.0")
     1229            add_DLLs('gstreamer', 'orc-test')
     1230            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1231                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1232                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1233                      "basecamerabinsrc", "mpegts", "photography",
     1234                      ):
     1235                add_DLLs('gst%s' % p)
     1236            #DLLs needed by the plugins:
     1237            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1238            #add the gstreamer plugins we need:
     1239            GST_PLUGINS = ("app",
     1240                           "cutter",
     1241                           #muxers:
     1242                           "gdp", "matroska", "ogg", "isomp4",
     1243                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1244                           "coreelements", "directsound", "directsoundsink", "directsoundsrc", "wasapi",
     1245                           #codecs:
     1246                           "opus", "opusparse", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1247                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1248                           "autodetect",
     1249                           #untested: a52dec, voaacenc
     1250                           )
     1251            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1252            #END OF SOUND
     1253
     1254        if server_ENABLED:
     1255            #used by proxy server:
     1256            external_includes += ["multiprocessing", "setproctitle"]
     1257
     1258        external_includes += ["encodings"]
     1259        if client_ENABLED:
     1260            #for parsing "open-command":
     1261            external_includes += ["shlex"]
     1262            #for version check:
     1263            external_includes += [
     1264                                  "ftplib", "fileinput",
     1265                                  ]
     1266            if PYTHON3:
     1267                external_includes += ["urllib", "http.cookiejar", "http.client"]
     1268            else:
     1269                external_includes += ["urllib2", "cookielib", "httplib"]
     1270
     1271        if PYTHON3:
     1272            #hopefully, cx_Freeze will fix this horror:
     1273            #(we shouldn't have to deal with DLL dependencies)
     1274            import site
     1275            lib_python = os.path.dirname(site.getsitepackages()[0])
     1276            lib_dynload_dir = os.path.join(lib_python, "lib-dynload")
     1277            add_data_files('', glob.glob("%s/zlib*dll" % lib_dynload_dir))
     1278            for x in ("io", "codecs", "abc", "_weakrefset", "encodings"):
     1279                add_data_files("lib/", glob.glob("%s/%s*" % (lib_python, x)))
     1280        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1281        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1282        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1283        cx_freeze_options = {
     1284                            "includes"          : external_includes,
     1285                            "packages"          : packages,
     1286                            "include_files"     : data_files,
     1287                            "excludes"          : excludes,
     1288                            "include_msvcr"     : True,
     1289                            "bin_excludes"      : bin_excludes,
     1290                            }
     1291        if not CX5:
     1292            cx_freeze_options.update({
     1293                            "compressed"        : True,
     1294                            "create_shared_zip" : zip_ENABLED,
     1295                            })
     1296        else:
     1297            #cx_Freeze v5 workarounds:
     1298            if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
     1299                add_packages("numpy.core._methods", "numpy.lib.format")
     1300
     1301        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1302        executables = []
     1303        setup_options["executables"] = executables
     1304
     1305        def add_exe(script, icon, base_name, base="Console"):
     1306            kwargs = {}
     1307            if not CX5:
     1308                kwargs = {
     1309                    "compress"              : True,
     1310                    "copyDependentFiles"    : True,
     1311                    "appendScriptToExe"     : False,
     1312                    "appendScriptToLibrary" : True,
     1313                    }
     1314            executables.append(Executable(
     1315                        script                  = script,
     1316                        initScript              = None,
     1317                        #targetDir               = "dist",
     1318                        icon                    = "icons/%s" % icon,
     1319                        targetName              = "%s.exe" % base_name,
     1320                        base                    = base,
     1321                        **kwargs))
     1322
     1323        def add_console_exe(script, icon, base_name):
     1324            add_exe(script, icon, base_name)
     1325        def add_gui_exe(script, icon, base_name):
     1326            add_exe(script, icon, base_name, base="Win32GUI")
     1327        def add_service_exe(script, icon, base_name):
     1328            add_exe(script, icon, base_name, base="Win32Service")
     1329
     1330        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1331        if (client_ENABLED or server_ENABLED) and (gtk2_ENABLED or gtk3_ENABLED):
     1332            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1333            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1334            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1335            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1336            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1337        if server_ENABLED:
     1338            add_gui_exe("scripts/auth_dialog",                  "authentication.ico", "Auth_Dialog")
     1339        if gtk2_ENABLED:
     1340            #these need porting..
     1341            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1342        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1343            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1344        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1345        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1346        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1347        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1348        if gtk2_ENABLED or gtk3_ENABLED:
     1349            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1350            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1351            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1352            add_gui_exe("xpra/client/gtk_base/example/tray.py", "xpra.ico",         "SystemTray_Test")
     1353            add_gui_exe("xpra/client/gtk_base/u2f_tool.py",     "authentication.ico", "U2F_Tool")
     1354        if client_ENABLED or server_ENABLED:
     1355            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1356            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1357        if server_ENABLED:
     1358            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1359            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1360            add_console_exe("xpra/server/auth/ldap_auth.py",    "authentication.ico", "LDAP-Auth-Test")
     1361            add_console_exe("xpra/server/auth/ldap3_auth.py",   "authentication.ico", "LDAP3-Auth-Test")
     1362            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1363            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1364        if client_ENABLED:
     1365            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1366            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1367            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1368        if client_ENABLED:
     1369            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1370            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1371        if sound_ENABLED:
     1372            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1373            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1374            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1375            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1376            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1377        if opengl_ENABLED:
     1378            if PYTHON3:
     1379                add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1380            else:
     1381                add_console_exe("xpra/client/gl/gtk_base/gtkgl_check.py", "opengl.ico", "OpenGL_check")
     1382        if webcam_ENABLED:
     1383            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1384            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1385        if printing_ENABLED:
     1386            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1387            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1388            add_DLLs("pdfium")  #libpdfium.dll
     1389        if nvenc_ENABLED:
     1390            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1391        if nvfbc_ENABLED:
     1392            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1393        if nvfbc_ENABLED or nvenc_ENABLED:
     1394            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1395
     1396        if example_ENABLED:
     1397            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1398            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1399            if not PYTHON3:
     1400                add_gui_exe("xpra/client/gtk_base/example/gl_colors_gradient.py",   "encoding.ico",     "OpenGL-Colors-Gradient")
     1401            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1402            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1403            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1404            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1405            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1406
     1407        #FIXME: how do we figure out what target directory to use?
     1408        print("calling build_xpra_conf in-place")
     1409        #building etc files in-place:
     1410        build_xpra_conf(".")
     1411        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1412        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1413        add_data_files('etc/xpra', glob.glob("etc/xpra/nvfbc*.keys"))
     1414        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1415        #build minified html5 client in temporary build dir:
     1416        if "clean" not in sys.argv and html5_ENABLED:
     1417            install_html5(os.path.join(install, "www"), )
     1418            for k,v in glob_recurse("build/www").items():
     1419                if (k!=""):
     1420                    k = os.sep+k
     1421                add_data_files('www'+k, v)
     1422
     1423    if client_ENABLED or server_ENABLED:
     1424        add_data_files('',      ['COPYING', 'README', 'win32/website.url'])
     1425        add_data_files('icons', glob.glob('icons\\*.ico') + glob.glob('icons\\*.png'))
     1426
     1427    if webcam_ENABLED:
     1428        add_data_files('',      ['win32\\DirectShow.tlb'])
     1429
    9071430    remove_packages(*external_excludes)
     1431    external_includes.append("pyu2f")
     1432    external_includes.append("mmap")
     1433    external_includes.append("comtypes")    #used by webcam and netdev_query
     1434    remove_packages("comtypes.gen")         #this is generated at runtime
     1435                                            #but we still have to remove the empty directory by hand
     1436                                            #afterwards because cx_freeze does weird things (..)
    9081437    remove_packages(#not used on win32:
    909                     "mmap",
    9101438                    #we handle GL separately below:
    9111439                    "OpenGL", "OpenGL_accelerate",
     
    9131441                    "ctypes.macholib")
    9141442
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1443    if webcam_ENABLED:
     1444        external_includes.append("cv2")
     1445    else:
     1446        remove_packages("cv2")
     1447
     1448    if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
    9161449        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1450        external_includes.append("numpy")
    9181451    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1452        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211453                        "pydoc")
    9221454
    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:
     1455    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1456    remove_packages("pygst", "gst", "gst.extend")
     1457
     1458    #add subset of PyOpenGL modules (only when installing):
     1459    if opengl_ENABLED and ("install_exe" in sys.argv or "install" in sys.argv):
    9291460        #for this hack to work, you must add "." to the sys.path
    9301461        #so python can load OpenGL from the install directory
    9311462        #(further complicated by the fact that "." is the "frozen" path...)
     1463        #but we re-add those two directories to the library.zip as part of the build script
    9321464        import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
     1465        print("*** copying PyOpenGL modules to %s ***" % install)
    9351466        for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
    9361467            module_dir = os.path.dirname(module.__file__ )
    9371468            try:
    9381469                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1470                    module_dir, os.path.join(install, module_name),
     1471                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411472                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1473            except Exception as e:
     1474                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441475                    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 
     1476
     1477    add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1478    add_data_files('', ["bell.wav"])
     1479    add_data_files('http-headers', glob.glob("http-headers\\*"))
     1480
     1481    #END OF win32
    9831482#*******************************************************************************
    9841483else:
    9851484    #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"]))
     1485    scripts += ["scripts/xpra", "scripts/xpra_launcher", "scripts/xpra_browser", "scripts/xpra_udev_product_version", "scripts/xpra_signal_listener"]
     1486    if POSIX and not OSX:
     1487        libexec_scripts = []
     1488        if is_Fedora() or is_CentOS():
     1489            libexec = "libexec"
     1490        else:
     1491            libexec = "lib"
     1492        if xdg_open_ENABLED:
     1493            libexec_scripts += ["scripts/xdg-open", "scripts/gnome-open", "scripts/gvfs-open"]
     1494        if server_ENABLED:
     1495            libexec_scripts.append("scripts/auth_dialog")
     1496        if libexec_scripts:
     1497            add_data_files("%s/xpra/" % libexec, libexec_scripts)
     1498    man_path = "share/man"
     1499    if OPENBSD:
     1500        man_path = "man"
     1501    add_data_files("%s/man1" % man_path,  ["man/xpra.1", "man/xpra_launcher.1", "man/xpra_browser.1"])
     1502    add_data_files("share/xpra",          ["README", "COPYING"])
     1503    add_data_files("share/xpra/icons",    glob.glob("icons/*png"))
     1504    add_data_files("share/applications",  ["xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1505    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1506    add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png"])
     1507    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1508    add_data_files('share/xpra/',         ["bell.wav"])
     1509    add_data_files('share/xpra/http-headers', glob.glob("http-headers/*"))
     1510
     1511    #here, we override build and install so we can
     1512    #generate our /etc/xpra/xpra.conf
     1513    class build_override(build):
     1514        def run(self):
     1515            build.run(self)
     1516            self.run_command("build_conf")
     1517
     1518    class build_conf(build):
     1519        def run(self):
     1520            try:
     1521                build_base = self.distribution.command_obj['build'].build_base
     1522            except:
     1523                build_base = self.build_base
     1524            build_xpra_conf(build_base)
     1525
     1526    class install_data_override(install_data):
     1527        def run(self):
     1528            print("install_data_override: install_dir=%s" % self.install_dir)
     1529            if html5_ENABLED:
     1530                install_html5(os.path.join(self.install_dir, "share/xpra/www"))
     1531            install_data.run(self)
     1532
     1533            root_prefix = self.install_dir.rstrip("/")
     1534            if root_prefix.endswith("/usr"):
     1535                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1536            build_xpra_conf(root_prefix)
     1537
     1538            def copytodir(src, dst_dir, dst_name=None, chmod=0o644):
     1539                #convert absolute paths:
     1540                if dst_dir.startswith("/"):
     1541                    dst_dir = root_prefix+dst_dir
     1542                else:
     1543                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1544                #make sure the target directory exists:
     1545                self.mkpath(dst_dir)
     1546                #generate the target filename:
     1547                filename = os.path.basename(src)
     1548                dst_file = os.path.join(dst_dir, dst_name or filename)
     1549                #copy it
     1550                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1551                shutil.copyfile(src, dst_file)
     1552                if chmod:
     1553                    os.chmod(dst_file, chmod)
     1554
     1555            if printing_ENABLED and POSIX:
     1556                #install "/usr/lib/cups/backend" with 0700 permissions:
     1557                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1558
     1559            if x11_ENABLED:
     1560                #install xpra_Xdummy if we need it:
     1561                xvfb_command = detect_xorg_setup()
     1562                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1563                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1564                #install xorg*.conf, cuda.conf and nvenc.keys:
     1565                etc_xpra_files = ["xorg.conf"]
     1566                if uinput_ENABLED:
     1567                    etc_xpra_files.append("xorg-uinput.conf")
     1568                if nvenc_ENABLED or nvfbc_ENABLED:
     1569                    etc_xpra_files.append("cuda.conf")
     1570                if nvenc_ENABLED:
     1571                    etc_xpra_files.append("nvenc.keys")
     1572                if nvfbc_ENABLED:
     1573                    etc_xpra_files.append("nvfbc.keys")
     1574                for x in etc_xpra_files:
     1575                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1576                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1577
     1578            if pam_ENABLED:
     1579                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1580
     1581            systemd_dir = "/lib/systemd/system"
     1582            if service_ENABLED:
     1583                #Linux init service:
     1584                if os.path.exists("/bin/systemctl"):
     1585                    if sd_listen_ENABLED:
     1586                        copytodir("service/xpra.service", systemd_dir)
     1587                    else:
     1588                        copytodir("service/xpra-nosocketactivation.service", systemd_dir, dst_name="xpra.service")
     1589                else:
     1590                    copytodir("service/xpra", "/etc/init.d")
     1591                if os.path.exists("/etc/sysconfig"):
     1592                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1593                elif os.path.exists("/etc/default"):
     1594                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1595            if sd_listen_ENABLED:
     1596                copytodir("service/xpra.socket", systemd_dir)
     1597            if dbus_ENABLED and proxy_ENABLED:
     1598                copytodir("dbus/xpra.conf", "/etc/dbus-1/system.d")
     1599
     1600
     1601    # add build_conf to build step
     1602    cmdclass.update({
     1603             'build'        : build_override,
     1604             'build_conf'   : build_conf,
     1605             'install_data' : install_data_override,
     1606             })
    9971607
    9981608    if OSX:
     1609        #pyobjc needs email.parser
     1610        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
     1611        external_includes += ["kerberos", "future", "pyu2f"]
     1612        if not PYTHON3:
     1613            external_includes += ["urllib2"]
    9991614        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001615        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011616        add_packages("xpra.platform.darwin")
     1617        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1618        #for u2f on python2:
     1619        if not PYTHON3:
     1620            modules.append("UserList")
     1621            modules.append("UserString")
     1622        #to support GStreamer 1.x we need this:
     1623        modules.append("importlib")
    10021624    else:
    10031625        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041626        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")
     1627        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1628        #not supported by all distros, but doesn't hurt to install them anyway:
     1629        for x in ("tmpfiles.d", "sysusers.d"):
     1630            add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1631        if uinput_ENABLED:
     1632            add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081633
    10091634    #gentoo does weird things, calls --no-compile with build *and* install
     
    10121637    #otherwise we use the flags to skip pkgconfig
    10131638    if ("--no-compile" in sys.argv or "--skip-build" in sys.argv) and not ("build" in sys.argv and "install" in sys.argv):
    1014         def pkgconfig(*pkgs_options, **ekw):
    1015             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))
     1639        pkgconfig = no_pkgconfig
    10331640
    10341641    if OSX and "py2app" in sys.argv:
     
    10451652        remove_packages(*external_excludes)
    10461653
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1654        try:
     1655            from xpra.src_info import REVISION
     1656        except:
     1657            REVISION = "unknown"
     1658        Plist = {
     1659            "CFBundleDocumentTypes" : {
     1660                "CFBundleTypeExtensions"    : ["Xpra"],
     1661                "CFBundleTypeName"          : "Xpra Session Config File",
     1662                "CFBundleName"              : "Xpra",
     1663                "CFBundleTypeRole"          : "Viewer",
     1664                },
     1665            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2018 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1666            "CFBundleIdentifier"            : "org.xpra.xpra",
     1667            }
    10531668        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541669        #so the make-app.sh script still has to hack around this problem.
    10551670        add_modules(*external_includes)
     1671        #needed by python-lz4:
     1672        add_modules("distutils")
    10561673        py2app_options = {
    10571674            'iconfile'          : '../osx/xpra.icns',
     
    10651682            }
    10661683        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1684        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1685
     1686    if OSX:
     1687        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1688        #so instead we have to query each package seperately and merge the results:
     1689        def osx_pkgconfig(*pkgs_options, **ekw):
     1690            kw = dict(ekw)
     1691            for pkg in pkgs_options:
     1692                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1693                if pkg.lower().startswith("x"):
     1694                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1695                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1696                kw = exec_pkgconfig(pkg, **kw)
     1697                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1698            return kw
     1699
     1700        pkgconfig = osx_pkgconfig
    10681701
    10691702
    10701703if 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 
     1704    if WIN32 or OSX:
     1705        external_includes.append("websockify")
     1706        external_includes.append("numpy")
     1707        external_includes.append("ssl")
     1708        external_includes.append("_ssl")
     1709        if not PYTHON3:
     1710            external_includes.append("mimetypes")
     1711            external_includes.append("mimetools")
     1712            external_includes.append("BaseHTTPServer")
     1713
     1714
     1715if annotate_ENABLED:
     1716    from Cython.Compiler import Options
     1717    Options.annotate = True
    10761718
    10771719
    10781720#*******************************************************************************
    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")
     1721buffers_c = "xpra/buffers/buffers.c"
     1722memalign_c = "xpra/buffers/memalign.c"
     1723xxhash_c = "xpra/buffers/xxhash.c"
     1724membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1725
     1726add_packages("xpra.buffers")
     1727buffers_pkgconfig = pkgconfig(optimize=3)
     1728cython_add(Extension("xpra.buffers.membuf",
     1729            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1730
     1731
     1732toggle_packages(dbus_ENABLED, "xpra.dbus")
     1733toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1734toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
     1735toggle_packages(rfb_ENABLED, "xpra.server.rfb")
     1736toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1737toggle_packages(server_ENABLED, "xpra.server.window")
     1738toggle_packages(server_ENABLED or shadow_ENABLED, "xpra.server.mixins", "xpra.server.source")
     1739toggle_packages(shadow_ENABLED, "xpra.server.shadow")
     1740toggle_packages(server_ENABLED or client_ENABLED, "xpra.clipboard")
     1741toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1742toggle_packages(client_ENABLED or server_ENABLED, "xpra.notifications")
     1743
     1744#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1745if dbus_ENABLED and server_ENABLED:
     1746    add_packages("xpra.server.dbus")
     1747
     1748if OSX:
     1749    if PYTHON3:
     1750        quartz_pkgconfig = pkgconfig("gtk+-3.0", "pygobject-3.0")
     1751        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1752                    "-ObjC",
     1753                    "-framework", "AppKit",
     1754                    "-I/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/")
     1755        cython_add(Extension("xpra.platform.darwin.gdk3_bindings",
     1756                ["xpra/platform/darwin/gdk3_bindings.pyx"],
     1757                language="objc",
     1758                **quartz_pkgconfig
     1759                ))
     1760    else:
     1761        quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1762        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1763                    '-mmacosx-version-min=10.10',
     1764                    '-framework', 'Foundation',
     1765                    '-framework', 'AppKit',
     1766                    '-ObjC',
     1767                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h")
     1768        cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1769                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1770                language="objc",
     1771                **quartz_pkgconfig
     1772                ))
     1773
     1774monotonic_time_pkgconfig = pkgconfig()
     1775if not OSX and not WIN32 and not OPENBSD:
     1776    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1777cython_add(Extension("xpra.monotonic_time",
     1778            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1779            **monotonic_time_pkgconfig
     1780            ))
     1781
     1782
     1783toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841784if x11_ENABLED:
    10851785    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1786    if gtk2_ENABLED:
     1787        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk2/constants.pxi")
     1788    if gtk3_ENABLED:
     1789        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk3/constants.pxi")
    10871790
    10881791    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981801                **pkgconfig("x11")
    10991802                ))
     1803    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1804                ["xpra/x11/bindings/posix_display_source.pyx"],
     1805                **pkgconfig("x11")
     1806                ))
     1807
    11001808    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011809                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041812    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051813                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1814                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071815                ))
    11081816
    11091817    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101818                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1819                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121820                ))
    11131821    cython_add(Extension("xpra.x11.bindings.ximage",
    11141822                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1823                **pkgconfig("x11", "xext", "xcomposite")
    11161824                ))
    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)
     1825if xinput_ENABLED:
     1826    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1827                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1828                **pkgconfig("x11", "xi")
    11221829                ))
    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)
     1830
     1831toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1832if gtk_x11_ENABLED:
     1833    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1834    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1835    if PYTHON3:
     1836        #GTK3 display source:
     1837        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1838                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1839                    **pkgconfig("gdk-3.0")
     1840                    ))
     1841        cython_add(Extension("xpra.x11.gtk3.gdk_bindings",
     1842                    ["xpra/x11/gtk3/gdk_bindings.pyx"],
     1843                    **pkgconfig("gdk-3.0")
     1844                    ))
     1845    else:
     1846        #GTK2:
     1847        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1848                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1849                    **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1850                    ))
     1851        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1852        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1853                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1854                    **pkgconfig(*GDK_BINDINGS_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1855                    ))
     1856
     1857toggle_packages(not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED), "xpra.gtk_common.gtk2")
     1858if gtk2_ENABLED or (gtk_x11_ENABLED and not PYTHON3):
     1859    cython_add(Extension("xpra.gtk_common.gtk2.gdk_bindings",
     1860                ["xpra/gtk_common/gtk2/gdk_bindings.pyx"],
     1861                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11271862                ))
    1128 
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1863elif gtk3_ENABLED or (gtk_x11_ENABLED and PYTHON3):
     1864    cython_add(Extension("xpra.gtk_common.gtk3.gdk_bindings",
     1865                ["xpra/gtk_common/gtk3/gdk_bindings.pyx"],
     1866                **pkgconfig("gtk+-3.0", "pygobject-3.0")
     1867                ))
     1868
     1869if client_ENABLED and gtk3_ENABLED:
     1870    #cairo workaround:
     1871    if OSX:
     1872        pycairo = "py3cairo"
     1873    else:
     1874        pycairo = "pycairo"
     1875    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1876                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1877                **pkgconfig(pycairo)
     1878                ))
     1879
     1880if client_ENABLED or server_ENABLED:
     1881    add_packages("xpra.codecs.argb")
     1882    argb_pkgconfig = pkgconfig(optimize=3)
    11321883    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1884                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1885
     1886
     1887#build tests, but don't install them:
     1888toggle_packages(tests_ENABLED, "unit")
    11341889
    11351890
    11361891if bundle_tests_ENABLED:
    11371892    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1893    for k,v in glob_recurse("unit").items():
    11391894        if (k!=""):
    11401895            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:
     1896        add_data_files("unit"+k, v)
     1897
     1898#python-cryptography needs workarounds for bundling:
     1899if crypto_ENABLED and (OSX or WIN32):
     1900    external_includes.append("_ssl")
     1901    external_includes.append("cffi")
     1902    external_includes.append("_cffi_backend")
     1903    external_includes.append("cryptography")
     1904    external_includes.append("idna")
     1905    external_includes.append("idna.idnadata")
     1906    external_includes.append("pkg_resources._vendor.packaging")
     1907    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1908    external_includes.append("pkg_resources._vendor.pyparsing")
     1909    add_modules("cryptography.hazmat.bindings._openssl")
     1910    add_modules("cryptography.hazmat.bindings._constant_time")
     1911    add_modules("cryptography.hazmat.bindings._padding")
     1912    add_modules("cryptography.hazmat.backends.openssl")
     1913    add_modules("cryptography.fernet")
     1914    if WIN32:
     1915        external_includes.append("appdirs")
     1916
     1917#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441918if client_ENABLED:
    1145     add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1919    add_modules("xpra.client", "xpra.client.mixins")
     1920    add_modules("xpra.scripts.gtk_info")
     1921    add_modules("xpra.scripts.show_webcam")
     1922toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471923toggle_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")
     1924toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1925toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501926toggle_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")
     1927toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1928toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1929toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED) and example_ENABLED, "xpra.client.gtk_base.example")
     1930if client_ENABLED and WIN32 and MINGW_PREFIX:
     1931    propsys_pkgconfig = pkgconfig()
     1932    if debug_ENABLED:
     1933        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1934    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1935    cython_add(Extension("xpra.platform.win32.propsys",
     1936                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1937                language="c++",
     1938                **propsys_pkgconfig))
     1939
     1940if client_ENABLED or server_ENABLED:
     1941    add_modules("xpra.codecs")
     1942toggle_packages(client_ENABLED or server_ENABLED, "xpra.keyboard")
     1943if client_ENABLED or server_ENABLED:
     1944    add_modules("xpra.scripts.config", "xpra.scripts.parsing", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1945if server_ENABLED or proxy_ENABLED:
     1946    add_modules("xpra.scripts.server")
     1947if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1948    add_modules("xpra.scripts.gtk_info")
     1949
     1950toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1951#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1952#and tries to include both gtk3 and gtk2, and fails hard..
     1953for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gl_drivers"):
     1954    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1955toggle_packages(client_ENABLED and opengl_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gl.gtk_base")
     1956
     1957
     1958toggle_modules(sound_ENABLED, "xpra.sound")
     1959toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541960
    11551961toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11561962if clipboard_ENABLED:
    1157     cython_add(Extension("xpra.gtk_common.gdk_atoms",
    1158                 ["xpra/gtk_common/gdk_atoms.pyx"],
    1159                 **pkgconfig(*PYGTK_PACKAGES)
    1160                 ))
    1161 
    1162 if cyxor_ENABLED:
     1963    if PYTHON3:
     1964        cython_add(Extension("xpra.gtk_common.gtk3.gdk_atoms",
     1965                             ["xpra/gtk_common/gtk3/gdk_atoms.pyx"],
     1966                             **pkgconfig("gtk+-3.0")
     1967                             ))
     1968    else:
     1969        cython_add(Extension("xpra.gtk_common.gtk2.gdk_atoms",
     1970                             ["xpra/gtk_common/gtk2/gdk_atoms.pyx"],
     1971                             **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1972                             ))
     1973
     1974toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1975if client_ENABLED or server_ENABLED:
    11631976    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641977                ["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")
     1978                **pkgconfig(optimize=3)))
     1979
     1980if server_ENABLED or shadow_ENABLED:
     1981    O3_pkgconfig = pkgconfig(optimize=3)
     1982    cython_add(Extension("xpra.server.cystats",
     1983                ["xpra/server/cystats.pyx"],
     1984                **O3_pkgconfig))
     1985    cython_add(Extension("xpra.server.window.region",
     1986                ["xpra/server/window/region.pyx"],
     1987                **O3_pkgconfig))
     1988    cython_add(Extension("xpra.server.window.motion",
     1989                ["xpra/server/window/motion.pyx"],
     1990                **O3_pkgconfig))
     1991
     1992if sd_listen_ENABLED:
     1993    sdp = pkgconfig("libsystemd")
     1994    cython_add(Extension("xpra.platform.xposix.sd_listen",
     1995                ["xpra/platform/xposix/sd_listen.pyx"],
     1996                **sdp))
     1997
     1998
    11781999toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11792000
     2001toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     2002if nvfbc_ENABLED:
     2003    nvfbc_pkgconfig = pkgconfig("nvfbc")
     2004    if WIN32:
     2005        add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     2006    platform = sys.platform.rstrip("0123456789")
     2007    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     2008                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     2009                         language="c++",
     2010                         **nvfbc_pkgconfig))
     2011
    11802012toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     2013toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     2014toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     2015
     2016if nvenc_ENABLED and cuda_kernels_ENABLED:
     2017    #find nvcc:
     2018    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     2019    if WIN32:
     2020        nvcc_exe = "nvcc.exe"
     2021        CUDA_DIR = os.environ.get("CUDA_DIR", "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA")
     2022        path_options = [os.path.join(CUDA_DIR, x, "bin") for x in ("v9.2", "v9.1", "v9.0", "v8.0", "v7.5")] + path_options
     2023        #pycuda may link against curand, find it and ship it:
     2024        for p in path_options:
     2025            if os.path.exists(p):
     2026                add_data_files("", glob.glob("%s\\curand64*.dll" % p))
     2027                add_data_files("", glob.glob("%s\\cudart64*.dll" % p))
     2028                break
     2029    else:
     2030        nvcc_exe = "nvcc"
     2031        for v in ("", "9.2", "9.1", "-9.0", "-8.0", "-7.5"):
     2032            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     2033    options = [os.path.join(x, nvcc_exe) for x in path_options]
     2034    def which(cmd):
     2035        try:
     2036            code, out, _ = get_status_output(["which", cmd])
     2037            if code==0:
     2038                return out
     2039        except:
     2040            pass
     2041    #prefer the one we find on the $PATH, if any:
     2042    try:
     2043        v = which(nvcc_exe)
     2044        if v and (v not in options):
     2045            options.insert(0, v)
     2046    except:
     2047        pass
     2048    nvcc_versions = {}
     2049    for filename in options:
     2050        if not os.path.exists(filename):
     2051            continue
     2052        code, out, err = get_status_output([filename, "--version"])
     2053        if code==0:
     2054            vpos = out.rfind(", V")
     2055            if vpos>0:
     2056                version = out[vpos+3:].strip("\n")
     2057                version_str = " version %s" % version
     2058            else:
     2059                version = "0"
     2060                version_str = " unknown version!"
     2061            print("found CUDA compiler: %s%s" % (filename, version_str))
     2062            nvcc_versions[version] = filename
     2063    assert nvcc_versions, "cannot find nvcc compiler!"
     2064    #choose the most recent one:
     2065    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     2066    if len(nvcc_versions)>1:
     2067        print(" using version %s from %s" % (version, nvcc))
     2068    if WIN32:
     2069        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     2070        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     2071    #first compile the cuda kernels
     2072    #(using the same cuda SDK for both nvenc modules for now..)
     2073    #TODO:
     2074    # * compile directly to output directory instead of using data files?
     2075    # * detect which arches we want to build for? (does it really matter much?)
     2076    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     2077    for kernel in kernels:
     2078        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     2079        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     2080        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     2081            continue
     2082        reason = should_rebuild(cuda_src, cuda_bin)
     2083        if not reason:
     2084            continue
     2085        print("rebuilding %s: %s" % (kernel, reason))
     2086        cmd = [nvcc,
     2087               '-fatbin',
     2088               #"-cubin",
     2089               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     2090               #"-gencode=arch=compute_50,code=sm_50",
     2091               #"-gencode=arch=compute_52,code=sm_52",
     2092               #"-gencode=arch=compute_52,code=compute_52",
     2093               "-c", cuda_src,
     2094               "-o", cuda_bin]
     2095        #GCC 6 uses C++11 by default:
     2096        if get_gcc_version()>=[6, 0]:
     2097            cmd.append("-std=c++11")
     2098        CL_VERSION = os.environ.get("CL_VERSION")
     2099        if CL_VERSION:
     2100            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     2101            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     2102            cmd += ["--machine", "32"]
     2103        if WIN32:
     2104            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     2105            #cmd += ["--input-drive-prefix", "/"]
     2106            #cmd += ["--dependency-drive-prefix", "/"]
     2107            cmd += ["-I%s" % os.path.abspath("win32")]
     2108        comp_code_options = [(30, 30), (35, 35)]
     2109        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     2110        if version!="0" and version<"7.5":
     2111            print("CUDA version %s is very unlikely to work")
     2112            print("try upgrading to version 7.5 or later")
     2113        if version>="7.5":
     2114            comp_code_options.append((50, 50))
     2115            comp_code_options.append((52, 52))
     2116            comp_code_options.append((53, 53))
     2117        if version>="8.0":
     2118            comp_code_options.append((60, 60))
     2119            comp_code_options.append((61, 61))
     2120            comp_code_options.append((62, 62))
     2121        if version>="9.0":
     2122            comp_code_options.append((70, 70))
     2123        for arch, code in comp_code_options:
     2124            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2125        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2126        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2127        c, stdout, stderr = get_status_output(cmd)
     2128        if c!=0:
     2129            print("Error: failed to compile CUDA kernel %s" % kernel)
     2130            print(stdout or "")
     2131            print(stderr or "")
     2132            sys.exit(1)
     2133    CUDA_BIN = "share/xpra/cuda"
     2134    if WIN32:
     2135        CUDA_BIN = "CUDA"
     2136    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2137
    11812138if 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))
     2139    nvencmodule = "nvenc"
     2140    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2141    #don't link against libnvidia-encode, we load it dynamically:
     2142    libraries = nvenc_pkgconfig.get("libraries", [])
     2143    if "nvidia-encode" in libraries:
     2144        libraries.remove("nvidia-encode")
     2145    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2146        #with gcc 6.2 on Fedora:
     2147        #xpra/codecs/nvenc/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2148        #xpra/codecs/nvenc/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2149        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2150        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2151    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2152                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2153                         **nvenc_pkgconfig))
    11872154
    11882155toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892156if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2157    x264_pkgconfig = pkgconfig("x264")
     2158    if get_gcc_version()>=[6, 0]:
     2159        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912160    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922161                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2162                **x264_pkgconfig))
    11942163
    11952164toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962165if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2166    x265_pkgconfig = pkgconfig("x265")
    11982167    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992168                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
     2169                **x265_pkgconfig))
     2170
     2171toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2172if pillow_ENABLED:
     2173    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
    12012174
    12022175toggle_packages(webp_ENABLED, "xpra.codecs.webp")
     
    12042177    webp_pkgconfig = pkgconfig("webp")
    12052178    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))
     2179                    ["xpra/codecs/webp/encode.pyx"],
     2180                    **webp_pkgconfig))
     2181    cython_add(Extension("xpra.codecs.webp.decode",
     2182                ["xpra/codecs/webp/decode.pyx"],
     2183                **webp_pkgconfig))
     2184
     2185toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2186if jpeg_ENABLED:
     2187    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2188    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2189                ["xpra/codecs/jpeg/encoder.pyx"],
     2190                **jpeg_pkgconfig))
     2191    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2192                ["xpra/codecs/jpeg/decoder.pyx"],
     2193                **jpeg_pkgconfig))
     2194
     2195#swscale and avcodec2 use libav_common/av_log:
     2196libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2197toggle_packages(libav_common, "xpra.codecs.libav_common")
     2198if libav_common:
     2199    avutil_pkgconfig = pkgconfig("avutil")
     2200    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2201                ["xpra/codecs/libav_common/av_log.pyx"],
     2202                **avutil_pkgconfig))
     2203
    12162204
    12172205toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182206if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2207    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", "avformat")
    12202208    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 
     2209                ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/dec_avcodec2/register_compat.c"],
     2210                **avcodec2_pkgconfig))
     2211
     2212
     2213toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2214if csc_libyuv_ENABLED:
     2215    libyuv_pkgconfig = pkgconfig("libyuv")
     2216    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2217                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2218                language="c++",
     2219                **libyuv_pkgconfig))
    12242220
    12252221toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262222if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2223    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292224    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))
     2225                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2226                **swscale_pkgconfig))
     2227
    12392228
    12402229toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412230if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2231    vpx_pkgconfig = pkgconfig("vpx")
    12432232    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))
     2233                ["xpra/codecs/vpx/encoder.pyx"],
     2234                **vpx_pkgconfig))
    12462235    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))
     2236                ["xpra/codecs/vpx/decoder.pyx"],
     2237                **vpx_pkgconfig))
     2238
     2239toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2240if enc_ffmpeg_ENABLED:
     2241    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2242    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2243                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2244                **ffmpeg_pkgconfig))
     2245
     2246toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2247if v4l2_ENABLED:
     2248    v4l2_pkgconfig = pkgconfig()
     2249    #fuly warning: cython makes this difficult,
     2250    #we have to figure out if "device_caps" exists in the headers:
     2251    ENABLE_DEVICE_CAPS = False
     2252    if os.path.exists("/usr/include/linux/videodev2.h"):
     2253        hdata = open("/usr/include/linux/videodev2.h").read()
     2254        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2255    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2256    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2257    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2258                ["xpra/codecs/v4l2/pusher.pyx"],
     2259                **v4l2_pkgconfig))
    12622260
    12632261
    12642262toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2263toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652264if 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")
     2265    bencode_pkgconfig = pkgconfig(optimize=3)
    12722266    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732267                ["xpra/net/bencode/cython_bencode.pyx"],
    12742268                **bencode_pkgconfig))
    12752269
     2270if netdev_ENABLED:
     2271    netdev_pkgconfig = pkgconfig()
     2272    cython_add(Extension("xpra.platform.xposix.netdev_query",
     2273                ["xpra/platform/xposix/netdev_query.pyx"],
     2274                **netdev_pkgconfig))
     2275
     2276if vsock_ENABLED:
     2277    vsock_pkgconfig = pkgconfig()
     2278    cython_add(Extension("xpra.net.vsock",
     2279                ["xpra/net/vsock.pyx"],
     2280                **vsock_pkgconfig))
     2281
     2282if pam_ENABLED:
     2283    pam_pkgconfig = pkgconfig()
     2284    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2285    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2286    cython_add(Extension("xpra.server.pam",
     2287                ["xpra/server/pam.pyx"],
     2288                **pam_pkgconfig))
     2289
    12762290
    12772291if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2292    from Cython.Build import cythonize
     2293    #this causes Cython to fall over itself:
     2294    #gdb_debug=debug_ENABLED
     2295    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792296if cmdclass:
    12802297    setup_options["cmdclass"] = cmdclass
     
    12832300
    12842301
    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 
    12932302def main():
    12942303    if OSX or WIN32 or debug_ENABLED:
     2304        print()
    12952305        print("setup options:")
     2306        if verbose_ENABLED:
     2307            print("setup_options=%s" % (setup_options,))
     2308        try:
     2309            from xpra.util import repr_ellipsized as pv
     2310        except:
     2311            def pv(v):
     2312                return str(v)
    12962313        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2314            print_option("", k, pv(v))
    12982315        print("")
    12992316
Note: See TracChangeset for help on using the changeset viewer.