Include a generated header file using a relative path - c++

Currently, I try to build a library with Bazel (5.1.0) that originally uses CMake as a build system.
I am running into a problem when trying to include a generated a header file using a relative path (in the CMake build it uses configure_file):
(The following example can also be found here)
WORKSPACE.bazel:
workspace(name = "TemplateRule")
main.cpp:
#include "kernels/bvh/some_header.h"
#include <iostream>
int main() {
std::cout << VERSION_STR << std::endl;
}
kernels/bvh/some_header.h:
#pragma once
// include config.h using a relative path
// if changed to kernels/config.h everything works as expected
// unfortunately, this is legacy code that I cannot change
#include "../config.h"
config.h.in:
#pragma once
#define VERSION_STR "#VERSION_STR#"
BUILD.bazel
load("//bazel:expand_template.bzl", "expand_template")
expand_template(
name = "config_h",
template = "config.h.in",
out = "kernels/config.h",
substitutions = {
"#VERSION_STR#": "1.0.3",
},
)
cc_binary(
name = "HelloWorld",
srcs = [
"main.cpp",
"kernels/bvh/some_header.h",
":config_h",
],
)
bazel/BUILD.bazel: < empty >
bazel/expand_template.bzl:
# Copied from https://github.com/tensorflow/tensorflow/blob/master/third_party/common.bzl with minor modifications
# SPDX-License-Identifier: Apache-2.0
def expand_template_impl(ctx):
ctx.actions.expand_template(
template = ctx.file.template,
output = ctx.outputs.out,
substitutions = ctx.attr.substitutions,
)
_expand_template = rule(
implementation = expand_template_impl,
attrs = {
"template": attr.label(mandatory = True, allow_single_file = True),
"substitutions": attr.string_dict(mandatory = True),
"out": attr.output(mandatory = True),
},
output_to_genfiles = True,
)
def expand_template(name, template, substitutions, out):
_expand_template(
name = name,
template = template,
substitutions = substitutions,
out = out,
)
When I run bazel build //...
I get the error:
In file included from main.cpp:1:
kernel/some_header.h:3:10: fatal error: ../config.h: No such file or directory
3 | #include "../config.h"
| ^~~~~~~~~~~~~
When I include config.h in main.cpp and remove it from kernel/bvh/some_header.h everything works as expected.
Any ideas how to the relative path .../config.h working?
Creating the file config.h and compiling the code manually works as expected:
g++ main.cpp kernel/bvh/some_header.h config.h
According to the Best Practices relative paths using .. should be avoided, but you can find such things in legacy code that uses CMake to build. Is this a restriction of Bazel? or is there a workaround?

While behaviour and order of search for file while using quotes is technically platform defined, most common principle in case of use path relative to file from which inclusion happens. For kernel/bvh/some_header.h the path ../config.h is resulting in searching ONLY in kernel folder, working folder or kernel/bvh/ will not be checked. Some compilers check alternative locations. The order of search is part of compiler documentation.
I once saw use of ../../config.h inside of another header kernel.h in situation like this and it worked. It was a maintenance liability. The kernel.h was moved to different location and it picked up a wrong folder and wrong file, which resulted in ODR breach much later in project's timeline. One who had committed the move had left ../../config.h in place and made a copy. Some units were using old header while other started to use modified one at new location.
For that reason the use reverse relative paths outside of library infrastructure is not recommended. Instead partial relative paths, e.g. kernel/config.h are preferred, assuming that core folder would be in list of include paths. In practice real projects can use preprocessor macros, e.g.#include KERNEL_CONFIG_FILE. An illustration can be seen in freetype, boost, etc.

Related

Meson creates build files which will not compile

