Fix using Bazel-built libraries by cmake_external and configure_make (#249)

Fix #232 - enable usage of Bazel-built libraries as dependencies of cmake_external and configure_make.

- fix logic of reading library data described by C/C++ Bazel interface (as CcInfo.linking_context appeared)
- symlink transitive include directories under $EXT_BUILD_DEPS
- gather all possible transitive include directories to pass to CMake, pass transitive include directories to CMAKE_PREFIX_PATH so that the transitive include directories were scanned by CMake
- fix logic of passing transitive libraries into CPPFLAGS (configure_make)
- add test with the chain: Bazel lib -> CMake lib -> Bazel cc_test
- add synthetic configure-make test with the chain: Bazel lib -> configure_make lib -> Bazel cc_test
- also notice, that passed include directories from Bazel C/C++ Sandwich interface are not always existing; for now, make symlinking code resistant to that

see https://www.gnu.org/software/autoconf/manual/autoconf-2.63/html_node/Preset-Output-Variables.html
This commit is contained in:
irengrig 2019-05-09 10:35:00 +02:00 committed by GitHub
parent 6b04da2262
commit e36f3cee8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 278 additions and 80 deletions

View File

@ -22,9 +22,11 @@ test_suite(
name = "tests",
tests = tests + [
"//cmake_synthetic:test_libs",
"//cmake_with_bazel_transitive:test",
"//configure_gnuplot:configure_libgd_tests",
"//cmake_hello_world_lib/shared:test_libhello",
"//cmake_hello_world_lib/binary:test_binary",
"//configure_with_bazel_transitive:test",
],
)

View File

@ -1,11 +1,5 @@
load("@rules_foreign_cc//tools/build_defs:cmake.bzl", "cmake_external")
filegroup(
name = "b_srcs",
srcs = glob(["libb/**"]),
visibility = ["//visibility:public"],
)
generate_crosstool = select({
"@bazel_tools//src/conditions:windows": True,
"//conditions:default": False,
@ -41,7 +35,7 @@ cc_test(
],
tags = ["windows"],
deps = [
":liba",
# liba should come from transitive dependencies
":libb",
],
)

View File

@ -3,3 +3,11 @@ filegroup(
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
cc_library(
name = "lib_a_bazel",
srcs = ["src/liba.cpp"],
hdrs = ["src/liba.h"],
includes = ["src"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,35 @@
load("@rules_foreign_cc//tools/build_defs:cmake.bzl", "cmake_external")
# Example of the cmake_external target built with Bazel-built dependency
cmake_external(
name = "cmake_libb",
cache_entries = {
# as currently we copy all libraries, built with Bazel, into $EXT_BUILD_DEPS/lib
# and the headers into $EXT_BUILD_DEPS/include
"LIBA_DIR": "$EXT_BUILD_DEPS",
# CMake's find_package wants to find cmake config for liba,
# which we do not have -> disable search
"CMAKE_DISABLE_FIND_PACKAGE_LIBA": "True",
},
cmake_options = ["-GNinja"],
lib_source = "//cmake_with_bazel_transitive/libb:b_srcs",
make_commands = [
"ninja",
"ninja install",
],
static_libraries = ["libb.a"],
# This library is with public visibility, we can reuse it here.
deps = ["//cmake_synthetic/liba:lib_a_bazel"],
)
# And cc_test built with cmake_external dependency and transitive Bazel dependency
cc_test(
name = "test",
srcs = [
"test_libb.cpp",
],
deps = [
# liba should come from transitive dependencies
":cmake_libb",
],
)

View File

@ -0,0 +1,5 @@
filegroup(
name = "b_srcs",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 2.8.4)
project(libb)
add_subdirectory(src)

View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 2.8.4)
set(LIBB_SRC libb.cpp libb.h)
add_library(libb_static STATIC ${LIBB_SRC})
set_target_properties(libb_static PROPERTIES OUTPUT_NAME "libb")
IF (WIN32)
set_target_properties(libb_static PROPERTIES ARCHIVE_OUTPUT_NAME "libb")
ELSE()
set_target_properties(libb_static PROPERTIES ARCHIVE_OUTPUT_NAME "b")
ENDIF()
find_package(LIBA NAMES alib liba libliba)
# LIBA_INCLUDE_DIRS is not set, so giving the path relative to liba_config.cmake
# would be happy to improve that
target_link_libraries(libb_static PUBLIC ${LIBA_DIR}/lib/liba.a)
target_include_directories(libb_static PUBLIC ${LIBA_DIR}/include)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
install(TARGETS libb_static ARCHIVE DESTINATION lib)
install(FILES libb.h DESTINATION include)

View File

@ -0,0 +1,5 @@
#include "libb.h"
std::string hello_libb(void) {
return hello_liba() + " Hello from LIBB!";
}

View File

@ -0,0 +1,10 @@
#ifndef LIBB_H_
#define LIBB_H_ (1)
#include <stdio.h>
#include <string>
#include "liba.h"
std::string hello_libb(void);
#endif

View File

@ -0,0 +1,13 @@
#include "libb.h"
#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
std:: string result = hello_libb();
if (result != "Hello from LIBA! Hello from LIBB!") {
throw std::runtime_error("Wrong result: " + result);
}
std::cout << "Everything's fine!";
}

View File

@ -8,6 +8,9 @@ configure_make(
configure_make(
name = "libpng",
configure_options = [
"--with-zlib=\"$$EXT_BUILD_DEPS$$/libz\"",
],
lib_source = "@libpng//:all",
out_include_dir = "include/libpng16",
static_libraries = ["libpng16.a"],
@ -16,13 +19,13 @@ configure_make(
configure_make(
name = "freetype",
lib_source = "@freetype//:all",
out_include_dir = "include/freetype2",
static_libraries = ["libfreetype.a"],
configure_options = [
"--with-png=\"$$EXT_BUILD_DEPS$$/libpng\"",
"--with-zlib=\"$$EXT_BUILD_DEPS$$/libz\"",
],
lib_source = "@freetype//:all",
out_include_dir = "include/freetype2",
static_libraries = ["libfreetype.a"],
deps = [
":libpng",
":libz",

View File

@ -0,0 +1,25 @@
load("@rules_foreign_cc//tools/build_defs:configure.bzl", "configure_make")
cc_library(
name = "built_with_bazel",
srcs = ["builtWithBazel.c"],
hdrs = ["builtWithBazel.h"],
includes = ["."],
)
configure_make(
name = "simple",
configure_in_place = True,
lib_source = "//configure_with_bazel_transitive/simple_lib:simple_srcs",
make_commands = [
"make simple",
"make install",
],
deps = [":built_with_bazel"],
)
cc_test(
name = "test",
srcs = ["testSimple.c"],
deps = [":simple"],
)

View File

@ -0,0 +1,5 @@
#include "builtWithBazel.h"
char* bazelSays(void) {
return "It's me, Bazel!";
}

View File

@ -0,0 +1,6 @@
#ifndef BUILT_WITH_BAZEL_H_
#define BUILT_WITH_BAZEL_H_ (1)
char* bazelSays(void);
#endif // BUILT_WITH_BAZEL_H_

View File

@ -0,0 +1,5 @@
filegroup(
name = "simple_srcs",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,7 @@
simple: ./src/simple.c
gcc $(CPPFLAGS) $(LDFLAGS) -o simple.a -c ./src/simple.c -I./include -I.
install:
mkdir -p simple
mkdir -p simple/lib
cp simple.a ./simple/lib/simple.a
cp -r ./include ./simple

View File

@ -0,0 +1,5 @@
ls "$EXT_BUILD_DEPS/lib"
echo "CPPFLAGS = $CPPFLAGS" > Makefile
echo "LDFLAGS = $LDFLAGS" >> Makefile
echo "BUILT_WITH_BAZEL = $EXT_BUILD_DEPS/lib/libbuilt_with_bazel.a" >> Makefile
cat Makefile.in >> Makefile

View File

@ -0,0 +1,6 @@
#ifndef SIMPLE_H_
#define SIMPLE_H_ (1)
void simpleFun(void);
#endif // SIMPLE_H_

View File

@ -0,0 +1,7 @@
#include <stdio.h>
#include "simple.h"
#include "builtWithBazel.h"
void simpleFun(void) {
printf("simpleFun: %s", bazelSays());
}

View File

@ -0,0 +1,9 @@
#include <stdio.h>
#include "builtWithBazel.h"
#include "simple.h"
int main(int argc, char **argv) {
printf("Call bazelSays() directly: %s\n", bazelSays());
simpleFun();
return 0;
}

View File

@ -65,19 +65,20 @@ def _create_configure_script(configureParameters):
define_install_prefix = "export INSTALL_PREFIX=\"" + _get_install_prefix(ctx) + "\"\n"
configure_script = create_cmake_script(
ctx.workspace_name,
workspace_name = ctx.workspace_name,
# as default, pass execution OS as target OS
os_name(ctx),
configureParameters.attrs.cmake_path,
tools,
flags,
"$$INSTALL_PREFIX$$",
root,
no_toolchain_file,
dict(ctx.attr.cache_entries),
dict(ctx.attr.env_vars),
ctx.attr.cmake_options,
is_debug_mode(ctx),
target_os = os_name(ctx),
cmake_path = configureParameters.attrs.cmake_path,
tools = tools,
flags = flags,
install_prefix = "$$INSTALL_PREFIX$$",
root = root,
no_toolchain_file = no_toolchain_file,
user_cache = dict(ctx.attr.cache_entries),
user_env = dict(ctx.attr.env_vars),
options = ctx.attr.cmake_options,
include_dirs = inputs.include_dirs,
is_debug_mode = is_debug_mode(ctx),
)
return define_install_prefix + configure_script

View File

@ -14,6 +14,7 @@ def create_cmake_script(
user_cache,
user_env,
options,
include_dirs = [],
is_debug_mode = True):
""" Constructs CMake script to be passed to cc_external_rule_impl.
Args:
@ -29,7 +30,7 @@ def create_cmake_script(
user_env - dictionary with user's values for CMake environment variables
options - other CMake options specified by user
"""
merged_prefix_path = _merge_prefix_path(user_cache)
merged_prefix_path = _merge_prefix_path(user_cache, include_dirs)
toolchain_dict = _fill_crossfile_from_toolchain(workspace_name, target_os, tools, flags)
params = None
@ -72,13 +73,14 @@ def wipe_empty_values(cache, user_cache):
cache.pop(key)
# From CMake documentation: ;-list of directories specifying installation prefixes to be searched...
def _merge_prefix_path(user_cache):
def _merge_prefix_path(user_cache, include_dirs):
user_prefix = user_cache.get("CMAKE_PREFIX_PATH")
values = ["$EXT_BUILD_DEPS"] + include_dirs
if user_prefix != None:
# remove it, it is gonna be merged specifically
user_cache.pop("CMAKE_PREFIX_PATH")
return "$EXT_BUILD_DEPS;" + user_prefix.strip("\"'")
return "$EXT_BUILD_DEPS"
values.append(user_prefix.strip("\"'"))
return ";".join(values)
_CMAKE_ENV_VARS_FOR_CROSSTOOL = {
"CC": struct(value = "CMAKE_C_COMPILER", replace = True),

View File

@ -41,19 +41,19 @@ def _create_configure_script(configureParameters):
define_install_prefix = "export INSTALL_PREFIX=\"" + _get_install_prefix(ctx) + "\"\n"
configure = create_configure_script(
ctx.workspace_name,
workspace_name = ctx.workspace_name,
# as default, pass execution OS as target OS
os_name(ctx),
tools,
flags,
root,
ctx.attr.configure_options,
dict(ctx.attr.configure_env_vars),
is_debug_mode(ctx),
ctx.attr.configure_command,
ctx.attr.deps,
inputs,
ctx.attr.configure_in_place,
target_os = os_name(ctx),
tools = tools,
flags = flags,
root = root,
user_options = ctx.attr.configure_options,
user_vars = dict(ctx.attr.configure_env_vars),
is_debug = is_debug_mode(ctx),
configure_command = ctx.attr.configure_command,
deps = ctx.attr.deps,
inputs = inputs,
configure_in_place = ctx.attr.configure_in_place,
)
return "\n".join([define_install_prefix, configure])

View File

@ -17,9 +17,11 @@ def create_configure_script(
vars = _get_configure_variables(tools, flags, user_vars)
deps_flags = _define_deps_flags(deps, inputs)
vars["LDFLAGS"] = vars["LDFLAGS"] + deps_flags["LDFLAGS"]
vars["CPPFLAGS"] = deps_flags["CPPFLAGS"]
# vars["SYSROOT"] = "$EXT_BUILD_DEPS"
vars["LDFLAGS"] = vars["LDFLAGS"] + deps_flags.libs
# -I flags should be put into preprocessor flags, CPPFLAGS
# https://www.gnu.org/software/autoconf/manual/autoconf-2.63/html_node/Preset-Output-Variables.html
vars["CPPFLAGS"] = deps_flags.flags
env_vars_string = " ".join(["{}=\"{}\"".format(key, _join_flags_list(workspace_name, vars[key])) for key in vars])
@ -57,14 +59,12 @@ def _define_deps_flags(deps, inputs):
lib_dirs += ["-L$$EXT_BUILD_ROOT$$/" + dir_]
include_dirs_set = {}
for dir_list in inputs.include_dirs:
for include_dir in dir_list:
for include_dir in inputs.include_dirs:
include_dirs_set[include_dir] = "-I$$EXT_BUILD_ROOT$$/" + include_dir
for header in inputs.headers:
include_dir = header.dirname
if not include_dirs_set.get(include_dir):
include_dirs_set[include_dir] = "-I$$EXT_BUILD_ROOT$$/" + include_dir
for header_list in inputs.headers:
for header in header_list:
include_dir = header.dirname
if not include_dirs_set.get(include_dir):
include_dirs_set[include_dir] = "-I$$EXT_BUILD_ROOT$$/" + include_dir
include_dirs = include_dirs_set.values()
# For the external libraries, we need to refer to the places where
@ -85,10 +85,10 @@ def _define_deps_flags(deps, inputs):
include_dirs += ["-I$$EXT_BUILD_DEPS$$/{}/{}".format(dir_name, artifact.include_dir_name)]
lib_dirs += ["-L$$EXT_BUILD_DEPS$$/{}/{}".format(dir_name, artifact.lib_dir_name)]
return {
"LDFLAGS": lib_dirs,
"CPPFLAGS": include_dirs,
}
return struct(
libs = lib_dirs,
flags = include_dirs,
)
# See https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
_CONFIGURE_FLAGS = {

View File

@ -449,10 +449,13 @@ def _symlink_contents_to_dir(dir_name, files_list):
paths_list = []
for file in files_list:
paths_list += [_file_path(file)]
paths_list += [_file_path(file).strip()]
for path in paths_list:
lines += ["##symlink_contents_to_dir## $$EXT_BUILD_ROOT$$/{} $$EXT_BUILD_DEPS$$/{}".format(path, dir_name)]
# Filter out empty subpaths
# (current directory may be passed as quote_includes, but we should not symlink it)
if path and path != ".":
lines += ["##symlink_contents_to_dir## $$EXT_BUILD_ROOT$$/{} $$EXT_BUILD_DEPS$$/{}".format(path, dir_name)]
return lines
@ -599,7 +602,11 @@ def get_foreign_cc_dep(dep):
# consider optimization here to do not iterate both collections
def _get_headers(compilation_info):
include_dirs = collections.uniq(compilation_info.system_includes.to_list())
# NB: current directory (".") is passed by quote_includes;
# ignore it by now, will be filtered by symlinking code.
include_dirs = collections.uniq(compilation_info.system_includes.to_list() +
compilation_info.includes.to_list() +
compilation_info.quote_includes.to_list())
headers = []
for header in compilation_info.headers:
path = header.path
@ -635,17 +642,18 @@ def _define_out_cc_info(ctx, attrs, inputs, outputs):
return cc_common.merge_cc_infos(cc_infos = [cc_info, inputs_info])
def _extract_link_params(cc_linking):
def _extract_libraries(library_to_link):
return [
cc_linking.static_mode_params_for_dynamic_library,
cc_linking.static_mode_params_for_executable,
cc_linking.dynamic_mode_params_for_dynamic_library,
cc_linking.dynamic_mode_params_for_executable,
library_to_link.static_library,
library_to_link.pic_static_library,
library_to_link.dynamic_library,
library_to_link.interface_library,
]
def _collect_libs(cc_linking):
libs = []
for params in _extract_link_params(cc_linking):
libs += [lib.artifact() for lib in params.libraries_to_link]
libs += params.dynamic_libraries_for_runtime.to_list()
for library_to_link in cc_linking.libraries_to_link:
for library in _extract_libraries(library_to_link):
if library:
libs.append(library)
return collections.uniq(libs)

View File

@ -73,10 +73,12 @@ if [[ -f $1 ]]; then
return 0
fi
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
if [[ -d $1 || -L $1 ]]; then
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
fi
"""
return FunctionAndCall(text = text)

View File

@ -73,10 +73,12 @@ if [[ -f $1 ]]; then
return 0
fi
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
if [[ -d $1 || -L $1 ]]; then
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
fi
"""
return FunctionAndCall(text = text)

View File

@ -81,10 +81,12 @@ if [[ -f $1 ]]; then
return 0
fi
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
if [[ -d $1 || -L $1 ]]; then
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
fi
"""
return FunctionAndCall(text = text)

View File

@ -71,10 +71,12 @@ if [[ -f $1 ]]; then
return 0
fi
local children=$($REAL_FIND $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
if [[ -d $1 || -L $1 ]]; then
local children=$(find $1 -maxdepth 1 -mindepth 1)
for child in $children; do
##symlink_to_dir## $child $target
done
fi
"""
return FunctionAndCall(text = text)

View File

@ -1 +1 @@
VERSION = "0.0.7" # output refactoring
VERSION = "0.0.8" # supports transitive dependencies bazel -> cmake -> bazel