CMake project with flex/bison - c++

I'm working on an hobby project in C++, using flex and bison. I'm using CMake as my build system, and I had struggle in making all work together.
I'm quite new to CMake so the documentation seems quite minimal to me. It is showed how to link everything in one executable.
But I'm trying to keep all the parts of my code separated, so I would like to have my flex/bison file in a subfolder. My structure is this:
.
├── build
| └-- ...
├── CMakeLists.txt
├── flexbison
│   ├── CMakeLists.txt
│   ├── lexer.ll
│   └── parser.yy
├── lib
│   ├── CMakeLists.txt
│   └── ...
├── main.cpp
└── run.sh
So I want to have my yyparse function separate from my main.cpp, as well as my other C++ files. I managed to get it done in this way:
project_root/CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(progetto-di-prova VERSION 0.1)
add_subdirectory(flexbison)
add_executable(executable main.cpp)
target_include_directories(executable PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_BINARY_DIR}/flexbison"
)
set_target_properties(executable PROPERTIES CXX_STANDARD 11)
target_link_libraries(executable parserlib)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
flexbison/CMakeLists.txt:
find_package(FLEX)
find_package(BISON)
flex_target(lexer lexer.ll "${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp")
bison_target(parser parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.h)
add_flex_bison_dependency(lexer parser)
add_library(parserlib STATIC
${FLEX_lexer_OUTPUTS}
${BISON_parser_OUTPUTS}
)
the main.cpp file:
#include <parser.h> // <-this was the line causing troule
#include <iostream>
extern int yylex(void);
int main(int argc, char *argv[])
{
yyparse();
return 0;
}
Now, the point is: is this the correct way of doing so? Is it better to put the main function in the bison code?
My main problem, as highlighted in the code, was to include the parser.h header file in the main.cpp. Is this the CMake-way to do it?
Thanks in advance!

If I were you I would have:
flexbison/CMakeLists.txt:
...
add_library(parserlib STATIC
${FLEX_lexer_OUTPUTS}
${BISON_parser_OUTPUTS}
)
target_include_directories(parserlib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<INSTALL_INTERFACE:include/parserlib> # <prefix>/include/parserlib
)
note: $<INSTALL_INTERFACE:include/parserlib> need to be adapted if you want and how you install parserlib...
project_root/CMakeLists.txt:
...
target_include_directories(executable PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
...
Since executable is "target_link" against parserlib which provides public include directories, you don't need to list these directories anymore in executable's include dirs.
ref: https://cmake.org/cmake/help/latest/command/target_include_directories.html

Related

CMake creating libraries that depend each other

my goal is to create libraries like client and generator and use them in src/main.cpp, but sometimes these libraries depend each other.
In this case: client/User.hpp uses generator/IdGenerator.hpp
Project
│
├── CMakeLists.txt
├── libs
│   ├── CMakeLists.txt
│   ├── client
│   │   ├── CMakeLists.txt
│   │   ├── User.cpp
│   │   └── User.hpp
│   └── generator
│   ├── CMakeLists.txt
│   ├── IdGenerator.cpp
│   ├── IdGenerator.hpp
│   └── Types.hpp
└── src
└── main.cpp
Project/CMakeLists.txt:
cmake_minimum_required (VERSION 3.8)
project(game-project VERSION 0.1.0)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_FLAGS "-Wall -Wextra -O0 -std=c++20")
add_executable (game src/main.cpp)
add_subdirectory(libs)
target_link_libraries(game libclient libgenerator)
libs/CMakeLists.txt:
add_subdirectory(generator)
add_subdirectory(client)
libs/client/CMakeLists.txt:
add_library(libclient STATIC
User.cpp
User.hpp
)
include_directories(generator/)
target_link_libraries(libclient libgenerator)
libs/generator/CMakeLists.txt:
add_library(libgenerator STATIC
IdGenerator.cpp
IdGenerator.hpp
Types.hpp
)
C++ files:
main.cpp:
#include <client/User.hpp>
int main(int argc, const char* argv[])
{
User user;
return 0;
}
client/User.hpp:
#pragma once
#include <generator/IdGenerator.hpp>
class User
{
Identifier id = IdGenerator::generateId();
};
When I run make after cmake, I get this error:
In file included from Project/libs/client/User.cpp:1:
Project/libs/client/User.hpp:3:10: fatal error: generator/IdGenerator.hpp: No such file or directory
3 | #include <generator/IdGenerator.hpp>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
Sorry for the verbose summary, I was able to shorten the example this much. What do you think the problem is?
This question might seem like a duplicate of CMake libraries that depend on each other but as you can see I'm already applying the
include_directories(generator/).
I misunderstood what the problem was initially, but now I think this might help.
Try adding to the CMakeLists.txt of your client instead of
include_directories(generator/)
this command
target_include_directories(libclient PUBLIC <Path to your generator file>)
Maybe you need to experiment a little to get the path correct as you might need to either specify it from the root directory or the current one (where this CMakeLists.txt resides), but it might solve your problem.