I am trying to port an existing code tree to the meson build system on a centos 7 machine. Meson configure works fine, but when I try to compile, it fails. The code is proprietary, so I have created an example that illustrates the problem (accurately enough, I hope.) I am not at liberty to restructure the directory tree.
Here's the tree:
mesonex/
alpha/
beta/
alpha/
inc/
funcs.h
numbers.h
src/
numbers.cpp
funcs.cpp
src/
example.cpp
meson.build
My meson.build:
project('example', 'cpp')
srcs=['example.cpp']
srcs+='../beta/alpha/src/funcs.cpp'
srcs+='../beta/alpha/src/funcs.cpp'
incdirs=include_directories('../beta/alpha/inc')
executable('example', srcs, include_directories: incdirs)
here is the main example.cpp file:
#include <iostream>
#include "../beta/alpha/inc/numbers.h"
#include "../beta/alpha/inc/funcs.h"
int main()
{
std::cout << "Hello" << std::endl;
std::cout << interestingNumber() << std::endl;
std::cout << interestingFunc() << std::endl;
}
These are the supporting cpp files:
// funcs.cpp
#include "../inc/numbers.h"
float interestingFunc()
{
return (interestingNumber()+1)/2;
}
// numbers.cpp
float interestingNumber()
{
return 11.3355;
}
And these are the header files:
// funcs.h
float interestingFunc();
// numbers.h
float interestingNumber();
Please note that the duplication in directory names is intentional. Maybe this confuses meson in figuring out how to handle the #includes?
This is just one example of many different build strategies I have tried.
I see right off the bat an issue that might just be an issue with your example, and not with your actual code: Meson considers the meson.build file with the project() call to be the "root" of the source directory structure. You cannot ask it to include files outside of the root. It would be about like cp /../foo . on a Unix-like OS. This may just be a mistake in your example, since this isn't the real code of course.
So, if we rewrite this as (mesonex/alpha/meson.build):
# no project(), that would be in mesonex/meson.build)
sources = files(
'example.cpp',
'../beta/alpha/src/funcs.cpp',
'../beta/alpha/src/numbers.cpp', # you have a typo in your example, funcs.cpp is listed twice.
)
executable(
'example',
sources,
include_directories : include_directories('../beta/alpha/inc'),
)
Should work.
Note, that you might want to consider using a convenience static library instead of reaching back to the code, as this is best practice, you could write something like (mesonex/alpha/beta/meson.build):
lib_beta = static_library(
'beta',
['src/funcs.cpp', 'src/numbers.cpp']
)
idep_beta = declare_dependency(
link_with : lib_beta,
include_directories : include_directories('.'),
)
and then in (src/meson.build):
executable(
'example',
'source.cpp',
dependencies : idep_beta
)
is all you need, as the idep_beta carries both the linkage and the include information.
This is a follow on - the solution worked for my example but not for the actual code. My model must have been incomplete (in a way I haven't determined yet.) The configuration stage works, but in the compile stage the #includes in the cpp source are flagged with a "File or directory does not exist." How does meson reconcile the specified include directories with #include statements in the source? The #include paths may be relative to the actual directory of the actual cpp source. Does this mean I have to edit all the #includes in all the sources - that would be a real negative. We work with some VERY large code bases.

Include a *.cpp file

Currently, I try to bazelize a library. Let's call this library lib_foo.
The setup is similar to this minimal example. My main application tries to use a function form lib_foo.
main.cpp:
#include "lib_foo/header.h"
#include <iostream>
int main() {
std::cout << foo<2>() << std::endl;
}
lib_foo/header.h:
#pragma once
#include "impl.inc" // works
lib_foo/impl.inc:
template <int number>
int foo() {
return number;
}
BUILD.bazel:
cc_library(
name = "lib_foo",
srcs = [
"lib_foo/header.h",
"lib_foo/impl.inc",
],
)
cc_binary(
name = "HelloWorld",
srcs = [
"main.cpp",
],
deps = [":lib_foo"],
)
There is also an empty WORKSPACE file.
If I run bazel run //:HelloWorld everything works as expected.
If I rename the file impl.inc to impl.cpp (and change this accordingly in BUILD and include file, etc.). I run in the following problem (on Ubuntu 20.04):
In file included from main.cpp:1:
lib_foo/header.h:3:10: fatal error: impl.cpp: No such file or directory
3 | #include "impl.cpp"
| ^~~~~~~~~
Also replacing the include path with a relative one to the WORKSPACE file does not help here.
It seems that it is a general problem to include cpp files in Bazel. Since the third party library I try to bazelize does include several times cpp files I wonder what a proper workaround here is.
Any ideas?
(I am using Bazel 3.7.2 on Ubuntu 20.04)
The normal workaround is to put includable cpp files in the textual_hdrs attribute of cc_library.

How to link to already compiled external shared libraries in RCPP using Makevars?

I have looked at multiple places but failed to find a simple example for my problem.
What I need is to be able to use multiple .so files which I have (together with .h files).
A simple example would be:
I have the following files: a.h, a.cpp the function jeden - this is my R library, accessor.cpp which exposes to R the function dwa from external library libdwa.so. I have the header file dwa.h and this library libdwa.so.
I can manage building binary package (and everything works fine) only and only then when I put the library file libdwa.so into the directory '/usr/lib' (I use Debian). But it is not partable solution.
Where I should put the library "dwa" files (dwa.h and libdwa.so) and what flags add in the Makevars file to get portable solution?
SUMMARY ADDED
I summarize my results here. I believe some extremaly simple examples are useful at start.
2 external libraries put inside inst:
/inst/include/dwa.h
/inst/include/trzy.h
/inst/jeden/libdwa.so
/inst/jeden/libtrzy.so
configure file /configure has the form (test6 is a name of the package):
#!/bin/bash
rpath=$(${R_HOME}/bin/Rscript -e 'cat(file.path(.libPaths()[1], "test6", "jeden"))') #'
sed -e"s|#rpath#|"${rpath}"|" src/Makevars.in > src/Makevars
exit 0
The file /src/Makevars.in has the form:
CXX_STD = CXX11
PKG_CPPFLAGS = -I../inst/include/ -I.
PKG_LIBS = -ldwa -ltrzy -L../inst/jeden -Wl,-rpath,#rpath#
The /src/accessor.cpp expose the libraries function to R:
#include <Rcpp.h>
#include "../inst/include/dwa.h"
#include "../inst/include/trzy.h"
// [[Rcpp::export]]
Rcpp::String r_dwa() {
Rcpp::String res = dwa();.
return res;
}
// [[Rcpp::export]]
Rcpp::String r_trzy() {
Rcpp::String res = trzy();.
return res;
}
Pure R library _/src/hello_world.cpp_ also exists:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
String hello_world() {
String hw = "Hello World";
return hw ;
}
Finaly /NAMESPACE file:
useDynLib("test6", .registration=TRUE)
import("Rcpp")
importFrom("utils", "object.size", "packageVersion")
exportPattern("^[[:alpha:]]+")
Building source and binary packages was successful but only source one is usefull as the binary has path to the libraries written rigidly.
Let's assume you have a directory structure like
/opt/dwa/include/dwa.h
/opt/dwa/lib/libdwa.so
In that case you could use is src/Makevars:
PKG_CPPFLAGS = -I/opt/dwa/include
PKG_LIBS = -L/opt/dwa/lib -ldwa -Wl,-rpath,/opt/dwa/lib
Of course, this is still not portable, since the path to the library and header files is hard coded. To circumvent this, one uses a template file, e.g. src/Makevars.in
PKG_CPPFLAGS = #DWA_INC#
PKG_LIBS = #DWA_LIB#
and writes a configure script that inspects the environment and creates src/Makevars based on the findings. There are different strategies for writing configure scripts. I typically use autoconf, e.g. in RcppArrayFire or dqmagic.

CLion doesn't resolve headers from external library

Some time ago I started a big header library in C++1x using XCode. The current layout of the library is () something like (partial output from ls -R sponf)
sponf/sponf:
ancestors sponf.h sponf_utilities.h
categories sponf_children.h utilities
children sponf_macros.h
sponf/sponf/ancestors:
function.h meter.h set.h simulation.h
sponf/sponf/categories:
free_space.h prng.h random_distribution.h series.h
sponf/sponf/children:
distributions histogram.h random simulations
meters numeric series spaces
sponf/sponf/children/distributions:
arcsine_der.h exponential.h
box_muller.h uniform.h
sponf/sponf/children/meters:
accumulator.h timer.h
#... other subdirs of 'children' ...
sponf/sponf/utilities:
common_math.h limits.h string_const.h
#... other directories ...
I wanted to port this project to CLion, which seems a really good IDE (based on the similar AndroidStudio IDE) but I'm getting some troubles.
Small test program
I tried this small program as a test:
#include <iostream>
#include <sponf/sponf.h>
using namespace std;
int main() {
using space = sponf::spaces::euclidean_free_space<double, 3>;
sponf::simulations::random_walk<space> rw;
rw.step(1);
std::cout << rw.position.value << std::endl;
return 0;
}
The program compiles and runs fine. However, CLion does not recognize the spaces namespace (declared in one of the children files), nor the simulations namespace; they are both marked red and I cannot inspect their content, nor navigate to their definitions by ⌘-clicking, etc. etc...
Relevant parts of the library
Looking in "sponf.h" we find
#ifndef sponf_h
#define sponf_h
/* The classes below are exported */
#pragma GCC visibility push(default)
// include some of the standard library files
// ...
#include <Eigen/Eigen>
#include "sponf_macros.h"
#include "sponf_utilities.h"
#include "sponf_children.h"
#pragma GCC visibility pop
#endif
while in "sponf_children.h" (which is located at the top level, next to "sponf.h") we find
#ifndef sponf_locp_sponf_children_h
#define sponf_locp_sponf_children_h
namespace sponf {
// include some of the children
// ...
#include "children/spaces/euclidean_free_space.h"
#include "children/simulations/random_walk.h"
// include remaining children
// ...
}
#endif
Each "child" header will then include its corresponding "ancestor" or "category" header (which defines the superclass of the "child" itself).
The reaction of CLion
Despite the autocompletition prediction, which easily finds all the subdirectories and the headers, all the include directives in this last file get marked red and ⌘-clicking on any of them leads to a popup message
Cannot find declaration to go to
while the right ribbon of the editor signal many errors like
',' or ) expected
) expected
Declarator expected
Expecting type
Missing ;
Unexpected symbol
which are not the same for each include statement (each generates from 2 to all of these errors).
On the other hand, CLion is perfectly able to find all Eigen headers, which have pretty much the same structure!
I have put both libs in /opt/local/include and changed CMakeLists.txt accordingly
cmake_minimum_required(VERSION 2.8.4)
project(sponf)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
include_directories(/opt/local/include/sponf /opt/local/include/eigen3)
set(SOURCE_FILES main.cpp)
add_executable(sponf ${SOURCE_FILES})
Why can't CLion properly parse the project structure? XCode, after having included /opt/local/include/sponf and /opt/local/include/eigen3 in the HEADER_SEARCH_PATHS env. variable of the project, is able to find any header while compiling the same exact program.
Is there anything else I need to know? Am I doing it wrong or is it that CLion isn't that mature yet and this is just a sorry bug? This is my first approach to the CLion and the CMake toolchain, so any kind of information about it will be greatly appreciated!
Sorry for the very long question, I didn't manage to shrink it further... Thanks in advance guys, see you soon!
Here what I did in windows using cigwin64. I wanted to use Eigen library include in my project.
Eigen library is places in /usr/include/eigen then edited CMakeLists.txt and add
include_directories("/usr/include/eigen")
into it. Now CLion can find all source files in eigen lib. May be this what you wanted too.
Downgrade to Clion 2016.1.4 fixes the problem

