xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r17234  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2017 Antoine Martin <antoine@devloop.org.uk>
    55# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <njs@pobox.com>
    66# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     
    1111# does the make_constants hack.)
    1212
    13 import commands
    1413import glob
    1514from distutils.core import setup
    1615from distutils.extension import Extension
    17 import subprocess, sys, traceback
     16import sys
    1817import os.path
    19 import stat
     18from distutils.command.build import build
     19from distutils.command.install_data import install_data
     20import shutil
     21
     22if sys.version<'2.7':
     23    raise Exception("xpra no longer supports Python 2 versions older than 2.7")
     24if sys.version[0]=='3' and sys.version<'3.4':
     25    raise Exception("xpra no longer supports Python 3 versions older than 3.4")
     26#we don't support versions of Python without the new ssl code:
     27import ssl
     28assert ssl.SSLContext, "xpra requires a Python version with ssl.SSLContext support"
    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")
     69PYTHON3 = sys.version_info[0] == 3
     70POSIX = os.name=="posix"
     71import struct
     72BITS = struct.calcsize("P")*8
     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, is_Ubuntu, is_Debian, is_Raspbian, getUbuntuVersion,\
     97    PYTHON3
     98
     99PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     100has_pkg_config = False
     101#we don't support building with "pkg-config" on win32 with python2:
     102if PKG_CONFIG:
     103    v = get_status_output([PKG_CONFIG, "--version"])
     104    has_pkg_config = v[0]==0 and v[1]
     105    if has_pkg_config:
     106        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     107
     108from Cython.Compiler.Version import version as cython_version
     109
     110for arg in list(sys.argv):
     111    if arg.startswith("--pkg-config-path="):
     112        pcp = arg[len("--pkg-config-path="):]
     113        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     114        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     115        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     116        sys.argv.remove(arg)
     117
     118def no_pkgconfig(*_pkgs_options, **_ekw):
     119    return {}
     120
    35121def 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 
     122    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     123    return get_status_output(cmd)[0]==0
     124
     125def pkg_config_version(req_version, pkgname):
     126    cmd = [PKG_CONFIG, "--modversion", pkgname]
     127    r, out, _ = get_status_output(cmd)
     128    if r!=0 or not out:
     129        return False
     130    from distutils.version import LooseVersion
     131    return LooseVersion(out)>=LooseVersion(req_version)
     132
     133def is_RH():
     134    try:
     135        with open("/etc/redhat-release", mode='rb') as f:
     136            data = f.read()
     137        return data.startswith("CentOS") or data.startswith("RedHat")
     138    except:
     139        pass
     140    return False
     141
     142DEFAULT = True
     143if "--minimal" in sys.argv:
     144    sys.argv.remove("--minimal")
     145    DEFAULT = False
     146
     147from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     148shadow_ENABLED = SHADOW_SUPPORTED and DEFAULT
     149server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and DEFAULT
     150rfb_ENABLED = server_ENABLED
     151service_ENABLED = LINUX and server_ENABLED
     152sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd") and (not is_Ubuntu() or getUbuntuVersion()>[16, 4])
     153proxy_ENABLED  = DEFAULT
     154client_ENABLED = DEFAULT
     155
     156x11_ENABLED = DEFAULT and not WIN32 and not OSX
     157xinput_ENABLED = x11_ENABLED
     158uinput_ENABLED = x11_ENABLED
     159dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     160gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     161gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     162gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     163opengl_ENABLED = DEFAULT and client_ENABLED
     164html5_ENABLED = DEFAULT
     165html5_gzip_ENABLED = DEFAULT
     166html5_brotli_ENABLED = DEFAULT
     167minify_ENABLED = html5_ENABLED
     168pam_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"))
     169
     170netdev_ENABLED          = LINUX and DEFAULT
     171vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     172bencode_ENABLED         = DEFAULT
     173cython_bencode_ENABLED  = DEFAULT
     174clipboard_ENABLED       = DEFAULT
     175Xdummy_ENABLED          = None          #None means auto-detect
     176Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     177if WIN32 or OSX:
     178    Xdummy_ENABLED = False
     179sound_ENABLED           = DEFAULT
     180printing_ENABLED        = DEFAULT
     181crypto_ENABLED          = DEFAULT
     182mdns_ENABLED            = DEFAULT
     183
     184enc_proxy_ENABLED       = DEFAULT
     185enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     186enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     187pillow_ENABLED          = DEFAULT
     188jpeg_ENABLED            = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     189vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     190enc_ffmpeg_ENABLED      = False
     191webcam_ENABLED          = DEFAULT and not OSX
     192v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD)
     193#ffmpeg 3.1 or later is required
     194dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     195csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     196nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("7", "nvenc")
     197nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     198cuda_kernels_ENABLED    = DEFAULT
     199cuda_rebuild_ENABLED    = DEFAULT
     200csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     201example_ENABLED         = DEFAULT
     202
     203#Cython / gcc / packaging build options:
     204annotate_ENABLED        = True
    86205warn_ENABLED            = True
    87206strict_ENABLED          = True
    88 PIC_ENABLED             = True
     207PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89208debug_ENABLED           = False
    90209verbose_ENABLED         = False
    91210bundle_tests_ENABLED    = False
     211tests_ENABLED           = False
     212rebuild_ENABLED         = True
    92213
    93214#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",
     215SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     216            "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     217            "vpx", "pillow", "jpeg",
     218            "v4l2",
     219            "dec_avcodec2", "csc_swscale",
     220            "csc_libyuv",
     221            "bencode", "cython_bencode", "vsock", "netdev", "mdns",
    104222            "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")
     223            "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     224            "gtk_x11", "service",
     225            "gtk2", "gtk3", "example",
     226            "html5", "minify", "html5_gzip", "html5_brotli",
     227            "pam",
     228            "sound", "opengl", "printing", "webcam",
     229            "rebuild",
     230            "annotate", "warn", "strict",
     231            "shadow", "proxy", "rfb",
     232            "debug", "PIC",
     233            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     234if WIN32:
     235    SWITCHES.append("zip")
     236    zip_ENABLED = True
    109237HELP = "-h" in sys.argv or "--help" in sys.argv
    110238if HELP:
     
    120248            default_str = "auto-detect"
    121249        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     250    print("  --pkg-config-path=PATH")
     251    print("  --rpath=PATH")
    122252    sys.exit(0)
    123253
     254install = "dist"
     255rpath = None
     256ssl_cert = None
     257ssl_key = None
     258minifier = None
    124259filtered_args = []
    125260for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     261    matched = False
     262    for x in ("rpath", "ssl-cert", "ssl-key", "install"):
     263        varg = "--%s=" % x
     264        if arg.startswith(varg):
     265            value = arg[len(varg):]
     266            globals()[x.replace("-", "_")] = value
     267            #remove these arguments from sys.argv,
     268            #except for --install=PATH
     269            matched = x!="install"
     270            break
     271    if matched:
    129272        continue
    130     matched = False
    131273    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     274        with_str = "--with-%s" % x
     275        without_str = "--without-%s" % x
     276        if arg.startswith(with_str+"="):
     277            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     278            matched = True
     279            break
     280        elif arg==with_str:
    133281            vars()["%s_ENABLED" % x] = True
    134282            matched = True
    135283            break
    136         elif arg=="--without-%s" % x:
     284        elif arg==without_str:
    137285            vars()["%s_ENABLED" % x] = False
    138286            matched = True
     
    145293    for x in SWITCHES:
    146294        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)
     295    print("build switches:")
     296    for k in sorted(SWITCHES):
     297        v = switches_info[k]
     298        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150299
    151300    #sanity check the flags:
     
    153302        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154303        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
    164304    if x11_ENABLED and WIN32:
    165305        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:
     306    if gtk_x11_ENABLED and not x11_ENABLED:
     307        print("Error: you must enable x11 to support gtk_x11!")
     308        exit(1)
     309    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167310        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)
     311    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     312        print("Warning: you probably want to build at least the client or server!")
     313    if DEFAULT and not pillow_ENABLED:
     314        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     315    if minify_ENABLED:
     316        r = get_status_output(["uglifyjs", "--version"])[0]
     317        if r==0:
     318            minifier = "uglifyjs"
     319        else:
     320            print("Warning: uglifyjs failed and return %i" % r)
     321            try:
     322                import yuicompressor
     323                assert yuicompressor
     324                minifier = "yuicompressor"
     325            except ImportError as e:
     326                print("Warning: yuicompressor module not found, cannot minify")
     327                minify_ENABLED = False
     328    if not enc_x264_ENABLED and not vpx_ENABLED:
     329        print("Warning: no x264 and no vpx support!")
     330        print(" you should enable at least one of these two video encodings")
    174331
    175332
    176333#*******************************************************************************
    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",
     334# default sets:
     335
     336external_includes = ["hashlib",
    204337                     "ctypes", "platform"]
     338
     339
     340if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     341    external_includes += ["gi"]
     342elif gtk2_ENABLED or x11_ENABLED:
     343    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205344
    206345external_excludes = [
     
    212351                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213352                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
     353                    "curses", "pdb",
     354                    "urllib2", "tty",
     355                    "cookielib", "ftplib", "httplib", "fileinput",
    218356                    "distutils", "setuptools", "doctest"
    219357                    ]
    220 
     358if not html5_ENABLED and not crypto_ENABLED:
     359    external_excludes += ["ssl", "_ssl"]
     360if not html5_ENABLED:
     361    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     362
     363if not client_ENABLED and not server_ENABLED:
     364    excludes += ["PIL"]
     365if not dbus_ENABLED:
     366    excludes += ["dbus"]
    221367
    222368
    223369#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     370#for distutils / py2app and cx_freeze
    225371#use the following functions, which should get the right
    226372#data in the global variables "packages", "modules" and "excludes"
     
    253399
    254400def add_modules(*mods):
     401    def add(v):
     402        global modules
     403        if v not in modules:
     404            modules.append(v)
     405    do_add_modules(add, *mods)
     406
     407def do_add_modules(op, *mods):
    255408    """ adds the packages and any .py module found in the packages to the "modules" list
    256409    """
    257410    global modules
    258411    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     412        #ugly path stripping:
     413        if x.startswith("./"):
     414            x = x[2:]
     415        if x.endswith(".py"):
     416            x = x[:-3]
     417            x = x.replace("/", ".") #.replace("\\", ".")
    261418        pathname = os.path.sep.join(x.split("."))
     419        #is this a file module?
     420        f = "%s.py" % pathname
     421        if os.path.exists(f) and os.path.isfile(f):
     422            op(x)
    262423        if os.path.exists(pathname) and os.path.isdir(pathname):
    263424            #add all file modules found in this directory
    264425            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     426                #make sure we only include python files,
     427                #and ignore eclipse copies
     428                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266429                    fname = os.path.join(pathname, f)
    267430                    if os.path.isfile(fname):
    268431                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     432                        op(modname)
    270433
    271434def toggle_packages(enabled, *module_names):
     
    275438        remove_packages(*module_names)
    276439
     440def toggle_modules(enabled, *module_names):
     441    if enabled:
     442        def op(v):
     443            global modules
     444            if v not in modules:
     445                modules.append(v)
     446        do_add_modules(op, *module_names)
     447    else:
     448        remove_packages(*module_names)
     449
     450
    277451#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 
     452add_modules("xpra", "xpra.platform", "xpra.net")
     453add_modules("xpra.scripts.main")
     454
     455
     456def add_data_files(target_dir, files):
     457    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     458    assert type(target_dir)==str
     459    assert type(files) in (list, tuple)
     460    data_files.append((target_dir, files))
     461
     462
     463#for pretty printing of options:
     464def print_option(prefix, k, v):
     465    if type(v)==dict:
     466        print("%s* %s:" % (prefix, k))
     467        for kk,vv in v.items():
     468            print_option(" "+prefix, kk, vv)
     469    else:
     470        print("%s* %s=%s" % (prefix, k, v))
    284471
    285472#*******************************************************************************
    286473# Utility methods for building with Cython
    287474def 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)
    292475    from distutils.version import LooseVersion
    293476    if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
     
    297480                 % (cython_version, ".".join([str(part) for part in min_version])))
    298481
    299 def cython_add(extension, min_version=(0, 14, 0)):
     482def cython_add(extension, min_version=(0, 19)):
    300483    #gentoo does weird things, calls --no-compile with build *and* install
    301484    #then expects to find the cython modules!? ie:
     
    303486    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304487        return
    305     global ext_modules, cmdclass
    306488    cython_version_check(min_version)
    307489    from Cython.Distutils import build_ext
    308490    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     491    global cmdclass
     492    cmdclass['build_ext'] = build_ext
     493
     494def insert_into_keywords(kw, key, *args):
     495    values = kw.setdefault(key, [])
     496    for arg in args:
     497        values.insert(0, arg)
    310498
    311499def add_to_keywords(kw, key, *args):
     
    319507
    320508
     509def checkdirs(*dirs):
     510    for d in dirs:
     511        if not os.path.exists(d) or not os.path.isdir(d):
     512            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     513
    321514PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
     515#override the pkgconfig file,
     516#we don't need to link against any of these:
     517gtk2_ignored_tokens=[("-l%s" % x) for x in
     518                     ["fontconfig", "freetype", "cairo",
     519                      "atk-1.0", "pangoft2-1.0", "pango-1.0", "pangocairo-1.0",
     520                      "gio-2.0", "gdk_pixbuf-2.0"]]
    322521
    323522GCC_VERSION = []
     
    326525    if len(GCC_VERSION)==0:
    327526        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:
     527        r, _, err = get_status_output(cmd)
     528        if r==0:
    332529            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     530            for line in err.splitlines():
    334531                if line.startswith(V_LINE):
    335532                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343540    return GCC_VERSION
    344541
    345 def make_constants_pxi(constants_path, pxi_path):
     542def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346543    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):
     544    with open(constants_path) as f:
     545        for line in f:
     546            data = line.split("#", 1)[0].strip()
     547            # data can be empty ''...
     548            if not data:
     549                continue
     550            # or a pair like 'cFoo "Foo"'...
     551            elif len(data.split()) == 2:
     552                (pyname, cname) = data.split()
     553                constants.append((pyname, cname))
     554            # or just a simple token 'Foo'
     555            else:
     556                constants.append(data)
     557
     558    with open(pxi_path, "w") as out:
     559        if constants:
     560            out.write("cdef extern from *:\n")
     561            ### Apparently you can't use | on enum's?!
     562            # out.write("    enum MagicNumbers:\n")
     563            # for const in constants:
     564            #     if isinstance(const, tuple):
     565            #         out.write('        %s %s\n' % const)
     566            #     else:
     567            #         out.write('        %s\n' % (const,))
     568            for const in constants:
     569                if isinstance(const, tuple):
     570                    out.write('    unsigned int %s %s\n' % const)
     571                else:
     572                    out.write('    unsigned int %s\n' % (const,))
     573
     574            out.write("constants = {\n")
     575            for const in constants:
     576                if isinstance(const, tuple):
     577                    pyname = const[0]
     578                else:
     579                    pyname = const
     580                out.write('    "%s": %s,\n' % (pyname, pyname))
     581            out.write("}\n")
     582            if kwargs:
     583                out.write("\n\n")
     584
     585        if kwargs:
     586            for k, v in kwargs.items():
     587                out.write('DEF %s = %s\n' % (k, v))
     588
     589
     590def should_rebuild(src_file, bin_file):
     591    if not os.path.exists(bin_file):
     592        return "no file"
     593    elif rebuild_ENABLED:
     594        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     595            return "binary file out of date"
     596        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     597            return "newer build file"
     598    return None
     599
     600def make_constants(*paths, **kwargs):
    384601    base = os.path.join(os.getcwd(), *paths)
    385602    constants_file = "%s.txt" % base
    386603    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"
     604    reason = should_rebuild(constants_file, pxi_file)
    394605    if reason:
    395606        if verbose_ENABLED:
    396607            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
     608        make_constants_pxi(constants_file, pxi_file, **kwargs)
     609
    414610
    415611# 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 
     612def exec_pkgconfig(*pkgs_options, **ekw):
    423613    kw = dict(ekw)
     614    if "optimize" in kw:
     615        optimize = kw["optimize"]
     616        del kw["optimize"]
     617        if type(optimize)==bool:
     618            optimize = int(optimize)*3
     619        if not debug_ENABLED:
     620            add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     621    ignored_flags = kw.pop("ignored_flags", [])
     622    ignored_tokens = kw.pop("ignored_tokens", [])
     623
    424624    if len(pkgs_options)>0:
    425625        package_names = []
     
    438638            for option in options:
    439639                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:
     640                r, _, _ = get_status_output(cmd)
     641                if r==0:
    443642                    valid_option = option
    444643                    break
    445644            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     645                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)")))
    447646            package_names.append(valid_option)
    448647        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     648            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450649        flag_map = {'-I': 'include_dirs',
    451650                    '-L': 'library_dirs',
    452651                    '-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))
     652        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     653        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     654        if r!=0:
     655            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     656        env_cflags = os.environ.get("CFLAGS")       #["dpkg-buildflags", "--get", "CFLAGS"]
     657        env_ldflags = os.environ.get("LDFLAGS")     #["dpkg-buildflags", "--get", "LDFLAGS"]
     658        for s in (pkg_config_out, env_cflags, env_ldflags):
     659            if not s:
     660                continue
     661            for token in s.split():
     662                if token in ignored_tokens:
     663                    pass
     664                elif token[:2] in ignored_flags:
     665                    pass
     666                elif token[:2] in flag_map:
     667                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     668                elif token.startswith("-W"):
     669                    add_to_keywords(kw, 'extra_compile_args', token)
     670                else:# throw others to extra_link_args
     671                    add_to_keywords(kw, 'extra_link_args', token)
    468672    if warn_ENABLED:
    469673        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470674        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471675    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"
     676        if os.environ.get("CC", "").find("clang")>=0:
     677            #clang emits too many warnings with cython code,
     678            #so we can't enable Werror without turning off some warnings:
     679            #this list of flags should allow clang to build the whole source tree,
     680            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     681            #(un)commenting other switches.
     682            eifd = ["-Werror",
     683                    #"-Wno-unneeded-internal-declaration",
     684                    #"-Wno-unknown-attributes",
     685                    #"-Wno-unused-function",
     686                    #"-Wno-self-assign",
     687                    #"-Wno-sometimes-uninitialized",
     688                    #cython adds rpath to the compilation command??
     689                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     690                    "-Wno-unused-command-line-argument",
     691                    ]
     692        elif get_gcc_version()>=[4, 4]:
     693            eifd = ["-Werror"]
     694            if is_Debian() or is_Ubuntu() or is_Raspbian():
     695                #needed on Debian and Ubuntu to avoid this error:
     696                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     697                eifd.append("-Wno-error=strict-prototypes")
     698            if NETBSD:
     699                #see: http://trac.cython.org/ticket/395
     700                eifd += ["-fno-strict-aliasing"]
     701            elif FREEBSD:
     702                eifd += ["-Wno-error=unused-function"]
    475703        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     704            #older versions of OSX ship an old gcc,
     705            #not much we can do with this:
     706            eifd = []
     707        for eif in eifd:
     708            add_to_keywords(kw, 'extra_compile_args', eif)
    478709    if PIC_ENABLED:
    479710        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481712        add_to_keywords(kw, 'extra_compile_args', '-g')
    482713        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     714        if get_gcc_version()>=[4, 8]:
    485715            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486716            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     717    if rpath and kw.get("libraries"):
     718        insert_into_keywords(kw, "library_dirs", rpath)
     719        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
    487720    #add_to_keywords(kw, 'include_dirs', '.')
    488721    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     722        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490723    return kw
     724pkgconfig = exec_pkgconfig
    491725
    492726
    493727#*******************************************************************************
    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()
     728
     729
     730def get_base_conf_dir(install_dir, stripbuildroot=True):
     731    #in some cases we want to strip the buildroot (to generate paths in the config file)
     732    #but in other cases we want the buildroot path (when writing out the config files)
     733    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     734    #this is a bit hackish, but I can't think of a better way of detecting it
     735    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     736    dirs = (install_dir or sys.prefix).split(os.path.sep)
     737    if install_dir and stripbuildroot:
     738        pkgdir = os.environ.get("pkgdir")
     739        if "debian" in dirs and "tmp" in dirs:
     740            #ugly fix for stripping the debian tmp dir:
     741            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     742            while "tmp" in dirs:
     743                dirs = dirs[dirs.index("tmp")+1:]
     744        elif "debian" in dirs:
     745            #same for recent debian versions:
     746            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     747            i = dirs.index("debian")
     748            if dirs[i+1] == "xpra":
     749                dirs = dirs[i+2:]
     750        elif "BUILDROOT" in dirs:
     751            #strip rpm style build root:
     752            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     753            dirs = dirs[dirs.index("BUILDROOT")+2:]
     754        elif pkgdir and install_dir.startswith(pkgdir):
     755            #arch build dir:
     756            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     757        elif "usr" in dirs:
     758            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     759            #assume "/usr" or "/usr/local" is the build root
     760            while "usr" in dirs[1:]:
     761                dirs = dirs[dirs[1:].index("usr")+1:]
     762        elif "image" in dirs:
     763            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     764            while "image" in dirs:
     765                dirs = dirs[dirs.index("image")+2:]
     766    #now deal with the fact that "/etc" is used for the "/usr" prefix
     767    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     768    if dirs and dirs[-1]=="usr":
     769        dirs = dirs[:-1]
     770    #is this an absolute path?
     771    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     772        #ie: ["/", "usr"] or ["/", "usr", "local"]
     773        dirs.insert(0, os.path.sep)
     774    return dirs
     775
     776def get_conf_dir(install_dir, stripbuildroot=True):
     777    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     778    dirs.append("etc")
     779    dirs.append("xpra")
     780    return os.path.join(*dirs)
     781
     782def detect_xorg_setup(install_dir=None):
     783    from xpra.scripts import config
     784    config.debug = config.warn
     785    conf_dir = get_conf_dir(install_dir)
     786    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     787
     788def build_xpra_conf(install_dir):
     789    #generates an actual config file from the template
     790    xvfb_command = detect_xorg_setup(install_dir)
     791    from xpra.platform.features import DEFAULT_ENV
     792    def bstr(b):
     793        if b is None:
     794            return "auto"
     795        return ["no", "yes"][int(b)]
     796    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     797    conf_dir = get_conf_dir(install_dir)
     798    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     799    from xpra.platform.paths import get_socket_dirs
     800    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     801    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     802    socket_dirs = get_socket_dirs()
     803    if WIN32:
     804        bind = "Main"
    533805    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()
     806        if os.getuid()>0:
     807            #remove any paths containing the uid,
     808            #osx uses /var/tmp/$UID-Xpra,
     809            #but this should not be included in the default config for all users!
     810            #(the buildbot's uid!)
     811            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     812        bind = "auto"
     813    #FIXME: we should probably get these values from the default config instead
     814    pdf, postscript = "", ""
     815    if POSIX and printing_ENABLED:
     816        try:
     817            if "/usr/sbin" not in sys.path:
     818                sys.path.append("/usr/sbin")
     819            from xpra.platform.pycups_printing import get_printer_definition
     820            print("probing cups printer definitions")
     821            pdf = get_printer_definition("pdf")
     822            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     823            print("pdf=%s, postscript=%s" % (pdf, postscript))
     824        except Exception as e:
     825            print("could not probe for pdf/postscript printers: %s" % e)
     826    def pretty_cmd(cmd):
     827        return " ".join(cmd)
     828    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     829    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     830    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     831    #no python-avahi on RH / CentOS, need dbus module on *nix:
     832    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     833    SUBS = {
     834            'xvfb_command'          : pretty_cmd(xvfb_command),
     835            'ssh_command'           : DEFAULT_SSH_COMMAND,
     836            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     837            'remote_logging'        : "both",
     838            'start_env'             : start_env,
     839            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     840            'pulseaudio_command'    : pretty_cmd(DEFAULT_PULSEAUDIO_COMMAND),
     841            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     842            'conf_dir'              : conf_dir,
     843            'bind'                  : bind,
     844            'ssl_cert'              : ssl_cert or "",
     845            'ssl_key'               : ssl_key or "",
     846            'systemd_run'           : get_default_systemd_run(),
     847            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     848            'log_dir'               : "auto",
     849            'mdns'                  : bstr(mdns),
     850            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     851            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     852            'pdf_printer'           : pdf,
     853            'postscript_printer'    : postscript,
     854            'webcam'                : ["no", "auto"][webcam],
     855            'mousewheel'            : "on",
     856            'printing'              : printing_ENABLED,
     857            'dbus_control'          : bstr(dbus_ENABLED),
     858            'mmap'                  : bstr(True),
     859            }
     860    def convert_templates(subdirs=[]):
     861        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     862        #get conf dir for install, without stripping the build root
     863        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     864        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     865        if not os.path.exists(target_dir):
     866            try:
     867                os.makedirs(target_dir)
     868            except Exception as e:
     869                print("cannot create target dir '%s': %s" % (target_dir, e))
     870        for f in sorted(os.listdir(dirname)):
     871            if f.endswith("osx.conf.in") and not OSX:
     872                continue
     873            filename = os.path.join(dirname, f)
     874            if os.path.isdir(filename):
     875                convert_templates(subdirs+[f])
     876                continue
     877            if not f.endswith(".in"):
     878                continue
     879            with open(filename, "r") as f_in:
     880                template  = f_in.read()
     881            target_file = os.path.join(target_dir, f[:-len(".in")])
     882            print("generating %s from %s" % (target_file, f))
     883            with open(target_file, "w") as f_out:
     884                config_data = template % SUBS
     885                f_out.write(config_data)
     886    convert_templates()
    576887
    577888
     
    580891    #clean and sdist don't actually use cython,
    581892    #so skip this (and avoid errors)
    582     def pkgconfig(*pkgs_options, **ekw):
    583         return {}
     893    pkgconfig = no_pkgconfig
    584894    #always include everything in this case:
    585895    add_packages("xpra")
    586896    #ensure we remove the files we generate:
    587897    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",
     898                   "xpra/build_info.py",
     899                   "xpra/monotonic_time.c",
     900                   "xpra/gtk_common/gtk2/gdk_atoms.c",
     901                   "xpra/gtk_common/gtk2/gdk_bindings.c",
     902                   "xpra/gtk_common/gtk3/gdk_atoms.c",
     903                   "xpra/x11/gtk2/constants.pxi",
     904                   "xpra/x11/gtk2/gdk_bindings.c",
     905                   "xpra/x11/gtk2/gdk_display_source.c",
     906                   "xpra/x11/gtk3/gdk_display_source.c",
    592907                   "xpra/x11/bindings/constants.pxi",
    593908                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597912                   "xpra/x11/bindings/randr_bindings.c",
    598913                   "xpra/x11/bindings/core_bindings.c",
     914                   "xpra/x11/bindings/posix_display_source.c",
    599915                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     916                   "xpra/x11/bindings/xi2_bindings.c",
     917                   "xpra/platform/win32/propsys.cpp",
     918                   "xpra/platform/darwin/gdk_bindings.c",
     919                   "xpra/platform/xposix/sd_listen.c",
     920                   "xpra/net/bencode/cython_bencode.c",
     921                   "xpra/net/vsock.c",
     922                   "xpra/buffers/membuf.c",
    601923                   "xpra/codecs/vpx/encoder.c",
    602924                   "xpra/codecs/vpx/decoder.c",
    603925                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     926                   "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     927                   "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     928                   "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     929                   "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
    605930                   "xpra/codecs/enc_x264/encoder.c",
    606931                   "xpra/codecs/enc_x265/encoder.c",
    607                    "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     932                   "xpra/codecs/jpeg/encoder.c",
     933                   "xpra/codecs/jpeg/decoder.c",
     934                   "xpra/codecs/enc_ffmpeg/encoder.c",
     935                   "xpra/codecs/v4l2/constants.pxi",
     936                   "xpra/codecs/v4l2/pusher.c",
     937                   "xpra/codecs/libav_common/av_log.c",
    610938                   "xpra/codecs/dec_avcodec2/decoder.c",
     939                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611940                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614941                   "xpra/codecs/xor/cyxor.c",
    615942                   "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")
     943                   "xpra/codecs/nvapi_version.c",
     944                   "xpra/client/gtk3/cairo_workaround.c",
     945                   "xpra/server/cystats.c",
     946                   "xpra/server/window/region.c",
     947                   "xpra/server/window/motion.c",
     948                   "xpra/server/pam.c",
     949                   "etc/xpra/xpra.conf",
     950                   #special case for the generated xpra conf files in build (see #891):
     951                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     952    for x in CLEAN_FILES:
     953        p, ext = os.path.splitext(x)
     954        if ext in (".c", ".cpp", ".pxi"):
     955            #clean the Cython annotated html files:
     956            CLEAN_FILES.append(p+".html")
     957            if WIN32 and ext!=".pxi":
     958                #on win32, the build creates ".pyd" files, clean those too:
     959                CLEAN_FILES.append(p+".pyd")
     960                #when building with python3, we need to clean files named like:
     961                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     962                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     963                CLEAN_FILES += glob.glob(filename)
    623964    if 'clean' in sys.argv:
    624965        CLEAN_FILES.append("xpra/build_info.py")
     
    630971            os.unlink(filename)
    631972
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     973from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    633974
    634975if "clean" not in sys.argv:
    635976    # Add build info to build_info.py file:
    636977    record_build_info()
     978    # ensure it is included in the module list if it didn't exist before
     979    add_modules(BUILD_INFO_FILE)
    637980
    638981if "sdist" in sys.argv:
    639982    record_src_info()
    640983
    641 if "install" in sys.argv:
     984if "install" in sys.argv or "build" in sys.argv:
    642985    #if installing from source tree rather than
    643986    #from a source snapshot, we may not have a "src_info" file
     
    645988    if not has_src_info():
    646989        record_src_info()
     990        # ensure it is now included in the module list
     991        add_modules(SRC_INFO_FILE)
    647992
    648993
     
    6631008    return m
    6641009
     1010
     1011def install_html5(install_dir="www"):
     1012    from setup_html5 import install_html5 as do_install_html5
     1013    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1014
     1015
    6651016#*******************************************************************************
    6661017if 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"]
     1018    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1019    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821020    add_packages("xpra.platform.win32")
    8831021    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"]
     1022
     1023    #this is where the win32 gi installer will put things:
     1024    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1025
     1026    #only add the cx_freeze specific options
     1027    #if we aren't just building the Cython bits with "build_ext":
     1028    if "build_ext" not in sys.argv:
     1029        #with cx_freeze, we don't use py_modules
     1030        del setup_options["py_modules"]
     1031        import cx_Freeze                            #@UnresolvedImport
     1032        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1033        CX5 = cx_Freeze.version>="5"
     1034        if CX5 and not hasattr(sys, "base_prefix"):
     1035            #workaround for broken sqlite hook with python 2.7, see:
     1036            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1037            sys.base_prefix = sys.prefix
     1038
     1039        #cx_freeze doesn't use "data_files"...
     1040        del setup_options["data_files"]
     1041        #it wants source files first, then where they are placed...
     1042        #one item at a time (no lists)
     1043        #all in its own structure called "include_files" instead of "data_files"...
     1044        def add_data_files(target_dir, files):
     1045            if verbose_ENABLED:
     1046                print("add_data_files(%s, %s)" % (target_dir, files))
     1047            assert type(target_dir)==str
     1048            assert type(files) in (list, tuple)
     1049            for f in files:
     1050                target_file = os.path.join(target_dir, os.path.basename(f))
     1051                data_files.append((f, target_file))
     1052
     1053        #pass a potentially nested dictionary representing the tree
     1054        #of files and directories we do want to include
     1055        #relative to gnome_include_path
     1056        def add_dir(base, defs):
     1057            if verbose_ENABLED:
     1058                print("add_dir(%s, %s)" % (base, defs))
     1059            if type(defs) in (list, tuple):
     1060                for sub in defs:
     1061                    if type(sub)==dict:
     1062                        add_dir(base, sub)
     1063                    else:
     1064                        assert type(sub)==str
     1065                        filename = os.path.join(gnome_include_path, base, sub)
     1066                        if os.path.exists(filename):
     1067                            add_data_files(base, [filename])
     1068                        else:
     1069                            print("Warning: missing '%s'" % filename)
     1070            else:
     1071                assert type(defs)==dict
     1072                for d, sub in defs.items():
     1073                    assert type(sub) in (dict, list, tuple)
     1074                    #recurse down:
     1075                    add_dir(os.path.join(base, d), sub)
     1076
     1077        #convenience method for adding GI libs and "typelib" and "gir":
     1078        def add_gi(*libs):
     1079            if verbose_ENABLED:
     1080                print("add_gi(%s)" % str(libs))
     1081            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1082            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1083
     1084        def add_DLLs(*dll_names):
     1085            try:
     1086                do_add_DLLs(*dll_names)
     1087            except Exception as e:
     1088                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1089                print(" %s" % e)
     1090                sys.exit(1)
     1091
     1092        def do_add_DLLs(*dll_names):
     1093            dll_names = list(dll_names)
     1094            dll_files = []
     1095            import re
     1096            version_re = re.compile("\-[0-9\.\-]+$")
     1097            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1098            if os.path.exists(gnome_include_path):
     1099                dirs.insert(0, gnome_include_path)
     1100            if verbose_ENABLED:
     1101                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1102            for d in dirs:
     1103                if not os.path.exists(d):
     1104                    continue
     1105                for x in os.listdir(d):
     1106                    dll_path = os.path.join(d, x)
     1107                    x = x.lower()
     1108                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1109                        continue
     1110                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1111                    if verbose_ENABLED:
     1112                        print("checking %s: %s" % (x, nameversion))
     1113                    m = version_re.search(nameversion)          #look for version part of filename
     1114                    if m:
     1115                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1116                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1117                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1118                    else:
     1119                        dll_version = ""                        #no version
     1120                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1121                    if dll_name in dll_names:
     1122                        #this DLL is on our list
     1123                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1124                        dll_files.append(dll_path)
     1125                        dll_names.remove(dll_name)
     1126            if len(dll_names)>0:
     1127                print("some DLLs could not be found:")
     1128                for x in dll_names:
     1129                    print(" - lib%s*.dll" % x)
     1130            add_data_files("", dll_files)
     1131
     1132        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1133        #(ie: "libatk-1.0-0.dll" -> "atk")
     1134        if sound_ENABLED or gtk3_ENABLED:
     1135            add_DLLs('gio', 'girepository', 'glib',
     1136                     'gnutls', 'gobject', 'gthread',
     1137                     'orc', 'stdc++',
     1138                     'winpthread',
     1139                     )
     1140        if gtk3_ENABLED:
     1141            add_DLLs('atk',
     1142                     'dbus', 'dbus-glib',
     1143                     'gdk', 'gdk_pixbuf', 'gtk',
     1144                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1145                     'harfbuzz', 'harfbuzz-gobject',
     1146                     'jasper', 'epoxy',
     1147                     'intl',
     1148                     'p11-kit',
     1149                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1150            #these are missing in newer aio installers (sigh):
     1151            do_add_DLLs('javascriptcoregtk')
     1152            if opengl_ENABLED:
     1153                do_add_DLLs('gdkglext', 'gtkglext')
     1154
     1155        if gtk3_ENABLED:
     1156            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1157            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1158                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1159            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1160                              "p11-kit", "xml",
     1161                              {"icons"  : ["hicolor"]},
     1162                              {"locale" : ["en"]},
     1163                              {"themes" : ["Default"]}
     1164                             ])
     1165        if gtk3_ENABLED or sound_ENABLED:
     1166            add_dir('lib', ["gio"])
     1167            packages.append("gi")
     1168            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1169                   "GObject-2.0")
     1170        if gtk3_ENABLED:
     1171            add_gi("Atk-1.0",
     1172                   "fontconfig-2.0", "freetype2-2.0",
     1173                   "GDesktopEnums-3.0", "Soup-2.4",
     1174                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0",
     1175                   "HarfBuzz-0.0",
     1176                   "Libproxy-1.0", "libxml2-2.0",
     1177                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1178                   "Rsvg-2.0",
     1179                   "win32-1.0")
     1180            if opengl_ENABLED:
     1181                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1182            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1183        if server_ENABLED and not PYTHON3:
     1184            add_DLLs('sqlite3')
     1185
     1186        if gtk2_ENABLED:
     1187            add_dir('lib',      {
     1188                "gdk-pixbuf-2.0":    {
     1189                    "2.10.0"    :   {
     1190                        "loaders"   :
     1191                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp")]
     1192                        },
     1193                    },
     1194                })
     1195
     1196        if sound_ENABLED:
     1197            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1198            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1199                   "GstTag-1.0")
     1200            add_DLLs('gstreamer', 'orc-test')
     1201            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1202                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1203                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1204                      "basecamerabinsrc", "mpegts", "photography",
     1205                      ):
     1206                add_DLLs('gst%s' % p)
     1207            #DLLs needed by the plugins:
     1208            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1209            #add the gstreamer plugins we need:
     1210            GST_PLUGINS = ("app",
     1211                           "cutter",
     1212                           #muxers:
     1213                           "gdp", "matroska", "ogg", "isomp4",
     1214                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1215                           "coreelements", "directsoundsink", "directsoundsrc", "wasapi",
     1216                           #codecs:
     1217                           "opus", "opusparse", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1218                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1219                           #untested: a52dec, voaacenc
     1220                           )
     1221            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1222            #END OF SOUND
     1223
     1224        if server_ENABLED:
     1225            #used by proxy server:
     1226            external_includes += ["multiprocessing", "setproctitle"]
     1227
     1228        external_includes += ["encodings"]
     1229        if PYTHON3:
     1230            #hopefully, cx_Freeze will fix this horror:
     1231            #(we shouldn't have to deal with DLL dependencies)
     1232            import site
     1233            lib_python = os.path.dirname(site.getsitepackages()[0])
     1234            lib_dynload_dir = os.path.join(lib_python, "lib-dynload")
     1235            add_data_files('', glob.glob("%s/zlib*dll" % lib_dynload_dir))
     1236            for x in ("io", "codecs", "abc", "_weakrefset", "encodings"):
     1237                add_data_files("", glob.glob("%s/%s*" % (lib_python, x)))
     1238        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1239        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1240        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1241        cx_freeze_options = {
     1242                            "includes"          : external_includes,
     1243                            "packages"          : packages,
     1244                            "include_files"     : data_files,
     1245                            "excludes"          : excludes,
     1246                            "include_msvcr"     : True,
     1247                            "bin_excludes"      : bin_excludes,
     1248                            }
     1249        if not CX5:
     1250            cx_freeze_options.update({
     1251                            "compressed"        : True,
     1252                            "create_shared_zip" : zip_ENABLED,
     1253                            })
     1254        else:
     1255            #cx_Freeze v5 workarounds:
     1256            if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
     1257                add_packages("numpy.core._methods", "numpy.lib.format")
     1258
     1259        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1260        executables = []
     1261        setup_options["executables"] = executables
     1262
     1263        def add_exe(script, icon, base_name, base="Console"):
     1264            kwargs = {}
     1265            if not CX5:
     1266                kwargs = {
     1267                    "compress"              : True,
     1268                    "copyDependentFiles"    : True,
     1269                    "appendScriptToExe"     : False,
     1270                    "appendScriptToLibrary" : True,
     1271                    }
     1272            executables.append(Executable(
     1273                        script                  = script,
     1274                        initScript              = None,
     1275                        #targetDir               = "dist",
     1276                        icon                    = "win32/%s" % icon,
     1277                        targetName              = "%s.exe" % base_name,
     1278                        base                    = base,
     1279                        **kwargs))
     1280
     1281        def add_console_exe(script, icon, base_name):
     1282            add_exe(script, icon, base_name)
     1283        def add_gui_exe(script, icon, base_name):
     1284            add_exe(script, icon, base_name, base="Win32GUI")
     1285        def add_service_exe(script, icon, base_name):
     1286            add_exe(script, icon, base_name, base="Win32Service")
     1287
     1288        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1289        if client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1290            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1291            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1292            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1293            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1294            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1295        if gtk2_ENABLED:
     1296            #these need porting..
     1297            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1298        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1299            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1300        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1301        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1302        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1303        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1304        if gtk2_ENABLED or gtk3_ENABLED:
     1305            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1306            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1307            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1308        if client_ENABLED or server_ENABLED:
     1309            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1310            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1311        if server_ENABLED:
     1312            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1313            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1314            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1315            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1316        if client_ENABLED:
     1317            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1318            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1319            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1320        if client_ENABLED:
     1321            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1322            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1323        if sound_ENABLED:
     1324            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1325            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1326            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1327            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1328            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1329        if opengl_ENABLED:
     1330            add_console_exe("xpra/client/gl/gtk_base/gtkgl_check.py", "opengl.ico",       "OpenGL_check")
     1331        if webcam_ENABLED:
     1332            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1333            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1334        if printing_ENABLED:
     1335            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1336            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1337            add_DLLs("pdfium")  #libpdfium.dll
     1338        if nvenc_ENABLED:
     1339            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1340        if nvfbc_ENABLED:
     1341            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1342        if nvfbc_ENABLED or nvenc_ENABLED:
     1343            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1344
     1345        if example_ENABLED:
     1346            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1347            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1348            if not PYTHON3:
     1349                add_gui_exe("xpra/client/gtk_base/example/gl_colors_gradient.py",   "encoding.ico",     "OpenGL-Colors-Gradient")
     1350            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1351            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1352            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1353            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1354            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1355
     1356        #FIXME: how do we figure out what target directory to use?
     1357        print("calling build_xpra_conf in-place")
     1358        #building etc files in-place:
     1359        build_xpra_conf(".")
     1360        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1361        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1362        add_data_files('etc/xpra', glob.glob("etc/xpra/nvfbc*.keys"))
     1363        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1364        #build minified html5 client in temporary build dir:
     1365        if "clean" not in sys.argv and html5_ENABLED:
     1366            install_html5(os.path.join(install, "www"), )
     1367            for k,v in glob_recurse("build/www").items():
     1368                if (k!=""):
     1369                    k = os.sep+k
     1370                add_data_files('www'+k, v)
     1371
     1372    if client_ENABLED or server_ENABLED:
     1373        add_data_files('',      ['COPYING', 'README', 'win32/website.url'])
     1374        add_data_files('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*'))
     1375
     1376    if webcam_ENABLED:
     1377        add_data_files('',      ['win32\\DirectShow.tlb'])
     1378        add_modules("comtypes.gen.stdole", "comtypes.gen.DirectShowLib")
     1379
    9071380    remove_packages(*external_excludes)
     1381    external_includes.append("mmap")
    9081382    remove_packages(#not used on win32:
    909                     "mmap",
    9101383                    #we handle GL separately below:
    9111384                    "OpenGL", "OpenGL_accelerate",
     
    9131386                    "ctypes.macholib")
    9141387
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1388    if webcam_ENABLED:
     1389        external_includes.append("cv2")
     1390
     1391    if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
    9161392        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1393        external_includes.append("numpy")
    9181394    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1395        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211396                        "pydoc")
    9221397
    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:
     1398    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1399    remove_packages("pygst", "gst", "gst.extend")
     1400
     1401    #add subset of PyOpenGL modules (only when installing):
     1402    if opengl_ENABLED and ("install_exe" in sys.argv or "install" in sys.argv):
    9291403        #for this hack to work, you must add "." to the sys.path
    9301404        #so python can load OpenGL from the install directory
    9311405        #(further complicated by the fact that "." is the "frozen" path...)
     1406        #but we re-add those two directories to the library.zip as part of the build script
    9321407        import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
     1408        print("*** copying PyOpenGL modules to %s ***" % install)
    9351409        for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
    9361410            module_dir = os.path.dirname(module.__file__ )
    9371411            try:
    9381412                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1413                    module_dir, os.path.join(install, module_name),
     1414                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411415                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1416            except Exception as e:
     1417                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441418                    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 
     1419
     1420    add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1421    add_data_files('', ["bell.wav"])
     1422
     1423    #END OF win32
    9831424#*******************************************************************************
    9841425else:
    9851426    #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"]))
     1427    scripts += ["scripts/xpra", "scripts/xpra_launcher", "scripts/xpra_browser", "scripts/udev_product_version"]
     1428    add_data_files("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1", "man/xpra_browser.1"])
     1429    add_data_files("share/xpra",          ["README", "COPYING"])
     1430    add_data_files("share/xpra/icons",    glob.glob("icons/*"))
     1431    add_data_files("share/applications",  ["xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1432    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1433    add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png"])
     1434    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1435    add_data_files('share/xpra/',         ["bell.wav"])
     1436
     1437    #here, we override build and install so we can
     1438    #generate our /etc/xpra/xpra.conf
     1439    class build_override(build):
     1440        def run(self):
     1441            build.run(self)
     1442            self.run_command("build_conf")
     1443
     1444    class build_conf(build):
     1445        def run(self):
     1446            try:
     1447                build_base = self.distribution.command_obj['build'].build_base
     1448            except:
     1449                build_base = self.build_base
     1450            build_xpra_conf(build_base)
     1451
     1452    class install_data_override(install_data):
     1453        def run(self):
     1454            print("install_data_override: install_dir=%s" % self.install_dir)
     1455            if html5_ENABLED:
     1456                install_html5(os.path.join(self.install_dir, "share/xpra/www"))
     1457            install_data.run(self)
     1458
     1459            root_prefix = self.install_dir.rstrip("/")
     1460            if root_prefix.endswith("/usr"):
     1461                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1462            build_xpra_conf(root_prefix)
     1463
     1464            def copytodir(src, dst_dir, dst_name=None, chmod=0o644):
     1465                #convert absolute paths:
     1466                if dst_dir.startswith("/"):
     1467                    dst_dir = root_prefix+dst_dir
     1468                else:
     1469                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1470                #make sure the target directory exists:
     1471                self.mkpath(dst_dir)
     1472                #generate the target filename:
     1473                filename = os.path.basename(src)
     1474                dst_file = os.path.join(dst_dir, dst_name or filename)
     1475                #copy it
     1476                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1477                shutil.copyfile(src, dst_file)
     1478                if chmod:
     1479                    os.chmod(dst_file, chmod)
     1480
     1481            if printing_ENABLED and POSIX:
     1482                #install "/usr/lib/cups/backend" with 0700 permissions:
     1483                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1484
     1485            if x11_ENABLED:
     1486                #install xpra_Xdummy if we need it:
     1487                xvfb_command = detect_xorg_setup()
     1488                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1489                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1490                #install xorg*.conf, cuda.conf and nvenc.keys:
     1491                etc_xpra_files = ["xorg.conf"]
     1492                if uinput_ENABLED:
     1493                    etc_xpra_files.append("xorg-uinput.conf")
     1494                if nvenc_ENABLED or nvfbc_ENABLED:
     1495                    etc_xpra_files.append("cuda.conf")
     1496                if nvenc_ENABLED:
     1497                    etc_xpra_files.append("nvenc.keys")
     1498                if nvfbc_ENABLED:
     1499                    etc_xpra_files.append("nvfbc.keys")
     1500                for x in etc_xpra_files:
     1501                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1502                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1503
     1504            if pam_ENABLED:
     1505                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1506
     1507            #fedora and centos can use just "/lib/systemd/system" here
     1508            #but debian gets totally confused if we do...
     1509            systemd_dir = "/usr/lib/systemd/system"
     1510            if service_ENABLED:
     1511                #Linux init service:
     1512                if os.path.exists("/bin/systemctl"):
     1513                    if sd_listen_ENABLED:
     1514                        copytodir("service/xpra.service", systemd_dir)
     1515                    else:
     1516                        copytodir("service/xpra-nosocketactivation.service", systemd_dir, dst_name="xpra.service")
     1517                else:
     1518                    copytodir("service/xpra", "/etc/init.d")
     1519                if os.path.exists("/etc/sysconfig"):
     1520                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1521                elif os.path.exists("/etc/default"):
     1522                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1523            if sd_listen_ENABLED:
     1524                copytodir("service/xpra.socket", systemd_dir)
     1525            if dbus_ENABLED and proxy_ENABLED:
     1526                copytodir("dbus/xpra.conf", "/etc/dbus-1/system.d")
     1527
     1528
     1529    # add build_conf to build step
     1530    cmdclass.update({
     1531             'build'        : build_override,
     1532             'build_conf'   : build_conf,
     1533             'install_data' : install_data_override,
     1534             })
    9971535
    9981536    if OSX:
     1537        #pyobjc needs email.parser
     1538        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
    9991539        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001540        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011541        add_packages("xpra.platform.darwin")
     1542        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1543        #to support GStreamer 1.x we need this:
     1544        modules.append("importlib")
     1545        modules.append("xpra.scripts.gtk_info")
     1546        modules.append("xpra.scripts.show_webcam")
    10021547    else:
    10031548        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041549        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")
     1550        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1551        #not supported by all distros, but doesn't hurt to install them anyway:
     1552        for x in ("tmpfiles.d", "sysusers.d"):
     1553            add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1554        if uinput_ENABLED:
     1555            add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081556
    10091557    #gentoo does weird things, calls --no-compile with build *and* install
     
    10121560    #otherwise we use the flags to skip pkgconfig
    10131561    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))
     1562        pkgconfig = no_pkgconfig
    10331563
    10341564    if OSX and "py2app" in sys.argv:
     
    10451575        remove_packages(*external_excludes)
    10461576
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1577        try:
     1578            from xpra.src_info import REVISION
     1579        except:
     1580            REVISION = "unknown"
     1581        Plist = {
     1582            "CFBundleDocumentTypes" : {
     1583                "CFBundleTypeExtensions"    : ["Xpra"],
     1584                "CFBundleTypeName"          : "Xpra Session Config File",
     1585                "CFBundleName"              : "Xpra",
     1586                "CFBundleTypeRole"          : "Viewer",
     1587                },
     1588            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2017 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1589            "CFBundleIdentifier"            : "org.xpra.xpra",
     1590            }
    10531591        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541592        #so the make-app.sh script still has to hack around this problem.
     
    10651603            }
    10661604        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1605        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1606
     1607    if OSX:
     1608        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1609        #so instead we have to query each package seperately and merge the results:
     1610        def osx_pkgconfig(*pkgs_options, **ekw):
     1611            kw = dict(ekw)
     1612            for pkg in pkgs_options:
     1613                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1614                if pkg.lower().startswith("x"):
     1615                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1616                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1617                kw = exec_pkgconfig(pkg, **kw)
     1618                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1619            return kw
     1620
     1621        pkgconfig = osx_pkgconfig
    10681622
    10691623
    10701624if 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 
     1625    if WIN32 or OSX:
     1626        external_includes.append("websockify")
     1627        external_includes.append("numpy")
     1628        external_includes.append("ssl")
     1629        external_includes.append("_ssl")
     1630        if not PYTHON3:
     1631            external_includes.append("mimetypes")
     1632            external_includes.append("mimetools")
     1633            external_includes.append("BaseHTTPServer")
     1634
     1635
     1636if annotate_ENABLED:
     1637    from Cython.Compiler import Options
     1638    Options.annotate = True
    10761639
    10771640
    10781641#*******************************************************************************
    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")
     1642buffers_c = "xpra/buffers/buffers.c"
     1643memalign_c = "xpra/buffers/memalign.c"
     1644xxhash_c = "xpra/buffers/xxhash.c"
     1645membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1646
     1647add_packages("xpra.buffers")
     1648buffers_pkgconfig = pkgconfig(optimize=3)
     1649cython_add(Extension("xpra.buffers.membuf",
     1650            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1651
     1652
     1653toggle_packages(dbus_ENABLED, "xpra.dbus")
     1654toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1655toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
     1656toggle_packages(rfb_ENABLED, "xpra.server.rfb")
     1657toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1658toggle_packages(server_ENABLED, "xpra.server.window")
     1659toggle_packages(shadow_ENABLED, "xpra.server.shadow")
     1660toggle_packages(server_ENABLED or client_ENABLED, "xpra.clipboard")
     1661toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1662
     1663#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1664if dbus_ENABLED and server_ENABLED:
     1665    add_packages("xpra.server.dbus")
     1666
     1667if OSX:
     1668    quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1669    add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1670                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h",
     1671                    '-ObjC',
     1672                    '-mmacosx-version-min=10.10')
     1673    add_to_keywords(quartz_pkgconfig, 'extra_link_args',
     1674                    '-framework', 'Foundation',
     1675                    '-framework', 'AppKit',
     1676                    )
     1677    cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1678                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1679                language="objc",
     1680                **quartz_pkgconfig
     1681                ))
     1682
     1683monotonic_time_pkgconfig = pkgconfig()
     1684if not OSX and not WIN32:
     1685    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1686cython_add(Extension("xpra.monotonic_time",
     1687            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1688            **monotonic_time_pkgconfig
     1689            ))
     1690
     1691
     1692toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841693if x11_ENABLED:
    10851694    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1695    make_constants("xpra", "x11", "gtk2", "constants")
    10871696
    10881697    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981707                **pkgconfig("x11")
    10991708                ))
     1709    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1710                ["xpra/x11/bindings/posix_display_source.pyx"],
     1711                **pkgconfig("x11")
     1712                ))
     1713
    11001714    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011715                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041718    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051719                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1720                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071721                ))
    11081722
    11091723    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101724                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1725                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121726                ))
    11131727    cython_add(Extension("xpra.x11.bindings.ximage",
    11141728                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1729                **pkgconfig("x11", "xext", "xcomposite")
    11161730                ))
    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)
     1731if xinput_ENABLED:
     1732    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1733                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1734                **pkgconfig("x11", "xi")
    11221735                ))
    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)
     1736
     1737toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1738if gtk_x11_ENABLED:
     1739    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1740    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1741    if PYTHON3:
     1742        #GTK3 display source:
     1743        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1744                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1745                    **pkgconfig("gdk-3.0")
     1746                    ))
     1747    else:
     1748        #GTK2:
     1749        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1750                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1751                    **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1752                    ))
     1753        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1754        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1755                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1756                    **pkgconfig(*GDK_BINDINGS_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1757                    ))
     1758
     1759toggle_packages(not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED), "xpra.gtk_common.gtk2")
     1760if not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED):
     1761    cython_add(Extension("xpra.gtk_common.gtk2.gdk_bindings",
     1762                ["xpra/gtk_common/gtk2/gdk_bindings.pyx"],
     1763                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11271764                ))
    11281765
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1766if client_ENABLED and gtk3_ENABLED:
     1767    #cairo workaround:
     1768    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1769                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1770                **pkgconfig("pycairo")
     1771                ))
     1772
     1773if client_ENABLED or server_ENABLED:
     1774    add_packages("xpra.codecs.argb")
     1775    argb_pkgconfig = pkgconfig(optimize=3)
    11321776    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1777                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1778
     1779
     1780#build tests, but don't install them:
     1781toggle_packages(tests_ENABLED, "unit")
    11341782
    11351783
    11361784if bundle_tests_ENABLED:
    11371785    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1786    for k,v in glob_recurse("unit").items():
    11391787        if (k!=""):
    11401788            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:
     1789        add_data_files("unit"+k, v)
     1790
     1791#python-cryptography needs workarounds for bundling:
     1792if crypto_ENABLED and (OSX or WIN32):
     1793    external_includes.append("_ssl")
     1794    external_includes.append("cffi")
     1795    external_includes.append("_cffi_backend")
     1796    external_includes.append("cryptography")
     1797    external_includes.append("pkg_resources._vendor.packaging")
     1798    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1799    external_includes.append("pkg_resources._vendor.pyparsing")
     1800    add_modules("cryptography.hazmat.bindings._openssl")
     1801    add_modules("cryptography.hazmat.bindings._constant_time")
     1802    add_modules("cryptography.hazmat.bindings._padding")
     1803    add_modules("cryptography.hazmat.backends.openssl")
     1804    add_modules("cryptography.fernet")
     1805    if WIN32:
     1806        external_includes.append("appdirs")
     1807
     1808#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441809if client_ENABLED:
    11451810    add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1811toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471812toggle_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")
     1813toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1814toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501815toggle_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")
     1816toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1817toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1818toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED) and example_ENABLED, "xpra.client.gtk_base.example")
     1819if client_ENABLED and WIN32 and MINGW_PREFIX:
     1820    propsys_pkgconfig = pkgconfig()
     1821    if debug_ENABLED:
     1822        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1823    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1824    cython_add(Extension("xpra.platform.win32.propsys",
     1825                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1826                language="c++",
     1827                **propsys_pkgconfig))
     1828
     1829if client_ENABLED or server_ENABLED:
     1830    add_modules("xpra.codecs")
     1831toggle_packages(client_ENABLED or server_ENABLED, "xpra.keyboard")
     1832if client_ENABLED or server_ENABLED:
     1833    add_modules("xpra.scripts.config", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1834if server_ENABLED or proxy_ENABLED:
     1835    add_modules("xpra.scripts.server")
     1836if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1837    add_modules("xpra.scripts.gtk_info")
     1838
     1839toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1840#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1841#and tries to include both gtk3 and gtk2, and fails hard..
     1842for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base"):
     1843    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1844toggle_packages(client_ENABLED and opengl_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gl.gtk_base")
     1845
     1846
     1847toggle_modules(sound_ENABLED, "xpra.sound")
     1848toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541849
    11551850toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11561851if 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:
     1852    if PYTHON3:
     1853        cython_add(Extension("xpra.gtk_common.gtk3.gdk_atoms",
     1854                             ["xpra/gtk_common/gtk3/gdk_atoms.pyx"],
     1855                             **pkgconfig("gtk+-3.0")
     1856                             ))
     1857    else:
     1858        cython_add(Extension("xpra.gtk_common.gtk2.gdk_atoms",
     1859                             ["xpra/gtk_common/gtk2/gdk_atoms.pyx"],
     1860                             **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1861                             ))
     1862
     1863toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1864if client_ENABLED or server_ENABLED:
    11631865    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641866                ["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")
     1867                **pkgconfig(optimize=3)))
     1868
     1869if server_ENABLED or shadow_ENABLED:
     1870    O3_pkgconfig = pkgconfig(optimize=3)
     1871    cython_add(Extension("xpra.server.cystats",
     1872                ["xpra/server/cystats.pyx"],
     1873                **O3_pkgconfig))
     1874    cython_add(Extension("xpra.server.window.region",
     1875                ["xpra/server/window/region.pyx"],
     1876                **O3_pkgconfig))
     1877    cython_add(Extension("xpra.server.window.motion",
     1878                ["xpra/server/window/motion.pyx"],
     1879                **O3_pkgconfig))
     1880
     1881if sd_listen_ENABLED:
     1882    sdp = pkgconfig("libsystemd")
     1883    cython_add(Extension("xpra.platform.xposix.sd_listen",
     1884                ["xpra/platform/xposix/sd_listen.pyx"],
     1885                **sdp))
     1886
     1887
    11781888toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11791889
     1890toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     1891if nvfbc_ENABLED:
     1892    nvfbc_pkgconfig = pkgconfig("nvfbc")
     1893    #add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     1894    platform = sys.platform.rstrip("0123456789")
     1895    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     1896                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     1897                         language="c++",
     1898                         **nvfbc_pkgconfig))
     1899
    11801900toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     1901toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     1902toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     1903
     1904if nvenc_ENABLED and cuda_kernels_ENABLED:
     1905    #find nvcc:
     1906    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     1907    if WIN32:
     1908        nvcc_exe = "nvcc.exe"
     1909        path_options = [
     1910                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v9.0\\bin",
     1911                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\bin",
     1912                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\bin",
     1913                         ] + path_options
     1914        #pycuda may link against curand, find it and ship it:
     1915        for p in path_options:
     1916            if os.path.exists(p):
     1917                add_data_files("", glob.glob("%s\\curand64*.dll" % p))
     1918                break
     1919    else:
     1920        nvcc_exe = "nvcc"
     1921        for v in ("", "-9.0", "-8.0", "-7.5"):
     1922            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     1923    options = [os.path.join(x, nvcc_exe) for x in path_options]
     1924    def which(cmd):
     1925        try:
     1926            code, out, _ = get_status_output(["which", cmd])
     1927            if code==0:
     1928                return out
     1929        except:
     1930            pass
     1931    #prefer the one we find on the $PATH, if any:
     1932    try:
     1933        v = which(nvcc_exe)
     1934        if v and (v not in options):
     1935            options.insert(0, v)
     1936    except:
     1937        pass
     1938    nvcc_versions = {}
     1939    for filename in options:
     1940        if not os.path.exists(filename):
     1941            continue
     1942        code, out, err = get_status_output([filename, "--version"])
     1943        if code==0:
     1944            vpos = out.rfind(", V")
     1945            if vpos>0:
     1946                version = out[vpos+3:].strip("\n")
     1947                version_str = " version %s" % version
     1948            else:
     1949                version = "0"
     1950                version_str = " unknown version!"
     1951            print("found CUDA compiler: %s%s" % (filename, version_str))
     1952            nvcc_versions[version] = filename
     1953    assert nvcc_versions, "cannot find nvcc compiler!"
     1954    #choose the most recent one:
     1955    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     1956    if len(nvcc_versions)>1:
     1957        print(" using version %s from %s" % (version, nvcc))
     1958    if WIN32:
     1959        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     1960        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     1961    #first compile the cuda kernels
     1962    #(using the same cuda SDK for both nvenc modules for now..)
     1963    #TODO:
     1964    # * compile directly to output directory instead of using data files?
     1965    # * detect which arches we want to build for? (does it really matter much?)
     1966    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     1967    for kernel in kernels:
     1968        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     1969        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     1970        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     1971            continue
     1972        reason = should_rebuild(cuda_src, cuda_bin)
     1973        if not reason:
     1974            continue
     1975        cmd = [nvcc,
     1976               '-fatbin',
     1977               #"-cubin",
     1978               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     1979               #"-gencode=arch=compute_50,code=sm_50",
     1980               #"-gencode=arch=compute_52,code=sm_52",
     1981               #"-gencode=arch=compute_52,code=compute_52",
     1982               "-c", cuda_src,
     1983               "-o", cuda_bin]
     1984        #GCC 6 uses C++11 by default:
     1985        if get_gcc_version()>=[6, 0]:
     1986            cmd.append("-std=c++11")
     1987        CL_VERSION = os.environ.get("CL_VERSION")
     1988        if CL_VERSION:
     1989            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     1990            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     1991            cmd += ["--machine", "32"]
     1992        if WIN32:
     1993            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     1994            #cmd += ["--input-drive-prefix", "/"]
     1995            #cmd += ["--dependency-drive-prefix", "/"]
     1996            cmd += ["-I%s" % os.path.abspath("win32")]
     1997        comp_code_options = [(30, 30), (35, 35)]
     1998        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     1999        if version!="0" and version<"7.5":
     2000            print("CUDA version %s is very unlikely to work")
     2001            print("try upgrading to version 7.5 or later")
     2002        if version>="7.5":
     2003            comp_code_options.append((50, 50))
     2004            comp_code_options.append((52, 52))
     2005            comp_code_options.append((53, 53))
     2006        if version>="8.0":
     2007            comp_code_options.append((60, 60))
     2008            comp_code_options.append((61, 61))
     2009            comp_code_options.append((62, 62))
     2010        if version>="9.0":
     2011            comp_code_options.append((70, 70))
     2012        for arch, code in comp_code_options:
     2013            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2014        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2015        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2016        c, stdout, stderr = get_status_output(cmd)
     2017        if c!=0:
     2018            print("Error: failed to compile CUDA kernel %s" % kernel)
     2019            print(stdout or "")
     2020            print(stderr or "")
     2021            sys.exit(1)
     2022    CUDA_BIN = "share/xpra/cuda"
     2023    if WIN32:
     2024        CUDA_BIN = "CUDA"
     2025    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2026
    11812027if 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))
     2028    nvencmodule = "nvenc"
     2029    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2030    #don't link against libnvidia-encode, we load it dynamically:
     2031    libraries = nvenc_pkgconfig.get("libraries", [])
     2032    if "nvidia-encode" in libraries:
     2033        libraries.remove("nvidia-encode")
     2034    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2035        #with gcc 6.2 on Fedora:
     2036        #xpra/codecs/nvenc/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2037        #xpra/codecs/nvenc/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2038        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2039        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2040    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2041                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2042                         **nvenc_pkgconfig))
    11872043
    11882044toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892045if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2046    x264_pkgconfig = pkgconfig("x264")
     2047    if get_gcc_version()>=[6, 0]:
     2048        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912049    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922050                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2051                **x264_pkgconfig))
    11942052
    11952053toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962054if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2055    x265_pkgconfig = pkgconfig("x265")
    11982056    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992057                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
    1201 
    1202 toggle_packages(webp_ENABLED, "xpra.codecs.webp")
    1203 if webp_ENABLED:
    1204     webp_pkgconfig = pkgconfig("webp")
    1205     cython_add(Extension("xpra.codecs.webp.encode",
    1206                 ["xpra/codecs/webp/encode.pyx"],
    1207                 **webp_pkgconfig), min_version=(0, 16))
    1208 
    1209 toggle_packages(dec_avcodec_ENABLED, "xpra.codecs.dec_avcodec")
    1210 if dec_avcodec_ENABLED:
    1211     make_constants("xpra", "codecs", "dec_avcodec", "constants")
    1212     avcodec_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec_static_ENABLED)
    1213     cython_add(Extension("xpra.codecs.dec_avcodec.decoder",
    1214                 ["xpra/codecs/dec_avcodec/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1215                 **avcodec_pkgconfig), min_version=(0, 19))
     2058                **x265_pkgconfig))
     2059
     2060toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2061if pillow_ENABLED:
     2062    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
     2063
     2064toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2065if jpeg_ENABLED:
     2066    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2067    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2068                ["xpra/codecs/jpeg/encoder.pyx"],
     2069                **jpeg_pkgconfig))
     2070    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2071                ["xpra/codecs/jpeg/decoder.pyx"],
     2072                **jpeg_pkgconfig))
     2073
     2074#swscale and avcodec2 use libav_common/av_log:
     2075libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2076toggle_packages(libav_common, "xpra.codecs.libav_common")
     2077if libav_common:
     2078    avutil_pkgconfig = pkgconfig("avutil")
     2079    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2080                ["xpra/codecs/libav_common/av_log.pyx"],
     2081                **avutil_pkgconfig))
     2082
    12162083
    12172084toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182085if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2086    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil")
    12202087    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 
     2088                ["xpra/codecs/dec_avcodec2/decoder.pyx"],
     2089                **avcodec2_pkgconfig))
     2090
     2091
     2092toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2093if csc_libyuv_ENABLED:
     2094    libyuv_pkgconfig = pkgconfig("libyuv")
     2095    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2096                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2097                language="c++",
     2098                **libyuv_pkgconfig))
    12242099
    12252100toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262101if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2102    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292103    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))
     2104                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2105                **swscale_pkgconfig))
     2106
    12392107
    12402108toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412109if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2110    vpx_pkgconfig = pkgconfig("vpx")
    12432111    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))
     2112                ["xpra/codecs/vpx/encoder.pyx"],
     2113                **vpx_pkgconfig))
    12462114    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))
     2115                ["xpra/codecs/vpx/decoder.pyx"],
     2116                **vpx_pkgconfig))
     2117
     2118toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2119if enc_ffmpeg_ENABLED:
     2120    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2121    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2122                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2123                **ffmpeg_pkgconfig))
     2124
     2125toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2126if v4l2_ENABLED:
     2127    v4l2_pkgconfig = pkgconfig()
     2128    #fuly warning: cython makes this difficult,
     2129    #we have to figure out if "device_caps" exists in the headers:
     2130    ENABLE_DEVICE_CAPS = False
     2131    if os.path.exists("/usr/include/linux/videodev2.h"):
     2132        hdata = open("/usr/include/linux/videodev2.h").read()
     2133        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2134    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2135    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2136    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2137                ["xpra/codecs/v4l2/pusher.pyx"],
     2138                **v4l2_pkgconfig))
    12622139
    12632140
    12642141toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2142toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652143if 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")
     2144    bencode_pkgconfig = pkgconfig(optimize=not debug_ENABLED)
    12722145    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732146                ["xpra/net/bencode/cython_bencode.pyx"],
    12742147                **bencode_pkgconfig))
    12752148
     2149if netdev_ENABLED:
     2150    netdev_pkgconfig = pkgconfig()
     2151    cython_add(Extension("xpra.platform.xposix.netdev_query",
     2152                ["xpra/platform/xposix/netdev_query.pyx"],
     2153                **netdev_pkgconfig))
     2154
     2155if vsock_ENABLED:
     2156    vsock_pkgconfig = pkgconfig()
     2157    cython_add(Extension("xpra.net.vsock",
     2158                ["xpra/net/vsock.pyx"],
     2159                **vsock_pkgconfig))
     2160
     2161if pam_ENABLED:
     2162    pam_pkgconfig = pkgconfig()
     2163    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2164    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2165    cython_add(Extension("xpra.server.pam",
     2166                ["xpra/server/pam.pyx"],
     2167                **pam_pkgconfig))
     2168
    12762169
    12772170if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2171    from Cython.Build import cythonize
     2172    #this causes Cython to fall over itself:
     2173    #gdb_debug=debug_ENABLED
     2174    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792175if cmdclass:
    12802176    setup_options["cmdclass"] = cmdclass
     
    12832179
    12842180
    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 
    12932181def main():
    12942182    if OSX or WIN32 or debug_ENABLED:
     2183        print()
    12952184        print("setup options:")
     2185        if verbose_ENABLED:
     2186            print("setup_options=%s" % (setup_options,))
     2187        try:
     2188            from xpra.util import repr_ellipsized as pv
     2189        except:
     2190            def pv(v):
     2191                return str(v)
    12962192        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2193            print_option("", k, pv(v))
    12982194        print("")
    12992195
Note: See TracChangeset for help on using the changeset viewer.