xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r21189  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2018 Antoine Martin <antoine@xpra.org>
    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@xpra.org",
     56                 "url"              : url,
     57                 "download_url"     : "http://xpra.org/src/",
     58                 "description"      : description,
     59                 "long_description" : long_description,
     60                 "data_files"       : data_files,
     61                 "py_modules"       : modules,
     62                 }
     63
     64WIN32 = sys.platform.startswith("win") or sys.platform.startswith("msys")
    2765OSX = sys.platform.startswith("darwin")
    28 
    29 
     66LINUX = sys.platform.startswith("linux")
     67NETBSD = sys.platform.startswith("netbsd")
     68FREEBSD = sys.platform.startswith("freebsd")
     69OPENBSD = sys.platform.startswith("openbsd")
     70
     71PYTHON3 = sys.version_info[0] == 3
     72POSIX = os.name=="posix"
     73
     74
     75if "pkg-info" in sys.argv:
     76    with open("PKG-INFO", "wb") as f:
     77        pkg_info_values = setup_options.copy()
     78        pkg_info_values.update({
     79                                "metadata_version"  : "1.1",
     80                                "summary"           :  description,
     81                                "home_page"         : url,
     82                                })
     83        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     84                  "Author", "Author-email", "License", "Download-URL", "Description"):
     85            v = pkg_info_values[k.lower().replace("-", "_")]
     86            f.write(b"%s: %s\n" % (k, v))
     87    sys.exit(0)
     88
     89
     90print("Xpra version %s" % XPRA_VERSION)
    3091#*******************************************************************************
    3192# Most of the options below can be modified on the command line
     
    3394# only the default values are specified here:
    3495#*******************************************************************************
     96from xpra.os_util import get_status_output, getUbuntuVersion, PYTHON3, BITS, \
     97    is_Ubuntu, is_Debian, is_Raspbian, is_Fedora, is_CentOS
     98
     99PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     100has_pkg_config = False
     101if PKG_CONFIG:
     102    v = get_status_output([PKG_CONFIG, "--version"])
     103    has_pkg_config = v[0]==0 and v[1]
     104    if has_pkg_config:
     105        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     106    else:
     107        print("WARNING: pkg-config not found!")
     108
     109from Cython.Compiler.Version import version as cython_version
     110
     111for arg in list(sys.argv):
     112    if arg.startswith("--pkg-config-path="):
     113        pcp = arg[len("--pkg-config-path="):]
     114        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     115        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     116        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     117        sys.argv.remove(arg)
     118
     119def no_pkgconfig(*_pkgs_options, **_ekw):
     120    return {}
     121
    35122def pkg_config_ok(*args):
    36     return commands.getstatusoutput("pkg-config %s" % (" ".join(args)))[0]==0
    37 
    38 shadow_ENABLED = SHADOW_SUPPORTED
    39 server_ENABLED = LOCAL_SERVERS_SUPPORTED or shadow_ENABLED
    40 client_ENABLED = True
    41 
    42 x11_ENABLED = not WIN32 and not OSX
    43 argb_ENABLED = True
    44 gtk2_ENABLED = client_ENABLED
    45 gtk3_ENABLED = False
    46 qt4_ENABLED = False
    47 opengl_ENABLED = client_ENABLED
    48 html5_ENABLED = not WIN32 and not OSX
    49 
    50 bencode_ENABLED         = True
    51 cython_bencode_ENABLED  = True
    52 rencode_ENABLED         = True
    53 cymaths_ENABLED         = True
    54 cyxor_ENABLED           = True
    55 clipboard_ENABLED       = True
    56 Xdummy_ENABLED          = None          #none means auto-detect
    57 sound_ENABLED           = True
    58 
    59 enc_proxy_ENABLED       = True
    60 enc_x264_ENABLED        = True          #too important to detect
    61 enc_x265_ENABLED        = pkg_config_ok("--exists", "x265")
    62 webp_ENABLED            = WIN32 or pkg_config_ok("--atleast-version=0.3", "libwebp")
    63 x264_static_ENABLED     = False
    64 x265_static_ENABLED     = False
    65 vpx_ENABLED             = WIN32 or pkg_config_ok("--atleast-version=1.0", "vpx") or pkg_config_ok("--atleast-version=1.0", "libvpx")
    66 vpx_static_ENABLED      = False
    67 #ffmpeg 1.x and libav:
    68 dec_avcodec_ENABLED     = not WIN32 and pkg_config_ok("--max-version=55", "libavcodec")
    69 #ffmpeg 2 onwards:
    70 dec_avcodec2_ENABLED    = WIN32 or pkg_config_ok("--atleast-version=55", "libavcodec")
    71 # some version strings I found:
    72 # Fedora 19: 54.92.100
    73 # Fedora 20: 55.39.101
    74 # Debian sid and jessie: 54.35.0
    75 # Debian wheezy: 53.35
    76 avcodec_static_ENABLED  = False
    77 avcodec2_static_ENABLED = False
    78 csc_swscale_ENABLED     = WIN32 or pkg_config_ok("--exists", "libswscale")
    79 swscale_static_ENABLED  = False
    80 csc_cython_ENABLED      = True
    81 webm_ENABLED            = True
    82 nvenc_ENABLED           = pkg_config_ok("--exists", "nvenc3")
    83 csc_nvcuda_ENABLED      = pkg_config_ok("--exists", "cuda")
    84 csc_opencl_ENABLED      = pkg_config_ok("--exists", "OpenCL")
    85 
     123    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     124    return get_status_output(cmd)[0]==0
     125
     126def pkg_config_version(req_version, pkgname):
     127    cmd = [PKG_CONFIG, "--modversion", pkgname]
     128    r, out, _ = get_status_output(cmd)
     129    if r!=0 or not out:
     130        return False
     131    from distutils.version import LooseVersion
     132    return LooseVersion(out)>=LooseVersion(req_version)
     133
     134def is_RH():
     135    try:
     136        with open("/etc/redhat-release", mode='rb') as f:
     137            data = f.read()
     138        return data.startswith("CentOS") or data.startswith("RedHat")
     139    except:
     140        pass
     141    return False
     142
     143DEFAULT = True
     144if "--minimal" in sys.argv:
     145    sys.argv.remove("--minimal")
     146    DEFAULT = False
     147
     148from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     149shadow_ENABLED = SHADOW_SUPPORTED and DEFAULT
     150server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and DEFAULT
     151rfb_ENABLED = server_ENABLED
     152service_ENABLED = LINUX and server_ENABLED
     153sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd") and (not is_Ubuntu() or getUbuntuVersion()>(16, 4))
     154proxy_ENABLED  = DEFAULT
     155client_ENABLED = DEFAULT
     156scripts_ENABLED = not WIN32
     157
     158x11_ENABLED = DEFAULT and not WIN32 and not OSX
     159xinput_ENABLED = x11_ENABLED
     160uinput_ENABLED = x11_ENABLED
     161dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     162gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     163gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     164gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     165opengl_ENABLED = DEFAULT and client_ENABLED
     166html5_ENABLED = DEFAULT
     167html5_gzip_ENABLED = DEFAULT
     168html5_brotli_ENABLED = DEFAULT
     169minify_ENABLED = html5_ENABLED
     170pam_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"))
     171
     172xdg_open_ENABLED        = LINUX and DEFAULT
     173netdev_ENABLED          = LINUX and DEFAULT
     174vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     175bencode_ENABLED         = DEFAULT
     176cython_bencode_ENABLED  = DEFAULT
     177clipboard_ENABLED       = DEFAULT
     178Xdummy_ENABLED          = None          #None means auto-detect
     179Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     180if WIN32 or OSX:
     181    Xdummy_ENABLED = False
     182sound_ENABLED           = DEFAULT
     183printing_ENABLED        = DEFAULT
     184crypto_ENABLED          = DEFAULT
     185mdns_ENABLED            = DEFAULT
     186
     187enc_proxy_ENABLED       = DEFAULT
     188enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     189#crashes on 32-bit windows:
     190enc_x265_ENABLED        = (not WIN32) and pkg_config_ok("--exists", "x265")
     191pillow_ENABLED          = DEFAULT
     192webp_ENABLED            = DEFAULT and pkg_config_version("0.5", "libwebp")
     193jpeg_ENABLED            = DEFAULT and pkg_config_version("1.2", "libturbojpeg")
     194vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     195enc_ffmpeg_ENABLED      = pkg_config_version("58.18", "libavcodec")
     196#opencv currently broken on 32-bit windows (crashes on load):
     197webcam_ENABLED          = DEFAULT and not OSX and (not WIN32 or BITS==64)
     198notifications_ENABLED   = DEFAULT
     199keyboard_ENABLED        = DEFAULT
     200v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD and not OPENBSD)
     201#ffmpeg 3.1 or later is required
     202dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     203csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     204nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("7", "nvenc")
     205nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     206cuda_kernels_ENABLED    = DEFAULT
     207cuda_rebuild_ENABLED    = DEFAULT
     208csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     209example_ENABLED         = DEFAULT
     210
     211#Cython / gcc / packaging build options:
     212annotate_ENABLED        = True
    86213warn_ENABLED            = True
    87214strict_ENABLED          = True
    88 PIC_ENABLED             = True
     215PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89216debug_ENABLED           = False
    90217verbose_ENABLED         = False
    91218bundle_tests_ENABLED    = False
     219tests_ENABLED           = False
     220rebuild_ENABLED         = True
    92221
    93222#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",
     223SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     224            "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     225            "vpx", "webp", "pillow", "jpeg",
     226            "v4l2",
     227            "dec_avcodec2", "csc_swscale",
     228            "csc_libyuv",
     229            "bencode", "cython_bencode", "vsock", "netdev", "mdns",
    104230            "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")
     231            "scripts",
     232            "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     233            "gtk_x11", "service",
     234            "gtk2", "gtk3", "example",
     235            "html5", "minify", "html5_gzip", "html5_brotli",
     236            "pam", "xdg_open",
     237            "sound", "opengl", "printing", "webcam", "notifications", "keyboard",
     238            "rebuild",
     239            "annotate", "warn", "strict",
     240            "shadow", "proxy", "rfb",
     241            "debug", "PIC",
     242            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     243if WIN32:
     244    SWITCHES.append("zip")
     245    zip_ENABLED = True
    109246HELP = "-h" in sys.argv or "--help" in sys.argv
    110247if HELP:
     
    120257            default_str = "auto-detect"
    121258        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     259    print("  --pkg-config-path=PATH")
     260    print("  --rpath=PATH")
    122261    sys.exit(0)
    123262
     263install = "dist"
     264rpath = None
     265ssl_cert = None
     266ssl_key = None
     267minifier = None
     268if WIN32:
     269    share_xpra = ""
     270else:
     271    share_xpra = "share/xpra/"
    124272filtered_args = []
    125273for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     274    matched = False
     275    for x in ("rpath", "ssl-cert", "ssl-key", "install", "share-xpra"):
     276        varg = "--%s=" % x
     277        if arg.startswith(varg):
     278            value = arg[len(varg):]
     279            globals()[x.replace("-", "_")] = value
     280            #remove these arguments from sys.argv,
     281            #except for --install=PATH
     282            matched = x!="install"
     283            break
     284    if matched:
    129285        continue
    130     matched = False
    131286    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     287        with_str = "--with-%s" % x
     288        without_str = "--without-%s" % x
     289        if arg.startswith(with_str+"="):
     290            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     291            matched = True
     292            break
     293        elif arg==with_str:
    133294            vars()["%s_ENABLED" % x] = True
    134295            matched = True
    135296            break
    136         elif arg=="--without-%s" % x:
     297        elif arg==without_str:
    137298            vars()["%s_ENABLED" % x] = False
    138299            matched = True
     
    145306    for x in SWITCHES:
    146307        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)
     308    print("build switches:")
     309    for k in sorted(SWITCHES):
     310        v = switches_info[k]
     311        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150312
    151313    #sanity check the flags:
     
    153315        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154316        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
    164317    if x11_ENABLED and WIN32:
    165318        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:
     319    if gtk_x11_ENABLED and not x11_ENABLED:
     320        print("Error: you must enable x11 to support gtk_x11!")
     321        exit(1)
     322    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167323        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)
     324    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     325        print("Warning: you probably want to build at least the client or server!")
     326    if DEFAULT and not pillow_ENABLED:
     327        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     328    if minify_ENABLED:
     329        r = get_status_output(["uglifyjs", "--version"])[0]
     330        if r==0:
     331            minifier = "uglifyjs"
     332        else:
     333            print("Warning: uglifyjs failed and return %i" % r)
     334            try:
     335                import yuicompressor
     336                assert yuicompressor
     337                minifier = "yuicompressor"
     338            except ImportError as e:
     339                print("Warning: yuicompressor module not found, cannot minify")
     340                minify_ENABLED = False
     341    if not enc_x264_ENABLED and not vpx_ENABLED:
     342        print("Warning: no x264 and no vpx support!")
     343        print(" you should enable at least one of these two video encodings")
    174344
    175345
    176346#*******************************************************************************
    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",
     347# default sets:
     348
     349external_includes = ["hashlib",
    204350                     "ctypes", "platform"]
     351
     352
     353if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     354    external_includes += ["gi"]
     355elif gtk2_ENABLED or x11_ENABLED:
     356    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205357
    206358external_excludes = [
     
    212364                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213365                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
    218                     "distutils", "setuptools", "doctest"
     366                    "curses", "pdb",
     367                    "tty",
     368                    "setuptools", "doctest"
    219369                    ]
    220 
     370if not html5_ENABLED and not crypto_ENABLED:
     371    external_excludes += ["ssl", "_ssl"]
     372if not html5_ENABLED:
     373    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     374
     375if not client_ENABLED and not server_ENABLED:
     376    excludes += ["PIL"]
     377if not dbus_ENABLED:
     378    excludes += ["dbus"]
    221379
    222380
    223381#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     382#for distutils / py2app and cx_freeze
    225383#use the following functions, which should get the right
    226384#data in the global variables "packages", "modules" and "excludes"
     
    253411
    254412def add_modules(*mods):
     413    def add(v):
     414        global modules
     415        if v not in modules:
     416            modules.append(v)
     417    do_add_modules(add, *mods)
     418
     419def do_add_modules(op, *mods):
    255420    """ adds the packages and any .py module found in the packages to the "modules" list
    256421    """
    257422    global modules
    258423    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     424        #ugly path stripping:
     425        if x.startswith("./"):
     426            x = x[2:]
     427        if x.endswith(".py"):
     428            x = x[:-3]
     429            x = x.replace("/", ".") #.replace("\\", ".")
    261430        pathname = os.path.sep.join(x.split("."))
     431        #is this a file module?
     432        f = "%s.py" % pathname
     433        if os.path.exists(f) and os.path.isfile(f):
     434            op(x)
    262435        if os.path.exists(pathname) and os.path.isdir(pathname):
    263436            #add all file modules found in this directory
    264437            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     438                #make sure we only include python files,
     439                #and ignore eclipse copies
     440                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266441                    fname = os.path.join(pathname, f)
    267442                    if os.path.isfile(fname):
    268443                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     444                        op(modname)
    270445
    271446def toggle_packages(enabled, *module_names):
     
    275450        remove_packages(*module_names)
    276451
     452def toggle_modules(enabled, *module_names):
     453    if enabled:
     454        def op(v):
     455            global modules
     456            if v not in modules:
     457                modules.append(v)
     458        do_add_modules(op, *module_names)
     459    else:
     460        remove_packages(*module_names)
     461
     462
    277463#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 
     464add_modules("xpra", "xpra.platform", "xpra.net")
     465add_modules("xpra.scripts.main")
     466
     467
     468def add_data_files(target_dir, files):
     469    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     470    assert type(target_dir)==str
     471    assert type(files) in (list, tuple)
     472    data_files.append((target_dir, files))
     473
     474
     475#for pretty printing of options:
     476def print_option(prefix, k, v):
     477    if type(v)==dict:
     478        print("%s* %s:" % (prefix, k))
     479        for kk,vv in v.items():
     480            print_option(" "+prefix, kk, vv)
     481    else:
     482        print("%s* %s=%s" % (prefix, k, v))
    284483
    285484#*******************************************************************************
    286485# Utility methods for building with Cython
    287486def 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)
    292487    from distutils.version import LooseVersion
    293488    if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
     
    297492                 % (cython_version, ".".join([str(part) for part in min_version])))
    298493
    299 def cython_add(extension, min_version=(0, 14, 0)):
     494def cython_add(extension, min_version=(0, 19)):
    300495    #gentoo does weird things, calls --no-compile with build *and* install
    301496    #then expects to find the cython modules!? ie:
     
    303498    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304499        return
    305     global ext_modules, cmdclass
    306500    cython_version_check(min_version)
    307501    from Cython.Distutils import build_ext
    308502    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     503    global cmdclass
     504    cmdclass['build_ext'] = build_ext
     505
     506def insert_into_keywords(kw, key, *args):
     507    values = kw.setdefault(key, [])
     508    for arg in args:
     509        values.insert(0, arg)
    310510
    311511def add_to_keywords(kw, key, *args):
     
    319519
    320520
     521def checkdirs(*dirs):
     522    for d in dirs:
     523        if not os.path.exists(d) or not os.path.isdir(d):
     524            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     525
    321526PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
     527#override the pkgconfig file,
     528#we don't need to link against any of these:
     529gtk2_ignored_tokens=[("-l%s" % x) for x in
     530                     ["fontconfig", "freetype", "cairo",
     531                      "atk-1.0", "pangoft2-1.0", "pango-1.0", "pangocairo-1.0",
     532                      "gio-2.0", "gdk_pixbuf-2.0"]]
    322533
    323534GCC_VERSION = []
     
    326537    if len(GCC_VERSION)==0:
    327538        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:
     539        r, _, err = get_status_output(cmd)
     540        if r==0:
    332541            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     542            for line in err.splitlines():
    334543                if line.startswith(V_LINE):
    335544                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343552    return GCC_VERSION
    344553
    345 def make_constants_pxi(constants_path, pxi_path):
     554def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346555    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):
     556    with open(constants_path) as f:
     557        for line in f:
     558            data = line.split("#", 1)[0].strip()
     559            # data can be empty ''...
     560            if not data:
     561                continue
     562            # or a pair like 'cFoo "Foo"'...
     563            elif len(data.split()) == 2:
     564                (pyname, cname) = data.split()
     565                constants.append((pyname, cname))
     566            # or just a simple token 'Foo'
     567            else:
     568                constants.append(data)
     569
     570    with open(pxi_path, "w") as out:
     571        if constants:
     572            out.write("cdef extern from *:\n")
     573            ### Apparently you can't use | on enum's?!
     574            # out.write("    enum MagicNumbers:\n")
     575            # for const in constants:
     576            #     if isinstance(const, tuple):
     577            #         out.write('        %s %s\n' % const)
     578            #     else:
     579            #         out.write('        %s\n' % (const,))
     580            for const in constants:
     581                if isinstance(const, tuple):
     582                    out.write('    unsigned int %s %s\n' % const)
     583                else:
     584                    out.write('    unsigned int %s\n' % (const,))
     585
     586            out.write("constants = {\n")
     587            for const in constants:
     588                if isinstance(const, tuple):
     589                    pyname = const[0]
     590                else:
     591                    pyname = const
     592                out.write('    "%s": %s,\n' % (pyname, pyname))
     593            out.write("}\n")
     594            if kwargs:
     595                out.write("\n\n")
     596
     597        if kwargs:
     598            for k, v in kwargs.items():
     599                out.write('DEF %s = %s\n' % (k, v))
     600
     601
     602def should_rebuild(src_file, bin_file):
     603    if not os.path.exists(bin_file):
     604        return "no file"
     605    elif rebuild_ENABLED:
     606        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     607            return "binary file out of date"
     608        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     609            return "newer build file"
     610    return None
     611
     612def make_constants(*paths, **kwargs):
    384613    base = os.path.join(os.getcwd(), *paths)
    385614    constants_file = "%s.txt" % base
    386     pxi_file = "%s.pxi" % base
    387     reason = None
    388     if not os.path.exists(pxi_file):
    389         reason = "no pxi file"
    390     elif os.path.getctime(pxi_file)<os.path.getctime(constants_file):
    391         reason = "pxi file out of date"
    392     elif os.path.getctime(pxi_file)<os.path.getctime(__file__):
    393         reason = "newer build file"
     615    try:
     616        pxi_file = kwargs.pop("pxi_file")
     617    except KeyError:
     618        pxi_file = "%s.pxi" % base
     619    reason = should_rebuild(constants_file, pxi_file)
    394620    if reason:
    395621        if verbose_ENABLED:
    396622            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
     623        make_constants_pxi(constants_file, pxi_file, **kwargs)
     624
    414625
    415626# 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 
     627def exec_pkgconfig(*pkgs_options, **ekw):
    423628    kw = dict(ekw)
     629    optimize = kw.pop("optimize", None)
     630    if optimize and not debug_ENABLED:
     631        if type(optimize)==bool:
     632            optimize = int(optimize)*3
     633        add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     634    ignored_flags = kw.pop("ignored_flags", [])
     635    ignored_tokens = kw.pop("ignored_tokens", [])
     636
     637    #for distros that don't patch distutils,
     638    #we have to add the python cflags:
     639    if not (is_Fedora() or is_Debian() or is_CentOS()):
     640        import shlex
     641        import sysconfig
     642        for x in shlex.split(sysconfig.get_config_var('CFLAGS') or ''):
     643            add_to_keywords(kw, 'extra_compile_args', x)
     644
     645    def add_tokens(s, extra="extra_link_args", extra_map={"-W" : "extra_compile_args"}):
     646        if not s:
     647            return
     648        flag_map = {'-I': 'include_dirs',
     649                    '-L': 'library_dirs',
     650                    '-l': 'libraries'}
     651        for token in s.split():
     652            if token in ignored_tokens:
     653                pass
     654            elif token[:2] in ignored_flags:
     655                pass
     656            elif token[:2] in flag_map:
     657                if len(token)>2:
     658                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     659                else:
     660                    print("Warning: invalid token '%s'" % token)
     661            else:
     662                extra_name = extra_map.get(token, extra)
     663                add_to_keywords(kw, extra_name, token)
     664
    424665    if len(pkgs_options)>0:
    425666        package_names = []
     
    431672            if type(package_options)==str:
    432673                options = [package_options]     #got given just one string
    433                 if not package_options.startswith("lib"):
    434                     options.append("lib%s" % package_options)
    435674            else:
    436675                assert type(package_options)==list
     
    438677            for option in options:
    439678                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:
     679                r, _, _ = get_status_output(cmd)
     680                if r==0:
    443681                    valid_option = option
    444682                    break
    445683            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     684                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)")))
    447685            package_names.append(valid_option)
    448686        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450         flag_map = {'-I': 'include_dirs',
    451                     '-L': 'library_dirs',
    452                     '-l': 'libraries'}
    453         cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
    454         proc = subprocess.Popen(cmd, env=os.environ, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    455         (output, _) = proc.communicate()
    456         status = proc.wait()
    457         if status!=0:
    458             sys.exit("ERROR: call to pkg-config ('%s') failed" % " ".join(cmd))
    459         if sys.version>='3':
    460             output = output.decode('utf-8')
    461         for token in output.split():
    462             if token[:2] in flag_map:
    463                 add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
    464             else: # throw others to extra_link_args
    465                 add_to_keywords(kw, 'extra_link_args', token)
    466             for k, v in kw.items(): # remove duplicates
    467                 kw[k] = list(set(v))
     687            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     688        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     689        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     690        if r!=0:
     691            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     692        add_tokens(pkg_config_out)
    468693    if warn_ENABLED:
    469694        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470695        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471696    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"
     697        if os.environ.get("CC", "").find("clang")>=0:
     698            #clang emits too many warnings with cython code,
     699            #so we can't enable Werror without turning off some warnings:
     700            #this list of flags should allow clang to build the whole source tree,
     701            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     702            #(un)commenting other switches.
     703            eifd = ["-Werror",
     704                    #"-Wno-unneeded-internal-declaration",
     705                    #"-Wno-unknown-attributes",
     706                    #"-Wno-unused-function",
     707                    #"-Wno-self-assign",
     708                    #"-Wno-sometimes-uninitialized",
     709                    #cython adds rpath to the compilation command??
     710                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     711                    "-Wno-deprecated-register",
     712                    "-Wno-unused-command-line-argument",
     713                    ]
     714        elif get_gcc_version()>=[4, 4]:
     715            eifd = ["-Werror"]
     716            if is_Debian() or is_Ubuntu() or is_Raspbian():
     717                #needed on Debian and Ubuntu to avoid this error:
     718                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     719                eifd.append("-Wno-error=strict-prototypes")
     720                #the cython version shipped with Xenial emits warnings:
     721                if getUbuntuVersion()<=(16,4):
     722                    eifd.append("-Wno-error=shift-count-overflow")
     723                    eifd.append("-Wno-error=sign-compare")
     724            if NETBSD:
     725                #see: http://trac.cython.org/ticket/395
     726                eifd += ["-fno-strict-aliasing"]
     727            elif FREEBSD:
     728                eifd += ["-Wno-error=unused-function"]
    475729        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     730            #older versions of OSX ship an old gcc,
     731            #not much we can do with this:
     732            eifd = []
     733        for eif in eifd:
     734            add_to_keywords(kw, 'extra_compile_args', eif)
     735    if sys.version_info>=(3,7):
     736        #we'll switch to the "new" buffer interface after we drop support for Python 2.7
     737        #until then, silence those deprecation warnings:
     738        add_to_keywords(kw, 'extra_compile_args', "-Wno-error=deprecated-declarations")
    478739    if PIC_ENABLED:
    479740        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481742        add_to_keywords(kw, 'extra_compile_args', '-g')
    482743        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     744        if get_gcc_version()>=[4, 8]:
    485745            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486746            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     747    if rpath and kw.get("libraries"):
     748        insert_into_keywords(kw, "library_dirs", rpath)
     749        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
     750    add_tokens(os.environ.get("CFLAGS"), "extra_compile_args", {})
     751    add_tokens(os.environ.get("LDFLAGS"), "extra_link_args", {})
    487752    #add_to_keywords(kw, 'include_dirs', '.')
    488753    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     754        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490755    return kw
     756pkgconfig = exec_pkgconfig
    491757
    492758
    493759#*******************************************************************************
    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()
     760
     761
     762def get_base_conf_dir(install_dir, stripbuildroot=True):
     763    #in some cases we want to strip the buildroot (to generate paths in the config file)
     764    #but in other cases we want the buildroot path (when writing out the config files)
     765    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     766    #this is a bit hackish, but I can't think of a better way of detecting it
     767    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     768    dirs = (install_dir or sys.prefix).split(os.path.sep)
     769    if install_dir and stripbuildroot:
     770        pkgdir = os.environ.get("pkgdir")
     771        if "debian" in dirs and "tmp" in dirs:
     772            #ugly fix for stripping the debian tmp dir:
     773            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     774            while "tmp" in dirs:
     775                dirs = dirs[dirs.index("tmp")+1:]
     776        elif "debian" in dirs:
     777            #same for recent debian versions:
     778            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     779            i = dirs.index("debian")
     780            if dirs[i+1] == "xpra":
     781                dirs = dirs[i+2:]
     782        elif "BUILDROOT" in dirs:
     783            #strip rpm style build root:
     784            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     785            dirs = dirs[dirs.index("BUILDROOT")+2:]
     786        elif pkgdir and install_dir.startswith(pkgdir):
     787            #arch build dir:
     788            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     789        elif "usr" in dirs:
     790            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     791            #assume "/usr" or "/usr/local" is the build root
     792            while "usr" in dirs[1:]:
     793                dirs = dirs[dirs[1:].index("usr")+1:]
     794        elif "image" in dirs:
     795            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     796            while "image" in dirs:
     797                dirs = dirs[dirs.index("image")+2:]
     798    #now deal with the fact that "/etc" is used for the "/usr" prefix
     799    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     800    if dirs and dirs[-1]=="usr":
     801        dirs = dirs[:-1]
     802    #is this an absolute path?
     803    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     804        #ie: ["/", "usr"] or ["/", "usr", "local"]
     805        dirs.insert(0, os.path.sep)
     806    return dirs
     807
     808def get_conf_dir(install_dir, stripbuildroot=True):
     809    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     810    dirs.append("etc")
     811    dirs.append("xpra")
     812    return os.path.join(*dirs)
     813
     814def detect_xorg_setup(install_dir=None):
     815    from xpra.scripts import config
     816    config.debug = config.warn
     817    conf_dir = get_conf_dir(install_dir)
     818    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     819
     820def build_xpra_conf(install_dir):
     821    #generates an actual config file from the template
     822    xvfb_command = detect_xorg_setup(install_dir)
     823    from xpra.platform.features import DEFAULT_ENV
     824    def bstr(b):
     825        if b is None:
     826            return "auto"
     827        return ["no", "yes"][int(b)]
     828    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     829    conf_dir = get_conf_dir(install_dir)
     830    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     831    from xpra.platform.paths import get_socket_dirs
     832    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, get_default_pulseaudio_command, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     833    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     834    socket_dirs = get_socket_dirs()
     835    if WIN32:
     836        bind = "Main"
    533837    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()
     838        if os.getuid()>0:
     839            #remove any paths containing the uid,
     840            #osx uses /var/tmp/$UID-Xpra,
     841            #but this should not be included in the default config for all users!
     842            #(the buildbot's uid!)
     843            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     844        bind = "auto"
     845    #FIXME: we should probably get these values from the default config instead
     846    pdf, postscript = "", ""
     847    if POSIX and printing_ENABLED:
     848        try:
     849            if "/usr/sbin" not in sys.path:
     850                sys.path.append("/usr/sbin")
     851            from xpra.platform.pycups_printing import get_printer_definition
     852            print("probing cups printer definitions")
     853            pdf = get_printer_definition("pdf")
     854            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     855            print("pdf=%s, postscript=%s" % (pdf, postscript))
     856        except Exception as e:
     857            print("could not probe for pdf/postscript printers: %s" % e)
     858    def pretty_cmd(cmd):
     859        return " ".join(cmd)
     860    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     861    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     862    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==(16, 10))
     863    #no python-avahi on RH / CentOS, need dbus module on *nix:
     864    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     865    SUBS = {
     866            'xvfb_command'          : pretty_cmd(xvfb_command),
     867            'ssh_command'           : DEFAULT_SSH_COMMAND,
     868            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     869            'remote_logging'        : "both",
     870            'start_env'             : start_env,
     871            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     872            'pulseaudio_command'    : pretty_cmd(get_default_pulseaudio_command()),
     873            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     874            'conf_dir'              : conf_dir,
     875            'bind'                  : bind,
     876            'ssl_cert'              : ssl_cert or "",
     877            'ssl_key'               : ssl_key or "",
     878            'systemd_run'           : get_default_systemd_run(),
     879            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     880            'log_dir'               : "auto",
     881            'mdns'                  : bstr(mdns),
     882            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     883            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     884            'pdf_printer'           : pdf,
     885            'postscript_printer'    : postscript,
     886            'webcam'                : ["no", "auto"][webcam],
     887            'mousewheel'            : "on",
     888            'printing'              : bstr(printing_ENABLED),
     889            'dbus_control'          : bstr(dbus_ENABLED),
     890            'mmap'                  : bstr(True),
     891            }
     892    def convert_templates(subdirs=[]):
     893        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     894        #get conf dir for install, without stripping the build root
     895        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     896        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     897        if not os.path.exists(target_dir):
     898            try:
     899                os.makedirs(target_dir)
     900            except Exception as e:
     901                print("cannot create target dir '%s': %s" % (target_dir, e))
     902        for f in sorted(os.listdir(dirname)):
     903            if f.endswith("osx.conf.in") and not OSX:
     904                continue
     905            filename = os.path.join(dirname, f)
     906            if os.path.isdir(filename):
     907                convert_templates(subdirs+[f])
     908                continue
     909            if not f.endswith(".in"):
     910                continue
     911            with open(filename, "r") as f_in:
     912                template  = f_in.read()
     913            target_file = os.path.join(target_dir, f[:-len(".in")])
     914            print("generating %s from %s" % (target_file, f))
     915            with open(target_file, "w") as f_out:
     916                config_data = template % SUBS
     917                f_out.write(config_data)
     918    convert_templates()
    576919
    577920
     
    580923    #clean and sdist don't actually use cython,
    581924    #so skip this (and avoid errors)
    582     def pkgconfig(*pkgs_options, **ekw):
    583         return {}
     925    pkgconfig = no_pkgconfig
    584926    #always include everything in this case:
    585927    add_packages("xpra")
    586928    #ensure we remove the files we generate:
    587929    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",
     930                   "xpra/build_info.py",
     931                   "xpra/monotonic_time.c",
     932                   "xpra/gtk_common/gtk2/gdk_atoms.c",
     933                   "xpra/gtk_common/gtk2/gdk_bindings.c",
     934                   "xpra/gtk_common/gtk3/gdk_atoms.c",
     935                   "xpra/gtk_common/gtk3/gdk_bindings.c",
     936                   "xpra/x11/gtk2/constants.pxi",
     937                   "xpra/x11/gtk2/gdk_bindings.c",
     938                   "xpra/x11/gtk2/gdk_display_source.c",
     939                   "xpra/x11/gtk3/constants.pxi",
     940                   "xpra/x11/gtk3/gdk_bindings.c",
     941                   "xpra/x11/gtk3/gdk_display_source.c",
    592942                   "xpra/x11/bindings/constants.pxi",
    593943                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597947                   "xpra/x11/bindings/randr_bindings.c",
    598948                   "xpra/x11/bindings/core_bindings.c",
     949                   "xpra/x11/bindings/posix_display_source.c",
    599950                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     951                   "xpra/x11/bindings/xi2_bindings.c",
     952                   "xpra/platform/win32/propsys.cpp",
     953                   "xpra/platform/darwin/gdk_bindings.c",
     954                   "xpra/platform/xposix/sd_listen.c",
     955                   "xpra/platform/xposix/netdev_query.c",
     956                   "xpra/net/bencode/cython_bencode.c",
     957                   "xpra/net/vsock.c",
     958                   "xpra/buffers/membuf.c",
    601959                   "xpra/codecs/vpx/encoder.c",
    602960                   "xpra/codecs/vpx/decoder.c",
    603961                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     962                   "xpra/codecs/nvfbc/fbc_capture_linux.cpp",
     963                   "xpra/codecs/nvfbc/fbc_capture_win.cpp",
    605964                   "xpra/codecs/enc_x264/encoder.c",
    606965                   "xpra/codecs/enc_x265/encoder.c",
     966                   "xpra/codecs/jpeg/encoder.c",
     967                   "xpra/codecs/jpeg/decoder.c",
     968                   "xpra/codecs/enc_ffmpeg/encoder.c",
     969                   "xpra/codecs/v4l2/constants.pxi",
     970                   "xpra/codecs/v4l2/pusher.c",
     971                   "xpra/codecs/libav_common/av_log.c",
    607972                   "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     973                   "xpra/codecs/webp/decode.c",
    610974                   "xpra/codecs/dec_avcodec2/decoder.c",
     975                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611976                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614977                   "xpra/codecs/xor/cyxor.c",
    615978                   "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")
     979                   "xpra/codecs/nvapi_version.c",
     980                   "xpra/gtk_common/gdk_atoms.c",
     981                   "xpra/client/gtk3/cairo_workaround.c",
     982                   "xpra/server/cystats.c",
     983                   "xpra/server/window/region.c",
     984                   "xpra/server/window/motion.c",
     985                   "xpra/server/pam.c",
     986                   "etc/xpra/xpra.conf",
     987                   #special case for the generated xpra conf files in build (see #891):
     988                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     989    if cuda_rebuild_ENABLED:
     990        CLEAN_FILES += [
     991            "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     992            "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     993            "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     994            "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
     995            ]
     996    for x in CLEAN_FILES:
     997        p, ext = os.path.splitext(x)
     998        if ext in (".c", ".cpp", ".pxi"):
     999            #clean the Cython annotated html files:
     1000            CLEAN_FILES.append(p+".html")
     1001            if WIN32 and ext!=".pxi":
     1002                #on win32, the build creates ".pyd" files, clean those too:
     1003                CLEAN_FILES.append(p+".pyd")
     1004                #when building with python3, we need to clean files named like:
     1005                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     1006                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     1007                CLEAN_FILES += glob.glob(filename)
    6231008    if 'clean' in sys.argv:
    6241009        CLEAN_FILES.append("xpra/build_info.py")
     
    6301015            os.unlink(filename)
    6311016
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     1017from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    6331018
    6341019if "clean" not in sys.argv:
    6351020    # Add build info to build_info.py file:
    6361021    record_build_info()
     1022    # ensure it is included in the module list if it didn't exist before
     1023    add_modules(BUILD_INFO_FILE)
    6371024
    6381025if "sdist" in sys.argv:
    6391026    record_src_info()
    6401027
    641 if "install" in sys.argv:
     1028if "install" in sys.argv or "build" in sys.argv:
    6421029    #if installing from source tree rather than
    6431030    #from a source snapshot, we may not have a "src_info" file
     
    6451032    if not has_src_info():
    6461033        record_src_info()
     1034        # ensure it is now included in the module list
     1035        add_modules(SRC_INFO_FILE)
    6471036
    6481037
     
    6631052    return m
    6641053
     1054
     1055def install_html5(install_dir="www"):
     1056    from setup_html5 import install_html5 as do_install_html5
     1057    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1058
     1059
    6651060#*******************************************************************************
    6661061if 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"]
    882     add_packages("xpra.platform.win32")
     1062    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1063    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
     1064    add_packages("xpra.platform.win32", "xpra.platform.win32.namedpipes")
    8831065    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"]
     1066
     1067    #this is where the win32 gi installer will put things:
     1068    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1069
     1070    #only add the cx_freeze specific options
     1071    #only if we are packaging:
     1072    if "install_exe" in sys.argv:
     1073        #with cx_freeze, we don't use py_modules
     1074        del setup_options["py_modules"]
     1075        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1076        if not hasattr(sys, "base_prefix"):
     1077            #workaround for broken sqlite hook with python 2.7, see:
     1078            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1079            sys.base_prefix = sys.prefix
     1080
     1081        #cx_freeze doesn't use "data_files"...
     1082        del setup_options["data_files"]
     1083        #it wants source files first, then where they are placed...
     1084        #one item at a time (no lists)
     1085        #all in its own structure called "include_files" instead of "data_files"...
     1086        def add_data_files(target_dir, files):
     1087            if verbose_ENABLED:
     1088                print("add_data_files(%s, %s)" % (target_dir, files))
     1089            assert type(target_dir)==str
     1090            assert type(files) in (list, tuple)
     1091            for f in files:
     1092                target_file = os.path.join(target_dir, os.path.basename(f))
     1093                data_files.append((f, target_file))
     1094
     1095        #pass a potentially nested dictionary representing the tree
     1096        #of files and directories we do want to include
     1097        #relative to gnome_include_path
     1098        def add_dir(base, defs):
     1099            if verbose_ENABLED:
     1100                print("add_dir(%s, %s)" % (base, defs))
     1101            if type(defs) in (list, tuple):
     1102                for sub in defs:
     1103                    if type(sub)==dict:
     1104                        add_dir(base, sub)
     1105                    else:
     1106                        assert type(sub)==str
     1107                        filename = os.path.join(gnome_include_path, base, sub)
     1108                        if os.path.exists(filename):
     1109                            add_data_files(base, [filename])
     1110                        else:
     1111                            print("Warning: missing '%s'" % filename)
     1112            else:
     1113                assert type(defs)==dict
     1114                for d, sub in defs.items():
     1115                    assert type(sub) in (dict, list, tuple)
     1116                    #recurse down:
     1117                    add_dir(os.path.join(base, d), sub)
     1118
     1119        #convenience method for adding GI libs and "typelib" and "gir":
     1120        def add_gi(*libs):
     1121            if verbose_ENABLED:
     1122                print("add_gi(%s)" % str(libs))
     1123            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1124            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1125
     1126        def add_DLLs(*dll_names):
     1127            try:
     1128                do_add_DLLs(*dll_names)
     1129            except Exception as e:
     1130                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1131                print(" %s" % e)
     1132                sys.exit(1)
     1133
     1134        def do_add_DLLs(*dll_names):
     1135            dll_names = list(dll_names)
     1136            dll_files = []
     1137            import re
     1138            version_re = re.compile("\-[0-9\.\-]+$")
     1139            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1140            if os.path.exists(gnome_include_path):
     1141                dirs.insert(0, gnome_include_path)
     1142            if verbose_ENABLED:
     1143                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1144            for d in dirs:
     1145                if not os.path.exists(d):
     1146                    continue
     1147                for x in os.listdir(d):
     1148                    dll_path = os.path.join(d, x)
     1149                    x = x.lower()
     1150                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1151                        continue
     1152                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1153                    if verbose_ENABLED:
     1154                        print("checking %s: %s" % (x, nameversion))
     1155                    m = version_re.search(nameversion)          #look for version part of filename
     1156                    if m:
     1157                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1158                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1159                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1160                    else:
     1161                        dll_version = ""                        #no version
     1162                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1163                    if dll_name in dll_names:
     1164                        #this DLL is on our list
     1165                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1166                        dll_files.append(dll_path)
     1167                        dll_names.remove(dll_name)
     1168            if len(dll_names)>0:
     1169                print("some DLLs could not be found:")
     1170                for x in dll_names:
     1171                    print(" - lib%s*.dll" % x)
     1172            add_data_files("", dll_files)
     1173
     1174        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1175        #(ie: "libatk-1.0-0.dll" -> "atk")
     1176        if sound_ENABLED or gtk3_ENABLED:
     1177            add_DLLs('gio', 'girepository', 'glib',
     1178                     'gnutls', 'gobject', 'gthread',
     1179                     'orc', 'stdc++',
     1180                     'winpthread',
     1181                     )
     1182        if gtk3_ENABLED:
     1183            add_DLLs('atk',
     1184                     'dbus', 'dbus-glib',
     1185                     'gdk', 'gdk_pixbuf', 'gtk',
     1186                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1187                     'harfbuzz', 'harfbuzz-gobject',
     1188                     'jasper', 'epoxy',
     1189                     'intl',
     1190                     'p11-kit',
     1191                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1192            #these are missing in newer aio installers (sigh):
     1193            do_add_DLLs('javascriptcoregtk')
     1194            if opengl_ENABLED:
     1195                do_add_DLLs('gdkglext', 'gtkglext')
     1196
     1197        if gtk3_ENABLED:
     1198            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1199            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1200                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1201            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1202                              "p11-kit", "xml",
     1203                              {"icons"  : ["hicolor"]},
     1204                              {"locale" : ["en"]},
     1205                              {"themes" : ["Default"]}
     1206                             ])
     1207        if gtk3_ENABLED or sound_ENABLED:
     1208            add_dir('lib', ["gio"])
     1209            packages.append("gi")
     1210            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1211                   "GObject-2.0")
     1212        if gtk3_ENABLED:
     1213            add_gi("Atk-1.0",
     1214                   "Notify-0.7",
     1215                   "fontconfig-2.0", "freetype2-2.0",
     1216                   "GDesktopEnums-3.0", "Soup-2.4",
     1217                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0",
     1218                   "HarfBuzz-0.0",
     1219                   "Libproxy-1.0", "libxml2-2.0",
     1220                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1221                   "Rsvg-2.0",
     1222                   "win32-1.0")
     1223            if opengl_ENABLED:
     1224                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1225            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1226        if server_ENABLED and not PYTHON3:
     1227            add_DLLs('sqlite3')
     1228
     1229        if gtk2_ENABLED:
     1230            add_dir('lib',      {
     1231                "gdk-pixbuf-2.0":    {
     1232                    "2.10.0"    :   {
     1233                        "loaders"   :
     1234                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp", "png",)]
     1235                        },
     1236                    },
     1237                })
     1238            if opengl_ENABLED:
     1239                add_DLLs("gtkglext-win32", "gdkglext-win32")
     1240            add_DLLs("gtk-win32", "gdk-win32",
     1241                     "gdk_pixbuf", "pyglib-2.0-python2")
     1242
     1243        if client_ENABLED:
     1244            #svg pixbuf loader:
     1245            add_DLLs("rsvg", "croco")
     1246
     1247        if sound_ENABLED:
     1248            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1249            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1250                   "GstTag-1.0")
     1251            add_DLLs('gstreamer', 'orc-test')
     1252            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1253                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1254                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1255                      "basecamerabinsrc", "mpegts", "photography",
     1256                      ):
     1257                add_DLLs('gst%s' % p)
     1258            #DLLs needed by the plugins:
     1259            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1260            #add the gstreamer plugins we need:
     1261            GST_PLUGINS = ("app",
     1262                           "cutter",
     1263                           #muxers:
     1264                           "gdp", "matroska", "ogg", "isomp4",
     1265                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1266                           "coreelements", "directsound", "directsoundsink", "directsoundsrc", "wasapi",
     1267                           #codecs:
     1268                           "opus", "opusparse", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1269                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1270                           "autodetect",
     1271                           #untested: a52dec, voaacenc
     1272                           )
     1273            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1274            #END OF SOUND
     1275
     1276        if server_ENABLED:
     1277            #used by proxy server:
     1278            external_includes += ["multiprocessing", "setproctitle"]
     1279
     1280        external_includes += ["encodings"]
     1281        if client_ENABLED:
     1282            #for parsing "open-command":
     1283            external_includes += ["shlex"]
     1284            #for version check:
     1285            external_includes += [
     1286                                  "ftplib", "fileinput",
     1287                                  ]
     1288            if PYTHON3:
     1289                external_includes += ["urllib", "http.cookiejar", "http.client"]
     1290            else:
     1291                external_includes += ["urllib2", "cookielib", "httplib"]
     1292
     1293        if PYTHON3:
     1294            #hopefully, cx_Freeze will fix this horror:
     1295            #(we shouldn't have to deal with DLL dependencies)
     1296            import site
     1297            lib_python = os.path.dirname(site.getsitepackages()[0])
     1298            lib_dynload_dir = os.path.join(lib_python, "lib-dynload")
     1299            add_data_files('', glob.glob("%s/zlib*dll" % lib_dynload_dir))
     1300            for x in ("io", "codecs", "abc", "_weakrefset", "encodings"):
     1301                add_data_files("lib/", glob.glob("%s/%s*" % (lib_python, x)))
     1302        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1303        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1304        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1305        cx_freeze_options = {
     1306                            "includes"          : external_includes,
     1307                            "packages"          : packages,
     1308                            "include_files"     : data_files,
     1309                            "excludes"          : excludes,
     1310                            "include_msvcr"     : True,
     1311                            "bin_excludes"      : bin_excludes,
     1312                            }
     1313        #cx_Freeze v5 workarounds:
     1314        if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
     1315            add_packages("numpy.core._methods", "numpy.lib.format")
     1316
     1317        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1318        executables = []
     1319        setup_options["executables"] = executables
     1320
     1321        def add_exe(script, icon, base_name, base="Console"):
     1322            executables.append(Executable(
     1323                        script                  = script,
     1324                        initScript              = None,
     1325                        #targetDir               = "dist",
     1326                        icon                    = "icons/%s" % icon,
     1327                        targetName              = "%s.exe" % base_name,
     1328                        base                    = base,
     1329                        ))
     1330
     1331        def add_console_exe(script, icon, base_name):
     1332            add_exe(script, icon, base_name)
     1333        def add_gui_exe(script, icon, base_name):
     1334            add_exe(script, icon, base_name, base="Win32GUI")
     1335        def add_service_exe(script, icon, base_name):
     1336            add_exe(script, icon, base_name, base="Win32Service")
     1337
     1338        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1339        if (client_ENABLED or server_ENABLED) and (gtk2_ENABLED or gtk3_ENABLED):
     1340            add_gui_exe("scripts/xpra",                         "xpra.ico",         "Xpra")
     1341            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1342            add_console_exe("scripts/xpra_launcher",            "xpra.ico",         "Xpra-Launcher-Debug")
     1343            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1344            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1345            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1346        if server_ENABLED:
     1347            add_gui_exe("scripts/auth_dialog",                  "authentication.ico", "Auth_Dialog")
     1348        if gtk2_ENABLED:
     1349            #these need porting..
     1350            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1351        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1352            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1353        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1354        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1355        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1356        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1357        if gtk2_ENABLED or gtk3_ENABLED:
     1358            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1359            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1360            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1361            add_gui_exe("xpra/client/gtk_base/example/tray.py", "xpra.ico",         "SystemTray_Test")
     1362            add_gui_exe("xpra/client/gtk_base/u2f_tool.py",     "authentication.ico", "U2F_Tool")
     1363        if client_ENABLED or server_ENABLED:
     1364            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1365            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1366        if server_ENABLED:
     1367            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1368            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1369            add_console_exe("xpra/server/auth/ldap_auth.py",    "authentication.ico", "LDAP-Auth-Test")
     1370            add_console_exe("xpra/server/auth/ldap3_auth.py",   "authentication.ico", "LDAP3-Auth-Test")
     1371            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1372            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1373        if client_ENABLED:
     1374            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1375            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1376            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1377        if client_ENABLED:
     1378            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1379            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1380        if sound_ENABLED:
     1381            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1382            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1383            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1384            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1385            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1386        if opengl_ENABLED:
     1387            if PYTHON3:
     1388                add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1389            else:
     1390                add_console_exe("xpra/client/gl/gtk_base/gtkgl_check.py", "opengl.ico", "OpenGL_check")
     1391        if webcam_ENABLED:
     1392            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1393            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1394        if printing_ENABLED:
     1395            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1396            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1397            add_DLLs("pdfium")  #libpdfium.dll
     1398        if nvenc_ENABLED:
     1399            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1400        if nvfbc_ENABLED:
     1401            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1402        if nvfbc_ENABLED or nvenc_ENABLED:
     1403            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1404
     1405        if example_ENABLED:
     1406            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1407            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1408            if not PYTHON3:
     1409                add_gui_exe("xpra/client/gtk_base/example/gl_colors_gradient.py",   "encoding.ico",     "OpenGL-Colors-Gradient")
     1410            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1411            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1412            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1413            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1414            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1415
     1416        #FIXME: how do we figure out what target directory to use?
     1417        print("calling build_xpra_conf in-place")
     1418        #building etc files in-place:
     1419        build_xpra_conf(".")
     1420        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1421        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1422        add_data_files('etc/xpra', glob.glob("etc/xpra/nvfbc*.keys"))
     1423        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1424        #build minified html5 client in temporary build dir:
     1425        if "clean" not in sys.argv and html5_ENABLED:
     1426            install_html5(os.path.join(install, "www"), )
     1427            for k,v in glob_recurse("build/www").items():
     1428                if (k!=""):
     1429                    k = os.sep+k
     1430                add_data_files('www'+k, v)
     1431
     1432    if client_ENABLED or server_ENABLED:
     1433        add_data_files(share_xpra,              ["win32/website.url"])
     1434        add_data_files('%sicons' % share_xpra,  glob.glob('icons\\*.ico'))
     1435
     1436    if webcam_ENABLED:
     1437        add_data_files(share_xpra,              ["win32\\DirectShow.tlb"])
     1438
    9071439    remove_packages(*external_excludes)
     1440    external_includes.append("pyu2f")
     1441    external_includes.append("mmap")
     1442    external_includes.append("comtypes")    #used by webcam and netdev_query
     1443    remove_packages("comtypes.gen")         #this is generated at runtime
     1444                                            #but we still have to remove the empty directory by hand
     1445                                            #afterwards because cx_freeze does weird things (..)
    9081446    remove_packages(#not used on win32:
    909                     "mmap",
    9101447                    #we handle GL separately below:
    9111448                    "OpenGL", "OpenGL_accelerate",
     
    9131450                    "ctypes.macholib")
    9141451
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1452    if webcam_ENABLED and False:
     1453        external_includes.append("cv2")
     1454    else:
     1455        remove_packages("cv2")
     1456
     1457    if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
    9161458        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1459        external_includes.append("numpy")
    9181460    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1461        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211462                        "pydoc")
    9221463
    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:
     1464    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1465    remove_packages("pygst", "gst", "gst.extend")
     1466
     1467    #add subset of PyOpenGL modules (only when installing):
     1468    if opengl_ENABLED and "install_exe" in sys.argv:
    9291469        #for this hack to work, you must add "." to the sys.path
    9301470        #so python can load OpenGL from the install directory
    9311471        #(further complicated by the fact that "." is the "frozen" path...)
    932         import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
    935         for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
     1472        #but we re-add those two directories to the library.zip as part of the build script
     1473        import OpenGL
     1474        print("*** copying PyOpenGL modules to %s ***" % install)
     1475        glmodules = {
     1476            "OpenGL" : OpenGL,
     1477            }
     1478        try:
     1479            import OpenGL_accelerate        #@UnresolvedImport
     1480        except ImportError as e:
     1481            print("Warning: missing OpenGL_accelerate module")
     1482            print(" %s" % e)
     1483        else:
     1484            glmodules["OpenGL_accelerate"] = OpenGL_accelerate
     1485        for module_name, module in glmodules.items():
    9361486            module_dir = os.path.dirname(module.__file__ )
    9371487            try:
    9381488                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1489                    module_dir, os.path.join(install, "lib", module_name),
     1490                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411491                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1492                print("copied %s to %s/%s" % (module_dir, install, module_name))
     1493            except Exception as e:
     1494                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441495                    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 
     1496
     1497        add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1498
     1499    #END OF win32
    9831500#*******************************************************************************
    9841501else:
    9851502    #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"]))
     1503    if LINUX:
     1504        if scripts_ENABLED:
     1505            scripts += ["scripts/xpra_udev_product_version", "scripts/xpra_signal_listener"]
     1506        libexec_scripts = []
     1507        if is_Fedora() or is_CentOS():
     1508            libexec = "libexec"
     1509        else:
     1510            libexec = "lib"
     1511        if xdg_open_ENABLED:
     1512            libexec_scripts += ["scripts/xdg-open", "scripts/gnome-open", "scripts/gvfs-open"]
     1513        if server_ENABLED:
     1514            libexec_scripts.append("scripts/auth_dialog")
     1515        if libexec_scripts:
     1516            add_data_files("%s/xpra/" % libexec, libexec_scripts)
     1517    man_path = "share/man"
     1518    if OPENBSD:
     1519        man_path = "man"
     1520    add_data_files("%s/man1" % man_path,  ["man/xpra.1", "man/xpra_launcher.1"])
     1521    add_data_files("share/applications",  ["xdg/xpra-shadow.desktop", "xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1522    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1523    add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png", "xdg/xpra-shadow.png"])
     1524    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1525
     1526    #here, we override build and install so we can
     1527    #generate our /etc/xpra/xpra.conf
     1528    class build_override(build):
     1529        def run(self):
     1530            build.run(self)
     1531            self.run_command("build_conf")
     1532
     1533    class build_conf(build):
     1534        def run(self):
     1535            try:
     1536                build_base = self.distribution.command_obj['build'].build_base
     1537            except:
     1538                build_base = self.build_base
     1539            build_xpra_conf(build_base)
     1540
     1541    class install_data_override(install_data):
     1542        def run(self):
     1543            print("install_data_override: install_dir=%s" % self.install_dir)
     1544            if html5_ENABLED:
     1545                install_html5(os.path.join(self.install_dir, "%swww" % share_xpra))
     1546            install_data.run(self)
     1547
     1548            root_prefix = self.install_dir.rstrip("/")
     1549            if root_prefix.endswith("/usr"):
     1550                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1551            build_xpra_conf(root_prefix)
     1552
     1553            def copytodir(src, dst_dir, dst_name=None, chmod=0o644):
     1554                #convert absolute paths:
     1555                if dst_dir.startswith("/"):
     1556                    dst_dir = root_prefix+dst_dir
     1557                else:
     1558                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1559                #make sure the target directory exists:
     1560                self.mkpath(dst_dir)
     1561                #generate the target filename:
     1562                filename = os.path.basename(src)
     1563                dst_file = os.path.join(dst_dir, dst_name or filename)
     1564                #copy it
     1565                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1566                shutil.copyfile(src, dst_file)
     1567                if chmod:
     1568                    os.chmod(dst_file, chmod)
     1569
     1570            if printing_ENABLED and POSIX:
     1571                #install "/usr/lib/cups/backend" with 0700 permissions:
     1572                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1573
     1574            if x11_ENABLED:
     1575                #install xpra_Xdummy if we need it:
     1576                xvfb_command = detect_xorg_setup()
     1577                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1578                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1579                #install xorg*.conf, cuda.conf and nvenc.keys:
     1580                etc_xpra_files = ["xorg.conf"]
     1581                if uinput_ENABLED:
     1582                    etc_xpra_files.append("xorg-uinput.conf")
     1583                if nvenc_ENABLED or nvfbc_ENABLED:
     1584                    etc_xpra_files.append("cuda.conf")
     1585                if nvenc_ENABLED:
     1586                    etc_xpra_files.append("nvenc.keys")
     1587                if nvfbc_ENABLED:
     1588                    etc_xpra_files.append("nvfbc.keys")
     1589                for x in etc_xpra_files:
     1590                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1591                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1592
     1593            if pam_ENABLED:
     1594                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1595
     1596            systemd_dir = "/lib/systemd/system"
     1597            if service_ENABLED:
     1598                #Linux init service:
     1599                if os.path.exists("/bin/systemctl"):
     1600                    if sd_listen_ENABLED:
     1601                        copytodir("service/xpra.service", systemd_dir)
     1602                    else:
     1603                        copytodir("service/xpra-nosocketactivation.service", systemd_dir, dst_name="xpra.service")
     1604                else:
     1605                    copytodir("service/xpra", "/etc/init.d")
     1606                if os.path.exists("/etc/sysconfig"):
     1607                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1608                elif os.path.exists("/etc/default"):
     1609                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1610            if sd_listen_ENABLED:
     1611                copytodir("service/xpra.socket", systemd_dir)
     1612            if dbus_ENABLED and proxy_ENABLED:
     1613                copytodir("dbus/xpra.conf", "/etc/dbus-1/system.d")
     1614
     1615
     1616    # add build_conf to build step
     1617    cmdclass.update({
     1618             'build'        : build_override,
     1619             'build_conf'   : build_conf,
     1620             'install_data' : install_data_override,
     1621             })
    9971622
    9981623    if OSX:
     1624        #pyobjc needs email.parser
     1625        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
     1626        external_includes += ["kerberos", "future", "pyu2f", "paramiko", "nacl"]
     1627        if not PYTHON3:
     1628            external_includes += ["urllib2"]
    9991629        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001630        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011631        add_packages("xpra.platform.darwin")
     1632        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1633        #for u2f on python2:
     1634        if not PYTHON3:
     1635            modules.append("UserList")
     1636            modules.append("UserString")
     1637        #to support GStreamer 1.x we need this:
     1638        modules.append("importlib")
    10021639    else:
    10031640        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041641        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")
     1642        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1643        #not supported by all distros, but doesn't hurt to install them anyway:
     1644        for x in ("tmpfiles.d", "sysusers.d"):
     1645            add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1646        if uinput_ENABLED:
     1647            add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081648
    10091649    #gentoo does weird things, calls --no-compile with build *and* install
     
    10121652    #otherwise we use the flags to skip pkgconfig
    10131653    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))
     1654        pkgconfig = no_pkgconfig
    10331655
    10341656    if OSX and "py2app" in sys.argv:
     
    10451667        remove_packages(*external_excludes)
    10461668
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1669        try:
     1670            from xpra.src_info import REVISION
     1671        except:
     1672            REVISION = "unknown"
     1673        Plist = {
     1674            "CFBundleDocumentTypes" : {
     1675                "CFBundleTypeExtensions"    : ["Xpra"],
     1676                "CFBundleTypeName"          : "Xpra Session Config File",
     1677                "CFBundleName"              : "Xpra",
     1678                "CFBundleTypeRole"          : "Viewer",
     1679                },
     1680            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2018 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1681            "CFBundleIdentifier"            : "org.xpra.xpra",
     1682            }
    10531683        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541684        #so the make-app.sh script still has to hack around this problem.
    10551685        add_modules(*external_includes)
     1686        #needed by python-lz4:
     1687        add_modules("distutils")
    10561688        py2app_options = {
    10571689            'iconfile'          : '../osx/xpra.icns',
     
    10651697            }
    10661698        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1699        setup_options["app"]     = ["xpra/scripts/main.py"]
     1700
     1701    if OSX:
     1702        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1703        #so instead we have to query each package seperately and merge the results:
     1704        def osx_pkgconfig(*pkgs_options, **ekw):
     1705            kw = dict(ekw)
     1706            for pkg in pkgs_options:
     1707                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1708                if pkg.lower().startswith("x"):
     1709                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1710                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1711                kw = exec_pkgconfig(pkg, **kw)
     1712                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1713            return kw
     1714
     1715        pkgconfig = osx_pkgconfig
     1716
     1717
     1718if scripts_ENABLED:
     1719    scripts += ["scripts/xpra", "scripts/xpra_launcher"]
     1720
     1721add_data_files(share_xpra,                      ["README", "COPYING"])
     1722add_data_files(share_xpra,                      ["bell.wav"])
     1723add_data_files("%shttp-headers" % share_xpra,   glob.glob("http-headers/*"))
     1724add_data_files("%sicons" % share_xpra,          glob.glob("icons/*png"))
     1725add_data_files("%scontent-type" % share_xpra,   glob.glob("content-type/*"))
     1726add_data_files("%scontent-categories" % share_xpra, glob.glob("content-categories/*"))
    10681727
    10691728
    10701729if 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 
     1730    if WIN32 or OSX:
     1731        external_includes.append("websockify")
     1732        external_includes.append("numpy")
     1733        external_includes.append("ssl")
     1734        external_includes.append("_ssl")
     1735        if not PYTHON3:
     1736            external_includes.append("mimetypes")
     1737            external_includes.append("mimetools")
     1738            external_includes.append("BaseHTTPServer")
     1739
     1740
     1741if annotate_ENABLED:
     1742    from Cython.Compiler import Options
     1743    Options.annotate = True
    10761744
    10771745
    10781746#*******************************************************************************
    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")
     1747buffers_c = "xpra/buffers/buffers.c"
     1748memalign_c = "xpra/buffers/memalign.c"
     1749xxhash_c = "xpra/buffers/xxhash.c"
     1750membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1751
     1752add_packages("xpra.buffers")
     1753buffers_pkgconfig = pkgconfig(optimize=3)
     1754cython_add(Extension("xpra.buffers.membuf",
     1755            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1756
     1757
     1758toggle_packages(dbus_ENABLED, "xpra.dbus")
     1759toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1760toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
     1761toggle_packages(rfb_ENABLED, "xpra.server.rfb")
     1762toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1763toggle_packages(server_ENABLED, "xpra.server.window")
     1764toggle_packages(server_ENABLED or shadow_ENABLED, "xpra.server.mixins", "xpra.server.source")
     1765toggle_packages(shadow_ENABLED, "xpra.server.shadow")
     1766toggle_packages(server_ENABLED or client_ENABLED, "xpra.clipboard")
     1767toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1768toggle_packages(notifications_ENABLED, "xpra.notifications")
     1769
     1770#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1771if dbus_ENABLED and server_ENABLED:
     1772    add_packages("xpra.server.dbus")
     1773
     1774if OSX:
     1775    if PYTHON3:
     1776        quartz_pkgconfig = pkgconfig("gtk+-3.0", "pygobject-3.0")
     1777        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1778                    "-ObjC",
     1779                    "-framework", "AppKit",
     1780                    "-I/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/")
     1781        cython_add(Extension("xpra.platform.darwin.gdk3_bindings",
     1782                ["xpra/platform/darwin/gdk3_bindings.pyx"],
     1783                language="objc",
     1784                **quartz_pkgconfig
     1785                ))
     1786    else:
     1787        quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1788        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1789                    '-mmacosx-version-min=10.10',
     1790                    '-framework', 'Foundation',
     1791                    '-framework', 'AppKit',
     1792                    '-ObjC',
     1793                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h")
     1794        cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1795                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1796                language="objc",
     1797                **quartz_pkgconfig
     1798                ))
     1799
     1800monotonic_time_pkgconfig = pkgconfig()
     1801if not OSX and not WIN32 and not OPENBSD:
     1802    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1803cython_add(Extension("xpra.monotonic_time",
     1804            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1805            **monotonic_time_pkgconfig
     1806            ))
     1807
     1808
     1809toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841810if x11_ENABLED:
    10851811    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1812    if gtk2_ENABLED:
     1813        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk2/constants.pxi")
     1814    if gtk3_ENABLED:
     1815        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk3/constants.pxi")
    10871816
    10881817    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981827                **pkgconfig("x11")
    10991828                ))
     1829    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1830                ["xpra/x11/bindings/posix_display_source.pyx"],
     1831                **pkgconfig("x11")
     1832                ))
     1833
    11001834    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011835                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041838    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051839                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1840                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071841                ))
    11081842
    11091843    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101844                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1845                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121846                ))
    11131847    cython_add(Extension("xpra.x11.bindings.ximage",
    11141848                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1849                **pkgconfig("x11", "xext", "xcomposite")
    11161850                ))
    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)
     1851if xinput_ENABLED:
     1852    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1853                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1854                **pkgconfig("x11", "xi")
    11221855                ))
    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)
     1856
     1857toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1858toggle_packages(server_ENABLED and gtk_x11_ENABLED, "xpra.x11.models")
     1859if gtk_x11_ENABLED:
     1860    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1861    toggle_packages(not PYTHON3, "xpra.x11.gtk2")
     1862    if PYTHON3:
     1863        #GTK3 display source:
     1864        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1865                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1866                    **pkgconfig("gdk-3.0")
     1867                    ))
     1868        cython_add(Extension("xpra.x11.gtk3.gdk_bindings",
     1869                    ["xpra/x11/gtk3/gdk_bindings.pyx", "xpra/x11/gtk3/gdk_x11_macros.c"],
     1870                    **pkgconfig("gdk-3.0")
     1871                    ))
     1872
     1873    else:
     1874        #GTK2:
     1875        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1876                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1877                    **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1878                    ))
     1879        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1880        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1881                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1882                    **pkgconfig(*GDK_BINDINGS_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1883                    ))
     1884
     1885toggle_packages(not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED), "xpra.gtk_common.gtk2")
     1886if gtk2_ENABLED or (gtk_x11_ENABLED and not PYTHON3):
     1887    cython_add(Extension("xpra.gtk_common.gtk2.gdk_bindings",
     1888                ["xpra/gtk_common/gtk2/gdk_bindings.pyx"],
     1889                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11271890                ))
    1128 
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1891elif gtk3_ENABLED or (gtk_x11_ENABLED and PYTHON3):
     1892    cython_add(Extension("xpra.gtk_common.gtk3.gdk_bindings",
     1893                ["xpra/gtk_common/gtk3/gdk_bindings.pyx"],
     1894                **pkgconfig("gtk+-3.0", "pygobject-3.0")
     1895                ))
     1896
     1897if client_ENABLED and gtk3_ENABLED:
     1898    #cairo workaround:
     1899    if OSX or is_Ubuntu() or is_Debian():
     1900        pycairo = "py3cairo"
     1901    else:
     1902        pycairo = "pycairo"
     1903    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1904                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1905                **pkgconfig(pycairo)
     1906                ))
     1907
     1908if client_ENABLED or server_ENABLED:
     1909    add_packages("xpra.codecs.argb")
     1910    argb_pkgconfig = pkgconfig(optimize=3)
    11321911    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1912                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1913
     1914
     1915#build tests, but don't install them:
     1916toggle_packages(tests_ENABLED, "unit")
    11341917
    11351918
    11361919if bundle_tests_ENABLED:
    11371920    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1921    for k,v in glob_recurse("unit").items():
    11391922        if (k!=""):
    11401923            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:
     1924        add_data_files("unit"+k, v)
     1925
     1926#python-cryptography needs workarounds for bundling:
     1927if crypto_ENABLED and (OSX or WIN32):
     1928    external_includes.append("_ssl")
     1929    external_includes.append("cffi")
     1930    external_includes.append("_cffi_backend")
     1931    external_includes.append("cryptography")
     1932    external_includes.append("idna")
     1933    external_includes.append("idna.idnadata")
     1934    external_includes.append("pkg_resources._vendor.packaging")
     1935    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1936    external_includes.append("pkg_resources._vendor.pyparsing")
     1937    add_modules("cryptography.hazmat.bindings._openssl")
     1938    add_modules("cryptography.hazmat.bindings._constant_time")
     1939    add_modules("cryptography.hazmat.bindings._padding")
     1940    add_modules("cryptography.hazmat.backends.openssl")
     1941    add_modules("cryptography.fernet")
     1942    if WIN32:
     1943        external_includes.append("appdirs")
     1944
     1945#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441946if client_ENABLED:
    1145     add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1947    add_modules("xpra.client", "xpra.client.mixins")
     1948    add_modules("xpra.scripts.gtk_info")
     1949    add_modules("xpra.scripts.show_webcam")
     1950add_modules("xpra.scripts.bug_report")
     1951toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471952toggle_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")
     1953toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1954toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501955toggle_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")
     1956toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1957toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1958toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED) and example_ENABLED, "xpra.client.gtk_base.example")
     1959if client_ENABLED and WIN32 and MINGW_PREFIX:
     1960    propsys_pkgconfig = pkgconfig()
     1961    if debug_ENABLED:
     1962        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1963    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1964    cython_add(Extension("xpra.platform.win32.propsys",
     1965                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1966                language="c++",
     1967                **propsys_pkgconfig))
     1968
     1969if client_ENABLED or server_ENABLED:
     1970    add_modules("xpra.codecs")
     1971toggle_packages(keyboard_ENABLED, "xpra.keyboard")
     1972if client_ENABLED or server_ENABLED:
     1973    add_modules("xpra.scripts.config", "xpra.scripts.parsing", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1974if server_ENABLED or proxy_ENABLED:
     1975    add_modules("xpra.scripts.server")
     1976if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1977    add_modules("xpra.scripts.gtk_info")
     1978
     1979toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1980#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1981#and tries to include both gtk3 and gtk2, and fails hard..
     1982for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gl_drivers", "window_backend"):
     1983    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1984toggle_packages(client_ENABLED and opengl_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gl.gtk_base")
     1985
     1986
     1987toggle_modules(sound_ENABLED, "xpra.sound")
     1988toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541989
    11551990toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11561991if 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:
     1992    if PYTHON3:
     1993        cython_add(Extension("xpra.gtk_common.gtk3.gdk_atoms",
     1994                             ["xpra/gtk_common/gtk3/gdk_atoms.pyx"],
     1995                             **pkgconfig("gtk+-3.0")
     1996                             ))
     1997    else:
     1998        cython_add(Extension("xpra.gtk_common.gtk2.gdk_atoms",
     1999                             ["xpra/gtk_common/gtk2/gdk_atoms.pyx"],
     2000                             **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     2001                             ))
     2002
     2003toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     2004if client_ENABLED or server_ENABLED:
    11632005    cython_add(Extension("xpra.codecs.xor.cyxor",
    11642006                ["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")
     2007                **pkgconfig(optimize=3)))
     2008
     2009if server_ENABLED or shadow_ENABLED:
     2010    O3_pkgconfig = pkgconfig(optimize=3)
     2011    cython_add(Extension("xpra.server.cystats",
     2012                ["xpra/server/cystats.pyx"],
     2013                **O3_pkgconfig))
     2014    cython_add(Extension("xpra.server.window.region",
     2015                ["xpra/server/window/region.pyx"],
     2016                **O3_pkgconfig))
     2017    cython_add(Extension("xpra.server.window.motion",
     2018                ["xpra/server/window/motion.pyx"],
     2019                **O3_pkgconfig))
     2020
     2021if sd_listen_ENABLED:
     2022    sdp = pkgconfig("libsystemd")
     2023    cython_add(Extension("xpra.platform.xposix.sd_listen",
     2024                ["xpra/platform/xposix/sd_listen.pyx"],
     2025                **sdp))
     2026
     2027
    11782028toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11792029
     2030toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     2031if nvfbc_ENABLED:
     2032    nvfbc_pkgconfig = pkgconfig("nvfbc")
     2033    if WIN32:
     2034        add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     2035    platform = sys.platform.rstrip("0123456789")
     2036    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     2037                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     2038                         language="c++",
     2039                         **nvfbc_pkgconfig))
     2040
    11802041toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     2042toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     2043toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     2044
     2045if nvenc_ENABLED and cuda_kernels_ENABLED:
     2046    #find nvcc:
     2047    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     2048    if WIN32:
     2049        nvcc_exe = "nvcc.exe"
     2050        CUDA_DIR = os.environ.get("CUDA_DIR", "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA")
     2051        path_options = [os.path.join(CUDA_DIR, x, "bin") for x in ("v9.2", "v9.1", "v9.0", "v8.0", "v7.5")] + path_options
     2052        #pycuda may link against curand, find it and ship it:
     2053        for p in path_options:
     2054            if os.path.exists(p):
     2055                add_data_files("", glob.glob("%s\\curand64*.dll" % p))
     2056                add_data_files("", glob.glob("%s\\cudart64*.dll" % p))
     2057                break
     2058    else:
     2059        nvcc_exe = "nvcc"
     2060        for v in ("", "10.0", "9.2", "9.1", "-9.0", "-8.0", "-7.5"):
     2061            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     2062    options = [os.path.join(x, nvcc_exe) for x in path_options]
     2063    def which(cmd):
     2064        try:
     2065            code, out, _ = get_status_output(["which", cmd])
     2066            if code==0:
     2067                return out
     2068        except:
     2069            pass
     2070    #prefer the one we find on the $PATH, if any:
     2071    try:
     2072        v = which(nvcc_exe)
     2073        if v and (v not in options):
     2074            options.insert(0, v)
     2075    except:
     2076        pass
     2077    nvcc_versions = {}
     2078    for filename in options:
     2079        if not os.path.exists(filename):
     2080            continue
     2081        code, out, err = get_status_output([filename, "--version"])
     2082        if code==0:
     2083            vpos = out.rfind(", V")
     2084            if vpos>0:
     2085                version = out[vpos+3:].strip("\n")
     2086                version_str = " version %s" % version
     2087            else:
     2088                version = "0"
     2089                version_str = " unknown version!"
     2090            print("found CUDA compiler: %s%s" % (filename, version_str))
     2091            vnum = tuple(int(x) for x in version.split("."))
     2092            nvcc_versions[vnum] = filename
     2093    assert nvcc_versions, "cannot find nvcc compiler!"
     2094    #choose the most recent one:
     2095    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     2096    if len(nvcc_versions)>1:
     2097        print(" using version %s from %s" % (version, nvcc))
     2098    if WIN32:
     2099        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     2100        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     2101    #first compile the cuda kernels
     2102    #(using the same cuda SDK for both nvenc modules for now..)
     2103    #TODO:
     2104    # * compile directly to output directory instead of using data files?
     2105    # * detect which arches we want to build for? (does it really matter much?)
     2106    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     2107    for kernel in kernels:
     2108        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     2109        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     2110        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     2111            continue
     2112        reason = should_rebuild(cuda_src, cuda_bin)
     2113        if not reason:
     2114            continue
     2115        print("rebuilding %s: %s" % (kernel, reason))
     2116        cmd = [nvcc,
     2117               '-fatbin',
     2118               #"-cubin",
     2119               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     2120               #"-gencode=arch=compute_50,code=sm_50",
     2121               #"-gencode=arch=compute_52,code=sm_52",
     2122               #"-gencode=arch=compute_52,code=compute_52",
     2123               "-c", cuda_src,
     2124               "-o", cuda_bin]
     2125        #GCC 8.1 has compatibility issues with CUDA 9.2,
     2126        #so revert to C++03:
     2127        if get_gcc_version()>=[8, 1]:
     2128            cmd.append("-std=c++03")
     2129        #GCC 6 uses C++11 by default:
     2130        elif get_gcc_version()>=[6, 0]:
     2131            cmd.append("-std=c++11")
     2132        CL_VERSION = os.environ.get("CL_VERSION")
     2133        if CL_VERSION:
     2134            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     2135            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     2136            cmd += ["--machine", "32"]
     2137        if WIN32:
     2138            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     2139            #cmd += ["--input-drive-prefix", "/"]
     2140            #cmd += ["--dependency-drive-prefix", "/"]
     2141            cmd += ["-I%s" % os.path.abspath("win32")]
     2142        comp_code_options = [(30, 30), (35, 35)]
     2143        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     2144        if version!=(0,) and version<(7, 5):
     2145            print("CUDA version %s is very unlikely to work")
     2146            print("try upgrading to version 7.5 or later")
     2147        if version>=(7, 5):
     2148            comp_code_options.append((50, 50))
     2149            comp_code_options.append((52, 52))
     2150            comp_code_options.append((53, 53))
     2151        if version>=(8, 0):
     2152            comp_code_options.append((60, 60))
     2153            comp_code_options.append((61, 61))
     2154            comp_code_options.append((62, 62))
     2155        if version>=(9, 0):
     2156            comp_code_options.append((70, 70))
     2157        if version>=(10, 0):
     2158            comp_code_options.append((75, 75))
     2159        for arch, code in comp_code_options:
     2160            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2161        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2162        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2163        c, stdout, stderr = get_status_output(cmd)
     2164        if c!=0:
     2165            print("Error: failed to compile CUDA kernel %s" % kernel)
     2166            print(stdout or "")
     2167            print(stderr or "")
     2168            sys.exit(1)
     2169    CUDA_BIN = "%scuda" % share_xpra
     2170    if WIN32:
     2171        CUDA_BIN = "CUDA"
     2172    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2173
    11812174if 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))
     2175    nvencmodule = "nvenc"
     2176    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2177    #don't link against libnvidia-encode, we load it dynamically:
     2178    libraries = nvenc_pkgconfig.get("libraries", [])
     2179    if "nvidia-encode" in libraries:
     2180        libraries.remove("nvidia-encode")
     2181    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2182        #with gcc 6.2 on Fedora:
     2183        #xpra/codecs/nvenc/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2184        #xpra/codecs/nvenc/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2185        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2186        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2187    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2188                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2189                         **nvenc_pkgconfig))
    11872190
    11882191toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892192if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2193    x264_pkgconfig = pkgconfig("x264")
     2194    if get_gcc_version()>=[6, 0]:
     2195        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912196    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922197                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2198                **x264_pkgconfig))
    11942199
    11952200toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962201if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2202    x265_pkgconfig = pkgconfig("x265")
    11982203    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992204                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
     2205                **x265_pkgconfig))
     2206
     2207toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2208if pillow_ENABLED:
     2209    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
    12012210
    12022211toggle_packages(webp_ENABLED, "xpra.codecs.webp")
    12032212if webp_ENABLED:
    1204     webp_pkgconfig = pkgconfig("webp")
     2213    webp_pkgconfig = pkgconfig("libwebp")
    12052214    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))
     2215                    ["xpra/codecs/webp/encode.pyx"],
     2216                    **webp_pkgconfig))
     2217    cython_add(Extension("xpra.codecs.webp.decode",
     2218                ["xpra/codecs/webp/decode.pyx"],
     2219                **webp_pkgconfig))
     2220
     2221toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2222if jpeg_ENABLED:
     2223    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2224    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2225                ["xpra/codecs/jpeg/encoder.pyx"],
     2226                **jpeg_pkgconfig))
     2227    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2228                ["xpra/codecs/jpeg/decoder.pyx"],
     2229                **jpeg_pkgconfig))
     2230
     2231#swscale and avcodec2 use libav_common/av_log:
     2232libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2233toggle_packages(libav_common, "xpra.codecs.libav_common")
     2234if libav_common:
     2235    avutil_pkgconfig = pkgconfig("libavutil")
     2236    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2237                ["xpra/codecs/libav_common/av_log.pyx"],
     2238                **avutil_pkgconfig))
     2239
    12162240
    12172241toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182242if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2243    avcodec2_pkgconfig = pkgconfig("libavcodec", "libavutil", "libavformat")
    12202244    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 
     2245                ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/dec_avcodec2/register_compat.c"],
     2246                **avcodec2_pkgconfig))
     2247
     2248
     2249toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2250if csc_libyuv_ENABLED:
     2251    libyuv_pkgconfig = pkgconfig("libyuv")
     2252    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2253                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2254                language="c++",
     2255                **libyuv_pkgconfig))
    12242256
    12252257toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262258if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2259    swscale_pkgconfig = pkgconfig("libswscale", "libavutil")
    12292260    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))
     2261                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2262                **swscale_pkgconfig))
     2263
    12392264
    12402265toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412266if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2267    vpx_pkgconfig = pkgconfig("vpx")
    12432268    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))
     2269                ["xpra/codecs/vpx/encoder.pyx"],
     2270                **vpx_pkgconfig))
    12462271    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))
     2272                ["xpra/codecs/vpx/decoder.pyx"],
     2273                **vpx_pkgconfig))
     2274
     2275toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2276if enc_ffmpeg_ENABLED:
     2277    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2278    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2279                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2280                **ffmpeg_pkgconfig))
     2281
     2282toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2283if v4l2_ENABLED:
     2284    v4l2_pkgconfig = pkgconfig()
     2285    #fuly warning: cython makes this difficult,
     2286    #we have to figure out if "device_caps" exists in the headers:
     2287    ENABLE_DEVICE_CAPS = False
     2288    if os.path.exists("/usr/include/linux/videodev2.h"):
     2289        hdata = open("/usr/include/linux/videodev2.h").read()
     2290        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2291    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2292    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2293    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2294                ["xpra/codecs/v4l2/pusher.pyx"],
     2295                **v4l2_pkgconfig))
    12622296
    12632297
    12642298toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2299toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652300if 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")
     2301    bencode_pkgconfig = pkgconfig(optimize=3)
    12722302    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732303                ["xpra/net/bencode/cython_bencode.pyx"],
    12742304                **bencode_pkgconfig))
    12752305
     2306if netdev_ENABLED:
     2307    netdev_pkgconfig = pkgconfig()
     2308    cython_add(Extension("xpra.platform.xposix.netdev_query",
     2309                ["xpra/platform/xposix/netdev_query.pyx"],
     2310                **netdev_pkgconfig))
     2311
     2312if vsock_ENABLED:
     2313    vsock_pkgconfig = pkgconfig()
     2314    cython_add(Extension("xpra.net.vsock",
     2315                ["xpra/net/vsock.pyx"],
     2316                **vsock_pkgconfig))
     2317
     2318if pam_ENABLED:
     2319    pam_pkgconfig = pkgconfig()
     2320    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2321    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2322    cython_add(Extension("xpra.server.pam",
     2323                ["xpra/server/pam.pyx"],
     2324                **pam_pkgconfig))
     2325
    12762326
    12772327if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2328    from Cython.Build import cythonize
     2329    #this causes Cython to fall over itself:
     2330    #gdb_debug=debug_ENABLED
     2331    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792332if cmdclass:
    12802333    setup_options["cmdclass"] = cmdclass
     
    12832336
    12842337
    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 
    12932338def main():
    12942339    if OSX or WIN32 or debug_ENABLED:
     2340        print()
    12952341        print("setup options:")
     2342        if verbose_ENABLED:
     2343            print("setup_options=%s" % (setup_options,))
     2344        try:
     2345            from xpra.util import repr_ellipsized as pv
     2346        except:
     2347            def pv(v):
     2348                return str(v)
    12962349        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2350            print_option("", k, pv(v))
    12982351        print("")
    12992352
Note: See TracChangeset for help on using the changeset viewer.