xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r15382  
    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 = os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep) + [pcp]
     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
     158minify_ENABLED = html5_ENABLED
     159pam_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"))
     160
     161vsock_ENABLED           = sys.platform.startswith("linux") and os.path.exists("/usr/include/linux/vm_sockets.h")
     162bencode_ENABLED         = DEFAULT
     163cython_bencode_ENABLED  = DEFAULT
     164clipboard_ENABLED       = DEFAULT and not PYTHON3
     165Xdummy_ENABLED          = None          #None means auto-detect
     166Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     167if WIN32 or OSX:
     168    Xdummy_ENABLED = False
     169sound_ENABLED           = DEFAULT
     170printing_ENABLED        = DEFAULT
     171crypto_ENABLED          = DEFAULT
     172mdns_ENABLED            = DEFAULT
     173
     174enc_proxy_ENABLED       = DEFAULT
     175enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264", fallback=WIN32)
     176enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     177pillow_ENABLED          = DEFAULT
     178jpeg_ENABLED            = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     179vpx_ENABLED             = DEFAULT and pkg_config_version("1.3", "vpx", fallback=WIN32)
     180enc_ffmpeg_ENABLED      = DEFAULT and pkg_config_version("56", "libavcodec")
     181webcam_ENABLED          = DEFAULT and not OSX
     182v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not sys.platform.startswith("freebsd"))
    69183#ffmpeg 2 onwards:
    70 dec_avcodec2_ENABLED    = WIN32 or pkg_config_ok("--atleast-version=55", "libavcodec")
     184dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("56", "libavcodec", fallback=WIN32)
    71185# 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 
     186# Fedora:
     187# * 19: 54.92.100
     188# * 20: 55.39.101
     189# * 21: 55.52.102
     190# Debian:
     191# * jessie and sid: (last updated 2014-05-26): 55.34.1
     192#   (moved to ffmpeg2 style buffer API sometime in early 2014)
     193# * wheezy: 53.35
     194csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale", fallback=WIN32)
     195nvenc7_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvenc7")
     196cuda_rebuild_ENABLED    = DEFAULT
     197csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv", fallback=WIN32)
     198
     199#Cython / gcc / packaging build options:
     200annotate_ENABLED        = True
    86201warn_ENABLED            = True
    87202strict_ENABLED          = True
    88 PIC_ENABLED             = True
     203PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89204debug_ENABLED           = False
    90205verbose_ENABLED         = False
    91206bundle_tests_ENABLED    = False
     207tests_ENABLED           = False
     208rebuild_ENABLED         = True
    92209
    93210#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",
     211SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     212            "nvenc7", "cuda_rebuild",
     213            "vpx", "pillow", "jpeg",
     214            "v4l2",
     215            "dec_avcodec2", "csc_swscale",
     216            "csc_libyuv",
     217            "bencode", "cython_bencode", "vsock", "mdns",
    104218            "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")
     219            "server", "client", "dbus", "x11", "gtk_x11", "service",
     220            "gtk2", "gtk3",
     221            "html5", "minify",
     222            "pam",
     223            "sound", "opengl", "printing", "webcam",
     224            "rebuild",
     225            "annotate", "warn", "strict",
     226            "shadow", "proxy",
     227            "debug", "PIC",
     228            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     229if WIN32:
     230    SWITCHES.append("zip")
     231    zip_ENABLED = True
    109232HELP = "-h" in sys.argv or "--help" in sys.argv
    110233if HELP:
     
    120243            default_str = "auto-detect"
    121244        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     245    print("  --pkg-config-path=PATH")
     246    print("  --rpath=PATH")
    122247    sys.exit(0)
    123248
     249install = "dist"
     250rpath = None
     251ssl_cert = None
     252ssl_key = None
    124253filtered_args = []
    125254for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     255    matched = False
     256    for x in ("rpath", "ssl-cert", "ssl-key", "install"):
     257        varg = "--%s=" % x
     258        if arg.startswith(varg):
     259            value = arg[len(varg):]
     260            globals()[x.replace("-", "_")] = value
     261            #remove these arguments from sys.argv,
     262            #except for --install=PATH
     263            matched = x!="install"
     264            break
     265    if matched:
    129266        continue
    130     matched = False
    131267    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     268        with_str = "--with-%s" % x
     269        without_str = "--without-%s" % x
     270        if arg.startswith(with_str+"="):
     271            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     272            matched = True
     273            break
     274        elif arg==with_str:
    133275            vars()["%s_ENABLED" % x] = True
    134276            matched = True
    135277            break
    136         elif arg=="--without-%s" % x:
     278        elif arg==without_str:
    137279            vars()["%s_ENABLED" % x] = False
    138280            matched = True
     
    145287    for x in SWITCHES:
    146288        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)
     289    print("build switches:")
     290    for k in SWITCHES:
     291        v = switches_info[k]
     292        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150293
    151294    #sanity check the flags:
     
    153296        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154297        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
    158298    if shadow_ENABLED and not server_ENABLED:
    159299        print("Warning: shadow requires server to be enabled!")
    160300        shadow_ENABLED = False
    161     if cymaths_ENABLED and not server_ENABLED:
    162         print("Warning: cymaths requires server to be enabled!")
    163         cymaths_ENABLED = False
    164301    if x11_ENABLED and WIN32:
    165302        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:
     303    if gtk_x11_ENABLED and not x11_ENABLED:
     304        print("Error: you must enable x11 to support gtk_x11!")
     305        exit(1)
     306    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167307        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)
    174 
     308    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     309        print("Warning: you probably want to build at least the client or server!")
     310    if DEFAULT and not pillow_ENABLED:
     311        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     312    if minify_ENABLED:
     313        r = get_status_output(["uglifyjs", "--version"])[0]
     314        if r==0:
     315            minifier = "uglifyjs"
     316        else:
     317            print("Warning: uglifyjs failed and return %i" % r)
     318            try:
     319                import yuicompressor
     320                assert yuicompressor
     321                minifier = "yuicompressor"
     322            except ImportError as e:
     323                print("Warning: yuicompressor module not found, cannot minify")
     324                minify_ENABLED = False
     325    if not enc_x264_ENABLED and not vpx_ENABLED:
     326        print("Warning: no x264 and no vpx support!")
     327        print(" you should enable at least one of these two video encodings")
    175328
    176329#*******************************************************************************
    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",
     330# default sets:
     331
     332external_includes = ["hashlib",
    204333                     "ctypes", "platform"]
     334
     335
     336if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     337    external_includes += ["gi"]
     338elif gtk2_ENABLED or x11_ENABLED:
     339    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205340
    206341external_excludes = [
     
    212347                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213348                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
     349                    "curses", "pdb",
     350                    "urllib2", "tty",
     351                    "cookielib", "ftplib", "httplib", "fileinput",
    218352                    "distutils", "setuptools", "doctest"
    219353                    ]
    220 
     354if not html5_ENABLED and not crypto_ENABLED:
     355    external_excludes += ["ssl", "_ssl"]
     356if not html5_ENABLED:
     357    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     358
     359if not client_ENABLED and not server_ENABLED:
     360    excludes += ["PIL"]
     361if not dbus_ENABLED:
     362    excludes += ["dbus"]
    221363
    222364
    223365#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     366#for distutils / py2app and cx_freeze
    225367#use the following functions, which should get the right
    226368#data in the global variables "packages", "modules" and "excludes"
     
    253395
    254396def add_modules(*mods):
     397    def add(v):
     398        global modules
     399        if v not in modules:
     400            modules.append(v)
     401    do_add_modules(add, *mods)
     402
     403def do_add_modules(op, *mods):
    255404    """ adds the packages and any .py module found in the packages to the "modules" list
    256405    """
    257406    global modules
    258407    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     408        #ugly path stripping:
     409        if x.startswith("./"):
     410            x = x[2:]
     411        if x.endswith(".py"):
     412            x = x[:-3]
     413            x = x.replace("/", ".") #.replace("\\", ".")
    261414        pathname = os.path.sep.join(x.split("."))
     415        #is this a file module?
     416        f = "%s.py" % pathname
     417        if os.path.exists(f) and os.path.isfile(f):
     418            op(x)
    262419        if os.path.exists(pathname) and os.path.isdir(pathname):
    263420            #add all file modules found in this directory
    264421            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     422                #make sure we only include python files,
     423                #and ignore eclipse copies
     424                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266425                    fname = os.path.join(pathname, f)
    267426                    if os.path.isfile(fname):
    268427                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     428                        op(modname)
    270429
    271430def toggle_packages(enabled, *module_names):
     
    275434        remove_packages(*module_names)
    276435
     436def toggle_modules(enabled, *module_names):
     437    if enabled:
     438        def op(v):
     439            global modules
     440            if v not in modules:
     441                modules.append(v)
     442        do_add_modules(op, *module_names)
     443    else:
     444        remove_packages(*module_names)
     445
     446
    277447#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 
     448add_modules("xpra", "xpra.platform", "xpra.net")
     449add_modules("xpra.scripts.main")
     450
     451
     452def add_data_files(target_dir, files):
     453    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     454    assert type(target_dir)==str
     455    assert type(files) in (list, tuple)
     456    data_files.append((target_dir, files))
     457
     458
     459def check_md5sums(md5sums):
     460    print("Verifying md5sums:")
     461    for filename, md5sum in md5sums.items():
     462        if not os.path.exists(filename) or not os.path.isfile(filename):
     463            sys.exit("ERROR: file %s is missing or not a file!" % filename)
     464        sys.stdout.write("* %s: " % str(filename).ljust(52))
     465        with open(filename, mode='rb') as f:
     466            data = f.read()
     467        m = md5()
     468        m.update(data)
     469        digest = m.hexdigest()
     470        assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (filename, md5sum, digest)
     471        sys.stdout.write("OK\n")
     472        sys.stdout.flush()
     473
     474#for pretty printing of options:
     475def print_option(prefix, k, v):
     476    if type(v)==dict:
     477        print("%s* %s:" % (prefix, k))
     478        for kk,vv in v.items():
     479            print_option(" "+prefix, kk, vv)
     480    else:
     481        print("%s* %s=%s" % (prefix, k, v))
    284482
    285483#*******************************************************************************
     
    288486    try:
    289487        from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
     488    except ImportError as e:
    291489        sys.exit("ERROR: Cannot find Cython: %s" % e)
    292490    from distutils.version import LooseVersion
     
    297495                 % (cython_version, ".".join([str(part) for part in min_version])))
    298496
    299 def cython_add(extension, min_version=(0, 14, 0)):
     497def cython_add(extension, min_version=(0, 19)):
    300498    #gentoo does weird things, calls --no-compile with build *and* install
    301499    #then expects to find the cython modules!? ie:
     
    303501    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304502        return
    305     global ext_modules, cmdclass
    306503    cython_version_check(min_version)
    307504    from Cython.Distutils import build_ext
    308505    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     506    global cmdclass
     507    cmdclass['build_ext'] = build_ext
     508
     509def insert_into_keywords(kw, key, *args):
     510    values = kw.setdefault(key, [])
     511    for arg in args:
     512        values.insert(0, arg)
    310513
    311514def add_to_keywords(kw, key, *args):
     
    319522
    320523
     524def checkdirs(*dirs):
     525    for d in dirs:
     526        if not os.path.exists(d) or not os.path.isdir(d):
     527            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     528
    321529PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
    322530
     
    326534    if len(GCC_VERSION)==0:
    327535        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:
     536        r, _, err = get_status_output(cmd)
     537        if r==0:
    332538            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     539            for line in err.splitlines():
    334540                if line.startswith(V_LINE):
    335541                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343549    return GCC_VERSION
    344550
    345 def make_constants_pxi(constants_path, pxi_path):
     551def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346552    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):
     553    with open(constants_path) as f:
     554        for line in f:
     555            data = line.split("#", 1)[0].strip()
     556            # data can be empty ''...
     557            if not data:
     558                continue
     559            # or a pair like 'cFoo "Foo"'...
     560            elif len(data.split()) == 2:
     561                (pyname, cname) = data.split()
     562                constants.append((pyname, cname))
     563            # or just a simple token 'Foo'
     564            else:
     565                constants.append(data)
     566
     567    with open(pxi_path, "w") as out:
     568        if constants:
     569            out.write("cdef extern from *:\n")
     570            ### Apparently you can't use | on enum's?!
     571            # out.write("    enum MagicNumbers:\n")
     572            # for const in constants:
     573            #     if isinstance(const, tuple):
     574            #         out.write('        %s %s\n' % const)
     575            #     else:
     576            #         out.write('        %s\n' % (const,))
     577            for const in constants:
     578                if isinstance(const, tuple):
     579                    out.write('    unsigned int %s %s\n' % const)
     580                else:
     581                    out.write('    unsigned int %s\n' % (const,))
     582
     583            out.write("constants = {\n")
     584            for const in constants:
     585                if isinstance(const, tuple):
     586                    pyname = const[0]
     587                else:
     588                    pyname = const
     589                out.write('    "%s": %s,\n' % (pyname, pyname))
     590            out.write("}\n")
     591            if kwargs:
     592                out.write("\n\n")
     593
     594        if kwargs:
     595            for k, v in kwargs.items():
     596                out.write('DEF %s = %s\n' % (k, v))
     597
     598
     599def should_rebuild(src_file, bin_file):
     600    if not os.path.exists(bin_file):
     601        return "no file"
     602    elif rebuild_ENABLED:
     603        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     604            return "binary file out of date"
     605        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     606            return "newer build file"
     607    return None
     608
     609def make_constants(*paths, **kwargs):
    384610    base = os.path.join(os.getcwd(), *paths)
    385611    constants_file = "%s.txt" % base
    386612    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"
     613    reason = should_rebuild(constants_file, pxi_file)
    394614    if reason:
    395615        if verbose_ENABLED:
    396616            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
     617        make_constants_pxi(constants_file, pxi_file, **kwargs)
     618
    414619
    415620# 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 
     621def exec_pkgconfig(*pkgs_options, **ekw):
    423622    kw = dict(ekw)
     623    if "optimize" in kw:
     624        optimize = kw["optimize"]
     625        del kw["optimize"]
     626        if type(optimize)==bool:
     627            optimize = int(optimize)*3
     628        if not debug_ENABLED:
     629            add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     630    ignored_flags = []
     631    if kw.get("ignored_flags"):
     632        ignored_flags = kw.get("ignored_flags")
     633        del kw["ignored_flags"]
     634
    424635    if len(pkgs_options)>0:
    425636        package_names = []
     
    438649            for option in options:
    439650                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:
     651                r, _, _ = get_status_output(cmd)
     652                if r==0:
    443653                    valid_option = option
    444654                    break
    445655            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     656                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)")))
    447657            package_names.append(valid_option)
    448658        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     659            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450660        flag_map = {'-I': 'include_dirs',
    451661                    '-L': 'library_dirs',
    452662                    '-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))
     663        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     664        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     665        if r!=0:
     666            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     667        env_cflags = os.environ.get("CFLAGS")       #["dpkg-buildflags", "--get", "CFLAGS"]
     668        env_ldflags = os.environ.get("LDFLAGS")     #["dpkg-buildflags", "--get", "LDFLAGS"]
     669        for s in (pkg_config_out, env_cflags, env_ldflags):
     670            if not s:
     671                continue
     672            for token in s.split():
     673                if token[:2] in ignored_flags:
     674                    pass
     675                elif token[:2] in flag_map:
     676                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     677                else: # throw others to extra_link_args
     678                    add_to_keywords(kw, 'extra_link_args', token)
    468679    if warn_ENABLED:
    469680        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470681        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471682    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"
     683        if os.environ.get("CC", "").find("clang")>=0:
     684            #clang emits too many warnings with cython code,
     685            #so we can't enable Werror
     686            eifd = ["-Werror",
     687                    "-Wno-unneeded-internal-declaration",
     688                    "-Wno-unknown-attributes",
     689                    "-Wno-unused-function",
     690                    "-Wno-self-assign",
     691                    "-Wno-sometimes-uninitialized"]
     692        elif get_gcc_version()>=[4, 4]:
     693            eifd = ["-Werror",
     694                    #CentOS 6.x gives us some invalid warnings in nvenc, ignore those:
     695                    #"-Wno-error=uninitialized",
     696                    ]
     697            from xpra.os_util import is_Ubuntu, is_Debian, is_Raspbian
     698            if is_Debian() or is_Ubuntu() or is_Raspbian():
     699                #needed on Debian and Ubuntu to avoid this error:
     700                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     701                eifd.append("-Wno-error=strict-prototypes")
     702            if sys.platform.startswith("netbsd"):
     703                #see: http://trac.cython.org/ticket/395
     704                eifd += ["-fno-strict-aliasing"]
     705            elif sys.platform.startswith("freebsd"):
     706                eifd += ["-Wno-error=unused-function"]
    475707        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     708            #older versions of OSX ship an old gcc,
     709            #not much we can do with this:
     710            eifd = []
     711        for eif in eifd:
     712            add_to_keywords(kw, 'extra_compile_args', eif)
    478713    if PIC_ENABLED:
    479714        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481716        add_to_keywords(kw, 'extra_compile_args', '-g')
    482717        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     718        if get_gcc_version()>=[4, 8]:
    485719            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486720            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     721    if rpath:
     722        #insert_into_keywords(kw, "library_dirs", rpath)
     723        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
    487724    #add_to_keywords(kw, 'include_dirs', '.')
    488725    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     726        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490727    return kw
     728pkgconfig = exec_pkgconfig
    491729
    492730
    493731#*******************************************************************************
    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()
     732
     733
     734def get_base_conf_dir(install_dir, stripbuildroot=True):
     735    #in some cases we want to strip the buildroot (to generate paths in the config file)
     736    #but in other cases we want the buildroot path (when writing out the config files)
     737    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     738    #this is a bit hackish, but I can't think of a better way of detecting it
     739    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     740    dirs = (install_dir or sys.prefix).split(os.path.sep)
     741    if install_dir and stripbuildroot:
     742        pkgdir = os.environ.get("pkgdir")
     743        if "debian" in dirs and "tmp" in dirs:
     744            #ugly fix for stripping the debian tmp dir:
     745            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     746            while "tmp" in dirs:
     747                dirs = dirs[dirs.index("tmp")+1:]
     748        elif "BUILDROOT" in dirs:
     749            #strip rpm style build root:
     750            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     751            dirs = dirs[dirs.index("BUILDROOT")+2:]
     752        elif pkgdir and install_dir.startswith(pkgdir):
     753            #arch build dir:
     754            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     755        elif "usr" in dirs:
     756            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     757            #assume "/usr" or "/usr/local" is the build root
     758            while "usr" in dirs[1:]:
     759                dirs = dirs[dirs[1:].index("usr")+1:]
     760        elif "image" in dirs:
     761            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     762            while "image" in dirs:
     763                dirs = dirs[dirs.index("image")+2:]
     764    #now deal with the fact that "/etc" is used for the "/usr" prefix
     765    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     766    if dirs and dirs[-1]=="usr":
     767        dirs = dirs[:-1]
     768    #is this an absolute path?
     769    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     770        #ie: ["/", "usr"] or ["/", "usr", "local"]
     771        dirs.insert(0, os.path.sep)
     772    return dirs
     773
     774def get_conf_dir(install_dir, stripbuildroot=True):
     775    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     776    dirs.append("etc")
     777    dirs.append("xpra")
     778    return os.path.join(*dirs)
     779
     780def detect_xorg_setup(install_dir=None):
     781    from xpra.scripts import config
     782    config.debug = config.warn
     783    conf_dir = get_conf_dir(install_dir)
     784    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     785
     786def build_xpra_conf(install_dir):
     787    #generates an actual config file from the template
     788    xvfb_command = detect_xorg_setup(install_dir)
     789    from xpra.platform.features import DEFAULT_ENV
     790    def bstr(b):
     791        if b is None:
     792            return "auto"
     793        return ["no", "yes"][int(b)]
     794    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     795    conf_dir = get_conf_dir(install_dir)
     796    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     797    from xpra.platform.paths import get_socket_dirs
     798    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     799    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     800    socket_dirs = get_socket_dirs()
     801    if WIN32:
     802        bind = "Main"
    533803    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()
     804        if os.getuid()>0:
     805            #remove any paths containing the uid,
     806            #osx uses /var/tmp/$UID-Xpra,
     807            #but this should not be included in the default config for all users!
     808            #(the buildbot's uid!)
     809            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     810        bind = "auto"
     811    #FIXME: we should probably get these values from the default config instead
     812    pdf, postscript = "", ""
     813    if os.name=="posix" and printing_ENABLED:
     814        try:
     815            if "/usr/sbin" not in sys.path:
     816                sys.path.append("/usr/sbin")
     817            from xpra.platform.pycups_printing import get_printer_definition
     818            print("probing cups printer definitions")
     819            pdf = get_printer_definition("pdf")
     820            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     821            print("pdf=%s, postscript=%s" % (pdf, postscript))
     822        except Exception as e:
     823            print("could not probe for pdf/postscript printers: %s" % e)
     824    def pretty_cmd(cmd):
     825        return " ".join(cmd)
     826    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     827    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     828    from xpra.os_util import getUbuntuVersion
     829    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     830    #no python-avahi on RH / CentOS, need dbus module on *nix:
     831    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     832    SUBS = {
     833            'xvfb_command'          : pretty_cmd(xvfb_command),
     834            'ssh_command'           : DEFAULT_SSH_COMMAND,
     835            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     836            'remote_logging'        : "both",
     837            'start_env'             : start_env,
     838            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     839            'pulseaudio_command'    : pretty_cmd(DEFAULT_PULSEAUDIO_COMMAND),
     840            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     841            'conf_dir'              : conf_dir,
     842            'bind'                  : bind,
     843            'ssl_cert'              : ssl_cert or "",
     844            'ssl_key'               : ssl_key or "",
     845            'systemd_run'           : get_default_systemd_run(),
     846            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     847            'log_dir'               : "auto",
     848            'mdns'                  : bstr(mdns),
     849            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     850            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     851            'pdf_printer'           : pdf,
     852            'postscript_printer'    : postscript,
     853            'webcam'                : ["no", "auto"][webcam],
     854            'printing'              : printing_ENABLED,
     855            'dbus_control'          : bstr(dbus_ENABLED),
     856            'mmap'                  : bstr(True),
     857            }
     858    def convert_templates(subdirs=[]):
     859        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     860        #get conf dir for install, without stripping the build root
     861        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     862        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     863        if not os.path.exists(target_dir):
     864            try:
     865                os.makedirs(target_dir)
     866            except Exception as e:
     867                print("cannot create target dir '%s': %s" % (target_dir, e))
     868        for f in sorted(os.listdir(dirname)):
     869            if f.endswith("osx.conf.in") and not sys.platform.startswith("darwin"):
     870                continue
     871            filename = os.path.join(dirname, f)
     872            if os.path.isdir(filename):
     873                convert_templates(subdirs+[f])
     874                continue
     875            if not f.endswith(".in"):
     876                continue
     877            with open(filename, "r") as f_in:
     878                template  = f_in.read()
     879            target_file = os.path.join(target_dir, f[:-len(".in")])
     880            print("generating %s from %s" % (target_file, f))
     881            with open(target_file, "w") as f_out:
     882                config_data = template % SUBS
     883                f_out.write(config_data)
     884    convert_templates()
    576885
    577886
     
    586895    #ensure we remove the files we generate:
    587896    CLEAN_FILES = [
     897                   "xpra/build_info.py",
     898                   "xpra/monotonic_time.c",
    588899                   "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",
     900                   "xpra/x11/gtk2/constants.pxi",
     901                   "xpra/x11/gtk2/gdk_bindings.c",
     902                   "xpra/x11/gtk2/gdk_display_source.c",
     903                   "xpra/x11/gtk3/gdk_display_source.c",
    592904                   "xpra/x11/bindings/constants.pxi",
    593905                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597909                   "xpra/x11/bindings/randr_bindings.c",
    598910                   "xpra/x11/bindings/core_bindings.c",
     911                   "xpra/x11/bindings/posix_display_source.c",
    599912                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     913                   "xpra/platform/win32/propsys.cpp",
     914                   "xpra/platform/darwin/gdk_bindings.c",
     915                   "xpra/net/bencode/cython_bencode.c",
     916                   "xpra/net/vsock.c",
     917                   "xpra/buffers/membuf.c",
    601918                   "xpra/codecs/vpx/encoder.c",
    602919                   "xpra/codecs/vpx/decoder.c",
    603                    "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     920                   "xpra/codecs/vpx/constants.pxi",
     921                   "xpra/codecs/nvenc7/encoder.c",
     922                   "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     923                   "xpra/codecs/cuda_common/BGRA_to_U.fatbin",
     924                   "xpra/codecs/cuda_common/BGRA_to_V.fatbin",
     925                   "xpra/codecs/cuda_common/BGRA_to_Y.fatbin",
     926                   "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
    605927                   "xpra/codecs/enc_x264/encoder.c",
    606928                   "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",
     929                   "xpra/codecs/jpeg/encoder.c",
     930                   "xpra/codecs/jpeg/decoder.c",
     931                   "xpra/codecs/enc_ffmpeg/encoder.c",
     932                   "xpra/codecs/v4l2/constants.pxi",
     933                   "xpra/codecs/v4l2/pusher.c",
     934                   "xpra/codecs/libav_common/av_log.c",
    610935                   "xpra/codecs/dec_avcodec2/decoder.c",
     936                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611937                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614938                   "xpra/codecs/xor/cyxor.c",
    615939                   "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")
     940                   "xpra/codecs/nvapi_version.c",
     941                   "xpra/client/gtk3/cairo_workaround.c",
     942                   "xpra/server/cystats.c",
     943                   "xpra/server/window/region.c",
     944                   "xpra/server/window/motion.c",
     945                   "xpra/server/pam.c",
     946                   "etc/xpra/xpra.conf",
     947                   #special case for the generated xpra conf files in build (see #891):
     948                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     949    for x in CLEAN_FILES:
     950        p, ext = os.path.splitext(x)
     951        if ext in (".c", ".cpp", ".pxi"):
     952            #clean the Cython annotated html files:
     953            CLEAN_FILES.append(p+".html")
     954            if WIN32 and ext!=".pxi":
     955                #on win32, the build creates ".pyd" files, clean those too:
     956                CLEAN_FILES.append(p+".pyd")
    623957    if 'clean' in sys.argv:
    624958        CLEAN_FILES.append("xpra/build_info.py")
     
    630964            os.unlink(filename)
    631965
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     966from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    633967
    634968if "clean" not in sys.argv:
    635969    # Add build info to build_info.py file:
    636970    record_build_info()
     971    # ensure it is included in the module list if it didn't exist before
     972    add_modules(BUILD_INFO_FILE)
    637973
    638974if "sdist" in sys.argv:
    639975    record_src_info()
    640976
    641 if "install" in sys.argv:
     977if "install" in sys.argv or "build" in sys.argv:
    642978    #if installing from source tree rather than
    643979    #from a source snapshot, we may not have a "src_info" file
     
    645981    if not has_src_info():
    646982        record_src_info()
     983        # ensure it is now included in the module list
     984        add_modules(SRC_INFO_FILE)
    647985
    648986
     
    6631001    return m
    6641002
     1003
     1004def install_html5(install_dir="www"):
     1005    if minify_ENABLED:
     1006        print("minifying html5 client to %s using %s" % (install_dir, minifier))
     1007    else:
     1008        print("copying html5 client to %s" % (install_dir, ))
     1009    for k,files in glob_recurse("html5").items():
     1010        if (k!=""):
     1011            k = os.sep+k
     1012        for f in files:
     1013            src = os.path.join(os.getcwd(), f)
     1014            parts = f.split(os.path.sep)
     1015            if parts[0]=="html5":
     1016                f = os.path.join(*parts[1:])
     1017            if install_dir==".":
     1018                install_dir = os.getcwd()
     1019            dst = os.path.join(install_dir, f)
     1020            ddir = os.path.split(dst)[0]
     1021            if ddir and not os.path.exists(ddir):
     1022                os.makedirs(ddir, 0o755)
     1023            ftype = os.path.splitext(f)[1].lstrip(".")
     1024            if minify_ENABLED and ftype=="js":
     1025                if minifier=="uglifyjs":
     1026                    minify_cmd = ["uglifyjs",
     1027                                  "--screw-ie8",
     1028                                  src,
     1029                                  "-o", dst,
     1030                                  "--compress",
     1031                                  ]
     1032                else:
     1033                    assert minifier=="yuicompressor"
     1034                    assert yuicompressor
     1035                    jar = yuicompressor.get_jar_filename()
     1036                    java_cmd = os.environ.get("JAVA", "java")
     1037                    minify_cmd = [java_cmd, "-jar", jar,
     1038                                  src,
     1039                                  "--nomunge",
     1040                                  "--line-break", "400",
     1041                                  "--type", ftype,
     1042                                  "-o", dst,
     1043                                  ]
     1044                r = get_status_output(minify_cmd)[0]
     1045                if r!=0:
     1046                    print("Error: failed to minify '%s', command returned error %i" % (f, r))
     1047                    if verbose_ENABLED:
     1048                        print(" command: %s" % (minify_cmd,))
     1049                else:
     1050                    print("minified %s" % (f, ))
     1051            else:
     1052                r = -1
     1053            if r!=0:
     1054                shutil.copyfile(src, dst)
     1055                os.chmod(dst, 0o644)
     1056
     1057
    6651058#*******************************************************************************
    6661059if 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"]
     1060    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1061    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821062    add_packages("xpra.platform.win32")
    8831063    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"]
     1064
     1065    #this is where the win32 gi installer will put things:
     1066    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1067
     1068    #only add the cx_freeze specific options
     1069    #if we aren't just building the Cython bits with "build_ext":
     1070    if "build_ext" not in sys.argv:
     1071        #with cx_freeze, we don't use py_modules
     1072        del setup_options["py_modules"]
     1073        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1074
     1075        #cx_freeze doesn't use "data_files"...
     1076        del setup_options["data_files"]
     1077        #it wants source files first, then where they are placed...
     1078        #one item at a time (no lists)
     1079        #all in its own structure called "include_files" instead of "data_files"...
     1080        def add_data_files(target_dir, files):
     1081            if verbose_ENABLED:
     1082                print("add_data_files(%s, %s)" % (target_dir, files))
     1083            assert type(target_dir)==str
     1084            assert type(files) in (list, tuple)
     1085            for f in files:
     1086                target_file = os.path.join(target_dir, os.path.basename(f))
     1087                data_files.append((f, target_file))
     1088
     1089        #pass a potentially nested dictionary representing the tree
     1090        #of files and directories we do want to include
     1091        #relative to gnome_include_path
     1092        def add_dir(base, defs):
     1093            if verbose_ENABLED:
     1094                print("add_dir(%s, %s)" % (base, defs))
     1095            if type(defs) in (list, tuple):
     1096                for sub in defs:
     1097                    if type(sub)==dict:
     1098                        add_dir(base, sub)
     1099                    else:
     1100                        assert type(sub)==str
     1101                        filename = os.path.join(gnome_include_path, base, sub)
     1102                        if os.path.exists(filename):
     1103                            add_data_files(base, [filename])
     1104                        else:
     1105                            print("Warning: missing '%s'" % filename)
     1106            else:
     1107                assert type(defs)==dict
     1108                for d, sub in defs.items():
     1109                    assert type(sub) in (dict, list, tuple)
     1110                    #recurse down:
     1111                    add_dir(os.path.join(base, d), sub)
     1112
     1113        #convenience method for adding GI libs and "typelib" and "gir":
     1114        def add_gi(*libs):
     1115            if verbose_ENABLED:
     1116                print("add_gi(%s)" % str(libs))
     1117            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1118            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1119
     1120        def add_DLLs(*dll_names):
     1121            try:
     1122                do_add_DLLs(*dll_names)
     1123            except Exception as e:
     1124                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1125                print(" %s" % e)
     1126                sys.exit(1)
     1127
     1128        def do_add_DLLs(*dll_names):
     1129            dll_names = list(dll_names)
     1130            dll_files = []
     1131            import re
     1132            version_re = re.compile("\-[0-9\.\-]+$")
     1133            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1134            if os.path.exists(gnome_include_path):
     1135                dirs.insert(0, gnome_include_path)
     1136            if verbose_ENABLED:
     1137                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1138            for d in dirs:
     1139                if not os.path.exists(d):
     1140                    continue
     1141                for x in os.listdir(d):
     1142                    dll_path = os.path.join(d, x)
     1143                    x = x.lower()
     1144                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1145                        continue
     1146                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1147                    if verbose_ENABLED:
     1148                        print("checking %s: %s" % (x, nameversion))
     1149                    m = version_re.search(nameversion)          #look for version part of filename
     1150                    if m:
     1151                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1152                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1153                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1154                    else:
     1155                        dll_version = ""                        #no version
     1156                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1157                    if dll_name in dll_names:
     1158                        #this DLL is on our list
     1159                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1160                        dll_files.append(dll_path)
     1161                        dll_names.remove(dll_name)
     1162            if len(dll_names)>0:
     1163                print("some DLLs could not be found:")
     1164                for x in dll_names:
     1165                    print(" - lib%s*.dll" % x)
     1166            add_data_files("", dll_files)
     1167
     1168        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1169        #(ie: "libatk-1.0-0.dll" -> "atk")
     1170        if sound_ENABLED or gtk3_ENABLED:
     1171            add_DLLs('gio', 'girepository', 'glib',
     1172                     'gnutls', 'gobject', 'gthread',
     1173                     'orc', 'stdc++',
     1174                     'winpthread',
     1175                     )
     1176        if gtk3_ENABLED:
     1177            add_DLLs('atk',
     1178                     'dbus', 'dbus-glib',
     1179                     'gdk', 'gdk_pixbuf', 'gtk',
     1180                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1181                     'harfbuzz', 'harfbuzz-gobject',
     1182                     'jasper', 'epoxy',
     1183                     'intl',
     1184                     'p11-kit',
     1185                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1186            #these are missing in newer aio installers (sigh):
     1187            do_add_DLLs('javascriptcoregtk')
     1188            if opengl_ENABLED:
     1189                do_add_DLLs('gdkglext', 'gtkglext')
     1190
     1191        if gtk3_ENABLED:
     1192            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1193            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1194                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1195            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1196                              "icons", "p11-kit", "xml",
     1197                              {"locale" : ["en"]},
     1198                              {"themes" : ["Default"]}
     1199                             ])
     1200        if gtk3_ENABLED or sound_ENABLED:
     1201            add_dir('lib', ["gio"])
     1202            packages.append("gi")
     1203            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1204                   "GObject-2.0")
     1205        if gtk3_ENABLED:
     1206            add_gi("Atk-1.0",
     1207                   "fontconfig-2.0", "freetype2-2.0",
     1208                   "GDesktopEnums-3.0", "Soup-2.4",
     1209                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0"
     1210                   "HarfBuzz-0.0",
     1211                   "Libproxy-1.0", "libxml2-2.0",
     1212                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1213                   "Rsvg-2.0",
     1214                   "win32-1.0")
     1215            if opengl_ENABLED:
     1216                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1217            add_DLLs('visual', 'curl', 'soup', 'sqlite3', 'openjpeg')
     1218
     1219        if gtk2_ENABLED:
     1220            add_dir('lib',      {
     1221                "gdk-pixbuf-2.0":    {
     1222                    "2.10.0"    :   {
     1223                        "loaders"   :
     1224                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp")]
     1225                        },
     1226                    },
     1227                })
     1228
     1229        if sound_ENABLED:
     1230            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1231            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1232                   "GstTag-1.0")
     1233            add_DLLs('gstreamer', 'orc-test')
     1234            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1235                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1236                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1237                      "basecamerabinsrc", "mpegts", "photography",
     1238                      ):
     1239                add_DLLs('gst%s' % p)
     1240            #DLLs needed by the plugins:
     1241            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1242            #add the gstreamer plugins we need:
     1243            GST_PLUGINS = ("app",
     1244                           #muxers:
     1245                           "gdp", "matroska", "ogg", "isomp4",
     1246                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1247                           "coreelements", "directsoundsink", "directsoundsrc",
     1248                           #codecs:
     1249                           "opus", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1250                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1251                           #untested: a52dec, voaacenc
     1252                           )
     1253            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1254            #END OF SOUND
     1255
     1256            if server_ENABLED:
     1257                #used by proxy server:
     1258                external_includes += ["multiprocessing"]
     1259            external_includes += ["encodings"]
     1260            #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1261            os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1262            bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL", "libsqlite3-0.dll"]
     1263            cx_freeze_options = {
     1264                                "compressed"        : True,
     1265                                "includes"          : external_includes,
     1266                                "packages"          : packages,
     1267                                "include_files"     : data_files,
     1268                                "excludes"          : excludes,
     1269                                "include_msvcr"     : True,
     1270                                "bin_excludes"      : bin_excludes,
     1271                                "create_shared_zip" : zip_ENABLED,
     1272                                }
     1273            setup_options["options"] = {"build_exe" : cx_freeze_options}
     1274            executables = []
     1275            setup_options["executables"] = executables
     1276
     1277        def add_exe(script, icon, base_name, base="Console"):
     1278            executables.append(Executable(
     1279                        script                  = script,
     1280                        initScript              = None,
     1281                        #targetDir               = "dist",
     1282                        icon                    = "win32/%s" % icon,
     1283                        targetName              = "%s.exe" % base_name,
     1284                        compress                = True,
     1285                        copyDependentFiles      = True,
     1286                        appendScriptToExe       = False,
     1287                        appendScriptToLibrary   = True,
     1288                        base                    = base))
     1289
     1290        def add_console_exe(script, icon, base_name):
     1291            add_exe(script, icon, base_name)
     1292        def add_gui_exe(script, icon, base_name):
     1293            add_exe(script, icon, base_name, base="Win32GUI")
     1294
     1295        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1296        if client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1297            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1298            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1299            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1300            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1301        if gtk2_ENABLED:
     1302            #these need porting..
     1303            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1304        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1305        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1306        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1307        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1308        if gtk2_ENABLED or gtk3_ENABLED:
     1309            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1310            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1311            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1312        if client_ENABLED or server_ENABLED:
     1313            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1314            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1315        if client_ENABLED:
     1316            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1317            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1318            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1319        if client_ENABLED:
     1320            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1321            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1322        if sound_ENABLED:
     1323            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1324            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1325            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1326            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1327            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1328        if opengl_ENABLED:
     1329            add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1330        if webcam_ENABLED:
     1331            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1332            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1333        if printing_ENABLED:
     1334            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1335            if os.path.exists("C:\\Program Files\\Ghostgum\\gsview"):
     1336                GSVIEW = "C:\\Program Files\\Ghostgum\\gsview"
     1337            else:
     1338                GSVIEW = "C:\\Program Files (x86)\\Ghostgum\\gsview"
     1339            if os.path.exists("C:\\Program Files\\gs"):
     1340                GHOSTSCRIPT_PARENT_DIR = "C:\\Program Files\\gs"
     1341            else:
     1342                GHOSTSCRIPT_PARENT_DIR = "C:\\Program Files (x86)\\gs"
     1343            GHOSTSCRIPT = None
     1344            for x in reversed(sorted(os.listdir(GHOSTSCRIPT_PARENT_DIR))):
     1345                f = os.path.join(GHOSTSCRIPT_PARENT_DIR, x)
     1346                if os.path.isdir(f):
     1347                    GHOSTSCRIPT = os.path.join(f, "bin")
     1348                    print("found ghostscript: %s" % GHOSTSCRIPT)
     1349                    break
     1350            assert GHOSTSCRIPT is not None, "cannot find ghostscript installation directory in %s" % GHOSTSCRIPT_PARENT_DIR
     1351            add_data_files('gsview', glob.glob(GSVIEW+'\\*.*'))
     1352            add_data_files('gsview', glob.glob(GHOSTSCRIPT+'\\*.*'))
     1353        if nvenc7_ENABLED:
     1354            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1355            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1356
     1357        #FIXME: how do we figure out what target directory to use?
     1358        print("calling build_xpra_conf in-place")
     1359        #building etc files in-place:
     1360        build_xpra_conf(".")
     1361        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1362        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1363        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1364        #build minified html5 client in temporary build dir:
     1365        if "clean" not in sys.argv and html5_ENABLED:
     1366            install_html5(os.path.join(install, "www"))
     1367            for k,v in glob_recurse("build/www").items():
     1368                if (k!=""):
     1369                    k = os.sep+k
     1370                add_data_files('www'+k, v)
     1371
     1372    if client_ENABLED or server_ENABLED:
     1373        add_data_files('',      ['COPYING', 'README', 'win32/website.url'])
     1374        add_data_files('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*'))
     1375
     1376    if webcam_ENABLED:
     1377        add_data_files('',      ['win32\\DirectShow.tlb'])
     1378        add_modules("comtypes.gen.stdole", "comtypes.gen.DirectShowLib")
     1379
    9071380    remove_packages(*external_excludes)
     1381    external_includes.append("mmap")
    9081382    remove_packages(#not used on win32:
    909                     "mmap",
    9101383                    #we handle GL separately below:
    9111384                    "OpenGL", "OpenGL_accelerate",
     
    9131386                    "ctypes.macholib")
    9141387
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1388    if webcam_ENABLED:
     1389        external_includes.append("cv2")
     1390
     1391    if opengl_ENABLED:
    9161392        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1393        external_includes.append("numpy")
    9181394    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1395        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211396                        "pydoc")
    9221397
    923     if sound_ENABLED:
    924         py2exe_includes += ["pygst", "gst", "gst.extend"]
    925     else:
    926         remove_packages("pygst", "gst", "gst.extend")
    927 
    928     if opengl_ENABLED:
     1398    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1399    remove_packages("pygst", "gst", "gst.extend")
     1400
     1401    #add subset of PyOpenGL modules (only when installing):
     1402    if opengl_ENABLED and ("install_exe" in sys.argv or "install" in sys.argv):
    9291403        #for this hack to work, you must add "." to the sys.path
    9301404        #so python can load OpenGL from the install directory
    9311405        #(further complicated by the fact that "." is the "frozen" path...)
     1406        #but we re-add those two directories to the library.zip as part of the build script
    9321407        import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
     1408        print("*** copying PyOpenGL modules to %s ***" % install)
    9351409        for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
    9361410            module_dir = os.path.dirname(module.__file__ )
    9371411            try:
    9381412                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1413                    module_dir, os.path.join(install, module_name),
     1414                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411415                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1416            except Exception as e:
     1417                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441418                    raise
    945     py2exe_options = {
    946                       "skip_archive"   : False,
    947                       "optimize"       : 0,    #WARNING: do not change - causes crashes
    948                       "unbuffered"     : True,
    949                       "compressed"     : True,
    950                       "skip_archive"   : False,
    951                       "packages"       : packages,
    952                       "includes"       : py2exe_includes,
    953                       "excludes"       : excludes,
    954                       "dll_excludes"   : dll_excludes,
    955                      }
    956     setup_options["options"] = {"py2exe" : py2exe_options}
    957     data_files += [
    958                    ('', ['COPYING', 'README',
    959                          'win32/website.url',
    960                          'etc/xpra/client-only/xpra.conf'] +
    961                          glob.glob('%s\\bin\\*.dll' % libffmpeg_path)),
    962                    ('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*')),
    963                    ('Microsoft.VC90.CRT', glob.glob('%s\\Microsoft.VC90.CRT\\*.*' % C_DLLs)),
    964                    ('Microsoft.VC90.MFC', glob.glob('%s\\Microsoft.VC90.MFC\\*.*' % C_DLLs)),
    965                    ]
    966     if enc_x264_ENABLED:
    967         data_files.append(('', ['%s\\libx264.dll' % x264_bin_dir]))
    968     html5_dir = ''
    969 
    970     if webm_ENABLED or webp_ENABLED:
    971         #Note: confusingly, the python bindings are called webm...
    972         #add the webp DLL to the output:
    973         #And since 0.2.1, you have to compile the DLL yourself..
    974         #the path after installing may look like this:
    975         #webp_DLL = "C:\\libwebp-0.3.1-windows-x86\\bin\\libwebp.dll"
    976         #but we use something more generic, without the version numbers:
    977         webp_DLL = webp_bin_dir+"\\libwebp.dll"
    978         data_files.append(('', [webp_DLL]))
    979         #and its license:
    980         data_files.append(('webm', ["xpra/codecs/webm/LICENSE"]))
    981 
    982 
     1419
     1420    #END OF win32
    9831421#*******************************************************************************
    9841422else:
    9851423    #OSX and *nix:
    9861424    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"]))
     1425    add_data_files("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1"])
     1426    add_data_files("share/xpra",          ["README", "COPYING"])
     1427    add_data_files("share/xpra/icons",    glob.glob("icons/*"))
     1428    add_data_files("share/applications",  ["xdg/xpra_launcher.desktop", "xdg/xpra.desktop"])
     1429    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1430    add_data_files("share/icons",         ["xdg/xpra.png"])
     1431    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1432
     1433    #here, we override build and install so we can
     1434    #generate our /etc/xpra/xpra.conf
     1435    class build_override(build):
     1436        def run(self):
     1437            build.run(self)
     1438            self.run_command("build_conf")
     1439
     1440    class build_conf(build):
     1441        def run(self):
     1442            try:
     1443                build_base = self.distribution.command_obj['build'].build_base
     1444            except:
     1445                build_base = self.build_base
     1446            build_xpra_conf(build_base)
     1447
     1448    class install_data_override(install_data):
     1449        def run(self):
     1450            print("install_data_override: install_dir=%s" % self.install_dir)
     1451            if html5_ENABLED:
     1452                install_html5(os.path.join(self.install_dir, "share/xpra/www"))
     1453            install_data.run(self)
     1454
     1455            etc_prefix = self.install_dir
     1456            if etc_prefix.endswith("/usr"):
     1457                etc_prefix = etc_prefix[:-3]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1458            build_xpra_conf(etc_prefix)
     1459
     1460            if printing_ENABLED and os.name=="posix":
     1461                #install "/usr/lib/cups/backend" with 0700 permissions:
     1462                xpraforwarder_src = os.path.join("cups", "xpraforwarder")
     1463                cups_backend_dir = os.path.join(self.install_dir, "lib", "cups", "backend")
     1464                self.mkpath(cups_backend_dir)
     1465                xpraforwarder_dst = os.path.join(cups_backend_dir, "xpraforwarder")
     1466                shutil.copyfile(xpraforwarder_src, xpraforwarder_dst)
     1467                os.chmod(xpraforwarder_dst, 0o700)
     1468
     1469            if x11_ENABLED:
     1470                #install xpra_Xdummy if we need it:
     1471                xvfb_command = detect_xorg_setup()
     1472                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])):
     1473                    bin_dir = os.path.join(self.install_dir, "bin")
     1474                    self.mkpath(bin_dir)
     1475                    dummy_script = os.path.join(bin_dir, "xpra_Xdummy")
     1476                    shutil.copyfile("scripts/xpra_Xdummy", dummy_script)
     1477                    os.chmod(dummy_script, 0o755)
     1478                #install xorg.conf, cuda.conf and nvenc.keys:
     1479                etc_xpra = os.path.join(etc_prefix, "etc", "xpra")
     1480                self.mkpath(etc_xpra)
     1481                etc_files = ["xorg.conf"]
     1482                if nvenc7_ENABLED:
     1483                    etc_files += ["cuda.conf", "nvenc.keys"]
     1484                for x in etc_files:
     1485                    shutil.copyfile("etc/xpra/%s" % x, os.path.join(etc_xpra, x))
     1486
     1487            if pam_ENABLED:
     1488                etc_pam_d = os.path.join(etc_prefix, "etc", "pam.d")
     1489                self.mkpath(etc_pam_d)
     1490                shutil.copyfile("etc/pam.d/xpra", os.path.join(etc_pam_d, "xpra"))
     1491
     1492    # add build_conf to build step
     1493    cmdclass.update({
     1494             'build'        : build_override,
     1495             'build_conf'   : build_conf,
     1496             'install_data' : install_data_override,
     1497             })
    9971498
    9981499    if OSX:
     1500        #pyobjc needs email.parser
     1501        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
    9991502        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001503        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011504        add_packages("xpra.platform.darwin")
     1505        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1506        #to support GStreamer 1.x we need this:
     1507        modules.append("importlib")
     1508        modules.append("xpra.scripts.gtk_info")
     1509        modules.append("xpra.scripts.show_webcam")
    10021510    else:
    10031511        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041512        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")
     1513        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1514        #not supported by all distros, but doesn't hurt to install it anyway:
     1515        add_data_files("lib/tmpfiles.d", ["tmpfiles.d/xpra.conf"])
     1516        add_data_files("lib/sysusers.d", ["sysusers.d/xpra.conf"])
    10081517
    10091518    #gentoo does weird things, calls --no-compile with build *and* install
     
    10141523        def pkgconfig(*pkgs_options, **ekw):
    10151524            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))
    10331525
    10341526    if OSX and "py2app" in sys.argv:
     
    10451537        remove_packages(*external_excludes)
    10461538
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1539        try:
     1540            from xpra.src_info import REVISION
     1541        except:
     1542            REVISION = "unknown"
     1543        Plist = {
     1544            "CFBundleDocumentTypes" : {
     1545                "CFBundleTypeExtensions"    : ["Xpra"],
     1546                "CFBundleTypeName"          : "Xpra Session Config File",
     1547                "CFBundleName"              : "Xpra",
     1548                "CFBundleTypeRole"          : "Viewer",
     1549                },
     1550            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2017 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1551            "CFBundleIdentifier"            : "org.xpra.xpra",
     1552            }
    10531553        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541554        #so the make-app.sh script still has to hack around this problem.
     
    10651565            }
    10661566        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1567        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1568
     1569    if OSX:
     1570        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1571        #so instead we have to query each package seperately and merge the results:
     1572        def osx_pkgconfig(*pkgs_options, **ekw):
     1573            kw = dict(ekw)
     1574            for pkg in pkgs_options:
     1575                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1576                if pkg.lower().startswith("x"):
     1577                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1578                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1579                kw = exec_pkgconfig(pkg, **kw)
     1580                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1581            return kw
     1582
     1583        pkgconfig = osx_pkgconfig
     1584    else:
     1585        if service_ENABLED:
     1586            #Linux init service:
     1587            if os.path.exists("/bin/systemctl"):
     1588                add_data_files("/usr/lib/systemd/system/", ["service/xpra.service"])
     1589            else:
     1590                add_data_files("/etc/init.d/", ["service/xpra"])
     1591            if os.path.exists("/etc/sysconfig"):
     1592                add_data_files("/etc/sysconfig/", ["etc/sysconfig/xpra"])
     1593            elif os.path.exists("/etc/default"):
     1594                add_data_files("/etc/default/", ["etc/sysconfig/xpra"])
    10681595
    10691596
    10701597if 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 
     1598    if WIN32 or OSX:
     1599        external_includes.append("websockify")
     1600        external_includes.append("numpy")
     1601        external_includes.append("ssl")
     1602        external_includes.append("_ssl")
     1603        if not PYTHON3:
     1604            external_includes.append("mimetypes")
     1605            external_includes.append("mimetools")
     1606            external_includes.append("BaseHTTPServer")
     1607
     1608
     1609if annotate_ENABLED:
     1610    from Cython.Compiler import Options
     1611    Options.annotate = True
    10761612
    10771613
    10781614#*******************************************************************************
    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")
     1615buffers_c = "xpra/buffers/buffers.c"
     1616memalign_c = "xpra/buffers/memalign.c"
     1617xxhash_c = "xpra/buffers/xxhash.c"
     1618membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1619
     1620add_packages("xpra.buffers")
     1621buffers_pkgconfig = pkgconfig(optimize=3)
     1622cython_add(Extension("xpra.buffers.membuf",
     1623            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1624
     1625
     1626toggle_packages(dbus_ENABLED, "xpra.dbus")
     1627toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1628toggle_packages(server_ENABLED or proxy_ENABLED or shadow_ENABLED, "xpra.server", "xpra.server.auth")
     1629toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1630toggle_packages(server_ENABLED, "xpra.server.window")
     1631toggle_packages(server_ENABLED and shadow_ENABLED, "xpra.server.shadow")
     1632toggle_packages(server_ENABLED or (client_ENABLED and gtk2_ENABLED), "xpra.clipboard")
     1633toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1634
     1635#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1636if dbus_ENABLED and server_ENABLED:
     1637    add_packages("xpra.server.dbus")
     1638
     1639if OSX:
     1640    quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1641    add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1642                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h",
     1643                    '-ObjC',
     1644                    '-mmacosx-version-min=10.10')
     1645    add_to_keywords(quartz_pkgconfig, 'extra_link_args',
     1646                    '-framework', 'Foundation',
     1647                    '-framework', 'AppKit',
     1648                    )
     1649    cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1650                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1651                language="objc",
     1652                **quartz_pkgconfig
     1653                ))
     1654
     1655monotonic_time_pkgconfig = pkgconfig()
     1656if not OSX and not WIN32:
     1657    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1658cython_add(Extension("xpra.monotonic_time",
     1659            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1660            **monotonic_time_pkgconfig
     1661            ))
     1662
     1663
     1664toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841665if x11_ENABLED:
    10851666    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1667    make_constants("xpra", "x11", "gtk2", "constants")
    10871668
    10881669    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981679                **pkgconfig("x11")
    10991680                ))
     1681    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1682                ["xpra/x11/bindings/posix_display_source.pyx"],
     1683                **pkgconfig("x11")
     1684                ))
     1685
    11001686    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011687                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041690    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051691                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1692                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071693                ))
    11081694
    11091695    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101696                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1697                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121698                ))
    11131699    cython_add(Extension("xpra.x11.bindings.ximage",
    11141700                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1701                **pkgconfig("x11", "xcomposite", "xdamage", "xext")
    11161702                ))
    11171703
    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)
     1704toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1705if gtk_x11_ENABLED:
     1706    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1707    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1708    if PYTHON3:
     1709        #GTK3 display source:
     1710        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1711                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1712                    **pkgconfig("gtk+-3.0")
     1713                    ))
     1714    else:
     1715        #below uses gtk/gdk:
     1716        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1717                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1718                    **pkgconfig(*PYGTK_PACKAGES)
     1719                    ))
     1720        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1721        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1722                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1723                    **pkgconfig(*GDK_BINDINGS_PACKAGES)
     1724                    ))
     1725
     1726if client_ENABLED and gtk3_ENABLED:
     1727    #cairo workaround:
     1728    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1729                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1730                **pkgconfig("pycairo")
    11221731                ))
    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:
     1732
     1733if client_ENABLED or server_ENABLED:
     1734    add_packages("xpra.codecs.argb")
     1735    argb_pkgconfig = pkgconfig(optimize=3)
    11321736    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1737                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1738
     1739
     1740#build tests, but don't install them:
     1741toggle_packages(tests_ENABLED, "unit")
    11341742
    11351743
    11361744if bundle_tests_ENABLED:
    11371745    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1746    for k,v in glob_recurse("unit").items():
    11391747        if (k!=""):
    11401748            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:
     1749        add_data_files("unit"+k, v)
     1750
     1751#python-cryptography needs workarounds for bundling:
     1752if crypto_ENABLED and (OSX or WIN32):
     1753    external_includes.append("_ssl")
     1754    external_includes.append("cffi")
     1755    external_includes.append("_cffi_backend")
     1756    external_includes.append("cryptography")
     1757    external_includes.append("pkg_resources._vendor.packaging")
     1758    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1759    external_includes.append("pkg_resources._vendor.pyparsing")
     1760    add_modules("cryptography.hazmat.bindings._openssl")
     1761    add_modules("cryptography.hazmat.bindings._constant_time")
     1762    add_modules("cryptography.hazmat.bindings._padding")
     1763    add_modules("cryptography.hazmat.backends.openssl")
     1764    add_modules("cryptography.fernet")
     1765    if WIN32:
     1766        external_includes.append("appdirs")
     1767
     1768#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441769if client_ENABLED:
    11451770    add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1771toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471772toggle_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")
     1773toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1774toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501775toggle_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")
     1776toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1777toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1778if client_ENABLED and WIN32 and MINGW_PREFIX:
     1779    propsys_pkgconfig = pkgconfig()
     1780    if debug_ENABLED:
     1781        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1782    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1783    cython_add(Extension("xpra.platform.win32.propsys",
     1784                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1785                language="c++",
     1786                **propsys_pkgconfig))
     1787
     1788if client_ENABLED or server_ENABLED:
     1789    add_modules("xpra.codecs")
     1790toggle_packages(client_ENABLED or server_ENABLED, "xpra.keyboard")
     1791if client_ENABLED or server_ENABLED:
     1792    add_modules("xpra.scripts.config", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1793if server_ENABLED or proxy_ENABLED:
     1794    add_modules("xpra.scripts.server")
     1795if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1796    add_modules("xpra.scripts.gtk_info")
     1797
     1798toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1799#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1800#and try to include both gtk3 and gtk2, and fail hard..
     1801for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gtk_compat"):
     1802    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1803
     1804toggle_modules(sound_ENABLED, "xpra.sound")
     1805toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541806
    11551807toggle_packages(clipboard_ENABLED, "xpra.clipboard")
     
    11601812                ))
    11611813
    1162 if cyxor_ENABLED:
     1814toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1815if client_ENABLED or server_ENABLED:
    11631816    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641817                ["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")
     1818                **pkgconfig(optimize=3)))
     1819
     1820if server_ENABLED:
     1821    O3_pkgconfig = pkgconfig(optimize=3)
     1822    cython_add(Extension("xpra.server.cystats",
     1823                ["xpra/server/cystats.pyx"],
     1824                **O3_pkgconfig))
     1825    cython_add(Extension("xpra.server.window.region",
     1826                ["xpra/server/window/region.pyx"],
     1827                **O3_pkgconfig))
     1828    cython_add(Extension("xpra.server.window.motion",
     1829                ["xpra/server/window/motion.pyx"],
     1830                **O3_pkgconfig))
     1831
     1832
    11781833toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11791834
    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))
     1835toggle_packages(nvenc7_ENABLED, "xpra.codecs.nvenc7")
     1836toggle_packages(nvenc7_ENABLED, "xpra.codecs.cuda_common", "xpra.codecs.nv_util")
     1837
     1838if nvenc7_ENABLED:
     1839    #find nvcc:
     1840    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     1841    if WIN32:
     1842        nvcc_exe = "nvcc.exe"
     1843        #FIXME: we try to use SDK 6.5 x86 first!
     1844        #(so that we can build on 32-bit envs)
     1845        path_options = [
     1846                         "C:\\Program Files (x86)\\NVIDIA GPU Computing Toolkit\\CUDA\\v5.5\\bin",
     1847                         "C:\\Program Files (x86)\\NVIDIA GPU Computing Toolkit\\CUDA\\v6.0\\bin",
     1848                         "C:\\Program Files (x86)\\NVIDIA GPU Computing Toolkit\\CUDA\\v6.5\\bin",
     1849                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v6.5\\bin",
     1850                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.0\\bin",
     1851                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\bin",
     1852                         "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\bin",
     1853                         ] + path_options
     1854    else:
     1855        nvcc_exe = "nvcc"
     1856        for v in ("-5.5", "-6.0", "-6.5", "-7.0", "-7.5", "-8.0", ""):
     1857            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     1858    options = [os.path.join(x, nvcc_exe) for x in path_options]
     1859    def which(cmd):
     1860        try:
     1861            code, out, _ = get_status_output(["which", cmd])
     1862            if code==0:
     1863                return out
     1864        except:
     1865            pass
     1866    #prefer the one we find on the $PATH, if any:
     1867    try:
     1868        v = which(nvcc_exe)
     1869        if v and (v not in options):
     1870            options.insert(0, v)
     1871    except:
     1872        pass
     1873    nvcc_versions = {}
     1874    for filename in options:
     1875        if not os.path.exists(filename):
     1876            continue
     1877        code, out, err = get_status_output([filename, "--version"])
     1878        if code==0:
     1879            vpos = out.rfind(", V")
     1880            if vpos>0:
     1881                version = out[vpos+3:].strip("\n")
     1882                version_str = " version %s" % version
     1883            else:
     1884                version = "0"
     1885                version_str = " unknown version!"
     1886            print("found CUDA compiler: %s%s" % (filename, version_str))
     1887            nvcc_versions[version] = filename
     1888    assert nvcc_versions, "cannot find nvcc compiler!"
     1889    #choose the most recent one:
     1890    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     1891    if len(nvcc_versions)>1:
     1892        print(" using version %s from %s" % (version, nvcc))
     1893    if WIN32:
     1894        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     1895        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     1896    #first compile the cuda kernels
     1897    #(using the same cuda SDK for both nvenc modules for now..)
     1898    #TODO:
     1899    # * compile directly to output directory instead of using data files?
     1900    # * detect which arches we want to build for? (does it really matter much?)
     1901    kernels = ("BGRA_to_NV12", "BGRA_to_YUV444")
     1902    for kernel in kernels:
     1903        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     1904        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     1905        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     1906            continue
     1907        reason = should_rebuild(cuda_src, cuda_bin)
     1908        if not reason:
     1909            continue
     1910        cmd = [nvcc,
     1911               '-fatbin',
     1912               #"-cubin",
     1913               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     1914               #"-gencode=arch=compute_50,code=sm_50",
     1915               #"-gencode=arch=compute_52,code=sm_52",
     1916               #"-gencode=arch=compute_52,code=compute_52",
     1917               "-c", cuda_src,
     1918               "-o", cuda_bin]
     1919        #GCC 6 uses C++11 by default:
     1920        if get_gcc_version()>=[6, 0]:
     1921            cmd.append("-std=c++11")
     1922        CL_VERSION = os.environ.get("CL_VERSION")
     1923        if CL_VERSION:
     1924            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     1925            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     1926            cmd += ["--machine", "32"]
     1927        if WIN32:
     1928            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     1929            #cmd += ["--input-drive-prefix", "/"]
     1930            #cmd += ["--dependency-drive-prefix", "/"]
     1931            cmd += ["-I%s" % os.path.abspath("win32")]
     1932        comp_code_options = [(30, 30), (35, 35)]
     1933        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     1934        if version!="0" and version<"7.5":
     1935            print("CUDA version %s is very unlikely to work")
     1936            print("try upgrading to version 7.5 or later")
     1937        if version>="7.5":
     1938            comp_code_options.append((53, 53))
     1939        if version>="8.0":
     1940            comp_code_options.append((60, 60))
     1941            comp_code_options.append((61, 61))
     1942            comp_code_options.append((62, 62))
     1943        for arch, code in comp_code_options:
     1944            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     1945        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     1946        print(" %s" % " ".join("'%s'" % x for x in cmd))
     1947        c, stdout, stderr = get_status_output(cmd)
     1948        if c!=0:
     1949            print("Error: failed to compile CUDA kernel %s" % kernel)
     1950            print(stdout or "")
     1951            print(stderr or "")
     1952            sys.exit(1)
     1953    CUDA_BIN = "share/xpra/cuda"
     1954    if WIN32:
     1955        CUDA_BIN = "CUDA"
     1956    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     1957    nvencmodule = "nvenc7"
     1958    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     1959    #don't link against libnvidia-encode, we load it dynamically:
     1960    libraries = nvenc_pkgconfig.get("libraries", [])
     1961    if "nvidia-encode" in libraries:
     1962        libraries.remove("nvidia-encode")
     1963    if PYTHON3 and get_gcc_version()>=[6, 2]:
     1964        #with gcc 6.2 on Fedora:
     1965        #xpra/codecs/nvenc7/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     1966        #xpra/codecs/nvenc7/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     1967        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     1968        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     1969    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     1970                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     1971                         **nvenc_pkgconfig))
    11871972
    11881973toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11891974if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     1975    x264_pkgconfig = pkgconfig("x264")
     1976    if get_gcc_version()>=[6, 0]:
     1977        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11911978    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11921979                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     1980                **x264_pkgconfig))
    11941981
    11951982toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11961983if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     1984    x265_pkgconfig = pkgconfig("x265")
    11981985    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11991986                ["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))
     1987                **x265_pkgconfig))
     1988
     1989toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     1990if pillow_ENABLED:
     1991    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
     1992
     1993toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     1994if jpeg_ENABLED:
     1995    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     1996    cython_add(Extension("xpra.codecs.jpeg.encoder",
     1997                ["xpra/codecs/jpeg/encoder.pyx"],
     1998                **jpeg_pkgconfig))
     1999    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2000                ["xpra/codecs/jpeg/decoder.pyx"],
     2001                **jpeg_pkgconfig))
     2002
     2003#swscale and avcodec2 use libav_common/av_log:
     2004libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2005toggle_packages(libav_common, "xpra.codecs.libav_common")
     2006if libav_common:
     2007    avutil_pkgconfig = pkgconfig("avutil")
     2008    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2009                ["xpra/codecs/libav_common/av_log.pyx"],
     2010                **avutil_pkgconfig))
     2011
    12162012
    12172013toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182014if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2015    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil")
    12202016    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 
     2017                ["xpra/codecs/dec_avcodec2/decoder.pyx"],
     2018                **avcodec2_pkgconfig))
     2019
     2020
     2021toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2022if csc_libyuv_ENABLED:
     2023    libyuv_pkgconfig = pkgconfig("libyuv")
     2024    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2025                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2026                language="c++",
     2027                **libyuv_pkgconfig))
    12242028
    12252029toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262030if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2031    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292032    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))
     2033                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2034                **swscale_pkgconfig))
     2035
    12392036
    12402037toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412038if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2039    #try both vpx and libvpx as package names:
     2040    kwargs = {
     2041              "LIBVPX14"    : pkg_config_version("1.4", "vpx"),
     2042              }
     2043    make_constants("xpra", "codecs", "vpx", "constants", **kwargs)
     2044    vpx_pkgconfig = pkgconfig("vpx")
    12432045    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))
     2046                ["xpra/codecs/vpx/encoder.pyx"],
     2047                **vpx_pkgconfig))
    12462048    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))
     2049                ["xpra/codecs/vpx/decoder.pyx"],
     2050                **vpx_pkgconfig))
     2051
     2052toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2053if enc_ffmpeg_ENABLED:
     2054    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2055    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2056                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2057                **ffmpeg_pkgconfig))
     2058
     2059toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2060if v4l2_ENABLED:
     2061    v4l2_pkgconfig = pkgconfig()
     2062    #fuly warning: cython makes this difficult,
     2063    #we have to figure out if "device_caps" exists in the headers:
     2064    ENABLE_DEVICE_CAPS = False
     2065    if os.path.exists("/usr/include/linux/videodev2.h"):
     2066        hdata = open("/usr/include/linux/videodev2.h").read()
     2067        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2068    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2069    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2070    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2071                ["xpra/codecs/v4l2/pusher.pyx"],
     2072                **v4l2_pkgconfig))
    12622073
    12632074
    12642075toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2076toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652077if 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")
     2078    bencode_pkgconfig = pkgconfig(optimize=not debug_ENABLED)
    12722079    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732080                ["xpra/net/bencode/cython_bencode.pyx"],
    12742081                **bencode_pkgconfig))
    12752082
     2083if vsock_ENABLED:
     2084    vsock_pkgconfig = pkgconfig()
     2085    cython_add(Extension("xpra.net.vsock",
     2086                ["xpra/net/vsock.pyx"],
     2087                **vsock_pkgconfig))
     2088
     2089if pam_ENABLED:
     2090    pam_pkgconfig = pkgconfig()
     2091    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2092    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2093    cython_add(Extension("xpra.server.pam",
     2094                ["xpra/server/pam.pyx"],
     2095                **pam_pkgconfig))
     2096
    12762097
    12772098if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2099    from Cython.Build import cythonize
     2100    #this causes Cython to fall over itself:
     2101    #gdb_debug=debug_ENABLED
     2102    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792103if cmdclass:
    12802104    setup_options["cmdclass"] = cmdclass
     
    12832107
    12842108
    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 
    12932109def main():
    12942110    if OSX or WIN32 or debug_ENABLED:
     2111        print()
    12952112        print("setup options:")
     2113        if verbose_ENABLED:
     2114            print("setup_options=%s" % (setup_options,))
     2115        try:
     2116            from xpra.util import repr_ellipsized as pv
     2117        except:
     2118            def pv(v):
     2119                return str(v)
    12962120        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2121            print_option("", k, pv(v))
    12982122        print("")
    12992123
Note: See TracChangeset for help on using the changeset viewer.