Running googletest with separated src- and test-folders

Here my minimal example (https://github.com/theCollectiv/cmake_minimal). I first got this project structure with seperate Headerplacement (-> src and include)
.
├── 1Cmake.sh
├── 2runExe.sh
├── 3cleanTarget.sh
├── 4runTest.sh
├── 7VersionOfCurrentTools.sh
├── build
│   ├── CMakeCache.txt
│   ├──...
│ ...
├── CMakeDefaults.txt
├── CMakeLists.txt
├── compile_commands.json -> ...
├── data
├── docs
│   ├── ...
├── include
│   └── addition
│   └── addition.hpp
├── src
│   ├── addition
│   │   └── addition.cpp
│   ├── main.cpp
├── tests
│   └── test01_proofOfWork
│   ├── CMakeLists.txt
│   └── tests.cpp
main.cpp
#include "addition.hpp"
#include <iostream>
int main() {
int a = 5;
int b = 3;
std::cout << "a is " << a << "\nb is " << b << "\nThe Sum of both "
<< add(a, b) << std::endl; }
addition.cpp
#include "addition.hpp"
int add(int a, int b) { return a + b; }
header of addition.cpp: addition.hpp
#pragma once
int add(int a, int b);
The CMakeList.txt in the root-folder looks like this:
cmake_minimum_required(VERSION 3.21.2)
set (This
Project_main)
project(${This}
LANGUAGES CXX
VERSION 1.000)
enable_testing()
find_package(GTest REQUIRED)
add_subdirectory(./tests/test01_proofOfWork)
# Custom Variables
set(QUELLE src/main.cpp)
set(ZIEL finalExecutable)
# integrate lib (with .cpp and its header)
set(LIB_1 addition)
add_library(
${LIB_1}
STATIC src/addition/addition.cpp
)
# add .hpp hinzufuegen and connect to lib
target_include_directories(
${LIB_1}
PUBLIC include/addition
)
# add executable
add_executable(${ZIEL} ${QUELLE})
# linking the lib
target_link_libraries(
${ZIEL}
PRIVATE ${LIB_1}
# PUBLIC ${LIB_1}
)
Now I want to add unit tests by using googletest. I'm using ubuntu and googletest is installed in my systempath (with shared libs) and working.
I'm now a little bit stuck, getting googletest well integrated in the project.
The CMakeList.txt in the tests-dir:
# Name of the Tests
set (This
runTest)
# Location of the test
set (Sources
tests.cpp)
# executable of the test
add_executable(${This} ${Sources})
# linking libs for the test
target_link_libraries(${This} PUBLIC
GTest::gmock
GTest::gtest
GTest::gmock_main
GTest::gtest_main)
# registrating the test
gtest_discover_tests(${This})
The test.cpp
// tests.cpp
// #include "./../../include/addition/addition.hpp"
#include <gtest/gtest.h>
#include <gtest/internal/gtest-internal.h>
// bool foo(){
// return true;
// }
//
//
// TEST(Simpletest, trueEqualsTrue) {
// EXPECT_TRUE(foo());
// EXPECT_FALSE(foo());
// EXPECT_EQ(true, foo());
// ASSERT_FALSE(foo());
// EXPECT_EQ(false, foo());
// }
//
TEST(SquareRootTest, PositiveNos) {
ASSERT_EQ(18.0, add(1,17));
EXPECT_EQ(6, add(1,6));
ASSERT_EQ(25.4, add(55, -1));
ASSERT_EQ(0, add(-4, 4));
}
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
The de-commented test code works and compiles fine, so gtest works fine (means: if I m testing code, thats in the same file the code is tested).
But... (I'm new to this stuff, so maybe its an easy (or noob) question):
The reason, why I added the googletest is to test the code in the src- or -include dir. Therefore I got the add all the headers and files in the these dirs for test cases all the time.
Is there a good way to make all of them available for the possible tests that I might run without configuring it all the time?
My problem is,
that the de-commented code works/compiles fine (of code in the same location like the test is). That means:
googletest is installed correctly
the tests-CMakeLists.txt is working correctly (and the root-CmakeLists.txt too)
If I want to compile the test-code, that is not de-commented (that means the 'code to test' is not in the same file like the test itself), it is complaining for missing headers. If I add them, the compilation of the tests quits with errors (like "cmake line XXX" ...doesnt tell me anything or "undefined reference too"). The problem I got
it doesnt compile the tests (obvious)
even if it would compile, I got to rebuild all the things (adding headers in the test-source-code) in the test-directory like I did in the root-directory (similar/same file linking in both of them). If I got a more complex project structure (using a function in a file which uses functions from another file), this is 'doing the same exact same stuff in for the normal project (src and include) and in the tests-dir'. Or am I wrong at this point?
Solution:
# linking libs for the test
target_link_libraries(${This} PUBLIC
GTest::gmock
GTest::gtest
GTest::gmock_main
GTest::gtest_main
# Solution: Got to link the lib(s) (which I want to test in the root-dir) against the test-executable
addition
)
You are not linking yout test project against your library. So it doesn't use your library. Link it.
target_link_libraries(${This} addition)

how to create a c++ dynamic library using project class?

I have a c++ project with many classes and sub-directory. I want to convert my communication code subdirectory to share library(dynamic library). But I have little experience with C++ and cmake. I don't know how to do. I share my test code tree and cmakelist.txt
.
├── CMakeLists.txt
├── main.cpp
└── sim_haberlesme
├── hab_ortak
│   ├── haberlesmeAraKatman.cpp
│   └── haberlesmeAraKatman.h
├── paketleyici2.cpp
├── paketleyici2.h
├── paketleyici.cpp
├── paketleyici.h
└── protobuf
├── protobuf_deserialize.cpp
├── protobuf_deserialize.h
├── protobuh_serialize.cpp
└── protobuh_serialize.h
cmake_minimum_required(VERSION 3.5)
project(CmakeTest)
set(CMAKE_CXX_STANDARD 14)
add_executable(CmakeTest main.cpp sim_haberlesme/paketleyici2.cpp sim_haberlesme/paketleyici2.h sim_haberlesme/paketleyici.cpp sim_haberlesme/paketleyici.h sim_haberlesme/protobuf/protobuh_serialize.cpp sim_haberlesme/protobuf/protobuh_serialize.h sim_haberlesme/protobuf/protobuf_deserialize.cpp sim_haberlesme/protobuf/protobuf_deserialize.h sim_haberlesme/hab_ortak/haberlesmeAraKatman.cpp sim_haberlesme/hab_ortak/haberlesmeAraKatman.h)
(If you want me to share the code, I'll share)
I want to create dynamic lib with using sim_haberlesme directory. But this directory have too many classes and subdirectory. In this reason i don't know how to start and how to edit cmakelist file.
Can you tell me how to start convert .cpp file to .so file and how to edit cmakelist.txt file?
By the way you can share tutorials
Something like that in your CMakeLists:
cmake_minimum_required(VERSION 3.5)
project(CmakeTest)
set(CMAKE_CXX_STANDARD 14)
add_library(sim_haberlesme SHARED)
target_sources(sim_haberlesme PRIVATE
hab_ortak/haberlesmeAraKatman.cpp
hab_ortak/haberlesmeAraKatman.h
paketleyici2.cpp
paketleyici2.h
paketleyici.cpp
paketleyici.h
protobuf/protobuf_deserialize.cpp
protobuf/protobuf_deserialize.h
protobuf/protobuh_serialize.cpp
protobuf/protobuh_serialize.h
)
add_executable(test)
target_sources(test PRIVATE main.cpp)
target_link_libraries(test PRIVATE sim_haberlesme)

CMake include library in another library

I have a given project structure
.
├── CMakeLists.txt
├── lib
│   ├── lodepng
│   │   ├── CMakeLists.txt
│   │   └── src
│   │   ├── lodepng.cpp
│   │   └── lodepng.h
│   └── pixel_reader
│   ├── CMakeLists.txt
│   └── src
│   ├── hello.cpp
│   └── hello.h
├── main.cpp
With the following CMakeLists
./CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(pov_system VERSION 1.0)
add_subdirectory(lib/lodepng)
add_subdirectory(lib/pixel_reader)
add_executable(pov_system main.cpp)
target_link_libraries(pixel_reader PRIVATE lodepng)
target_link_libraries(pov_system PRIVATE pixel_reader)
./lodepng/CMakeLists.txt
add_library(
lodepng
src/lodepng.cpp
src/lodepng.h
)
target_include_directories(lodepng PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
./pixel_reader/CMakeLists.txt
add_library(
pixel_reader SHARED
src/hello.cpp
src/hello.h
)
target_include_directories(pixel_reader PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
As one can see, I try to link the 'lodepng' library to the 'pixel_reader' library and include the 'lodepng.h' to the 'hello.h' file.
But at the moment I get the following error while trying to build the project.
[build] <path-to-project>/pov_system/lib/pixel_reader/src/hello.h:2:10: fatal error: lodepng.h: No such file or directory
[build] 2 | #include "lodepng.h"
[build] | ^~~~~~~~~~~
[build] compilation terminated.
Question
Why is my code not finding the 'lodepng.h' file or (and even more important) is it a good practice to link from one library to another?
Maybe two really simple questions, but just started to dive into the world of CMake, Compiling, etc... and I really appreciate your help.
Why is my code not finding the 'lodepng.h' file or (and even more important)
Because you probably didn't give it correct path. One way to fix that would be to give the exact path in hello.h
#include "../../lodepng/src/lodepng.h
Second way is to use target_include_directories:
target_include_directories(pixel_reader PUBLIC "../../lodepng/src/")
is it a good practice to link from one library to another?
It depends on your project. If library A requires library B, then yes, it is okay in my opinion.
More importantly, you are creating the target in the wrong place i.e., in the root CMakeLists file. It must be done in the directory in which target is created.
./pixel_reader/CMakeLists.txt
# create target
add_library(
pixel_reader SHARED
src/hello.cpp
src/hello.h
)
target_link_libraries(pixel_reader PRIVATE lodepng) #link library where target is created
target_include_directories(pixel_reader PUBLIC "../../lodepng/src/")
target_include_directories(pixel_reader PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
Your pixel_reader library target possibly needs lodepng.h header to compile, because it depends on it.
something like
target_include_directories(pixel_reader PUBLIC "PATH_TO_LODE_PNG_HEADER_DIRECTORY")
could solve this problem.

How to use modern cmake for this project

I have for some time had some problems incorporating modern cmake into a project I've been working fr some while and seem to be stuck on how i should define the the individual CMakeLists, and the top-level CMakeList.
My project structure is pretty simple.
├── build
└── src
├── include
│   ├── database
│   │   ├── database.cpp
│   │   └── database.h
│   ├── match
│   │   ├── match.h
│   │   └── mathc.cpp
│   ├── record
│   │   ├── lib
│   │   ├── record.cpp
│   │   └── record.h
│   └── spectogram
│   ├── spectogram.cpp
│   └── spectrogram.h
└── main.cpp
main.cpp are linked to all the includes, and some of the includes should know the presence of other includes, meaning, I should be able to include match.h in
database.h. Some third-party libs are also going to be used, in this case I am using portaudio, download and installed using externalproject_add, which should only be visible for the include which holds the library, in this case record, should only see this.
But how I should define the individual CMakeList, is currently unknown.
I've scouted the net for a proper way of setting this up, but cannot seem to find one that I understand.
How do i define the CMakeLists for this project, and how do i make sure that the includes, are visible for the Main.cpp and the includes files that need them, and how do I make third-party-libraries visible only for the includes that it is being used for.
CMakeLists example structure tried:
CMakeLists.txt
cmake_minimum_required(VERSION 3.2)
project(soundcloud)
#add_subdirectory(src/database)
#add_subdirectory(src/match)
#add_subdirectory(src/record)
add_subdirectory(src/include/spectogram)
add_executable(cmakeDemo src/main.cpp)
SET_TARGET_PROPERTIES(cmakeDemo PROPERTIES LINKER_LANGUAGE Cxx)
target_link_libraries(cmakeDemo spectogram)
#target_link_libraries(cmakeDemo database match record spectogram)
src/include/database/CMakeLists.txt
add_library(spectogram STATIC .)
target_include_directories(spectogram PUBLIC .)
getting error message:
CMake Error: Error required internal CMake variable not set, cmake may not be built correctly.
Missing variable is:
CMAKE_Cxx_LINK_EXECUTABLE
CMake Error: Cannot determine link language for target "spectogram".
CMake Error: CMake can not determine linker language for target: spectogram
You can use the PRIVATE property of target_include_directories. When PRIVATE, it means that the include directories will be available to the sources of the target. When PUBLIC, it will also be available to whomever links to the target.
If I were writing this in a single CMakeLists.txt, I'd do this:
cmake_minimum_required(VERSION 3.0)
add_library(database STATIC src/include/database.cpp)
target_include_directories(database PUBLIC src/include/database)
################
add_library(match STATIC src/include/mathc.cpp)
target_include_directories(match PUBLIC src/include/match)
################
include(ExternalProject)
ExternalProject_Add(portAudio ${SomeCommands})
add_library(record STATIC src/include/record.cpp)
target_include_directories(record PUBLIC src/include/record) # When cmakeDemo links to this, it'll get these includes
target_include_directories(record PRIVATE src/include/record/lib) # When cmakeDemo links to this, it won't get these includes
target_link_libraries(record portAudio)
#################
add_library(spectogram STATIC src/include/spectogram.cpp)
target_include_directories(spectogram PUBLIC src/include/spectogram)
##################
add_executable(cmakeDemo src/main.cpp)
target_link_libraries(cmakeDemo database match record spectogram)
If I were to do this with distributed CMakeLists.txt, I'd split the files where the ###### lines are, fix the paths, and use add_subdirectory() to include the sub-directories from higher-level cmake files.
First, you don't link to includes, you just "include" them during compilation.
Some people put one CMakeList.txt into every directory that contains a fairly independent compilation unit.
Some people just use one big one at the the top.
Having just one CMakeList.txt file makes it easier to start, but if the project gets huge, things get messy.
For every compilation unit, you can specify the include directories with target_include_directories