How to use a program which is not in the source code's folder?

For example: I'm on MS DOS, I have a source code in the folder C:\Documents and Settings\Programs. Can i make my source code use a program (for example gnuplot) that is in a random folder?
http://www.codeproject.com/KB/system/newbiespawn.aspx
ShellExecute will look into the PATH environment variable, so you don't need to specify the full PATH. Now, if it's really a random location and it's not even in the PATH environment variable, then I guess you are out of luck.
If they aren't even in the PATH, then you have to search for it in the candidates folder. Here's sample code on how to traverse a file system path in C++.
And an example using Boost:
directoryList.h
#ifndef DIRECTORYLIST_H_INCLUDED
#define DIRECTORYLIST_H_INCLUDED
#define BOOST_FILESYSTEM_NO_DEPRECATED
#include <iostream>
#include <list>
#include <string>
class directoryList {
public:
directoryList();
~directoryList();
std::list<std::string> getListing(std::string path);
};
#endif // DIRECTORYLIST_H_INCLUDED
directoryList.cpp
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/convenience.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/progress.hpp"
#include "directoryList.h"
using namespace std;
namespace fs = boost::filesystem;
directoryList::directoryList() {}
directoryList::~directoryList() {}
list<string> directoryList::getListing(string base_dir) {
list<string> rv;
fs::path p(base_dir);
for (fs::recursive_directory_iterator it(p);
it != fs::recursive_directory_iterator(); ++it) {
string complete_filename = it->path().string();
rv.insert(rv.begin(),complete_filename);
}
return rv;
}
Usage sample:
directoryList *dl = new directoryList();
filenames = dl->getListing("C:\\Program Files");
//search for the file here, or modify the getListing to supply a filter
Here are some options:
Search in the system PATH for the executable you want to run
Allow the user to specify the location on the command-line
Store the location in a configuration file, and allow the user to specify it during install (if you have an install process) or by editing the file by hand
Ideally you'd do all 3
Also there are some core functions _exec/exec and its modifications. Similar functions are available for Linux.
The location of source code has nothing to do with the way programs are located by system() call (I assume you use that call). The only relevant consideration is the location of the compiled executable.
Please take a look at PATH environment variable in Windows - this is how programs are found. It's a semicolon-separated list of directories where Windows looks for executables and BAT files and DLLs. To that list, current directory and (I think) the place where your EXE is are prepended.
The PATH is set in Windows XP from System control panel widget Advanced tab Environment button. For Vista, things are more complicated - you need to do it as administrator.
As Vinko said, the PATH environment variable determines where Windows will look for program files.
It's usually best to avoid hard-coding the path of an executable into your compiled program. Even if gnuplot is in a particular folder on your machine then it might well not be in the same folder on someone else's computer. This would cause your call to the other program to fail. You could store it in the registry and let the user configure the program location, or provide an installer that searched for it.