C++ header-only library with waf - c++

Good day,
before fully migrating to waf (1.7.5), I have tried to create a simple project of this structure:
wafproject
├── application
│ ├── main.cpp
│ └── wscript
├── library1
│ ├── foo1.hpp
│ ├── foo2.hpp
│ └── wscript
└── wscript
This is the root wscript:
def options(opt) :
opt.load('compiler_cxx')
def configure(cnf) :
cnf.load('compiler_cxx')
def build(bld) :
bld.recurse('library1')
bld.recurse('application')
This is the application wscript:
def build(bld) :
bld( features = 'cxx cxxprogram'
, target = 'application'
, source = 'main.cpp'
, use = ['library1']
)
This is the library1 wscript
def build(bld) :
bld( name = 'library1'
, inludes = '../../'
, export_inludes = '../../'
)
(Note: I have tried using target instead of name for library1, and I have also
tried enabling cxx cxxshlib features for library1.)
This is the main.cpp:
#include <wafproject/library1/foo1.hpp>
#include <wafproject/library1/foo2.hpp>
int main()
{
}
And this is the error I get:
Setting top to : /home/<path>/wafproject
Setting out to : /home/<path>/wafproject/build
Checking for 'g++' (c++ compiler) : /usr/bin/g++
'configure' finished successfully (0.038s)
Waf: Entering directory `/home/<path>/wafproject/build'
[1/3] cxxshlib: -> build/library1/liblibrary1.so
[2/3] cxx: application/main.cpp -> build/application/main.cpp.1.o
../application/main.cpp:1:40: fatal error: wafproject/library1/foo1.hpp: Directory or file does not exist.
compilation terminated.
Waf: Leaving directory `/home/<path>/wafproject/build'
Build failed
-> task in 'application' failed (exit status 1):
{task 139729350901264: cxx main.cpp -> main.cpp.1.o}
['/usr/bin/g++', '../application/main.cpp', '-c', '-o', 'application/main.cpp.1.o']
I do not want to change the way I include the headers, but for that I apparentely need to change the way my project is set up.
I would be glad for any input, thanks.
EDIT: Solved, it was just a typo (inludes instead of includes and export_inludes instead of export_includes).

Since this is the first thing on google for 'header only library waf', I thought I should post the generic solution.
bld(name = 'libname', export_includes = 'PATH/TO/lib/')
Which works for me.

Related

