Skip to main content
Glama
conanfile.py8.4 kB
# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. import collections import os import re from conans import ConanFile from conans.model import Generator def _rel_to_root(rootpath, path): """Make the given path relative to the given root path.""" return os.path.relpath(path, rootpath) def _map_rel_to_root(rootpath, paths): """Make all given paths relative to the given root path.""" return [_rel_to_root(rootpath, p) for p in paths] _LibraryFiles = collections.namedtuple("_LibraryFiles", ["static", "shared"]) def _find_libs(rootpath, lib_paths, lib_names): """Collect static and shared library files for the given library names. Searches for library files under the given library search paths. Retains order as defined in the given library names in case it matters for linking order. """ result = collections.OrderedDict( (name, _LibraryFiles([], [])) for name in lib_names ) name_pattern = "(?P<name>{})".format("|".join(lib_names)) ext_pattern = ( "(?:(?P<static>a|lib)|(?P<shared>so(?:\\.\\d+(?:\\.\\d+)?)?|dylib|dll))" ) regex = re.compile("lib{}.{}".format(name_pattern, ext_pattern)) files = ( os.path.join(libdir, filepath) for libdir in lib_paths for filepath in os.listdir(os.path.join(rootpath, libdir)) if os.path.isfile(os.path.join(rootpath, libdir, filepath)) ) for filepath in files: m = regex.match(os.path.basename(filepath)) if m: name = m.group("name") # TODO[AH] Can we distinguish static and static-pic libs? if m.group("static"): result[name].static.append(filepath) elif m.group("shared"): result[name].shared.append(filepath) return result class _Requirement(collections.namedtuple("_Requirement", ["package", "component"])): """Represents a Conan requirement. Requirements can be * a relative reference to a component of the current package - `package` None, `component` set, * an absolute reference to a package - `package` set, `component` None, or * an absolute reference to another package's component - `package` set, `component` set. """ @classmethod def parse(cls, requirement): """Parse a Conan requirement. These take the form * `somecomponent` for a relative component reference, * `somepackage::somepackage` for an absolute package reference, or * `somepackage::somecomponent` for an absolute component reference. """ if "::" in requirement: package, component = requirement.split("::", 1) if package == component: return cls(package, None) else: return cls(package, component) else: return cls(None, requirement) def to_name(self, current_package): """Generate the Buck2 target name for a requirement.""" package = self.package or current_package component = self.component or package return "_component_{}_{}".format(package, component) def to_label(self, current_package): """Generate the Buck2 label for a requirement. Relative requirements use the given current package's name to refer directly to the component target. Absolute requirements refer to the package target or sub-target. """ if self.package and self.component: return ":{}[{}]".format(self.package, self.component) elif self.package: return ":{}".format(self.package) else: return ":{}".format(self.to_name(current_package)) class _BucklerDepCppComponent(object): """A Conan package component or the package itself if it has no components. You can learn more about Conan package components [here][conan-components]. [conan-components]: https://docs.conan.io/en/1.53/creating_packages/package_information.html#using-components """ def __init__(self, package_name, component_info): self.package_name = package_name self.component_name = component_info.name self.rootpath = component_info.rootpath rootpath = self.rootpath self.defines = component_info.defines self.cflags = component_info.cflags self.cppflags = component_info.cppflags self.include_paths = _map_rel_to_root(rootpath, component_info.include_paths) lib_paths = _map_rel_to_root(rootpath, component_info.lib_paths) self.libs = _find_libs(rootpath, lib_paths, component_info.libs) self.system_libs = component_info.system_libs self.requires = [_Requirement.parse(req) for req in component_info.requires] def generate(self): """Generate Buck2 target definitions for the component.""" name = _Requirement(None, self.component_name).to_name(self.package_name) deps = [req.to_label(self.package_name) for req in self.requires] return """\ conan_component( name = {name!r}, defines = {defines!r}, cflags = {cflags!r}, cppflags = {cppflags!r}, include_paths = {include_paths!r}, libs = {libs!r}, static_libs = {static_libs!r}, shared_libs = {shared_libs!r}, system_libs = {system_libs!r}, deps = {deps!r}, package = {package!r}, ) """.format( name=name, defines=self.defines, cflags=self.cflags, cppflags=self.cppflags, include_paths=self.include_paths, libs=list(self.libs.keys()), static_libs={ name: sorted(libs.static) for name, libs in self.libs.items() if libs.static }, shared_libs={ name: sorted(libs.shared) for name, libs in self.libs.items() if libs.shared }, system_libs=self.system_libs, deps=deps, package=":_package_" + self.package_name, ) class _BucklerDepCpp(object): """A Conan package.""" def __init__(self, dep_name, dep_cpp_info, public=False): self.name = dep_name self.public = public self.rootpath = dep_cpp_info.rootpath if dep_cpp_info.components: self.components = collections.OrderedDict( (name, _BucklerDepCppComponent(dep_name, component_info)) for (name, component_info) in dep_cpp_info.components.items() ) else: self.components = collections.OrderedDict( [(dep_name, _BucklerDepCppComponent(dep_name, dep_cpp_info))] ) def generate(self): """Generate Buck2 target definitions for the package and its components.""" result = """\ conan_dep( name = {name!r}, components = {components!r}, visibility = {visibility!r}, ) """.format( name=self.name, components={ name: _Requirement(None, name).to_label(self.name) for name in self.components.keys() }, visibility=["PUBLIC"] if self.public else [], ) for component in self.components.values(): result += component.generate() return result class BucklerGenerator(Generator): @property def filename(self): return "conan-imports.bzl" @property def content(self): result = "" for dep_name, dep_cpp_info in self.deps_build_info.dependencies: direct_dep = dep_name in self.conanfile.requires buckler_dep = _BucklerDepCpp(dep_name, dep_cpp_info, public=direct_dep) result += buckler_dep.generate() return result class Buckler(ConanFile): name = "buckler" version = "0.1" description = """\ Buckler - Conan extension for Buck2 This package provides a - [Generator][generator] to import Conan built packages into Buck2. [generator]: https://docs.conan.io/en/latest/reference/generators.html#generators-reference """ url = "https://github.com/facebookincubator/buck2" license = "Apache-2.0"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server