1# Licensed to the Apache Software Foundation (ASF) under one 2# or more contributor license agreements. See the NOTICE file 3# distributed with this work for additional information 4# regarding copyright ownership. The ASF licenses this file 5# to you under the Apache License, Version 2.0 (the 6# "License"); you may not use this file except in compliance 7# with the License. You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, 12# software distributed under the License is distributed on an 13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14# KIND, either express or implied. See the License for the 15# specific language governing permissions and limitations 16# under the License. 17"""Util to invoke C/C++ compilers in the system.""" 18# pylint: disable=invalid-name 19from __future__ import absolute_import as _abs 20import sys 21import subprocess 22import os 23 24from .._ffi.base import py_str 25from .util import tempdir 26 27def create_shared(output, 28 objects, 29 options=None, 30 cc="g++"): 31 """Create shared library. 32 33 Parameters 34 ---------- 35 output : str 36 The target shared library. 37 38 objects : List[str] 39 List of object files. 40 41 options : List[str] 42 The list of additional options string. 43 44 cc : Optional[str] 45 The compiler command. 46 """ 47 if sys.platform == "darwin" or sys.platform.startswith("linux"): 48 _linux_compile(output, objects, options, cc) 49 elif sys.platform == "win32": 50 _windows_shared(output, objects, options) 51 else: 52 raise ValueError("Unsupported platform") 53 54 55# assign so as default output format 56create_shared.output_format = "so" if sys.platform != "win32" else "dll" 57 58 59def build_create_shared_func(options=None, compile_cmd="g++"): 60 """Build create_shared function with particular default options and compile_cmd. 61 62 Parameters 63 ---------- 64 options : List[str] 65 The list of additional options string. 66 67 compile_cmd : Optional[str] 68 The compiler command. 69 70 Returns 71 ------- 72 create_shared_wrapper : Callable[[str, str, Optional[str]], None] 73 A compilation function that can be passed to export_library or to autotvm.LocalBuilder. 74 """ 75 def create_shared_wrapper(output, objects, options=options, compile_cmd=compile_cmd): 76 create_shared(output, objects, options, compile_cmd) 77 create_shared_wrapper.output_format = create_shared.output_format 78 return create_shared_wrapper 79 80 81def cross_compiler(compile_func, base_options=None, output_format="so"): 82 """Create a cross compiler function. 83 84 Parameters 85 ---------- 86 compile_func : Callable[[str, str, Optional[str]], None] 87 Function that performs the actual compilation 88 89 base_options : Optional[List[str]] 90 List of additional optional string. 91 92 output_format : Optional[str] 93 Library output format. 94 95 Returns 96 ------- 97 fcompile : Callable[[str, str, Optional[str]], None] 98 A compilation function that can be passed to export_library. 99 """ 100 if base_options is None: 101 base_options = [] 102 def _fcompile(outputs, objects, options=None): 103 all_options = base_options 104 if options is not None: 105 all_options += options 106 compile_func(outputs, objects, options=all_options) 107 _fcompile.output_format = output_format 108 return _fcompile 109 110 111def _linux_compile(output, objects, options, compile_cmd="g++"): 112 cmd = [compile_cmd] 113 if output.endswith(".so") or output.endswith(".dylib"): 114 cmd += ["-shared", "-fPIC"] 115 if sys.platform == "darwin": 116 cmd += ["-undefined", "dynamic_lookup"] 117 elif output.endswith(".obj"): 118 cmd += ["-c"] 119 cmd += ["-o", output] 120 if isinstance(objects, str): 121 cmd += [objects] 122 else: 123 cmd += objects 124 if options: 125 cmd += options 126 proc = subprocess.Popen( 127 cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 128 (out, _) = proc.communicate() 129 if proc.returncode != 0: 130 msg = "Compilation error:\n" 131 msg += py_str(out) 132 raise RuntimeError(msg) 133 134 135def _windows_shared(output, objects, options): 136 cl_cmd = ["cl"] 137 cl_cmd += ["-c"] 138 if isinstance(objects, str): 139 objects = [objects] 140 cl_cmd += objects 141 if options: 142 cl_cmd += options 143 144 temp = tempdir() 145 dllmain_path = temp.relpath("dllmain.cc") 146 with open(dllmain_path, "w") as dllmain_obj: 147 dllmain_obj.write('#include <windows.h>\ 148BOOL APIENTRY DllMain( HMODULE hModule,\ 149 DWORD ul_reason_for_call,\ 150 LPVOID lpReserved)\ 151{return TRUE;}') 152 153 cl_cmd += [dllmain_path] 154 155 temp_path = dllmain_path.replace("dllmain.cc", "") 156 cl_cmd += ["-Fo:" + temp_path] 157 try: 158 proc = subprocess.Popen( 159 cl_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 160 (out, _) = proc.communicate() 161 except FileNotFoundError: 162 raise RuntimeError("Can not find cl.exe," 163 "please run this in Vistual Studio Command Prompt.") 164 if proc.returncode != 0: 165 msg = "Compilation error:\n" 166 msg += py_str(out) 167 raise RuntimeError(msg) 168 link_cmd = ["lld-link"] 169 link_cmd += ["-dll", "-FORCE:MULTIPLE"] 170 171 for obj in objects: 172 if obj.endswith(".cc"): 173 (_, temp_file_name) = os.path.split(obj) 174 (shot_name, _) = os.path.splitext(temp_file_name) 175 link_cmd += [os.path.join(temp_path, shot_name + ".obj")] 176 if obj.endswith(".o"): 177 link_cmd += [obj] 178 179 link_cmd += ["-EXPORT:__tvm_main__"] 180 link_cmd += [temp_path + "dllmain.obj"] 181 link_cmd += ["-out:" + output] 182 183 try: 184 proc = subprocess.Popen( 185 link_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 186 (out, _) = proc.communicate() 187 except FileNotFoundError: 188 raise RuntimeError("Can not find the LLVM linker for Windows (lld-link.exe)." 189 "Make sure it's installed" 190 " and the installation directory is in the %PATH% environment " 191 "variable. Prebuilt binaries can be found at: https://llvm.org/" 192 "For building the linker on your own see: https://lld.llvm.org/#build") 193 if proc.returncode != 0: 194 msg = "Compilation error:\n" 195 msg += py_str(out) 196 197 raise RuntimeError(msg) 198