xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r15744  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2017 Antoine Martin <antoine@devloop.org.uk>
    55# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <njs@pobox.com>
    66# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     
    1111# does the make_constants hack.)
    1212
    13 import commands
    1413import glob
    1514from distutils.core import setup
    1615from distutils.extension import Extension
    17 import subprocess, sys, traceback
     16import sys
    1817import os.path
    19 import stat
     18from distutils.command.build import build
     19from distutils.command.install_data import install_data
     20import shutil
     21
     22if sys.version<'2.7':
     23    raise Exception("xpra no longer supports Python 2 versions older than 2.7")
     24if sys.version[0]=='3' and sys.version<'3.4':
     25    raise Exception("xpra no longer supports Python 3 versions older than 3.4")
     26#we don't support versions of Python without the new ssl code:
     27import ssl
     28assert ssl.SSLContext, "xpra requires a Python version with ssl.SSLContext support"
     29
     30from hashlib import md5
    2031
    2132print(" ".join(sys.argv))
    2233
     34#*******************************************************************************
     35# build options, these may get modified further down..
     36#
    2337import xpra
    24 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
    25 
    26 WIN32 = sys.platform.startswith("win")
     38data_files = []
     39modules = []
     40packages = []       #used by py2app
     41excludes = []       #only used by cx_freeze on win32
     42ext_modules = []
     43cmdclass = {}
     44scripts = []
     45description = "multi-platform screen and application forwarding system"
     46long_description = "Xpra is a multi platform persistent remote display server and client for " + \
     47            "forwarding applications and desktop screens. Also known as 'screen for X11'."
     48url = "http://xpra.org/"
     49
     50
     51XPRA_VERSION = xpra.__version__         #@UndefinedVariable
     52setup_options = {
     53                 "name"             : "xpra",
     54                 "version"          : XPRA_VERSION,
     55                 "license"          : "GPLv2+",
     56                 "author"           : "Antoine Martin",
     57                 "author_email"     : "antoine@devloop.org.uk",
     58                 "url"              : url,
     59                 "download_url"     : "http://xpra.org/src/",
     60                 "description"      : description,
     61                 "long_description" : long_description,
     62                 "data_files"       : data_files,
     63                 "py_modules"       : modules,
     64                 }
     65
     66WIN32 = sys.platform.startswith("win") or sys.platform.startswith("msys")
    2767OSX = sys.platform.startswith("darwin")
    28 
    29 
     68LINUX = sys.platform.startswith("linux")
     69PYTHON3 = sys.version_info[0] == 3
     70import struct
     71BITS = struct.calcsize("P")*8
     72
     73
     74if "pkg-info" in sys.argv:
     75    with open("PKG-INFO", "wb") as f:
     76        pkg_info_values = setup_options.copy()
     77        pkg_info_values.update({
     78                                "metadata_version"  : "1.1",
     79                                "summary"           :  description,
     80                                "home_page"         : url,
     81                                })
     82        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     83                  "Author", "Author-email", "License", "Download-URL", "Description"):
     84            v = pkg_info_values[k.lower().replace("-", "_")]
     85            f.write(b"%s: %s\n" % (k, v))
     86    sys.exit(0)
     87
     88
     89print("Xpra version %s" % XPRA_VERSION)
    3090#*******************************************************************************
    3191# Most of the options below can be modified on the command line
     
    3393# only the default values are specified here:
    3494#*******************************************************************************
    35 def pkg_config_ok(*args):
    36     return commands.getstatusoutput("pkg-config %s" % (" ".join(args)))[0]==0
    37 
    38 shadow_ENABLED = SHADOW_SUPPORTED
    39 server_ENABLED = LOCAL_SERVERS_SUPPORTED or shadow_ENABLED
    40 client_ENABLED = True
    41 
    42 x11_ENABLED = not WIN32 and not OSX
    43 argb_ENABLED = True
    44 gtk2_ENABLED = client_ENABLED
    45 gtk3_ENABLED = False
    46 qt4_ENABLED = False
    47 opengl_ENABLED = client_ENABLED
    48 html5_ENABLED = not WIN32 and not OSX
    49 
    50 bencode_ENABLED         = True
    51 cython_bencode_ENABLED  = True
    52 rencode_ENABLED         = True
    53 cymaths_ENABLED         = True
    54 cyxor_ENABLED           = True
    55 clipboard_ENABLED       = True
    56 Xdummy_ENABLED          = None          #none means auto-detect
    57 sound_ENABLED           = True
    58 
    59 enc_proxy_ENABLED       = True
    60 enc_x264_ENABLED        = True          #too important to detect
    61 enc_x265_ENABLED        = pkg_config_ok("--exists", "x265")
    62 webp_ENABLED            = WIN32 or pkg_config_ok("--atleast-version=0.3", "libwebp")
    63 x264_static_ENABLED     = False
    64 x265_static_ENABLED     = False
    65 vpx_ENABLED             = WIN32 or pkg_config_ok("--atleast-version=1.0", "vpx") or pkg_config_ok("--atleast-version=1.0", "libvpx")
    66 vpx_static_ENABLED      = False
    67 #ffmpeg 1.x and libav:
    68 dec_avcodec_ENABLED     = not WIN32 and pkg_config_ok("--max-version=55", "libavcodec")
     95from xpra.os_util import get_status_output
     96
     97PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     98has_pkg_config = False
     99#we don't support building with "pkg-config" on win32 with python2:
     100if PKG_CONFIG:
     101    v = get_status_output([PKG_CONFIG, "--version"])
     102    has_pkg_config = v[0]==0 and v[1]
     103    if has_pkg_config:
     104        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     105
     106for arg in list(sys.argv):
     107    if arg.startswith("--pkg-config-path="):
     108        pcp = arg[len("--pkg-config-path="):]
     109        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     110        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     111        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     112        sys.argv.remove(arg)
     113
     114def pkg_config_ok(*args, **kwargs):
     115    if not has_pkg_config:
     116        return kwargs.get("fallback", False)
     117    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     118    return get_status_output(cmd)[0]==0
     119
     120def pkg_config_version(req_version, pkgname, **kwargs):
     121    if not has_pkg_config:
     122        return kwargs.get("fallback", False)
     123    cmd = [PKG_CONFIG, "--modversion", pkgname]
     124    r, out, _ = get_status_output(cmd)
     125    if r!=0 or not out:
     126        return False
     127    from distutils.version import LooseVersion
     128    return LooseVersion(out)>=LooseVersion(req_version)
     129
     130def is_RH():
     131    try:
     132        with open("/etc/redhat-release", mode='rb') as f:
     133            data = f.read()
     134        return data.startswith("CentOS") or data.startswith("RedHat")
     135    except:
     136        pass
     137    return False
     138
     139DEFAULT = True
     140if "--minimal" in sys.argv:
     141    sys.argv.remove("--minimal")
     142    DEFAULT = False
     143
     144from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     145shadow_ENABLED = SHADOW_SUPPORTED and not (PYTHON3 and LINUX) and DEFAULT       #shadow servers use some GTK2 code..
     146server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and not (PYTHON3 and LINUX) and DEFAULT
     147service_ENABLED = LINUX and server_ENABLED
     148proxy_ENABLED  = DEFAULT
     149client_ENABLED = DEFAULT
     150
     151x11_ENABLED = DEFAULT and not WIN32 and not OSX
     152dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     153gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     154gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     155gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     156opengl_ENABLED = DEFAULT and client_ENABLED
     157html5_ENABLED = DEFAULT
     158html5_gzip_ENABLED = DEFAULT
     159html5_brotli_ENABLED = DEFAULT
     160minify_ENABLED = html5_ENABLED
     161pam_ENABLED = DEFAULT and (server_ENABLED or proxy_ENABLED) and os.name=="posix" and not OSX and (os.path.exists("/usr/include/pam/pam_misc.h") or os.path.exists("/usr/include/security/pam_misc.h"))
     162
     163vsock_ENABLED           = sys.platform.startswith("linux") and os.path.exists("/usr/include/linux/vm_sockets.h")
     164bencode_ENABLED         = DEFAULT
     165cython_bencode_ENABLED  = DEFAULT
     166clipboard_ENABLED       = DEFAULT and not PYTHON3
     167Xdummy_ENABLED          = None          #None means auto-detect
     168Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     169if WIN32 or OSX:
     170    Xdummy_ENABLED = False
     171sound_ENABLED           = DEFAULT
     172printing_ENABLED        = DEFAULT
     173crypto_ENABLED          = DEFAULT
     174mdns_ENABLED            = DEFAULT
     175
     176enc_proxy_ENABLED       = DEFAULT
     177enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264", fallback=WIN32)
     178enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     179pillow_ENABLED          = DEFAULT
     180jpeg_ENABLED            = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     181vpx_ENABLED             = DEFAULT and pkg_config_version("1.3", "vpx", fallback=WIN32)
     182enc_ffmpeg_ENABLED      = DEFAULT and pkg_config_version("56", "libavcodec")
     183webcam_ENABLED          = DEFAULT and not OSX
     184v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not sys.platform.startswith("freebsd"))
    69185#ffmpeg 2 onwards:
    70 dec_avcodec2_ENABLED    = WIN32 or pkg_config_ok("--atleast-version=55", "libavcodec")
     186dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("56", "libavcodec", fallback=WIN32)
    71187# 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 
     188# Fedora:
     189# * 19: 54.92.100
     190# * 20: 55.39.101
     191# * 21: 55.52.102
     192# Debian:
     193# * jessie and sid: (last updated 2014-05-26): 55.34.1
     194#   (moved to ffmpeg2 style buffer API sometime in early 2014)
     195# * wheezy: 53.35
     196csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale", fallback=WIN32)
     197nvenc7_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvenc7")
     198nvfbc_ENABLED = DEFAULT and BITS==64 and WIN32
     199cuda_rebuild_ENABLED    = DEFAULT
     200csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv", fallback=WIN32)
     201
     202#Cython / gcc / packaging build options:
     203annotate_ENABLED        = True
    86204warn_ENABLED            = True
    87205strict_ENABLED          = True
    88 PIC_ENABLED             = True
     206PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89207debug_ENABLED           = False
    90208verbose_ENABLED         = False
    91209bundle_tests_ENABLED    = False
     210tests_ENABLED           = False
     211rebuild_ENABLED         = True
    92212
    93213#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",
     214SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     215            "nvenc7", "cuda_rebuild", "nvfbc",
     216            "vpx", "pillow", "jpeg",
     217            "v4l2",
     218            "dec_avcodec2", "csc_swscale",
     219            "csc_libyuv",
     220            "bencode", "cython_bencode", "vsock", "mdns",
    104221            "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")
     222            "server", "client", "dbus", "x11", "gtk_x11", "service",
     223            "gtk2", "gtk3",
     224            "html5", "minify", "html5_gzip", "html5_brotli",
     225            "pam",
     226            "sound", "opengl", "printing", "webcam",
     227            "rebuild",
     228            "annotate", "warn", "strict",
     229            "shadow", "proxy",
     230            "debug", "PIC",
     231            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     232if WIN32:
     233    SWITCHES.append("zip")
     234    zip_ENABLED = True
    109235HELP = "-h" in sys.argv or "--help" in sys.argv
    110236if HELP:
     
    120246            default_str = "auto-detect"
    121247        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     248    print("  --pkg-config-path=PATH")
     249    print("  --rpath=PATH")
    122250    sys.exit(0)
    123251
     252install = "dist"
     253rpath = None
     254ssl_cert = None
     255ssl_key = None
     256nvfbc_path = "E:\\NVIDIA Capture SDK\\"
    124257filtered_args = []
    125258for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     259    matched = False
     260    for x in ("rpath", "ssl-cert", "ssl-key", "install", "nvfbc_path"):
     261        varg = "--%s=" % x
     262        if arg.startswith(varg):
     263            value = arg[len(varg):]
     264            globals()[x.replace("-", "_")] = value
     265            #remove these arguments from sys.argv,
     266            #except for --install=PATH
     267            matched = x!="install"
     268            break
     269    if matched:
    129270        continue
    130     matched = False
    131271    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     272        with_str = "--with-%s" % x
     273        without_str = "--without-%s" % x
     274        if arg.startswith(with_str+"="):
     275            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     276            matched = True
     277            break
     278        elif arg==with_str:
    133279            vars()["%s_ENABLED" % x] = True
    134280            matched = True
    135281            break
    136         elif arg=="--without-%s" % x:
     282        elif arg==without_str:
    137283            vars()["%s_ENABLED" % x] = False
    138284            matched = True
     
    145291    for x in SWITCHES:
    146292        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)
     293    print("build switches:")
     294    for k in SWITCHES:
     295        v = switches_info[k]
     296        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150297
    151298    #sanity check the flags:
     
    153300        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154301        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
    158302    if shadow_ENABLED and not server_ENABLED:
    159303        print("Warning: shadow requires server to be enabled!")
    160304        shadow_ENABLED = False
    161     if cymaths_ENABLED and not server_ENABLED:
    162         print("Warning: cymaths requires server to be enabled!")
    163         cymaths_ENABLED = False
    164305    if x11_ENABLED and WIN32:
    165306        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:
     307    if gtk_x11_ENABLED and not x11_ENABLED:
     308        print("Error: you must enable x11 to support gtk_x11!")
     309        exit(1)
     310    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167311        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)
     312    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     313        print("Warning: you probably want to build at least the client or server!")
     314    if DEFAULT and not pillow_ENABLED:
     315        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     316    if minify_ENABLED:
     317        r = get_status_output(["uglifyjs", "--version"])[0]
     318        if r==0:
     319            minifier = "uglifyjs"
     320        else:
     321            print("Warning: uglifyjs failed and return %i" % r)
     322            try:
     323                import yuicompressor
     324                assert yuicompressor
     325                minifier = "yuicompressor"
     326            except ImportError as e:
     327                print("Warning: yuicompressor module not found, cannot minify")
     328                minify_ENABLED = False
     329    if not enc_x264_ENABLED and not vpx_ENABLED:
     330        print("Warning: no x264 and no vpx support!")
     331        print(" you should enable at least one of these two video encodings")
     332    if nvfbc_ENABLED and not os.path.exists(nvfbc_path):
     333        print("Warning: cannot find NvFBC SDK in")
     334        print(" %s" % nvfbc_path)
     335        print(" nvfbc codec disabled")
     336        nvfbc_ENABLED = False
    174337
    175338
    176339#*******************************************************************************
    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",
     340# default sets:
     341
     342external_includes = ["hashlib",
    204343                     "ctypes", "platform"]
     344
     345
     346if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     347    external_includes += ["gi"]
     348elif gtk2_ENABLED or x11_ENABLED:
     349    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205350
    206351external_excludes = [
     
    212357                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213358                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
     359                    "curses", "pdb",
     360                    "urllib2", "tty",
     361                    "cookielib", "ftplib", "httplib", "fileinput",
    218362                    "distutils", "setuptools", "doctest"
    219363                    ]
    220 
     364if not html5_ENABLED and not crypto_ENABLED:
     365    external_excludes += ["ssl", "_ssl"]
     366if not html5_ENABLED:
     367    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     368
     369if not client_ENABLED and not server_ENABLED:
     370    excludes += ["PIL"]
     371if not dbus_ENABLED:
     372    excludes += ["dbus"]
    221373
    222374
    223375#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     376#for distutils / py2app and cx_freeze
    225377#use the following functions, which should get the right
    226378#data in the global variables "packages", "modules" and "excludes"
     
    253405
    254406def add_modules(*mods):
     407    def add(v):
     408        global modules
     409        if v not in modules:
     410            modules.append(v)
     411    do_add_modules(add, *mods)
     412
     413def do_add_modules(op, *mods):
    255414    """ adds the packages and any .py module found in the packages to the "modules" list
    256415    """
    257416    global modules
    258417    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     418        #ugly path stripping:
     419        if x.startswith("./"):
     420            x = x[2:]
     421        if x.endswith(".py"):
     422            x = x[:-3]
     423            x = x.replace("/", ".") #.replace("\\", ".")
    261424        pathname = os.path.sep.join(x.split("."))
     425        #is this a file module?
     426        f = "%s.py" % pathname
     427        if os.path.exists(f) and os.path.isfile(f):
     428            op(x)
    262429        if os.path.exists(pathname) and os.path.isdir(pathname):
    263430            #add all file modules found in this directory
    264431            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     432                #make sure we only include python files,
     433                #and ignore eclipse copies
     434                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266435                    fname = os.path.join(pathname, f)
    267436                    if os.path.isfile(fname):
    268437                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     438                        op(modname)
    270439
    271440def toggle_packages(enabled, *module_names):
     
    275444        remove_packages(*module_names)
    276445
     446def toggle_modules(enabled, *module_names):
     447    if enabled:
     448        def op(v):
     449            global modules
     450            if v not in modules:
     451                modules.append(v)
     452        do_add_modules(op, *module_names)
     453    else:
     454        remove_packages(*module_names)
     455
     456
    277457#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 
     458add_modules("xpra", "xpra.platform", "xpra.net")
     459add_modules("xpra.scripts.main")
     460
     461
     462def add_data_files(target_dir, files):
     463    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     464    assert type(target_dir)==str
     465    assert type(files) in (list, tuple)
     466    data_files.append((target_dir, files))
     467
     468
     469def check_md5sums(md5sums):
     470    print("Verifying md5sums:")
     471    for filename, md5sum in md5sums.items():
     472        if not os.path.exists(filename) or not os.path.isfile(filename):
     473            sys.exit("ERROR: file %s is missing or not a file!" % filename)
     474        sys.stdout.write("* %s: " % str(filename).ljust(52))
     475        with open(filename, mode='rb') as f:
     476            data = f.read()
     477        m = md5()
     478        m.update(data)
     479        digest = m.hexdigest()
     480        assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (filename, md5sum, digest)
     481        sys.stdout.write("OK\n")
     482        sys.stdout.flush()
     483
     484#for pretty printing of options:
     485def print_option(prefix, k, v):
     486    if type(v)==dict:
     487        print("%s* %s:" % (prefix, k))
     488        for kk,vv in v.items():
     489            print_option(" "+prefix, kk, vv)
     490    else:
     491        print("%s* %s=%s" % (prefix, k, v))
    284492
    285493#*******************************************************************************
     
    288496    try:
    289497        from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
     498    except ImportError as e:
    291499        sys.exit("ERROR: Cannot find Cython: %s" % e)
    292500    from distutils.version import LooseVersion
     
    297505                 % (cython_version, ".".join([str(part) for part in min_version])))
    298506
    299 def cython_add(extension, min_version=(0, 14, 0)):
     507def cython_add(extension, min_version=(0, 19)):
    300508    #gentoo does weird things, calls --no-compile with build *and* install
    301509    #then expects to find the cython modules!? ie:
     
    303511    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304512        return
    305     global ext_modules, cmdclass
    306513    cython_version_check(min_version)
    307514    from Cython.Distutils import build_ext
    308515    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     516    global cmdclass
     517    cmdclass['build_ext'] = build_ext
     518
     519def insert_into_keywords(kw, key, *args):
     520    values = kw.setdefault(key, [])
     521    for arg in args:
     522        values.insert(0, arg)
    310523
    311524def add_to_keywords(kw, key, *args):
     
    319532
    320533
     534def checkdirs(*dirs):
     535    for d in dirs:
     536        if not os.path.exists(d) or not os.path.isdir(d):
     537            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     538
    321539PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
    322540
     
    326544    if len(GCC_VERSION)==0:
    327545        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:
     546        r, _, err = get_status_output(cmd)
     547        if r==0:
    332548            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     549            for line in err.splitlines():
    334550                if line.startswith(V_LINE):
    335551                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343559    return GCC_VERSION
    344560
    345 def make_constants_pxi(constants_path, pxi_path):
     561def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346562    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):
     563    with open(constants_path) as f:
     564        for line in f:
     565            data = line.split("#", 1)[0].strip()
     566            # data can be empty ''...
     567            if not data:
     568                continue
     569            # or a pair like 'cFoo "Foo"'...
     570            elif len(data.split()) == 2:
     571                (pyname, cname) = data.split()
     572                constants.append((pyname, cname))
     573            # or just a simple token 'Foo'
     574            else:
     575                constants.append(data)
     576
     577    with open(pxi_path, "w") as out:
     578        if constants:
     579            out.write("cdef extern from *:\n")
     580            ### Apparently you can't use | on enum's?!
     581            # out.write("    enum MagicNumbers:\n")
     582            # for const in constants:
     583            #     if isinstance(const, tuple):
     584            #         out.write('        %s %s\n' % const)
     585            #     else:
     586            #         out.write('        %s\n' % (const,))
     587            for const in constants:
     588                if isinstance(const, tuple):
     589                    out.write('    unsigned int %s %s\n' % const)
     590                else:
     591                    out.write('    unsigned int %s\n' % (const,))
     592
     593            out.write("constants = {\n")
     594            for const in constants:
     595                if isinstance(const, tuple):
     596                    pyname = const[0]
     597                else:
     598                    pyname = const
     599                out.write('    "%s": %s,\n' % (pyname, pyname))
     600            out.write("}\n")
     601            if kwargs:
     602                out.write("\n\n")
     603
     604        if kwargs:
     605            for k, v in kwargs.items():
     606                out.write('DEF %s = %s\n' % (k, v))
     607
     608
     609def should_rebuild(src_file, bin_file):
     610    if not os.path.exists(bin_file):
     611        return "no file"
     612    elif rebuild_ENABLED:
     613        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     614            return "binary file out of date"
     615        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     616            return "newer build file"
     617    return None
     618
     619def make_constants(*paths, **kwargs):
    384620    base = os.path.join(os.getcwd(), *paths)
    385621    constants_file = "%s.txt" % base
    386622    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"
     623    reason = should_rebuild(constants_file, pxi_file)
    394624    if reason:
    395625        if verbose_ENABLED:
    396626            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
     627        make_constants_pxi(constants_file, pxi_file, **kwargs)
     628
    414629
    415630# 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 
     631def exec_pkgconfig(*pkgs_options, **ekw):
    423632    kw = dict(ekw)
     633    if "optimize" in kw:
     634        optimize = kw["optimize"]
     635        del kw["optimize"]
     636        if type(optimize)==bool:
     637            optimize = int(optimize)*3
     638        if not debug_ENABLED:
     639            add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     640    ignored_flags = []
     641    if kw.get("ignored_flags"):
     642        ignored_flags = kw.get("ignored_flags")
     643        del kw["ignored_flags"]
     644
    424645    if len(pkgs_options)>0:
    425646        package_names = []
     
    438659            for option in options:
    439660                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:
     661                r, _, _ = get_status_output(cmd)
     662                if r==0:
    443663                    valid_option = option
    444664                    break
    445665            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     666                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)")))
    447667            package_names.append(valid_option)
    448668        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     669            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450670        flag_map = {'-I': 'include_dirs',
    451671                    '-L': 'library_dirs',
    452672                    '-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))
     673        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     674        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     675        if r!=0:
     676            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     677        env_cflags = os.environ.get("CFLAGS")       #["dpkg-buildflags", "--get", "CFLAGS"]
     678        env_ldflags = os.environ.get("LDFLAGS")     #["dpkg-buildflags", "--get", "LDFLAGS"]
     679        for s in (pkg_config_out, env_cflags, env_ldflags):
     680            if not s:
     681                continue
     682            for token in s.split():
     683                if token[:2] in ignored_flags:
     684                    pass
     685                elif token[:2] in flag_map:
     686                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     687                else: # throw others to extra_link_args
     688                    add_to_keywords(kw, 'extra_link_args', token)
    468689    if warn_ENABLED:
    469690        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470691        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471692    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"
     693        if os.environ.get("CC", "").find("clang")>=0:
     694            #clang emits too many warnings with cython code,
     695            #so we can't enable Werror
     696            eifd = ["-Werror",
     697                    "-Wno-unneeded-internal-declaration",
     698                    "-Wno-unknown-attributes",
     699                    "-Wno-unused-function",
     700                    "-Wno-self-assign",
     701                    "-Wno-sometimes-uninitialized"]
     702        elif get_gcc_version()>=[4, 4]:
     703            eifd = ["-Werror",
     704                    #CentOS 6.x gives us some invalid warnings in nvenc, ignore those:
     705                    #"-Wno-error=uninitialized",
     706                    ]
     707            from xpra.os_util import is_Ubuntu, is_Debian, is_Raspbian
     708            if is_Debian() or is_Ubuntu() or is_Raspbian():
     709                #needed on Debian and Ubuntu to avoid this error:
     710                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     711                eifd.append("-Wno-error=strict-prototypes")
     712            if sys.platform.startswith("netbsd"):
     713                #see: http://trac.cython.org/ticket/395
     714                eifd += ["-fno-strict-aliasing"]
     715            elif sys.platform.startswith("freebsd"):
     716                eifd += ["-Wno-error=unused-function"]
    475717        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     718            #older versions of OSX ship an old gcc,
     719            #not much we can do with this:
     720            eifd = []
     721        for eif in eifd:
     722            add_to_keywords(kw, 'extra_compile_args', eif)
    478723    if PIC_ENABLED:
    479724        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481726        add_to_keywords(kw, 'extra_compile_args', '-g')
    482727        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     728        if get_gcc_version()>=[4, 8]:
    485729            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486730            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     731    if rpath:
     732        insert_into_keywords(kw, "library_dirs", rpath)
     733        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
    487734    #add_to_keywords(kw, 'include_dirs', '.')
    488735    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     736        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490737    return kw
     738pkgconfig = exec_pkgconfig
    491739
    492740
    493741#*******************************************************************************
    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()
     742
     743
     744def get_base_conf_dir(install_dir, stripbuildroot=True):
     745    #in some cases we want to strip the buildroot (to generate paths in the config file)
     746    #but in other cases we want the buildroot path (when writing out the config files)
     747    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     748    #this is a bit hackish, but I can't think of a better way of detecting it
     749    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     750    dirs = (install_dir or sys.prefix).split(os.path.sep)
     751    if install_dir and stripbuildroot:
     752        pkgdir = os.environ.get("pkgdir")
     753        if "debian" in dirs and "tmp" in dirs:
     754            #ugly fix for stripping the debian tmp dir:
     755            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     756            while "tmp" in dirs:
     757                dirs = dirs[dirs.index("tmp")+1:]
     758        elif "BUILDROOT" in dirs:
     759            #strip rpm style build root:
     760            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     761            dirs = dirs[dirs.index("BUILDROOT")+2:]
     762        elif pkgdir and install_dir.startswith(pkgdir):
     763            #arch build dir:
     764            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     765        elif "usr" in dirs:
     766            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     767            #assume "/usr" or "/usr/local" is the build root
     768            while "usr" in dirs[1:]:
     769                dirs = dirs[dirs[1:].index("usr")+1:]
     770        elif "image" in dirs:
     771            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     772            while "image" in dirs:
     773                dirs = dirs[dirs.index("image")+2:]
     774    #now deal with the fact that "/etc" is used for the "/usr" prefix
     775    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     776    if dirs and dirs[-1]=="usr":
     777        dirs = dirs[:-1]
     778    #is this an absolute path?
     779    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     780        #ie: ["/", "usr"] or ["/", "usr", "local"]
     781        dirs.insert(0, os.path.sep)
     782    return dirs
     783
     784def get_conf_dir(install_dir, stripbuildroot=True):
     785    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     786    dirs.append("etc")
     787    dirs.append("xpra")
     788    return os.path.join(*dirs)
     789
     790def detect_xorg_setup(install_dir=None):
     791    from xpra.scripts import config
     792    config.debug = config.warn
     793    conf_dir = get_conf_dir(install_dir)
     794    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     795
     796def build_xpra_conf(install_dir):
     797    #generates an actual config file from the template
     798    xvfb_command = detect_xorg_setup(install_dir)
     799    from xpra.platform.features import DEFAULT_ENV
     800    def bstr(b):
     801        if b is None:
     802            return "auto"
     803        return ["no", "yes"][int(b)]
     804    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     805    conf_dir = get_conf_dir(install_dir)
     806    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     807    from xpra.platform.paths import get_socket_dirs
     808    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     809    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     810    socket_dirs = get_socket_dirs()
     811    if WIN32:
     812        bind = "Main"
    533813    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()
     814        if os.getuid()>0:
     815            #remove any paths containing the uid,
     816            #osx uses /var/tmp/$UID-Xpra,
     817            #but this should not be included in the default config for all users!
     818            #(the buildbot's uid!)
     819            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     820        bind = "auto"
     821    #FIXME: we should probably get these values from the default config instead
     822    pdf, postscript = "", ""
     823    if os.name=="posix" and printing_ENABLED:
     824        try:
     825            if "/usr/sbin" not in sys.path:
     826                sys.path.append("/usr/sbin")
     827            from xpra.platform.pycups_printing import get_printer_definition
     828            print("probing cups printer definitions")
     829            pdf = get_printer_definition("pdf")
     830            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     831            print("pdf=%s, postscript=%s" % (pdf, postscript))
     832        except Exception as e:
     833            print("could not probe for pdf/postscript printers: %s" % e)
     834    def pretty_cmd(cmd):
     835        return " ".join(cmd)
     836    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     837    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     838    from xpra.os_util import getUbuntuVersion
     839    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     840    #no python-avahi on RH / CentOS, need dbus module on *nix:
     841    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     842    SUBS = {
     843            'xvfb_command'          : pretty_cmd(xvfb_command),
     844            'ssh_command'           : DEFAULT_SSH_COMMAND,
     845            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     846            'remote_logging'        : "both",
     847            'start_env'             : start_env,
     848            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     849            'pulseaudio_command'    : pretty_cmd(DEFAULT_PULSEAUDIO_COMMAND),
     850            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     851            'conf_dir'              : conf_dir,
     852            'bind'                  : bind,
     853            'ssl_cert'              : ssl_cert or "",
     854            'ssl_key'               : ssl_key or "",
     855            'systemd_run'           : get_default_systemd_run(),
     856            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     857            'log_dir'               : "auto",
     858            'mdns'                  : bstr(mdns),
     859            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     860            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     861            'pdf_printer'           : pdf,
     862            'postscript_printer'    : postscript,
     863            'webcam'                : ["no", "auto"][webcam],
     864            'mousewheel'            : "on",
     865            'printing'              : printing_ENABLED,
     866            'dbus_control'          : bstr(dbus_ENABLED),
     867            'mmap'                  : bstr(True),
     868            }
     869    def convert_templates(subdirs=[]):
     870        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     871        #get conf dir for install, without stripping the build root
     872        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     873        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     874        if not os.path.exists(target_dir):
     875            try:
     876                os.makedirs(target_dir)
     877            except Exception as e:
     878                print("cannot create target dir '%s': %s" % (target_dir, e))
     879        for f in sorted(os.listdir(dirname)):
     880            if f.endswith("osx.conf.in") and not sys.platform.startswith("darwin"):
     881                continue
     882            filename = os.path.join(dirname, f)
     883            if os.path.isdir(filename):
     884                convert_templates(subdirs+[f])
     885                continue
     886            if not f.endswith(".in"):
     887                continue
     888            with open(filename, "r") as f_in:
     889                template  = f_in.read()
     890            target_file = os.path.join(target_dir, f[:-len(".in")])
     891            print("generating %s from %s" % (target_file, f))
     892            with open(target_file, "w") as f_out:
     893                config_data = template % SUBS
     894                f_out.write(config_data)
     895    convert_templates()
    576896
    577897
     
    586906    #ensure we remove the files we generate:
    587907    CLEAN_FILES = [
     908                   "xpra/build_info.py",
     909                   "xpra/monotonic_time.c",
    588910                   "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",
     911                   "xpra/x11/gtk2/constants.pxi",
     912                   "xpra/x11/gtk2/gdk_bindings.c",
     913                   "xpra/x11/gtk2/gdk_display_source.c",
     914                   "xpra/x11/gtk3/gdk_display_source.c",
    592915                   "xpra/x11/bindings/constants.pxi",
    593916                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597920                   "xpra/x11/bindings/randr_bindings.c",
    598921                   "xpra/x11/bindings/core_bindings.c",
     922                   "xpra/x11/bindings/posix_display_source.c",
    599923                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     924                   "xpra/platform/win32/propsys.cpp",
     925                   "xpra/platform/darwin/gdk_bindings.c",
     926                   "xpra/net/bencode/cython_bencode.c",
     927                   "xpra/net/vsock.c",
     928                   "xpra/buffers/membuf.c",
    601929                   "xpra/codecs/vpx/encoder.c",
    602930                   "xpra/codecs/vpx/decoder.c",
    603                    "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     931                   "xpra/codecs/vpx/constants.pxi",
     932                   "xpra/codecs/nvenc7/encoder.c",
     933                   "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     934                   "xpra/codecs/cuda_common/BGRA_to_U.fatbin",
     935                   "xpra/codecs/cuda_common/BGRA_to_V.fatbin",
     936                   "xpra/codecs/cuda_common/BGRA_to_Y.fatbin",
     937                   "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
    605938                   "xpra/codecs/enc_x264/encoder.c",
    606939                   "xpra/codecs/enc_x265/encoder.c",
    607                    "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     940                   "xpra/codecs/jpeg/encoder.c",
     941                   "xpra/codecs/jpeg/decoder.c",
     942                   "xpra/codecs/enc_ffmpeg/encoder.c",
     943                   "xpra/codecs/v4l2/constants.pxi",
     944                   "xpra/codecs/v4l2/pusher.c",
     945                   "xpra/codecs/libav_common/av_log.c",
    610946                   "xpra/codecs/dec_avcodec2/decoder.c",
     947                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611948                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614949                   "xpra/codecs/xor/cyxor.c",
    615950                   "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")
     951                   "xpra/codecs/nvapi_version.c",
     952                   "xpra/client/gtk3/cairo_workaround.c",
     953                   "xpra/server/cystats.c",
     954                   "xpra/server/window/region.c",
     955                   "xpra/server/window/motion.c",
     956                   "xpra/server/pam.c",
     957                   "etc/xpra/xpra.conf",
     958                   #special case for the generated xpra conf files in build (see #891):
     959                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     960    for x in CLEAN_FILES:
     961        p, ext = os.path.splitext(x)
     962        if ext in (".c", ".cpp", ".pxi"):
     963            #clean the Cython annotated html files:
     964            CLEAN_FILES.append(p+".html")
     965            if WIN32 and ext!=".pxi":
     966                #on win32, the build creates ".pyd" files, clean those too:
     967                CLEAN_FILES.append(p+".pyd")
    623968    if 'clean' in sys.argv:
    624969        CLEAN_FILES.append("xpra/build_info.py")
     
    630975            os.unlink(filename)
    631976
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     977from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    633978
    634979if "clean" not in sys.argv:
    635980    # Add build info to build_info.py file:
    636981    record_build_info()
     982    # ensure it is included in the module list if it didn't exist before
     983    add_modules(BUILD_INFO_FILE)
    637984
    638985if "sdist" in sys.argv:
    639986    record_src_info()
    640987
    641 if "install" in sys.argv:
     988if "install" in sys.argv or "build" in sys.argv:
    642989    #if installing from source tree rather than
    643990    #from a source snapshot, we may not have a "src_info" file
     
    645992    if not has_src_info():
    646993        record_src_info()
     994        # ensure it is now included in the module list
     995        add_modules(SRC_INFO_FILE)
    647996
    648997
     
    6631012    return m
    6641013
     1014
     1015def install_html5(install_dir="www"):
     1016    if minify_ENABLED:
     1017        print("minifying html5 client to %s using %s" % (install_dir, minifier))
     1018    else:
     1019        print("copying html5 client to %s" % (install_dir, ))
     1020    for k,files in glob_recurse("html5").items():
     1021        if (k!=""):
     1022            k = os.sep+k
     1023        for f in files:
     1024            src = os.path.join(os.getcwd(), f)
     1025            parts = f.split(os.path.sep)
     1026            if parts[0]=="html5":
     1027                f = os.path.join(*parts[1:])
     1028            if install_dir==".":
     1029                install_dir = os.getcwd()
     1030            dst = os.path.join(install_dir, f)
     1031            ddir = os.path.split(dst)[0]
     1032            if ddir and not os.path.exists(ddir):
     1033                os.makedirs(ddir, 0o755)
     1034            ftype = os.path.splitext(f)[1].lstrip(".")
     1035            if minify_ENABLED and ftype=="js":
     1036                if minifier=="uglifyjs":
     1037                    minify_cmd = ["uglifyjs",
     1038                                  "--screw-ie8",
     1039                                  src,
     1040                                  "-o", dst,
     1041                                  "--compress",
     1042                                  ]
     1043                else:
     1044                    assert minifier=="yuicompressor"
     1045                    assert yuicompressor
     1046                    jar = yuicompressor.get_jar_filename()
     1047                    java_cmd = os.environ.get("JAVA", "java")
     1048                    minify_cmd = [java_cmd, "-jar", jar,
     1049                                  src,
     1050                                  "--nomunge",
     1051                                  "--line-break", "400",
     1052                                  "--type", ftype,
     1053                                  "-o", dst,
     1054                                  ]
     1055                r = get_status_output(minify_cmd)[0]
     1056                if r!=0:
     1057                    print("Error: failed to minify '%s', command returned error %i" % (f, r))
     1058                    if verbose_ENABLED:
     1059                        print(" command: %s" % (minify_cmd,))
     1060                else:
     1061                    print("minified %s" % (f, ))
     1062            else:
     1063                r = -1
     1064            if r!=0:
     1065                shutil.copyfile(src, dst)
     1066                os.chmod(dst, 0o644)
     1067                if ftype not in ("png", ):
     1068                    if html5_gzip_ENABLED:
     1069                        gzip_dst = "%s.gz" % dst
     1070                        if os.path.exists(gzip_dst):
     1071                            os.unlink(gzip_dst)
     1072                        cmd = ["gzip", "-9", "-k", dst]
     1073                        get_status_output(cmd)
     1074                        if os.path.exists(gzip_dst):
     1075                            os.chmod(gzip_dst, 0o644)
     1076                    if html5_brotli_ENABLED:
     1077                        br_dst = "%s.br" % dst
     1078                        if os.path.exists(br_dst):
     1079                            os.unlink(br_dst)
     1080                        cmd = ["bro", "--input", dst, "--output", br_dst]
     1081                        get_status_output(cmd)
     1082                        if os.path.exists(br_dst):
     1083                            os.chmod(br_dst, 0o644)
     1084                       
     1085
     1086
    6651087#*******************************************************************************
    6661088if 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"]
     1089    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1090    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821091    add_packages("xpra.platform.win32")
    8831092    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"]
     1093
     1094    #this is where the win32 gi installer will put things:
     1095    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1096
     1097    #only add the cx_freeze specific options
     1098    #if we aren't just building the Cython bits with "build_ext":
     1099    if "build_ext" not in sys.argv:
     1100        #with cx_freeze, we don't use py_modules
     1101        del setup_options["py_modules"]
     1102        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1103
     1104        #cx_freeze doesn't use "data_files"...
     1105        del setup_options["data_files"]
     1106        #it wants source files first, then where they are placed...
     1107        #one item at a time (no lists)
     1108        #all in its own structure called "include_files" instead of "data_files"...
     1109        def add_data_files(target_dir, files):
     1110            if verbose_ENABLED:
     1111                print("add_data_files(%s, %s)" % (target_dir, files))
     1112            assert type(target_dir)==str
     1113            assert type(files) in (list, tuple)
     1114            for f in files:
     1115                target_file = os.path.join(target_dir, os.path.basename(f))
     1116                data_files.append((f, target_file))
     1117
     1118        #pass a potentially nested dictionary representing the tree
     1119        #of files and directories we do want to include
     1120        #relative to gnome_include_path
     1121        def add_dir(base, defs):
     1122            if verbose_ENABLED:
     1123                print("add_dir(%s, %s)" % (base, defs))
     1124            if type(defs) in (list, tuple):
     1125                for sub in defs:
     1126                    if type(sub)==dict:
     1127                        add_dir(base, sub)
     1128                    else:
     1129                        assert type(sub)==str
     1130                        filename = os.path.join(gnome_include_path, base, sub)
     1131                        if os.path.exists(filename):
     1132                            add_data_files(base, [filename])
     1133                        else:
     1134                            print("Warning: missing '%s'" % filename)
     1135            else:
     1136                assert type(defs)==dict
     1137                for d, sub in defs.items():
     1138                    assert type(sub) in (dict, list, tuple)
     1139                    #recurse down:
     1140                    add_dir(os.path.join(base, d), sub)
     1141
     1142        #convenience method for adding GI libs and "typelib" and "gir":
     1143        def add_gi(*libs):
     1144            if verbose_ENABLED:
     1145                print("add_gi(%s)" % str(libs))
     1146            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1147            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1148
     1149        def add_DLLs(*dll_names):
     1150            try:
     1151                do_add_DLLs(*dll_names)
     1152            except Exception as e:
     1153                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1154                print(" %s" % e)
     1155                sys.exit(1)
     1156
     1157        def do_add_DLLs(*dll_names):
     1158            dll_names = list(dll_names)
     1159            dll_files = []
     1160            import re
     1161            version_re = re.compile("\-[0-9\.\-]+$")
     1162            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1163            if os.path.exists(gnome_include_path):
     1164                dirs.insert(0, gnome_include_path)
     1165            if verbose_ENABLED:
     1166                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1167            for d in dirs:
     1168                if not os.path.exists(d):
     1169                    continue
     1170                for x in os.listdir(d):
     1171                    dll_path = os.path.join(d, x)
     1172                    x = x.lower()
     1173                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1174                        continue
     1175                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1176                    if verbose_ENABLED:
     1177                        print("checking %s: %s" % (x, nameversion))
     1178                    m = version_re.search(nameversion)          #look for version part of filename
     1179                    if m:
     1180                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1181                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1182                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1183                    else:
     1184                        dll_version = ""                        #no version
     1185                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1186                    if dll_name in dll_names:
     1187                        #this DLL is on our list
     1188                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1189                        dll_files.append(dll_path)
     1190                        dll_names.remove(dll_name)
     1191            if len(dll_names)>0:
     1192                print("some DLLs could not be found:")
     1193                for x in dll_names:
     1194                    print(" - lib%s*.dll" % x)
     1195            add_data_files("", dll_files)
     1196
     1197        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1198        #(ie: "libatk-1.0-0.dll" -> "atk")
     1199        if sound_ENABLED or gtk3_ENABLED:
     1200            add_DLLs('gio', 'girepository', 'glib',
     1201                     'gnutls', 'gobject', 'gthread',
     1202                     'orc', 'stdc++',
     1203                     'winpthread',
     1204                     )
     1205        if gtk3_ENABLED:
     1206            add_DLLs('atk',
     1207                     'dbus', 'dbus-glib',
     1208                     'gdk', 'gdk_pixbuf', 'gtk',
     1209                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1210                     'harfbuzz', 'harfbuzz-gobject',
     1211                     'jasper', 'epoxy',
     1212                     'intl',
     1213                     'p11-kit',
     1214                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1215            #these are missing in newer aio installers (sigh):
     1216            do_add_DLLs('javascriptcoregtk')
     1217            if opengl_ENABLED:
     1218                do_add_DLLs('gdkglext', 'gtkglext')
     1219
     1220        if gtk3_ENABLED:
     1221            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1222            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1223                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1224            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1225                              "icons", "p11-kit", "xml",
     1226                              {"locale" : ["en"]},
     1227                              {"themes" : ["Default"]}
     1228                             ])
     1229        if gtk3_ENABLED or sound_ENABLED:
     1230            add_dir('lib', ["gio"])
     1231            packages.append("gi")
     1232            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1233                   "GObject-2.0")
     1234        if gtk3_ENABLED:
     1235            add_gi("Atk-1.0",
     1236                   "fontconfig-2.0", "freetype2-2.0",
     1237                   "GDesktopEnums-3.0", "Soup-2.4",
     1238                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0"
     1239                   "HarfBuzz-0.0",
     1240                   "Libproxy-1.0", "libxml2-2.0",
     1241                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1242                   "Rsvg-2.0",
     1243                   "win32-1.0")
     1244            if opengl_ENABLED:
     1245                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1246            add_DLLs('visual', 'curl', 'soup', 'sqlite3', 'openjpeg')
     1247
     1248        if gtk2_ENABLED:
     1249            add_dir('lib',      {
     1250                "gdk-pixbuf-2.0":    {
     1251                    "2.10.0"    :   {
     1252                        "loaders"   :
     1253                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp")]
     1254                        },
     1255                    },
     1256                })
     1257
     1258        if sound_ENABLED:
     1259            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1260            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1261                   "GstTag-1.0")
     1262            add_DLLs('gstreamer', 'orc-test')
     1263            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1264                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1265                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1266                      "basecamerabinsrc", "mpegts", "photography",
     1267                      ):
     1268                add_DLLs('gst%s' % p)
     1269            #DLLs needed by the plugins:
     1270            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1271            #add the gstreamer plugins we need:
     1272            GST_PLUGINS = ("app",
     1273                           #muxers:
     1274                           "gdp", "matroska", "ogg", "isomp4",
     1275                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1276                           "coreelements", "directsoundsink", "directsoundsrc",
     1277                           #codecs:
     1278                           "opus", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1279                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1280                           #untested: a52dec, voaacenc
     1281                           )
     1282            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1283            #END OF SOUND
     1284
     1285        if server_ENABLED:
     1286            #used by proxy server:
     1287            external_includes += ["multiprocessing"]
     1288
     1289        external_includes += ["encodings"]
     1290        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1291        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1292        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL", "libsqlite3-0.dll"]
     1293        cx_freeze_options = {
     1294                            "compressed"        : True,
     1295                            "includes"          : external_includes,
     1296                            "packages"          : packages,
     1297                            "include_files"     : data_files,
     1298                            "excludes"          : excludes,
     1299                            "include_msvcr"     : True,
     1300                            "bin_excludes"      : bin_excludes,
     1301                            "create_shared_zip" : zip_ENABLED,
     1302                            }
     1303        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1304        executables = []
     1305        setup_options["executables"] = executables
     1306
     1307        def add_exe(script, icon, base_name, base="Console"):
     1308            executables.append(Executable(
     1309                        script                  = script,
     1310                        initScript              = None,
     1311                        #targetDir               = "dist",
     1312                        icon                    = "win32/%s" % icon,
     1313                        targetName              = "%s.exe" % base_name,
     1314                        compress                = True,
     1315                        copyDependentFiles      = True,
     1316                        appendScriptToExe       = False,
     1317                        appendScriptToLibrary   = True,
     1318                        base                    = base))
     1319
     1320        def add_console_exe(script, icon, base_name):
     1321            add_exe(script, icon, base_name)
     1322        def add_gui_exe(script, icon, base_name):
     1323            add_exe(script, icon, base_name, base="Win32GUI")
     1324
     1325        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1326        if client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1327            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1328            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1329            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1330            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1331            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1332        if gtk2_ENABLED:
     1333            #these need porting..
     1334            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1335        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1336            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1337        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1338        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1339        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1340        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1341        if gtk2_ENABLED or gtk3_ENABLED:
     1342            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1343            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1344            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1345        if client_ENABLED or server_ENABLED:
     1346            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1347            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1348        if client_ENABLED:
     1349            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1350            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1351            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1352        if client_ENABLED:
     1353            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1354            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1355        if sound_ENABLED:
     1356            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1357            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1358            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1359            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1360            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1361        if opengl_ENABLED:
     1362            add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1363        if webcam_ENABLED:
     1364            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1365            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1366        if printing_ENABLED:
     1367            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1368            if os.path.exists("C:\\Program Files\\Ghostgum\\gsview"):
     1369                GSVIEW = "C:\\Program Files\\Ghostgum\\gsview"
     1370            else:
     1371                GSVIEW = "C:\\Program Files (x86)\\Ghostgum\\gsview"
     1372            if os.path.exists("C:\\Program Files\\gs"):
     1373                GHOSTSCRIPT_PARENT_DIR = "C:\\Program Files\\gs"
     1374            else:
     1375                GHOSTSCRIPT_PARENT_DIR = "C:\\Program Files (x86)\\gs"
     1376            GHOSTSCRIPT = None
     1377            for x in reversed(sorted(os.listdir(GHOSTSCRIPT_PARENT_DIR))):
     1378                f = os.path.join(GHOSTSCRIPT_PARENT_DIR, x)
     1379                if os.path.isdir(f):
     1380                    GHOSTSCRIPT = os.path.join(f, "bin")
     1381                    print("found ghostscript: %s" % GHOSTSCRIPT)
     1382                    break
     1383            assert GHOSTSCRIPT is not None, "cannot find ghostscript installation directory in %s" % GHOSTSCRIPT_PARENT_DIR
     1384            add_data_files('gsview', glob.glob(GSVIEW+'\\*.*'))
     1385            add_data_files('gsview', glob.glob(GHOSTSCRIPT+'\\*.*'))
     1386        if nvenc7_ENABLED:
     1387            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1388        if nvfbc_ENABLED:
     1389            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1390        if nvfbc_ENABLED or nvenc7_ENABLED:
     1391            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1392
     1393        #FIXME: how do we figure out what target directory to use?
     1394        print("calling build_xpra_conf in-place")
     1395        #building etc files in-place:
     1396        build_xpra_conf(".")
     1397        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1398        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1399        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1400        #build minified html5 client in temporary build dir:
     1401        if "clean" not in sys.argv and html5_ENABLED:
     1402            install_html5(os.path.join(install, "www"))
     1403            for k,v in glob_recurse("build/www").items():
     1404                if (k!=""):
     1405                    k = os.sep+k
     1406                add_data_files('www'+k, v)
     1407
     1408    if client_ENABLED or server_ENABLED:
     1409        add_data_files('',      ['COPYING', 'README', 'win32/website.url'])
     1410        add_data_files('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*'))
     1411
     1412    if webcam_ENABLED:
     1413        add_data_files('',      ['win32\\DirectShow.tlb'])
     1414        add_modules("comtypes.gen.stdole", "comtypes.gen.DirectShowLib")
     1415
    9071416    remove_packages(*external_excludes)
     1417    external_includes.append("mmap")
    9081418    remove_packages(#not used on win32:
    909                     "mmap",
    9101419                    #we handle GL separately below:
    9111420                    "OpenGL", "OpenGL_accelerate",
     
    9131422                    "ctypes.macholib")
    9141423
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1424    if webcam_ENABLED:
     1425        external_includes.append("cv2")
     1426
     1427    if opengl_ENABLED:
    9161428        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1429        external_includes.append("numpy")
    9181430    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1431        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211432                        "pydoc")
    9221433
    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:
     1434    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1435    remove_packages("pygst", "gst", "gst.extend")
     1436
     1437    #add subset of PyOpenGL modules (only when installing):
     1438    if opengl_ENABLED and ("install_exe" in sys.argv or "install" in sys.argv):
    9291439        #for this hack to work, you must add "." to the sys.path
    9301440        #so python can load OpenGL from the install directory
    9311441        #(further complicated by the fact that "." is the "frozen" path...)
     1442        #but we re-add those two directories to the library.zip as part of the build script
    9321443        import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
     1444        print("*** copying PyOpenGL modules to %s ***" % install)
    9351445        for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
    9361446            module_dir = os.path.dirname(module.__file__ )
    9371447            try:
    9381448                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1449                    module_dir, os.path.join(install, module_name),
     1450                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411451                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1452            except Exception as e:
     1453                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441454                    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 
     1455
     1456    add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1457
     1458    #END OF win32
    9831459#*******************************************************************************
    9841460else:
    9851461    #OSX and *nix:
    9861462    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"]))
     1463    add_data_files("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1"])
     1464    add_data_files("share/xpra",          ["README", "COPYING"])
     1465    add_data_files("share/xpra/icons",    glob.glob("icons/*"))
     1466    add_data_files("share/applications",  ["xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1467    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1468    add_data_files("share/icons",         ["xdg/xpra.png"])
     1469    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1470
     1471    #here, we override build and install so we can
     1472    #generate our /etc/xpra/xpra.conf
     1473    class build_override(build):
     1474        def run(self):
     1475            build.run(self)
     1476            self.run_command("build_conf")
     1477
     1478    class build_conf(build):
     1479        def run(self):
     1480            try:
     1481                build_base = self.distribution.command_obj['build'].build_base
     1482            except:
     1483                build_base = self.build_base
     1484            build_xpra_conf(build_base)
     1485
     1486    class install_data_override(install_data):
     1487        def run(self):
     1488            print("install_data_override: install_dir=%s" % self.install_dir)
     1489            if html5_ENABLED:
     1490                install_html5(os.path.join(self.install_dir, "share/xpra/www"))
     1491            install_data.run(self)
     1492
     1493            etc_prefix = self.install_dir
     1494            if etc_prefix.endswith("/usr"):
     1495                etc_prefix = etc_prefix[:-3]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1496            build_xpra_conf(etc_prefix)
     1497
     1498            if printing_ENABLED and os.name=="posix":
     1499                #install "/usr/lib/cups/backend" with 0700 permissions:
     1500                xpraforwarder_src = os.path.join("cups", "xpraforwarder")
     1501                cups_backend_dir = os.path.join(self.install_dir, "lib", "cups", "backend")
     1502                self.mkpath(cups_backend_dir)
     1503                xpraforwarder_dst = os.path.join(cups_backend_dir, "xpraforwarder")
     1504                shutil.copyfile(xpraforwarder_src, xpraforwarder_dst)
     1505                os.chmod(xpraforwarder_dst, 0o700)
     1506
     1507            if x11_ENABLED:
     1508                #install xpra_Xdummy if we need it:
     1509                xvfb_command = detect_xorg_setup()
     1510                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])):
     1511                    bin_dir = os.path.join(self.install_dir, "bin")
     1512                    self.mkpath(bin_dir)
     1513                    dummy_script = os.path.join(bin_dir, "xpra_Xdummy")
     1514                    shutil.copyfile("scripts/xpra_Xdummy", dummy_script)
     1515                    os.chmod(dummy_script, 0o755)
     1516                #install xorg.conf, cuda.conf and nvenc.keys:
     1517                etc_xpra = os.path.join(etc_prefix, "etc", "xpra")
     1518                self.mkpath(etc_xpra)
     1519                etc_files = ["xorg.conf"]
     1520                if nvenc7_ENABLED:
     1521                    etc_files += ["cuda.conf", "nvenc.keys"]
     1522                for x in etc_files:
     1523                    shutil.copyfile("etc/xpra/%s" % x, os.path.join(etc_xpra, x))
     1524
     1525            if pam_ENABLED:
     1526                etc_pam_d = os.path.join(etc_prefix, "etc", "pam.d")
     1527                self.mkpath(etc_pam_d)
     1528                shutil.copyfile("etc/pam.d/xpra", os.path.join(etc_pam_d, "xpra"))
     1529
     1530    # add build_conf to build step
     1531    cmdclass.update({
     1532             'build'        : build_override,
     1533             'build_conf'   : build_conf,
     1534             'install_data' : install_data_override,
     1535             })
    9971536
    9981537    if OSX:
     1538        #pyobjc needs email.parser
     1539        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
    9991540        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001541        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011542        add_packages("xpra.platform.darwin")
     1543        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1544        #to support GStreamer 1.x we need this:
     1545        modules.append("importlib")
     1546        modules.append("xpra.scripts.gtk_info")
     1547        modules.append("xpra.scripts.show_webcam")
    10021548    else:
    10031549        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041550        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")
     1551        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1552        #not supported by all distros, but doesn't hurt to install them anyway:
     1553        for x in ("tmpfiles.d", "sysusers.d"):
     1554            add_data_files("/usr/lib/%s" % x, ["%s/xpra.conf" % x])
    10081555
    10091556    #gentoo does weird things, calls --no-compile with build *and* install
     
    10141561        def pkgconfig(*pkgs_options, **ekw):
    10151562            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))
    10331563
    10341564    if OSX and "py2app" in sys.argv:
     
    10451575        remove_packages(*external_excludes)
    10461576
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1577        try:
     1578            from xpra.src_info import REVISION
     1579        except:
     1580            REVISION = "unknown"
     1581        Plist = {
     1582            "CFBundleDocumentTypes" : {
     1583                "CFBundleTypeExtensions"    : ["Xpra"],
     1584                "CFBundleTypeName"          : "Xpra Session Config File",
     1585                "CFBundleName"              : "Xpra",
     1586                "CFBundleTypeRole"          : "Viewer",
     1587                },
     1588            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2017 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1589            "CFBundleIdentifier"            : "org.xpra.xpra",
     1590            }
    10531591        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541592        #so the make-app.sh script still has to hack around this problem.
     
    10651603            }
    10661604        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1605        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1606
     1607    if OSX:
     1608        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1609        #so instead we have to query each package seperately and merge the results:
     1610        def osx_pkgconfig(*pkgs_options, **ekw):
     1611            kw = dict(ekw)
     1612            for pkg in pkgs_options:
     1613                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1614                if pkg.lower().startswith("x"):
     1615                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1616                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1617                kw = exec_pkgconfig(pkg, **kw)
     1618                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1619            return kw
     1620
     1621        pkgconfig = osx_pkgconfig
     1622    else:
     1623        if service_ENABLED:
     1624            #Linux init service:
     1625            if os.path.exists("/bin/systemctl"):
     1626                add_data_files("/usr/lib/systemd/system/", ["service/xpra.service"])
     1627            else:
     1628                add_data_files("/etc/init.d/", ["service/xpra"])
     1629            if os.path.exists("/etc/sysconfig"):
     1630                add_data_files("/etc/sysconfig/", ["etc/sysconfig/xpra"])
     1631            elif os.path.exists("/etc/default"):
     1632                add_data_files("/etc/default/", ["etc/sysconfig/xpra"])
    10681633
    10691634
    10701635if 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 
     1636    if WIN32 or OSX:
     1637        external_includes.append("websockify")
     1638        external_includes.append("numpy")
     1639        external_includes.append("ssl")
     1640        external_includes.append("_ssl")
     1641        if not PYTHON3:
     1642            external_includes.append("mimetypes")
     1643            external_includes.append("mimetools")
     1644            external_includes.append("BaseHTTPServer")
     1645
     1646
     1647if annotate_ENABLED:
     1648    from Cython.Compiler import Options
     1649    Options.annotate = True
    10761650
    10771651
    10781652#*******************************************************************************
    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")
     1653buffers_c = "xpra/buffers/buffers.c"
     1654memalign_c = "xpra/buffers/memalign.c"
     1655xxhash_c = "xpra/buffers/xxhash.c"
     1656membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1657
     1658add_packages("xpra.buffers")
     1659buffers_pkgconfig = pkgconfig(optimize=3)
     1660cython_add(Extension("xpra.buffers.membuf",
     1661            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1662
     1663
     1664toggle_packages(dbus_ENABLED, "xpra.dbus")
     1665toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1666toggle_packages(server_ENABLED or proxy_ENABLED or shadow_ENABLED, "xpra.server", "xpra.server.auth")
     1667toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1668toggle_packages(server_ENABLED, "xpra.server.window")
     1669toggle_packages(server_ENABLED and shadow_ENABLED, "xpra.server.shadow")
     1670toggle_packages(server_ENABLED or (client_ENABLED and gtk2_ENABLED), "xpra.clipboard")
     1671toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1672
     1673#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1674if dbus_ENABLED and server_ENABLED:
     1675    add_packages("xpra.server.dbus")
     1676
     1677if OSX:
     1678    quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1679    add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1680                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h",
     1681                    '-ObjC',
     1682                    '-mmacosx-version-min=10.10')
     1683    add_to_keywords(quartz_pkgconfig, 'extra_link_args',
     1684                    '-framework', 'Foundation',
     1685                    '-framework', 'AppKit',
     1686                    )
     1687    cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1688                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1689                language="objc",
     1690                **quartz_pkgconfig
     1691                ))
     1692
     1693monotonic_time_pkgconfig = pkgconfig()
     1694if not OSX and not WIN32:
     1695    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1696cython_add(Extension("xpra.monotonic_time",
     1697            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1698            **monotonic_time_pkgconfig
     1699            ))
     1700
     1701
     1702toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841703if x11_ENABLED:
    10851704    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1705    make_constants("xpra", "x11", "gtk2", "constants")
    10871706
    10881707    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981717                **pkgconfig("x11")
    10991718                ))
     1719    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1720                ["xpra/x11/bindings/posix_display_source.pyx"],
     1721                **pkgconfig("x11")
     1722                ))
     1723
    11001724    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011725                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041728    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051729                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1730                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071731                ))
    11081732
    11091733    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101734                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1735                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121736                ))
    11131737    cython_add(Extension("xpra.x11.bindings.ximage",
    11141738                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1739                **pkgconfig("x11", "xcomposite", "xdamage", "xext")
    11161740                ))
    11171741
    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)
     1742toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1743if gtk_x11_ENABLED:
     1744    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1745    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1746    if PYTHON3:
     1747        #GTK3 display source:
     1748        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1749                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1750                    **pkgconfig("gtk+-3.0")
     1751                    ))
     1752    else:
     1753        #below uses gtk/gdk:
     1754        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1755                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1756                    **pkgconfig(*PYGTK_PACKAGES)
     1757                    ))
     1758        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1759        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1760                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1761                    **pkgconfig(*GDK_BINDINGS_PACKAGES)
     1762                    ))
     1763
     1764if client_ENABLED and gtk3_ENABLED:
     1765    #cairo workaround:
     1766    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1767                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1768                **pkgconfig("pycairo")
    11221769                ))
    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)
    1127                 ))
    1128 
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1770
     1771if client_ENABLED or server_ENABLED:
     1772    add_packages("xpra.codecs.argb")
     1773    argb_pkgconfig = pkgconfig(optimize=3)
    11321774    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1775                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1776
     1777
     1778#build tests, but don't install them:
     1779toggle_packages(tests_ENABLED, "unit")
    11341780
    11351781
    11361782if bundle_tests_ENABLED:
    11371783    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1784    for k,v in glob_recurse("unit").items():
    11391785        if (k!=""):
    11401786            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:
     1787        add_data_files("unit"+k, v)
     1788
     1789#python-cryptography needs workarounds for bundling:
     1790if crypto_ENABLED and (OSX or WIN32):
     1791    external_includes.append("_ssl")
     1792    external_includes.append("cffi")
     1793    external_includes.append("_cffi_backend")
     1794    external_includes.append("cryptography")
     1795    external_includes.append("pkg_resources._vendor.packaging")
     1796    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1797    external_includes.append("pkg_resources._vendor.pyparsing")
     1798    add_modules("cryptography.hazmat.bindings._openssl")
     1799    add_modules("cryptography.hazmat.bindings._constant_time")
     1800    add_modules("cryptography.hazmat.bindings._padding")
     1801    add_modules("cryptography.hazmat.backends.openssl")
     1802    add_modules("cryptography.fernet")
     1803    if WIN32:
     1804        external_includes.append("appdirs")
     1805
     1806#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441807if client_ENABLED:
    11451808    add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1809toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471810toggle_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")
     1811toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1812toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501813toggle_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")
     1814toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1815toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1816if client_ENABLED and WIN32 and MINGW_PREFIX:
     1817    propsys_pkgconfig = pkgconfig()
     1818    if debug_ENABLED:
     1819        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1820    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1821    cython_add(Extension("xpra.platform.win32.propsys",
     1822                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1823                language="c++",
     1824                **propsys_pkgconfig))
     1825
     1826if client_ENABLED or server_ENABLED:
     1827    add_modules("xpra.codecs")
     1828toggle_packages(client_ENABLED or server_ENABLED, "xpra.keyboard")
     1829if client_ENABLED or server_ENABLED:
     1830    add_modules("xpra.scripts.config", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1831if server_ENABLED or proxy_ENABLED:
     1832    add_modules("xpra.scripts.server")
     1833if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1834    add_modules("xpra.scripts.gtk_info")
     1835
     1836toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1837#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1838#and try to include both gtk3 and gtk2, and fail hard..
     1839for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gtk_compat"):
     1840    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1841
     1842toggle_modules(sound_ENABLED, "xpra.sound")
     1843toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541844
    11551845toggle_packages(clipboard_ENABLED, "xpra.clipboard")
     
    11601850                ))
    11611851
    1162 if cyxor_ENABLED:
     1852toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1853if client_ENABLED or server_ENABLED:
    11631854    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641855                ["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")
     1856                **pkgconfig(optimize=3)))
     1857
     1858if server_ENABLED:
     1859    O3_pkgconfig = pkgconfig(optimize=3)
     1860    cython_add(Extension("xpra.server.cystats",
     1861                ["xpra/server/cystats.pyx"],
     1862                **O3_pkgconfig))
     1863    cython_add(Extension("xpra.server.window.region",
     1864                ["xpra/server/window/region.pyx"],
     1865                **O3_pkgconfig))
     1866    cython_add(Extension("xpra.server.window.motion",
     1867                ["xpra/server/window/motion.pyx"],
     1868                **O3_pkgconfig))
     1869
     1870
    11781871toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11791872
    1180 toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
    1181 if 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))
     1873toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     1874if nvfbc_ENABLED:
     1875    nvfbc_pkgconfig = pkgconfig()
     1876    nvfbc_inc = os.path.join(nvfbc_path, "inc")
     1877    add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-I%s" % nvfbc_inc)
     1878    add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     1879    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture",
     1880                         ["xpra/codecs/nvfbc/fbc_capture.pyx"],
     1881                         language="c++",
     1882                         **nvfbc_pkgconfig))
     1883
     1884toggle_packages(nvenc7_ENABLED, "xpra.codecs.nvenc7")
     1885toggle_packages(nvenc7_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     1886toggle_packages(nvenc7_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     1887
     1888if nvenc7_ENABLED:
     1889    #find nvcc:
     1890    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     1891    if WIN32:
     1892        nvcc_exe = "nvcc.exe"
     1893        path_options = [
     1894                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\bin",
     1895                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\bin",
     1896                         ] + path_options
     1897    else:
     1898        nvcc_exe = "nvcc"
     1899        for v in ("-7.5", "-8.0", ""):
     1900            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     1901    options = [os.path.join(x, nvcc_exe) for x in path_options]
     1902    def which(cmd):
     1903        try:
     1904            code, out, _ = get_status_output(["which", cmd])
     1905            if code==0:
     1906                return out
     1907        except:
     1908            pass
     1909    #prefer the one we find on the $PATH, if any:
     1910    try:
     1911        v = which(nvcc_exe)
     1912        if v and (v not in options):
     1913            options.insert(0, v)
     1914    except:
     1915        pass
     1916    nvcc_versions = {}
     1917    for filename in options:
     1918        if not os.path.exists(filename):
     1919            continue
     1920        code, out, err = get_status_output([filename, "--version"])
     1921        if code==0:
     1922            vpos = out.rfind(", V")
     1923            if vpos>0:
     1924                version = out[vpos+3:].strip("\n")
     1925                version_str = " version %s" % version
     1926            else:
     1927                version = "0"
     1928                version_str = " unknown version!"
     1929            print("found CUDA compiler: %s%s" % (filename, version_str))
     1930            nvcc_versions[version] = filename
     1931    assert nvcc_versions, "cannot find nvcc compiler!"
     1932    #choose the most recent one:
     1933    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     1934    if len(nvcc_versions)>1:
     1935        print(" using version %s from %s" % (version, nvcc))
     1936    if WIN32:
     1937        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     1938        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     1939    #first compile the cuda kernels
     1940    #(using the same cuda SDK for both nvenc modules for now..)
     1941    #TODO:
     1942    # * compile directly to output directory instead of using data files?
     1943    # * detect which arches we want to build for? (does it really matter much?)
     1944    kernels = ("BGRA_to_NV12", "BGRA_to_YUV444")
     1945    for kernel in kernels:
     1946        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     1947        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     1948        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     1949            continue
     1950        reason = should_rebuild(cuda_src, cuda_bin)
     1951        if not reason:
     1952            continue
     1953        cmd = [nvcc,
     1954               '-fatbin',
     1955               #"-cubin",
     1956               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     1957               #"-gencode=arch=compute_50,code=sm_50",
     1958               #"-gencode=arch=compute_52,code=sm_52",
     1959               #"-gencode=arch=compute_52,code=compute_52",
     1960               "-c", cuda_src,
     1961               "-o", cuda_bin]
     1962        #GCC 6 uses C++11 by default:
     1963        if get_gcc_version()>=[6, 0]:
     1964            cmd.append("-std=c++11")
     1965        CL_VERSION = os.environ.get("CL_VERSION")
     1966        if CL_VERSION:
     1967            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     1968            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     1969            cmd += ["--machine", "32"]
     1970        if WIN32:
     1971            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     1972            #cmd += ["--input-drive-prefix", "/"]
     1973            #cmd += ["--dependency-drive-prefix", "/"]
     1974            cmd += ["-I%s" % os.path.abspath("win32")]
     1975        comp_code_options = [(30, 30), (35, 35)]
     1976        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     1977        if version!="0" and version<"7.5":
     1978            print("CUDA version %s is very unlikely to work")
     1979            print("try upgrading to version 7.5 or later")
     1980        if version>="7.5":
     1981            comp_code_options.append((50, 50))
     1982            comp_code_options.append((52, 52))
     1983            comp_code_options.append((53, 53))
     1984        if version>="8.0":
     1985            comp_code_options.append((60, 60))
     1986            comp_code_options.append((61, 61))
     1987            comp_code_options.append((62, 62))
     1988        for arch, code in comp_code_options:
     1989            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     1990        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     1991        print(" %s" % " ".join("'%s'" % x for x in cmd))
     1992        c, stdout, stderr = get_status_output(cmd)
     1993        if c!=0:
     1994            print("Error: failed to compile CUDA kernel %s" % kernel)
     1995            print(stdout or "")
     1996            print(stderr or "")
     1997            sys.exit(1)
     1998    CUDA_BIN = "share/xpra/cuda"
     1999    if WIN32:
     2000        CUDA_BIN = "CUDA"
     2001    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2002    nvencmodule = "nvenc7"
     2003    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2004    #don't link against libnvidia-encode, we load it dynamically:
     2005    libraries = nvenc_pkgconfig.get("libraries", [])
     2006    if "nvidia-encode" in libraries:
     2007        libraries.remove("nvidia-encode")
     2008    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2009        #with gcc 6.2 on Fedora:
     2010        #xpra/codecs/nvenc7/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2011        #xpra/codecs/nvenc7/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2012        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2013        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2014    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2015                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2016                         **nvenc_pkgconfig))
    11872017
    11882018toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892019if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2020    x264_pkgconfig = pkgconfig("x264")
     2021    if get_gcc_version()>=[6, 0]:
     2022        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912023    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922024                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2025                **x264_pkgconfig))
    11942026
    11952027toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962028if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2029    x265_pkgconfig = pkgconfig("x265")
    11982030    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992031                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
    1201 
    1202 toggle_packages(webp_ENABLED, "xpra.codecs.webp")
    1203 if webp_ENABLED:
    1204     webp_pkgconfig = pkgconfig("webp")
    1205     cython_add(Extension("xpra.codecs.webp.encode",
    1206                 ["xpra/codecs/webp/encode.pyx"],
    1207                 **webp_pkgconfig), min_version=(0, 16))
    1208 
    1209 toggle_packages(dec_avcodec_ENABLED, "xpra.codecs.dec_avcodec")
    1210 if dec_avcodec_ENABLED:
    1211     make_constants("xpra", "codecs", "dec_avcodec", "constants")
    1212     avcodec_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec_static_ENABLED)
    1213     cython_add(Extension("xpra.codecs.dec_avcodec.decoder",
    1214                 ["xpra/codecs/dec_avcodec/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1215                 **avcodec_pkgconfig), min_version=(0, 19))
     2032                **x265_pkgconfig))
     2033
     2034toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2035if pillow_ENABLED:
     2036    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
     2037
     2038toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2039if jpeg_ENABLED:
     2040    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2041    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2042                ["xpra/codecs/jpeg/encoder.pyx"],
     2043                **jpeg_pkgconfig))
     2044    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2045                ["xpra/codecs/jpeg/decoder.pyx"],
     2046                **jpeg_pkgconfig))
     2047
     2048#swscale and avcodec2 use libav_common/av_log:
     2049libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2050toggle_packages(libav_common, "xpra.codecs.libav_common")
     2051if libav_common:
     2052    avutil_pkgconfig = pkgconfig("avutil")
     2053    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2054                ["xpra/codecs/libav_common/av_log.pyx"],
     2055                **avutil_pkgconfig))
     2056
    12162057
    12172058toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182059if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2060    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil")
    12202061    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 
     2062                ["xpra/codecs/dec_avcodec2/decoder.pyx"],
     2063                **avcodec2_pkgconfig))
     2064
     2065
     2066toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2067if csc_libyuv_ENABLED:
     2068    libyuv_pkgconfig = pkgconfig("libyuv")
     2069    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2070                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2071                language="c++",
     2072                **libyuv_pkgconfig))
    12242073
    12252074toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262075if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2076    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292077    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))
     2078                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2079                **swscale_pkgconfig))
     2080
    12392081
    12402082toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412083if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2084    #try both vpx and libvpx as package names:
     2085    kwargs = {
     2086              "LIBVPX14"    : pkg_config_version("1.4", "vpx"),
     2087              }
     2088    make_constants("xpra", "codecs", "vpx", "constants", **kwargs)
     2089    vpx_pkgconfig = pkgconfig("vpx")
    12432090    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))
     2091                ["xpra/codecs/vpx/encoder.pyx"],
     2092                **vpx_pkgconfig))
    12462093    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))
     2094                ["xpra/codecs/vpx/decoder.pyx"],
     2095                **vpx_pkgconfig))
     2096
     2097toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2098if enc_ffmpeg_ENABLED:
     2099    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2100    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2101                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2102                **ffmpeg_pkgconfig))
     2103
     2104toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2105if v4l2_ENABLED:
     2106    v4l2_pkgconfig = pkgconfig()
     2107    #fuly warning: cython makes this difficult,
     2108    #we have to figure out if "device_caps" exists in the headers:
     2109    ENABLE_DEVICE_CAPS = False
     2110    if os.path.exists("/usr/include/linux/videodev2.h"):
     2111        hdata = open("/usr/include/linux/videodev2.h").read()
     2112        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2113    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2114    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2115    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2116                ["xpra/codecs/v4l2/pusher.pyx"],
     2117                **v4l2_pkgconfig))
    12622118
    12632119
    12642120toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2121toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652122if 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")
     2123    bencode_pkgconfig = pkgconfig(optimize=not debug_ENABLED)
    12722124    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732125                ["xpra/net/bencode/cython_bencode.pyx"],
    12742126                **bencode_pkgconfig))
    12752127
     2128if vsock_ENABLED:
     2129    vsock_pkgconfig = pkgconfig()
     2130    cython_add(Extension("xpra.net.vsock",
     2131                ["xpra/net/vsock.pyx"],
     2132                **vsock_pkgconfig))
     2133
     2134if pam_ENABLED:
     2135    pam_pkgconfig = pkgconfig()
     2136    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2137    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2138    cython_add(Extension("xpra.server.pam",
     2139                ["xpra/server/pam.pyx"],
     2140                **pam_pkgconfig))
     2141
    12762142
    12772143if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2144    from Cython.Build import cythonize
     2145    #this causes Cython to fall over itself:
     2146    #gdb_debug=debug_ENABLED
     2147    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792148if cmdclass:
    12802149    setup_options["cmdclass"] = cmdclass
     
    12832152
    12842153
    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 
    12932154def main():
    12942155    if OSX or WIN32 or debug_ENABLED:
     2156        print()
    12952157        print("setup options:")
     2158        if verbose_ENABLED:
     2159            print("setup_options=%s" % (setup_options,))
     2160        try:
     2161            from xpra.util import repr_ellipsized as pv
     2162        except:
     2163            def pv(v):
     2164                return str(v)
    12962165        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2166            print_option("", k, pv(v))
    12982167        print("")
    12992168
Note: See TracChangeset for help on using the changeset viewer.