Link archive to shared library with Bazel - c++

Using Bazel, I'd like to link a position independent static archive directly to a shared library (not to the application using the shared library). Something like:
g++ -shared mylib.cpp archive.a -o libmylib.so
g++ mybin.cpp -lmylib -o mybin
Rationale: RedHat packages libelf as a static archive, that has some otherwise public symbols (e.g: elf_errmsg) marked as hidden: the archive must be linked to the module using it (in this case, the shared library): linking to the application is too late.
I tried:
Wrapping the archive in a cc_import rule
Adding the rule to the deps of the cc_library: doesn't work, archive gets linked to the app
Adding the rule to the srcs of the cc_library: doesn't work, archive gets linked to the app
Adding the archive file directly to the linkopts of the cc_library: almost works, but the archive gets linked to both the library and the binaries depending on it.
Renaming the archive to .lo or .pic.lo: doesn't work, archive gets linked to the app with -Wl,wholearchive
Adding alwayslink = True to the cc_import: doesn't work, archive gets linked to the app with -Wl,wholearchive

Something like this should work for what you're trying to do:
cc_binary(
name = "libmylib.so",
srcs = ["mylib.cpp", "archive.a"],
linkshared = True,
)
cc_library(
name = "mylib",
srcs = ["libmylib.so"],
hdrs = ["mylib.hpp"],
)
cc_binary(
name = "mybin",
srcs = ["mybin.cpp"],
deps = [":mylib"],
)
You can build a shared library (which is done as cc_binary(linkshared = True); this bit may not seem entirely intuitive) from your source and the library archive.
You can build a cc_library to use as a dependency of your other targets... and use that to build a cc_binary target.
In theory if this was just one time/place thing, you could probably shorten it like this (but it's more of a minimal length example):
cc_binary(
name = "libmylib.so",
srcs = ["mylib.cpp", "archive.a"],
linkshared = True,
)
cc_binary(
name = "mybin",
srcs = ["mybin.cpp", "mylib.hpp", ":libmylib.so"],
)

Related

Can't use linkopts when building libpqxx using Bazel

I am trying to use Bazel with libpqxx, which does not have native Bazel support. Rather than trying to write my own BUILD file, I am using rules_foreign_cc cmake() to translate the provided CMakeLists.txt, and this builds a library named pqxx and links a static library libpqxx-7.7.a. When I try to add this as a dependency to a cc_binary, it is able to see all the headers, but is not able to run the program.
When installed locally, gcc requires the libraries -lpqxx -lpq (the C++ and C bindings). Locally, all the commands work fine: For example: g++ -L/usr/local/lib -lpqxx -lpq foo.cpp where /usr/local/lib is where the library was installed. I cannot duplicate this behavior in Bazel.
I can see that there is a libpqxx-7.7.a file in bazel-bin, but adding the path from "$(bazel info bazel-bin)/workspace/libpqxx-7.7.a" throws a error: ld: library not found for -lpqxx.
Leaving the link options out produces
Undefined symbols for architecture arm64:
"_ASN1_STRING_get0_data", referenced from:
_pgtls_verify_peer_name_matches_certificate_guts in libpq.a(fe-secure-openssl.o)
"_ASN1_STRING_length", referenced from:
_pgtls_verify_peer_name_matches_certificate_guts in libpq.a(fe-secure-openssl.o)
"_BIO_clear_flags", referenced from:
_my_sock_write in libpq.a(fe-secure-openssl.o)
_my_sock_read in libpq.a(fe-secure-openssl.o)
which I believe is a linking error. I am new to C++, and am trying to translate the CMakeLists to work with Bazel. Where am I building my cmake wrong?
WORKSPACE
http_archive(
name = "libpqxx",
url = "https://github.com/jtv/libpqxx/archive/refs/heads/master.zip",
build_file_content = _ALL_CONTENT,
strip_prefix = "libpqxx-master",
)
BUILD
load("#rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("#rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
cmake(
name = "pqxx",
cache_entries = {
"DPostgreSQL_ROOT=": "/Library/PostgreSQL/14",
"DSKIP_BUILD_TEST": "on"
},
lib_source = "#libpqxx//:all_srcs",
visibility = ["//visibility:public"],
out_static_libs = ["libpqxx-7.7.a"],
targets = ["pqxx"]
)
cc_binary(
name = "database_tester",
srcs = ["database_access.cpp"],
deps = [
":pqxx",
],
)

Create a fully static C++ binary in Bazel

Is it possible to create a fully static binary in Bazel or is there a bug in GCC or bazel preventing me.
This is the error that I get
/bin/ld.gold: error: /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginT.o: requires dynamic R_X86_64_32 reloc against '__TMC_END__' which may overflow at runtime; recompile with -fPIC
Which if I understand correctly is a version libstdc++ that is not statically linked. Was wondering if there was a version of libstdc++ that is statically linked that I could make Bazel use.
Tried looking at this issue on Bazel's repo but the workarounds mentioned do not work for me
https://github.com/bazelbuild/bazel/issues/8672
Info Dump
Bazel version:3.3.1
GCC version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
OS: Ubuntu 20.04 in a Docker container
My build file
cc_binary(
name = "main",
srcs = ["main.cpp"],
linkshared=True,
copts = ["-shared", "-fpic", "-fPIC"],
features = ["fully_static_link"],
)
main.cpp
int main()
{
return 1;
}
Other versions of the build file with similar results
cc_binary(
name = "main",
srcs = ["main.cpp"],
linkshared=True,
linkstatic=1,
linkopts = ["-static"]
)
cc_binary(
name = "main",
srcs = ["main.cpp"],
linkshared=True,
linkstatic=1,
linkopts = ["-static"],
features = ["fully_static_link"],
copts=["-fpic"],
)
TL;DR
Bazel will not compile a fully static binary in Ubuntu 20.04, GCC 9.3.0. I'm seeking to understand if the issue posted is related or if I don't understand something about static linking?

How to use glog with bazel?

I was able to install glog with:
brew install glog
Then I can successfully compile and use it using g++:
g++ src/main/main_logger.cc -std=c++17 -lglog
How can I do this with bazel?
I get this error:
fatal error: 'glog/logging.h' file not found
#include <glog/logging.h>
^~~~~~~~~~~~~~~~
1 error generated.
UPDATE:
Instead of installing and building glog locallay, I ended up referencing it as a git repo in the WORKSPACE file:
git_repository(
name = "glog",
remote = "https://github.com/google/glog.git",
tag = "v0.5.0",
)
Now I can depend on it in my cc_binary rules like this:
cc_binary(
name = "main_logger",
srcs = ["main_logger.cc"],
deps = [
"//src/lib:CPPLib",
"#com_github_gflags_gflags//:gflags",
"#glog",
],
)
Complete example here.
There is already a doc about to use glog within a project which uses the Bazel build tool. link
Then you can create a BUILD file, and use bazel build //src/main:main_logger to build it.
cc_binary(
name = "main_logger",
srcs = ["main_logger.cc"],
deps = ["#com_github_google_glog//:glog"],
)

Linking with NS3 module with circular dependency to other library

I am trying to build custom NS3 module which depends on some static library. This static library depends on NS3 module.
Platform: Ubuntu 16.04 x64
Toolchain: GCC 5.4.0
I will refer to my custom NS3 module as mymodule
I will refer to the library which mymodule depends on as mylib
I will refer to the program which links with mymodule and mylib as myprog
wscript for mymodule:
def build(bld):
module = bld.create_ns3_module('mymodule', ['network'])
module.features = 'c cxx cxxstlib ns3module'
module.source = [
'model/mymodule.cc' ]
# Make a dependency to some other static lib:
bld.env.INCLUDES_MYLIB = [ "some/include/path" ]
bld.env.LIB_MYLIB = ['mylib']
bld.env.LIBPATH_MYLIB = [ "some/path" ]
module.use.append('MYLIB')
# Create a program which uses mymodule
p = bld.create_ns3_program('myprog', ['core', 'mymodule'])
p.source = 'prog.cpp'
headers = bld(features='ns3header')
headers.module = 'mymodule'
headers.source = ['model/mymodule.h']
When I do ./waf build it fails: LD cannot link myprog because mylib has unresolved symbols. This failure is actually expected because mylib and mymodule are codependent and should be linked in non-standard way.
Workarounds:
If I build myprog by hand and use -Wl,--start-group
-lns3.26-mymodule-debug -lmylib -Wl,--end-group it links perfectly fine and works as expected.
If I combine two static libs by hand (using ar -M script) and then run ./waf build it also works fine.
The question: How can I integrate one of the workarounds above into wscript?
it looks like a known problem with order of static libs inclusion. The behavior has changed in waf 1.9, due to this problem.
One workaround might be to use the linkflags attribute of program. You should prefer the use of STLIB_MYLIB and STLIBPATH_MYLIB as mylib is static. In waf 1.9 with he correct order of libs it might suffice.
Anyway, use -v to see the command line generated by waf, it might help !

Makefile - cannot find shared library

I have a Makefile for a c++ Linux project:
MODE ?= dbg
DIR = ../../../../../somdir/$(MODE)
SRC_FILES = a.cpp b.cpp
H_FILES = a.h
LDFLAGS += -L$(DIR)/lib/linux '-Wl,-R$$ORIGIN'
CPPFLAGS = -I$(DIR)/include
LIBRARIES = -lsomeso
ifeq (rel, $(MODE))
CFLAGS = -Wall -g -DNDEBUG
else
CFLAGS = -Wall -ansi -pedantic -Wconversion -g -DDEBUG -D_DEBUG
endif
sample: $(SRC_FILES) $(H_FILES) Makefile
g++ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LIBRARIES) $(SRC_FILES) -o sample
when i run 'make' it builds the project, with no errors.
but when i run the project it complains that:
error while loading shared libraries: libsomeso.so: cannot open shared object file: No such file or directory
The path that i give in DIR goes to the folder where the shared object is held(relatively to where the makefile is placed), and if it was the wrong path why didn't it complain during the make process.
does someone know what am i missing?
Thanks
Matt
LDFLAGS += -L$(DIR)/lib/linux '-Wl,-R$$ORIGIN'
The above should be:
LDFLAGS += -L$(DIR)/lib/linux -Wl,-R$(DIR)/lib/linux '-Wl,-R$$ORIGIN'
That is, for each non-standard dynamic library location -L a corresponding -Wl,-R should be specified. $ORIGIN is needed to locate dynamic libraries relative to the executable, not sure if you need it here.
People often advise using LD_LIBRARY_PATH. This is a bad advice, in my opinion, because it makes deployment more complicated.
When you run your application, location of libsomeso.so should be in LD_LIBRARY_PATH environment variable. Try running program like this:
LD_LIBRARY_PATH="path_to_libsomeso_so:$LD_LIBRARY_PATH" myprogram
Here path_to_libsomeso_so is full path to a directory where libsomeso.so is located, and myprogram is your program executable. Note, that you should specify path to a directory containing libsomeso.so, not to libsomeso.so file itself.
The trouble is not during compilation time. Everything goes fine. There's a problem at runtime.
Indeed, your program has been linked with a shared object library. Therefore, at runtime, it need to load this shared object file. During compilation, you instructs the compiler where this file was with the -L flag.
For the runtime, you have to set the LD_LIBRARY_PATH environment variable to point to the directory where your libsomeso.so file resides.
Alternatively, you can place this file in one of the standard directory where these shared object files are searched for: /usr/local/lib, /usr/lib, /lib, but this should be what you'll do for the final distributed version of your library.
As told from Maxim Egorushkin, LD_LIBRARY_PATH is a bad choice. Meanwhile, using -L$(your lib path) -l$(your lib name) gcc/g++ argument to link shared library isn't a good choice. Because, after build the exe, you should told exe where the shared library directory is. By default, executable file only search shared library at /usr/lib or /usr/local/lib. Although, you have told makefile where the shared library is when build the executable file. But when you execute this exe file, they are different.
However, link static library don't have such problem.
So, the best way to deal with your problem is change the way you link your custom shared file. Like this:
DYNAMIC_LIB_DIR = ../lib (your lib path ,this is a example)
OBJLIBS = xxx.so (your lib name)
gcc/g++ -o exe_name sourcefile/object_file $(DYNAMIC_LIB_DIR)/$(OBJLIBS)
Refresh that dynamic library cache!
After adding a custom, non-standard library to /usr/local/lib, first check that /usr/local/lib is listed under /etc/ld.so.conf.d/libc.conf.
Then, finish off with a dynamic link library cache refresh:
$ sudo ldconfig