dune-ocaml : No implementations provided for the following modules: [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 11 months ago.
Improve this question
I tried to compile an OCaml code with Dune but got the following error:
Error: No implementations provided for the following modules:
CallccBp referenced from bin/.CallccTest.eobjs/native/dune__exe__CallccTest.cmx
by executing the command : $ dune build
My project hierarchy is as follows:
callcc/
bin/
callccTest.ml
dune
[
(executable
(name callccTest)
(libraries CallccBp))
]
lib/
CallccBp.mli
dune
[
(library
(name CallccBp)
(modules_without_implementation CallccBp))
]
test/
callccTest.ml
dune [
(test
(name callccTest))
]
callcc.opam
dune-project
How can I solve this problem?
Looking at the discussion you had with octachron, let's start from the basics:
my_module.mli
File where you declare the signatures of your values, modules etc. Not mandatory, I'd advise not using them if you start with OCaml
my_module.ml
File where you implement your values, modules etc. Mandatory since these are the files that make your program run
Let's see this with a toy project:
.
├── bin
│   ├── dune
(executable
(name main)
)
│   └── main.ml
├── dune-project
├── lib
│   ├── dune
(library
(name my_module)
)
│   └── my_module.ml
└── program.opam
If I want to use values from my_module in bin/main.ml, I have to:
have values in my_module.ml
add the (libraries my_module) stanza in my bin/dune file
use these values with My_module.<name_of_value>
So this looks like:
.
├── bin
│   ├── dune
(executable
(name main)
(libraries my_module)
)
│   └── main.ml
let () =
let b = My_module.incr 3 in
Printf.printf "%d\n" b
├── dune-project
├── lib
│   ├── dune
│   └── my_module.ml
let incr a = a + 1
└── program.opam
Now, let's go back to your hierarchy:
callcc/
bin/
callccTest.ml
dune
[
(executable
(name callccTest)
(libraries CallccBp))
]
lib/
CallccBp.mli
dune
[
(library
(name CallccBp)
(modules_without_implementation CallccBp))
]
test/
callccTest.ml
dune [
(test
(name callccTest))
]
callcc.opam
dune-project
Everything looks fine except from the fact that CallccBp.mli is just an interface, not an implementation. As a starter you could remove this file, create CallccBp.ml filled with these two functions:
CallccBp.ml
let callcc = failwith "TODO"
let throw = failwith "TODO"
If you compile, dune should not complain and now all you'll have to do will be to provide a much useful implementation than failwith "TODO"
And if we go back to our toy project, to see why you'd want to have an mli file:
.
├── bin
│ ├── dune
(executable
(name main)
(libraries my_module)
)
│ └── main.ml
let () =
let b = My_module.incr 3 in
Printf.printf "%d\n" b
├── dune-project
├── lib
│ ├── dune
│ └── my_module.ml
let dummy _ = failwith "USELESS"
let incr a = a + 1
│ └── my_module.mli
val incr : int -> int
(** [incr d] will return [d] incremented by 1. Highly efficient. Trust me. *)
└── program.opam
I'll be able to use My_module.incr in bin/main.ml but not My_module.dummy because it is not shown by the mli file and thus not accessible outside of my_module.ml. And as a bonus, my_module.mli file is the entry point for a library user who doesn't want to know how it is implemented but just wants to use it knowing the available values, their types and, often, what they do from the comment.
The modules_without_implementation stanza is for mli files that don't need implementations, namely, type declarations files so modules looking like this:
AST.mli
type ('a, 'b) res = Ok of 'a | Error of 'b
type num = Int of int | Float of float
type person = {id : int; name : string; age : num}
And you can use them in another file like this:
file.ml
type t = {player1 : AST.person; player2 : AST.person}
let whos_older p1 p2 =
Printf.printf "%s is older\n"
(if p1.AST.age > p2.AST.age then p1.name else p2.name)
But that's not really useful when starting since, once again, I'd advise not touching mli files in the beginning
The error message is complaining that there is no implementation (aka no ml file) for the module CallccBp. Without knowing the content of CallccBp, it is probable that this module contain either an exception or an extension constructor declaration (or any other kind of runtime components). Your first fix should be to add a callccBp.ml file and remove the (module_without_implementation ...) line.

Please explain how we're supposed to test Julia libraries and why one of two breaks

In my Advent of Code repository I've had a utility library since last year and have been using stuff from that also this year.
This year I wanted to add a second one for loading the input files quicker. For some reason unittests and using it works for the old library but not for the second.
I tried to unify the two folders as much as possible until the Project.toml for instance are equal now.
The two directories look like this (ProblemParser failing and Utils working):
ProblemParser ⛔
├── Manifest.toml
├── Project.toml
├── src
│ └── ProblemParser.jl
└── test
├── Manifest.toml
├── Project.toml
└── runtests.jl
Utils ✅
├── Manifest.toml
├── Project.toml
├── src
│ └── Utils.jl
└── test
├── Manifest.toml
├── Project.toml
└── runtests.jl
Adding them to the Project (Manifest) works fine (other stuff left out):
(AoC 2021) pkg> status
Status `~/src/me/AoC/21/Project.toml`
[16064a1e] ProblemParser v0.1.0 `../ProblemParser`
[c4255648] Utils v0.1.0 `../Utils`
However trying to use ProblemParser doesn't go so well.
julia> using Utils
julia> # that worked
julia> using ProblemParser
ERROR: KeyError: key ProblemParser [16064a1e-6b5f-4a50-97c7-fe66cda9553b] not found
Stacktrace:
[1] getindex
# ./dict.jl:481 [inlined]
[2] root_module
# ./loading.jl:1056 [inlined]
[3] require(uuidkey::Base.PkgId)
# Base ./loading.jl:1022
[4] require(into::Module, mod::Symbol)
# Base ./loading.jl:997
The same yes/no happens when trying to run the tests.
(AoC 2021) pkg> activate ../Utils/
Activating project at `~/src/me/AoC/Utils`
(Utils) pkg> test
Testing Utils
Status `/tmp/jl_AGawpC/Project.toml`
[c4255648] Utils v0.1.0 `~/src/me/AoC/Utils`
[8dfed614] Test `#stdlib/Test`
Status `/tmp/jl_AGawpC/Manifest.toml`
[79e6a3ab] Adapt v3.3.1
----- 8< snipped 8< -----
[4536629a] OpenBLAS_jll `#stdlib/OpenBLAS_jll`
[8e850b90] libblastrampoline_jll `#stdlib/libblastrampoline_jll`
Testing Running tests...
Test Summary: | Pass Total
#something_nothing | 15 15
Testing Utils tests passed
(Utils) pkg> activate ../ProblemParser/
Activating project at `~/src/me/AoC/ProblemParser`
(ProblemParser) pkg> test
Testing ProblemParser
Status `/tmp/jl_6v5Y3D/Project.toml`
[16064a1e] ProblemParser v0.1.0 `~/src/me/AoC/ProblemParser`
[8dfed614] Test `#stdlib/Test`
Status `/tmp/jl_6v5Y3D/Manifest.toml`
[16064a1e] ProblemParser v0.1.0 `~/src/me/AoC/ProblemParser`
[2a0f44e3] Base64 `#stdlib/Base64`
----- 8< snipped 8< -----
[9e88b42a] Serialization `#stdlib/Serialization`
[8dfed614] Test `#stdlib/Test`
Testing Running tests...
ERROR: LoadError: ArgumentError: Package ProjectParser not found in current path:
- Run `import Pkg; Pkg.add("ProjectParser")` to install the ProjectParser package.
Stacktrace:
[1] require(into::Module, mod::Symbol)
# Base ./loading.jl:967
[2] include(fname::String)
# Base.MainInclude ./client.jl:451
[3] top-level scope
# none:6
in expression starting at /home/tsbr/src/me/AoC/ProblemParser/test/runtests.jl:1
ERROR: Package ProblemParser errored during testing
What is the difference between the two? What makes one work and the other not?
I just don't see it.
Ah, you have the module name defined wrong in src/ProblemParser.jl - the first line is module ProjectParser instead of module ProblemParser.

How to package a header-only C++ library for iOS with Bazel?

I'm working on a header-only C++ library with several dependencies (xtensor, fmtlib, Boost, Accelerate.framework) using Bazel as the build system. There is a currently a single class whose interface I'd like to expose to an iOS app (to be) written in Objective-C++.
I have tried using something along the lines of the following configuration:
BUILD:
package(
default_visibility = ["//visibility:public"],
)
ios_static_framework(
name = "MyFramework",
hdrs = ["objc_lib.h"],
deps = [":objc_lib"],
families = ["iphone"],
minimum_os_version = "12.0",
)
objc_library(
name = "objc_lib",
hdrs = ["objc_lib.h"],
non_arc_srcs = ["objc_lib.mm"],
deps = [":cc_lib"],
sdk_frameworks = ["Accelerate"],
)
cc_library(
name = "cc_lib",
hdrs = ["cc_lib.hpp"],
deps = [
# several header-only targets
":dep1",
":dep2",
],
)
...
cc_lib.hpp:
#include "dep1.hpp"
#include "dep2.hpp"
class MyClass {
// ...
};
objc_lib.h:
#include "cc_lib.hpp"
objc_lib.mm:
#include "objc_lib.h"
Running bazel build //... produces a framework archive with the following structure that doesn't have any of the headers of objc_lib's transitive dependencies.
MyFramework.framework
├── Headers
│   ├── MyFramework.h
│   └── objc_lib.h
├── Modules
│   └── module.modulemap
└── MyFramework
Is there a "canonical" way of packaging a header-only library for iOS (as a framework or otherwise) with Bazel?

C++ project with Bazel and GTest

I want to create a Bazel C++ project with gtest for unit tests.
What is the minimal setup?
(I only have Bazel installed on my computer and I am running under Linux)
This is even easier now that googletest provides a BUILD file:
In WORKSPACE
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "gtest",
remote = "https://github.com/google/googletest",
branch = "v1.10.x",
)
In BUILD
cc_test (
name = "hello_test",
srcs = [
"hello_test.cc",
],
deps = [
"#gtest//:gtest",
"#gtest//:gtest_main" # Only if hello_test.cc has no main()
],
)
The project structure is:
.
├── bin
│   ├── BUILD
│ ├── hello.cpp
├── MyLib
│   ├── BUILD
│ ├── message.hpp
│ ├── message.cpp
│ ├── ...
├── test
│ ├── BUILD
│ ├── message_test.cpp
│ ├── ...
├── gmock.BUILD
└── WORKSPACE
Files related to Bazel+GTest
WORKSPACE
There you download gtest from github:
new_git_repository(
name = "googletest",
build_file = "gmock.BUILD",
remote = "https://github.com/google/googletest",
tag = "release-1.8.0",
)
You define a gmock BUILD file defined below:
gmock.BUILD
This BUILD file is in charge of compiling gtest/gmock:
cc_library(
name = "gtest",
srcs = [
"googletest/src/gtest-all.cc",
"googlemock/src/gmock-all.cc",
],
hdrs = glob([
"**/*.h",
"googletest/src/*.cc",
"googlemock/src/*.cc",
]),
includes = [
"googlemock",
"googletest",
"googletest/include",
"googlemock/include",
],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
)
cc_library(
name = "gtest_main",
srcs = ["googlemock/src/gmock_main.cc"],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
deps = [":gtest"],
)
test/BUILD
This build file generate the tests:
cc_test(
name = "MyTest",
srcs = glob(["**/*.cpp"]),
deps = ["//MyLib:MyLib",
"#googletest//:gtest_main"],
)
The test/message_test.cpp file is defined by:
#include "gtest/gtest.h"
#include "MyLib/message.hpp"
TEST(message_test,content)
{
EXPECT_EQ(get_message(),"Hello World!");
}
And that is all! The other files are defined as usual:
Files for the supporting example
MyLib/BUILD
Creates the libMyLib.so and libMyLib.a libraries.
cc_library(
name="MyLib",
hdrs=glob(["**/*.hpp"]),
srcs=glob(["**/*.cpp"]),
visibility = ["//visibility:public"],
)
with a basic message.hpp
#include <string>
std::string get_message();
and message.cpp
#include "MyLib/message.hpp"
std::string get_message()
{
return "Hello World!";
}
example.
bin/BUILD
Creates the hello executable.
cc_binary(
name = "hello",
srcs = ["hello.cpp"],
deps = ["//MyLib:MyLib"],
)
which is:
#include "MyLib/message.hpp"
#include <iostream>
int main()
{
std::cout << "\n" << get_message() << std::endl;
return EXIT_SUCCESS;
}
Usage:
Compiles all targets:
This will also download gtest from its github repo and compile it
bazel build ...
Checks the hello target:
You can run it with:
bazel run bin:hello
Running your tests using GTest
That was the main point of this note:
bazel test ... --test_output=errors
You should get something like:
INFO: Analysed 3 targets (0 packages loaded).
INFO: Found 2 targets and 1 test target...
INFO: Elapsed time: 0.205s, Critical Path: 0.05s
INFO: Build completed successfully, 2 total actions
//test:MyTest
PASSED in 0.0s
Executed 1 out of 1 test: 1 test passes.
Reproduce the results
For your ease I have created a github repo containing this example. I hope it works out of the box.
The current recommended practice is to use http_archive to avoid depending on the system git and take advantage of repository cache.
In WORKSPACE
# 5376968f6948923e2411081fd9372e71a59d8e77 is the commit sha for v1.12.0.
# Periodically update to the latest to "live at head"
http_archive(
name = "com_google_googletest",
sha256 = "199e68f9dff997b30d420bf23cd9a0d3f66bfee4460e2cd95084a2c45ee00f1a",
strip_prefix = "googletest-5376968f6948923e2411081fd9372e71a59d8e77",
urls = ["https://github.com/google/googletest/archive/5376968f6948923e2411081fd9372e71a59d8e77.zip"],
)
In test/BUILD
cc_test(
name = "test_greet",
srcs = ["greeting_test.cpp"],
deps = [
"//src:greeting",
"#com_google_googletest//:gtest_main",
],
)

