1import textwrap 2 3from jinja2 import Template 4 5from conan.tools._check_build_profile import check_using_build_profile 6from conans.util.files import save 7 8 9class BazelDeps(object): 10 def __init__(self, conanfile): 11 self._conanfile = conanfile 12 check_using_build_profile(self._conanfile) 13 14 def generate(self): 15 local_repositories = [] 16 17 for build_dependency in self._conanfile.dependencies.direct_build.values(): 18 content = self._get_build_dependency_buildfile_content(build_dependency) 19 filename = self._save_dependendy_buildfile(build_dependency, content) 20 21 local_repository = self._create_new_local_repository(build_dependency, filename) 22 local_repositories.append(local_repository) 23 24 for dependency in self._conanfile.dependencies.host.values(): 25 content = self._get_dependency_buildfile_content(dependency) 26 filename = self._save_dependendy_buildfile(dependency, content) 27 28 local_repository = self._create_new_local_repository(dependency, filename) 29 local_repositories.append(local_repository) 30 31 content = self._get_main_buildfile_content(local_repositories) 32 self._save_main_buildfiles(content) 33 34 def _save_dependendy_buildfile(self, dependency, buildfile_content): 35 filename = 'conandeps/{}/BUILD'.format(dependency.ref.name) 36 save(filename, buildfile_content) 37 return filename 38 39 def _get_build_dependency_buildfile_content(self, dependency): 40 filegroup = textwrap.dedent(""" 41 filegroup( 42 name = "{}_binaries", 43 data = glob(["**"]), 44 visibility = ["//visibility:public"], 45 ) 46 47 """).format(dependency.ref.name) 48 49 return filegroup 50 51 def _get_dependency_buildfile_content(self, dependency): 52 template = textwrap.dedent(""" 53 load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library") 54 55 {% for lib in libs %} 56 cc_import( 57 name = "{{ lib }}_precompiled", 58 static_library = "{{ libdir }}/lib{{ lib }}.a" 59 ) 60 {% endfor %} 61 62 cc_library( 63 name = "{{ name }}", 64 {% if headers %} 65 hdrs = glob([{{ headers }}]), 66 {% endif %} 67 {% if includes %} 68 includes = [{{ includes }}], 69 {% endif %} 70 {% if defines %} 71 defines = [{{ defines }}], 72 {% endif %} 73 {% if linkopts %} 74 linkopts = [{{ linkopts }}], 75 {% endif %} 76 visibility = ["//visibility:public"], 77 {% if libs %} 78 deps = [ 79 {% for lib in libs %} 80 ":{{ lib }}_precompiled", 81 {% endfor %} 82 ], 83 {% endif %} 84 ) 85 86 """) 87 88 cpp_info = dependency.cpp_info.aggregated_components() 89 90 if not cpp_info.libs and not cpp_info.includedirs: 91 return None 92 93 headers = [] 94 includes = [] 95 96 for path in cpp_info.includedirs: 97 headers.append('"{}/**"'.format(path)) 98 includes.append('"{}"'.format(path)) 99 100 headers = ', '.join(headers) 101 includes = ', '.join(includes) 102 103 defines = ('"{}"'.format(define.replace('"', "'")) 104 for define in cpp_info.defines) 105 defines = ', '.join(defines) 106 107 linkopts = [] 108 for linkopt in cpp_info.system_libs: 109 linkopts.append('"-l{}"'.format(linkopt)) 110 linkopts = ', '.join(linkopts) 111 112 context = { 113 "name": dependency.ref.name, 114 "libs": cpp_info.libs, 115 "libdir": cpp_info.libdirs[0], 116 "headers": headers, 117 "includes": includes, 118 "defines": defines, 119 "linkopts": linkopts 120 } 121 122 content = Template(template).render(**context) 123 return content 124 125 def _create_new_local_repository(self, dependency, dependency_buildfile_name): 126 snippet = textwrap.dedent(""" 127 native.new_local_repository( 128 name="{}", 129 path="{}", 130 build_file="{}", 131 ) 132 """).format( 133 dependency.ref.name, 134 dependency.package_folder, 135 dependency_buildfile_name 136 ) 137 138 return snippet 139 140 def _get_main_buildfile_content(self, local_repositories): 141 template = textwrap.dedent(""" 142 def load_conan_dependencies(): 143 {} 144 """) 145 146 if local_repositories: 147 function_content = "\n".join(local_repositories) 148 function_content = ' '.join(line for line in function_content.splitlines(True)) 149 else: 150 function_content = ' pass' 151 152 content = template.format(function_content) 153 154 return content 155 156 def _save_main_buildfiles(self, content): 157 # A BUILD file must exist, even if it's empty, in order for bazel 158 # to detect it as a bazel package and allow to load the .bzl files 159 save("conandeps/BUILD", "") 160 161 save("conandeps/dependencies.bzl", content) 162