xcode.py 5.96 KB
Newer Older
1
# pylint: disable=invalid-name
2
"""Utility to invoke Xcode compiler toolchain"""
3
from __future__ import absolute_import as _abs
4

Tianqi Chen committed
5
import os
6 7
import sys
import subprocess
8
from .._ffi.base import py_str
9 10
from . import util

Tianqi Chen committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
def xcrun(cmd):
    """Run xcrun and return the output.

    Parameters
    ----------
    cmd : list of str
        The command sequence.

    Returns
    -------
    out : str
        The output string.
    """
    cmd = ["xcrun"] + cmd
    proc = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    (out, _) = proc.communicate()
    return out.strip()


def codesign(lib):
    """Codesign the shared libary

    This is an required step for library to be loaded in
    the app.

    Parameters
    ----------
    lib : The path to the library.
    """
    if "TVM_IOS_CODESIGN" not in os.environ:
        raise RuntimeError("Require environment variable TVM_IOS_CODESIGN "
                           " to be the signature")
    signature = os.environ["TVM_IOS_CODESIGN"]
    cmd = ["codesign", "--force", "--sign", signature]
    cmd += [lib]
    proc = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    (out, _) = proc.communicate()
    if proc.returncode != 0:
        msg = "Codesign error:\n"
54
        msg += py_str(out)
Tianqi Chen committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
        raise RuntimeError(msg)


def create_dylib(output, objects, arch, sdk="macosx"):
    """Create dynamic library.

    Parameters
    ----------
    output : str
        The target shared library.

    objects : list
        List of object files.

    options : str
        The additional options.

    arch : str
        Target major architectures

    sdk : str
        The sdk to be used.
    """
    clang = xcrun(["-sdk", sdk, "-find", "clang"])
    sdk_path = xcrun(["-sdk", sdk, "--show-sdk-path"])
    cmd = [clang]
    cmd += ["-dynamiclib"]
    cmd += ["-arch", arch]
    cmd += ["-isysroot", sdk_path]
    cmd += ["-o", output]
    if isinstance(objects, str):
        cmd += [objects]
    else:
        cmd += objects

    proc = subprocess.Popen(
        cmd, stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT)
    (out, _) = proc.communicate()

    if proc.returncode != 0:
        msg = "Compilation error:\n"
97
        msg += py_str(out)
Tianqi Chen committed
98 99 100
        raise RuntimeError(msg)


101
def compile_metal(code, path_target=None, sdk="macosx"):
102 103 104 105 106 107 108 109 110 111
    """Compile metal with CLI tool from env.

    Parameters
    ----------
    code : str
        The cuda code.

    path_target : str, optional
        Output file.

112 113 114
    sdk : str, optional
        The target platform SDK.

115 116 117 118 119 120 121 122 123 124 125 126 127 128
    Return
    ------
    metallib : bytearray
        The bytearray of the metallib
    """
    temp = util.tempdir()
    temp_code = temp.relpath("my_lib.metal")
    temp_ir = temp.relpath("my_lib.air")
    temp_target = temp.relpath("my_lib.metallib")

    with open(temp_code, "w") as out_file:
        out_file.write(code)
    file_target = path_target if path_target else temp_target

129
    cmd1 = ["xcrun", "-sdk", sdk, "metal", "-O3"]
130
    cmd1 += [temp_code, "-o", temp_ir]
131
    cmd2 = ["xcrun", "-sdk", sdk, "metallib"]
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    cmd2 += [temp_ir, "-o", file_target]
    proc = subprocess.Popen(
        ' '.join(cmd1) + ";" + ' '.join(cmd2),
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT)
    (out, _) = proc.communicate()
    if proc.returncode != 0:
        sys.stderr.write("Compilation error:\n")
        sys.stderr.write(out)
        sys.stderr.flush()
        libbin = None
    else:
        libbin = bytearray(open(file_target, "rb").read())
    return libbin
Tianqi Chen committed
147 148


149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
class XCodeRPCServer(object):
    """Wrapper for RPC server

    Parameters
    ----------
    cmd : list of str
       The command to run

    lock: FileLock
       Lock on the path
    """
    def __init__(self, cmd, lock):
        self.proc = subprocess.Popen(cmd)
        self.lock = lock

    def join(self):
        """Wait server to finish and release its resource
        """
        self.proc.wait()
        self.lock.release()


Tianqi Chen committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
def popen_test_rpc(host,
                   port,
                   key,
                   destination,
                   libs=None,
                   options=None):
    """Launch rpc server via xcodebuild test through another process.

    Parameters
    ----------
    host : str
        The address of RPC proxy host.

    port : int
        The port of RPC proxy host

    key : str
        The key of the RPC server

    destination : str
        Destination device of deployment, as in xcodebuild

    libs : list of str
        List of files to be packed into app/Frameworks/tvm
        These can be dylibs that can be loaed remoted by RPC.

    options : list of str
        Additional options to xcodebuild

    Returns
    -------
    proc : Popen
        The test rpc server process.
        Don't do wait() on proc, since it can terminate normally.
    """
    if "TVM_IOS_RPC_ROOT" in os.environ:
        rpc_root = os.environ["TVM_IOS_RPC_ROOT"]
    else:
        curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
        rpc_root = os.path.join(curr_path, "../../../apps/ios_rpc")
    proj_path = os.path.abspath(os.path.join(rpc_root, "tvmrpc.xcodeproj"))
    if not os.path.exists(proj_path):
        raise RuntimeError("Cannot find tvmrpc.xcodeproj in %s," +
                           (" please set env TVM_IOS_RPC_ROOT correctly" % rpc_root))
215 216 217 218

    # Lock the path so only one file can run
    lock = util.filelock(os.path.join(rpc_root, "ios_rpc.lock"))

Tianqi Chen committed
219 220 221 222 223 224 225 226 227 228 229 230 231
    with open(os.path.join(rpc_root, "rpc_config.txt"), "w") as fo:
        fo.write("%s %d %s\n" % (host, port, key))
        libs = libs if libs else []
        for file_name in libs:
            fo.write("%s\n" % file_name)

    cmd = ["xcrun", "xcodebuild",
           "-scheme", "tvmrpc",
           "-project", proj_path,
           "-destination", destination]
    if options:
        cmd += options
    cmd += ["test"]
232 233

    return XCodeRPCServer(cmd, lock)