-
Notifications
You must be signed in to change notification settings - Fork 993
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Bazel][1.x][Part 1]: Bazel + BazelToolchain refactor (#14958)
* BazelDeps refactor * BazelToolchain refactor * bazel_layout refactor * Bazel helper refactor * Reverted BazelDeps * Applying suggestions * Applying suggestions * removed Conan 2 imports * Testing BazelToolchain and bazel_layout * Fixed tests on macos * Skipping windows * reordering imports * Building all the targets by default * 1.x conftest tools * test refactored * Keeping baward compatibility. Safer command runner. Fixed Windows rc paths * Better comment * Removed configure from templates. Useless * removed strip attr and fastbuild as default value * removed strip attr * Added validate step * Validate test * Keeping backward-compatibility. Added deprecated warnings * Keeping original bazel_layout. Applied suggestions
- Loading branch information
1 parent
e992ec6
commit 49bc8e5
Showing
11 changed files
with
490 additions
and
244 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,84 @@ | ||
from conan.tools.files.files import load_toolchain_args | ||
import os | ||
import platform | ||
|
||
from conan.tools.google import BazelToolchain | ||
|
||
|
||
class Bazel(object): | ||
|
||
def __init__(self, conanfile, namespace=None): | ||
self._conanfile = conanfile | ||
self._namespace = namespace | ||
self._get_bazel_project_configuration() | ||
# TODO: Remove namespace in Conan 2.x | ||
if namespace: | ||
self._conanfile.output.warning("In Bazel() call, namespace param has been " | ||
"deprecated as it's not used anymore.") | ||
|
||
def configure(self, args=None): | ||
# TODO: Remove in Conan 2.x. Keeping it backward compatible | ||
self._conanfile.output.warning("Bazel.configure() function has been deprecated." | ||
" Removing in Conan 2.x.") | ||
pass | ||
|
||
def build(self, args=None, label=None): | ||
# TODO: Change the directory where bazel builds the project (by default, /var/tmp/_bazel_<username> ) | ||
|
||
bazelrc_path = '--bazelrc={}'.format(self._bazelrc_path) if self._bazelrc_path else '' | ||
bazel_config = " ".join(['--config={}'.format(conf) for conf in self._bazel_config]) | ||
|
||
# arch = self._conanfile.settings.get_safe("arch") | ||
# cpu = { | ||
# "armv8": "arm64", | ||
# "x86_64": "" | ||
# }.get(arch, arch) | ||
# | ||
# command = 'bazel {} build --sandbox_debug --subcommands=pretty_print --cpu={} {} {}'.format( | ||
# bazelrc_path, | ||
# cpu, | ||
# bazel_config, | ||
# label | ||
# ) | ||
command = 'bazel {} build {} {}'.format( | ||
bazelrc_path, | ||
bazel_config, | ||
label | ||
) | ||
|
||
self._conanfile.run(command) | ||
|
||
def _get_bazel_project_configuration(self): | ||
toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder, | ||
namespace=self._namespace) | ||
configs = toolchain_file_content.get("bazel_configs") | ||
self._bazel_config = configs.split(",") if configs else [] | ||
self._bazelrc_path = toolchain_file_content.get("bazelrc_path") | ||
def _safe_run_command(self, command): | ||
""" | ||
Windows is having problems for stopping bazel processes, so it ends up locking | ||
some files if something goes wrong. Better to shut down the Bazel server after running | ||
each command. | ||
""" | ||
try: | ||
self._conanfile.run(command) | ||
finally: | ||
if platform.system() == "Windows": | ||
self._conanfile.run("bazel shutdown") | ||
|
||
def build(self, args=None, label=None, target="//..."): | ||
""" | ||
Runs "bazel <rcpaths> build <configs> <args> <targets>" | ||
:param label: DEPRECATED: It'll disappear in Conan 2.x. It is the target label | ||
:param target: It is the target label | ||
:param args: list of extra arguments | ||
:return: | ||
""" | ||
# TODO: Remove in Conan 2.x. Superseded by target | ||
if label: | ||
self._conanfile.output.warning("In Bazel.build() call, label param has been deprecated." | ||
" Migrating to target.") | ||
target = label | ||
# Use BazelToolchain generated file if exists | ||
conan_bazelrc = os.path.join(self._conanfile.generators_folder, BazelToolchain.bazelrc_name) | ||
use_conan_config = os.path.exists(conan_bazelrc) | ||
bazelrc_paths = [] | ||
bazelrc_configs = [] | ||
if use_conan_config: | ||
bazelrc_paths.append(conan_bazelrc) | ||
bazelrc_configs.append(BazelToolchain.bazelrc_config) | ||
# User bazelrc paths have more prio than Conan one | ||
# See more info in https://bazel.build/run/bazelrc | ||
# TODO: Legacy Bazel allowed only one value. Remove for Conan 2.x and check list-type. | ||
rc_paths = self._conanfile.conf.get("tools.google.bazel:bazelrc_path", default=[]) | ||
rc_paths = [rc_paths.strip()] if isinstance(rc_paths, str) else rc_paths | ||
bazelrc_paths.extend(rc_paths) | ||
command = "bazel" | ||
for rc in bazelrc_paths: | ||
command += f" --bazelrc={rc}" | ||
command += " build" | ||
# TODO: Legacy Bazel allowed only one value or several ones separate by commas. | ||
# Remove for Conan 2.x and check list-type. | ||
configs = self._conanfile.conf.get("tools.google.bazel:configs", default=[]) | ||
configs = [c.strip() for c in configs.split(",")] if isinstance(configs, str) else configs | ||
bazelrc_configs.extend(configs) | ||
for config in bazelrc_configs: | ||
command += f" --config={config}" | ||
if args: | ||
command += " ".join(f" {arg}" for arg in args) | ||
command += f" {target}" | ||
self._safe_run_command(command) | ||
|
||
def test(self, target=None): | ||
""" | ||
Runs "bazel test <target>" | ||
""" | ||
if self._conanfile.conf.get("tools.build:skip_test", check_type=bool) or target is None: | ||
return | ||
self._safe_run_command(f'bazel test {target}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,16 @@ | ||
import os | ||
|
||
|
||
def bazel_layout(conanfile, generator=None, src_folder="."): | ||
"""The layout for bazel is very limited, it builds in the root folder even specifying | ||
"bazel --output_base=xxx" in the other hand I don't know how to inject a custom path so | ||
the build can load the dependencies.bzl from the BazelDeps""" | ||
conanfile.folders.build = "" | ||
conanfile.folders.generators = "" | ||
# used in test package for example, to know where the binaries are (editables not supported yet) | ||
conanfile.cpp.build.bindirs = ["bazel-bin"] | ||
conanfile.cpp.build.libdirs = ["bazel-bin"] | ||
def bazel_layout(conanfile, src_folder="."): | ||
"""Bazel layout is so limited. It does not allow to create its special symlinks in other | ||
folder. See more information in https://bazel.build/remote/output-directories""" | ||
subproject = conanfile.folders.subproject | ||
conanfile.folders.source = src_folder if not subproject else os.path.join(subproject, src_folder) | ||
conanfile.folders.build = "." # Bazel always build the whole project in the root folder | ||
# FIXME: Keeping backward-compatibility. Defaulting to "conan" in Conan 2.x. | ||
conanfile.output.warning("In bazel_layout() call, generators folder changes its default value " | ||
"from './' to './conan/' in Conan 2.x") | ||
conanfile.folders.generators = "." | ||
# FIXME: used in test package for example, to know where the binaries are (editables not supported yet)? | ||
conanfile.cpp.build.bindirs = [os.path.join(conanfile.folders.build, "bazel-bin")] | ||
conanfile.cpp.build.libdirs = [os.path.join(conanfile.folders.build, "bazel-bin")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,158 @@ | ||
import textwrap | ||
|
||
from jinja2 import Template | ||
|
||
from conan.tools._check_build_profile import check_using_build_profile | ||
from conan.tools.files.files import save_toolchain_args | ||
from conan.tools._compilers import cppstd_flag | ||
from conan.tools.apple import to_apple_arch, is_apple_os | ||
from conan.tools.build.cross_building import cross_building | ||
from conan.tools.files import save | ||
|
||
|
||
def _get_cpu_name(conanfile): | ||
host_os = conanfile.settings.get_safe('os').lower() | ||
host_arch = conanfile.settings.get_safe('arch') | ||
if is_apple_os(conanfile): | ||
host_os = "darwin" if host_os == "macos" else host_os | ||
host_arch = to_apple_arch(conanfile) | ||
# FIXME: Probably it's going to fail, but let's try it because it normally follows this syntax | ||
return f"{host_os}_{host_arch}" | ||
|
||
|
||
class BazelToolchain(object): | ||
# FIXME: In the future, it could be BazelPlatform instead? Check https://bazel.build/concepts/platforms | ||
class BazelToolchain: | ||
""" | ||
Creates a simple conan_bzl.rc file which defines a conan-config configuration with all the | ||
attributes defined by the consumer. Bear in mind that this is not a complete toolchain, it | ||
only fills some common CLI attributes and save them in a *.rc file. | ||
Important: Maybe, this toolchain should create a new Conan platform with the user | ||
constraints, but it's not the goal for now as Bazel has tons of platforms and toolchains | ||
already available in its bazel_tools repo. For now, it only admits a list of platforms defined | ||
by the user. | ||
More information related: | ||
* Toolchains: https://bazel.build/extending/toolchains (deprecated) | ||
* Platforms: https://bazel.build/concepts/platforms (new default since Bazel 7.x) | ||
* Migrating to platforms: https://bazel.build/concepts/platforms | ||
* Issue related: https://github.com/bazelbuild/bazel/issues/6516 | ||
Others: | ||
CROOSTOOL: https://github.com/bazelbuild/bazel/blob/cb0fb033bad2a73e0457f206afb87e195be93df2/tools/cpp/CROSSTOOL | ||
Cross-compiling with Bazel: https://ltekieli.com/cross-compiling-with-bazel/ | ||
bazelrc files: https://bazel.build/run/bazelrc | ||
CLI options: https://bazel.build/reference/command-line-reference | ||
User manual: https://bazel.build/docs/user-manual | ||
""" | ||
|
||
bazelrc_name = "conan_bzl.rc" | ||
bazelrc_config = "conan-config" | ||
bazelrc_template = textwrap.dedent(""" | ||
# Automatic bazelrc file created by Conan | ||
{% if copt %}build:conan-config {{copt}}{% endif %} | ||
{% if conlyopt %}build:conan-config {{conlyopt}}{% endif %} | ||
{% if cxxopt %}build:conan-config {{cxxopt}}{% endif %} | ||
{% if linkopt %}build:conan-config {{linkopt}}{% endif %} | ||
{% if force_pic %}build:conan-config --force_pic={{force_pic}}{% endif %} | ||
{% if dynamic_mode %}build:conan-config --dynamic_mode={{dynamic_mode}}{% endif %} | ||
{% if compilation_mode %}build:conan-config --compilation_mode={{compilation_mode}}{% endif %} | ||
{% if compiler %}build:conan-config --compiler={{compiler}}{% endif %} | ||
{% if cpu %}build:conan-config --cpu={{cpu}}{% endif %} | ||
{% if crosstool_top %}build:conan-config --crosstool_top={{crosstool_top}}{% endif %}""") | ||
|
||
def __init__(self, conanfile, namespace=None): | ||
self._conanfile = conanfile | ||
self._namespace = namespace | ||
# TODO: Remove namespace and check_using_build_profile in Conan 2.x | ||
if namespace: | ||
self._conanfile.output.warning("In BazelToolchain() call, namespace param has been " | ||
"deprecated as it's not used anymore.") | ||
check_using_build_profile(self._conanfile) | ||
|
||
# Flags | ||
# TODO: Should we read the buildenv to get flags? | ||
self.extra_cxxflags = [] | ||
self.extra_cflags = [] | ||
self.extra_ldflags = [] | ||
self.extra_defines = [] | ||
|
||
# Bazel build parameters | ||
shared = self._conanfile.options.get_safe("shared") | ||
fpic = self._conanfile.options.get_safe("fPIC") | ||
self.force_pic = fpic if (not shared and fpic is not None) else None | ||
# FIXME: Keeping this option but it's not working as expected. It's not creating the shared | ||
# libraries at all. | ||
self.dynamic_mode = "fully" if shared else "off" | ||
self.cppstd = cppstd_flag(self._conanfile.settings) | ||
self.copt = [] | ||
self.conlyopt = [] | ||
self.cxxopt = [] | ||
self.linkopt = [] | ||
self.compilation_mode = {'Release': 'opt', 'Debug': 'dbg'}.get( | ||
self._conanfile.settings.get_safe("build_type") | ||
) | ||
# Be aware that this parameter does not admit a compiler absolute path | ||
# If you want to add it, you will have to use a specific Bazel toolchain | ||
self.compiler = None | ||
# cpu is the target architecture, and it's a bit tricky. If it's not a cross-compilation, | ||
# let Bazel guess it. | ||
self.cpu = None | ||
# TODO: cross-compilation process is so powerless. Needs to use the new platforms. | ||
if cross_building(self._conanfile): | ||
# Bazel is using those toolchains/platforms by default. | ||
# It's better to let it configure the project in that case | ||
self.cpu = _get_cpu_name(conanfile) | ||
# This is itself a toolchain but just in case | ||
self.crosstool_top = None | ||
# TODO: Have a look at https://bazel.build/reference/be/make-variables | ||
# FIXME: Missing host_xxxx options. When are they needed? Cross-compilation? | ||
|
||
@staticmethod | ||
def _filter_list_empty_fields(v): | ||
return list(filter(bool, v)) | ||
|
||
@property | ||
def cxxflags(self): | ||
ret = [self.cppstd] | ||
conf_flags = self._conanfile.conf.get("tools.build:cxxflags", default=[], check_type=list) | ||
ret = ret + self.extra_cxxflags + conf_flags | ||
return self._filter_list_empty_fields(ret) | ||
|
||
@property | ||
def cflags(self): | ||
conf_flags = self._conanfile.conf.get("tools.build:cflags", default=[], check_type=list) | ||
ret = self.extra_cflags + conf_flags | ||
return self._filter_list_empty_fields(ret) | ||
|
||
@property | ||
def ldflags(self): | ||
conf_flags = self._conanfile.conf.get("tools.build:sharedlinkflags", default=[], | ||
check_type=list) | ||
conf_flags.extend(self._conanfile.conf.get("tools.build:exelinkflags", default=[], | ||
check_type=list)) | ||
linker_scripts = self._conanfile.conf.get("tools.build:linker_scripts", default=[], check_type=list) | ||
conf_flags.extend(["-T'" + linker_script + "'" for linker_script in linker_scripts]) | ||
ret = self.extra_ldflags + conf_flags | ||
return self._filter_list_empty_fields(ret) | ||
|
||
def _context(self): | ||
return { | ||
"copt": " ".join(f"--copt={flag}" for flag in self.copt), | ||
"conlyopt": " ".join(f"--conlyopt={flag}" for flag in (self.conlyopt + self.cflags)), | ||
"cxxopt": " ".join(f"--cxxopt={flag}" for flag in (self.cxxopt + self.cxxflags)), | ||
"linkopt": " ".join(f"--linkopt={flag}" for flag in (self.linkopt + self.ldflags)), | ||
"force_pic": self.force_pic, | ||
"dynamic_mode": self.dynamic_mode, | ||
"compilation_mode": self.compilation_mode, | ||
"compiler": self.compiler, | ||
"cpu": self.cpu, | ||
"crosstool_top": self.crosstool_top, | ||
} | ||
|
||
@property | ||
def _content(self): | ||
context = self._context() | ||
content = Template(self.bazelrc_template).render(context) | ||
return content | ||
|
||
def generate(self): | ||
content = {} | ||
configs = ",".join(self._conanfile.conf.get("tools.google.bazel:configs", | ||
default=[], | ||
check_type=list)) | ||
if configs: | ||
content["bazel_configs"] = configs | ||
|
||
bazelrc = self._conanfile.conf.get("tools.google.bazel:bazelrc_path") | ||
if bazelrc: | ||
content["bazelrc_path"] = bazelrc | ||
|
||
save_toolchain_args(content, namespace=self._namespace) | ||
# check_duplicated_generator(self, self._conanfile) # uncomment for Conan 2.x | ||
save(self._conanfile, BazelToolchain.bazelrc_name, self._content) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.