Eunit error with multiple apps

I have the following directory structure:
myapp
├── apps
│   ├── myapp
│   ├── myotherapp
│   └── myapp_common
├── deps
│   ├── cowboy
......
I run eunit using rebar as follows in the main myapp directory:
./rebar skip_deps=true eunit
It correctly runs eunit for three apps in apps/. After that it tries to run eunit in the parent myapp directory and throws the following error:
......
==> myapp (eunit)
ERROR: eunit failed while processing /home/msheikh/myapp: {'EXIT',{{badmatch,{error,{1,
"cp: missing destination file operand after `.eunit'\nTry `cp --help' for more information.\n"}}},
[{rebar_file_utils,cp_r,2,[]},
{rebar_eunit,eunit,2,[]},
{rebar_core,run_modules,4,[]},
{rebar_core,execute,4,[]},
{rebar_core,process_dir,4,[]},
{rebar_core,process_commands,2,[]},
{rebar,main,1,[]},
{escript,run,2,[{file,"escript.erl"},{line,727}]}]}}
Question: How can I fix this or prevent eunit from running for the parent myapp directory?
The rebar.config file in the main myapp directory looks like this:
{lib_dirs, ["deps", "apps"]}.
{deps, [
{lager, ".*", {git, "https://github.com/basho/lager.git", {branch, "master"}}},
{jsx, ".*", {git, "git://github.com/talentdeficit/jsx.git", {tag, "v0.9.0"}}},
{cowboy, "", {git, "git://github.com/extend/cowboy.git", {branch, "master"}}},
....
]}.
{require_otp_vsn, "R15"}.
{escript_incl_apps, [getopt]}.
{erl_opts, [
debug_info,
warn_missing_spec,
{parse_transform, lager_transform}
]}.
{eunit_opts, [verbose]}.
{validate_app_modules, false}.
{sub_dirs, [
"apps/myapp/",
"apps/myotherapp/",
"apps/myapp_common/"]}.
I have the same project structure, and it works.
Are you sure you don't have src, test, ebin folders in the top-level directory?
If not, what happens if you mkdir .eunit? (I am not suggesting to keep this, but go looking for a solution from there).