Skip to content

Instantly share code, notes, and snippets.

@nicolov
Last active March 7, 2019 19:01
Show Gist options
  • Save nicolov/1eef555aa488c5194f6d16fa3b800d07 to your computer and use it in GitHub Desktop.
Save nicolov/1eef555aa488c5194f6d16fa3b800d07 to your computer and use it in GitHub Desktop.
Bazel to CMake for CLion integration

Bazel to CMake for CLion

This script generates a skeleton CMakeLists.txt from a Bazel project. The resulting file can be loaded as a project in CLion (build/test features won't work).

Use like:

./bazel_to_cmake.py //tensorflow:libtensorflow_cc.so

Excerpt from the generated file:

# From target //tensorflow/core:decode_proto_ops_op_lib
add_library(tensorflow_core_decode_proto_ops_op_lib
  tensorflow/core/ops/decode_proto_ops.cc)
target_include_directories(tensorflow_core_decode_proto_ops_op_lib PUBLIC
  bazel-tensorflow-1.13.1/external/eigen_archive
  bazel-out/k8-opt/genfiles/external/eigen_archive
  bazel-out/k8-opt/bin/external/eigen_archive

License: MIT

#!/usr/bin/env python
"""
Generate a skeleton CMakeLists.txt from a Bazel project. The CMakeLists.txt
can be used in IDEs like CLion.
Use like:
./bazel_to_cmake.py //tensorflow:libtensorflow_cc.so
For best results, build the codebase first, so that CLion can find the
generated files.
License: MIT
"""
from __future__ import print_function
import argparse
import json
import os
import subprocess
# Hardcode the contents of the .bzl file here, so the script is easier to share.
BZL_CONTENTS = """
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
load(
"@bazel_tools//tools/build_defs/cc:action_names.bzl",
"CPP_COMPILE_ACTION_NAME",
"CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME",
"C_COMPILE_ACTION_NAME",
)
_cpp_extensions = [
"cc",
"cpp",
"cxx",
]
def _cmake_skeleton_aspect_impl(target, ctx):
if ctx.rule.kind not in [
"cc_library",
"cc_binary",
"cc_test",
"cc_inc_library",
"cc_proto_library",
]:
return []
sources_list = [f.path for src in ctx.rule.attr.srcs for f in src.files]
target_name = str(target.label)
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
compile_variables = cc_common.create_compile_variables(
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
user_compile_flags = ctx.fragments.cpp.cxxopts +
ctx.fragments.cpp.copts,
add_legacy_cxx_options = True,
)
compiler_options = cc_common.get_memory_inefficient_command_line(
feature_configuration = feature_configuration,
action_name = CPP_COMPILE_ACTION_NAME,
variables = compile_variables,
)
data = struct(
label = target_name,
srcs = sources_list,
system_include_directories = target.cc.system_include_directories,
quote_include_directories = target.cc.quote_include_directories,
include_directories = target.cc.include_directories,
)
json_file = ctx.new_file('%s.ideinfo.json' % (target.label.name))
ctx.file_action(json_file, data.to_json())
# Collect json files for dependencies too.
all_ide_json_files = depset([json_file])
for dep in ctx.rule.attr.deps:
if OutputGroupInfo not in dep:
continue
all_ide_json_files += dep[OutputGroupInfo].ide_json_files
return [
OutputGroupInfo(ide_json_files = all_ide_json_files)
]
cmake_skeleton_aspect = aspect(
attr_aspects = ["deps"],
attrs = {
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
},
fragments = ["cpp"],
implementation = _cmake_skeleton_aspect_impl,
)
"""
def main():
parser = argparse.ArgumentParser()
parser.add_argument("target", nargs="*", default=["//..."])
parser.add_argument("--convenience-symlink", default="bazel-")
args = parser.parse_args()
bin_path = subprocess.check_output(["bazel", "info", "bazel-bin"]).strip()
# We could use bazel info execution_root instead, but that creates
# ugly, very long paths.
# execroot_path = subprocess.check_output(['bazel', 'info', 'execution_root']).strip()
execroot_path = args.convenience_symlink + os.path.basename(os.getcwd())
if not os.path.exists(execroot_path):
print(
"Convenience symlink `{}` not found. Check your --convenience-symlink arg".format(
execroot_path
)
)
return 1
find_filter = ["find", "-L", bin_path, "-name", "*.ideinfo.json"]
# Clean up old files
if os.path.exists(bin_path):
subprocess.check_output(find_filter + ["-delete"])
bzl_file_path = "json_ide_aspect.bzl"
with open(bzl_file_path, "w") as f:
f.write(BZL_CONTENTS)
# Generate new jsons with the aspects.
subprocess.check_call(
[
"bazel",
"build",
"--aspects={}%cmake_skeleton_aspect".format(bzl_file_path),
"--noshow_progress",
"--noshow_loading_progress",
"--show_result=0",
"--output_groups=ide_json_files",
] + args.target
)
json_paths = subprocess.check_output(find_filter).splitlines()
def add_execroot_if_needed(p):
if p.startswith("external/"):
return os.path.join(execroot_path, p)
else:
return p
contents = "cmake_minimum_required(VERSION 2.8)\n\n"
for jp in json_paths:
data = json.load(open(jp))
fixed_srcs = [add_execroot_if_needed(p) for p in data["srcs"]]
# Filter out non-existing genfiles to avoid errors during configuration
# if the project is not built yet.
fixed_srcs = [p for p in fixed_srcs if os.path.exists(p)]
# Also filter out headers
fixed_srcs = [p for p in fixed_srcs if not p.endswith(".h")]
if not fixed_srcs:
continue
all_include_dirs = (
data["system_include_directories"]
+ data["quote_include_directories"]
+ data["include_directories"]
)
fixed_include_dirs = [add_execroot_if_needed(p) for p in all_include_dirs]
fixed_library_name = (
data["label"]
.replace("//", "")
.replace("/", "_")
.replace(":", "_")
.replace("@", "external_")
)
contents += """
# From target {target}
add_library({name}
{srcs})
target_include_directories({name} PUBLIC
{include_dirs}
)
""".format(
target=data["label"],
name=fixed_library_name,
srcs="\n ".join(fixed_srcs),
include_dirs="\n ".join(fixed_include_dirs),
)
with open("CMakeLists.txt", "w") as f:
f.write(contents)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment