1

I am trying to learn cmake. My goal is to create a library and compile some example program with it.

my project tree

/home/username/project/--|
                         |-- CMakeLists.txt
                         |-- example/--|
                         |             |-- CMakeLists.txt
                         |             |-- main.cpp
                         |                             
                         |--fraction/--|
                         |             |--CMakeLists.txt
                         |             |--include/--|
                         |             |            |--fraction.h
                         |             |
                         |             |--src/------|
                         |             |            |--fraction.cpp 
                         |             
                         |--operation/-|
                         |             |--CMakeLists.txt
                         |             |--include/--|
                         |             |            |--operation.h
                         |             |
                         |             |--src/------|
                         |             |            |--operation.cpp             

in fraction.h

#include <iostream>

using namespace std;

int least_common_multiple(int d1, int d2)
{
    int large = d1 > d2 ? d1 : d2;
    int small = large == d1 ? d2 : d1;
    for (int i = 1; ; i++)
    {
        if ((large*i) % small == 0)
        {
            large = large*i;
            break;
        }
    }
    return large;
}

class fraction
{
    private:
        int numerator, denominator; 
    public:
        fraction(int numr = 1, int denomr = 1);
        fraction operator+(fraction &frac);
        ~fraction();
};

in fraction.cpp

#include "fraction.h"

fraction::fraction(int numr, int denomr) : numerator(numr), denominator(denomr) {}

fraction fraction::operator+(fraction &frac)
{
    fraction sum;
    sum.numerator = denominator*frac.numerator + numerator*frac.denominator;
    sum.denominator = least_common_multiple(denominator, frac.denominator);
    return sum;
}

fraction::~fraction() {}

in CMakeLists.txt inside fraction

set(target fraction)

# Sources
set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(source_path  "${CMAKE_CURRENT_SOURCE_DIR}/src")

# Set the source files to compile
file(GLOB_RECURSE sources ${source_path}/*.cpp)
file(GLOB_RECURSE headers ${include_path}/*.h)

# Build library
add_library(${target} ${sources})

# Add include directories
target_include_directories(${target} PUBLIC ${include_path})

install(FILES ${headers} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${target})

install(TARGETS ${target}
    DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)

in operation.h

#include "fraction.h"

class operation
{
    public:
        fraction add(fraction a, fraction b);
        ~operation();
};

in operation.cpp

#include "operation.h"

fraction operation::add(fraction a, fraction b)
{
    return a+b;
}

operation::~operation() { }

in CMakeLists.txt inside operation

set(target operation)

# Sources
set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(source_path  "${CMAKE_CURRENT_SOURCE_DIR}/src")

# Set the source files to compile
file(GLOB_RECURSE sources ${source_path}/*.cpp)
file(GLOB_RECURSE headers ${include_path}/*.h)

add_library(${target} ${sources})
target_link_libraries(${target} fraction)

# Add include directories
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")

install(FILES ${headers} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${target})
install(TARGETS ${target}
    DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)

in main.cpp

#include "operation.h"

int main()
{
    fraction a(3,5);
    fraction b(4,7);

    operation ADD;
    ADD.add(a,b);

    return 0;
}

in CMakeLists.txt inside example

add_executable(example1 main.cpp)

set(headers ${PROJECT_SOURCE_DIR}/fraction/include ${PROJECT_SOURCE_DIR}/operation/include)

target_include_directories(example1 PRIVATE ${headers})

and in the root CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(demo)

set(CMAKE_BUILD_TYPE "Release")

add_subdirectory(fraction)
add_subdirectory(operation)
add_subdirectory(example)

when I compile with the following commands

mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=path/to/build folder inside project ..
make

this error is generated

[ 16%] Building CXX object fraction/CMakeFiles/fraction.dir/src/fraction.cpp.o
[ 33%] Linking CXX static library libfraction.a
[ 33%] Built target fraction
[ 50%] Building CXX object operation/CMakeFiles/operation.dir/src/operation.cpp.o
[ 66%] Linking CXX static library liboperation.a
[ 66%] Built target operation
[ 83%] Building CXX object example/CMakeFiles/example1.dir/main.cpp.o
[100%] Linking CXX executable example1
/usr/bin/ld: CMakeFiles/example1.dir/main.cpp.o: in function `main.cold':
main.cpp:(.text.unlikely+0x4): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.unlikely+0xc): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.unlikely+0x14): undefined reference to `operation::~operation()'
/usr/bin/ld: main.cpp:(.text.unlikely+0x1f): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.unlikely+0x27): undefined reference to `fraction::~fraction()'
/usr/bin/ld: CMakeFiles/example1.dir/main.cpp.o: in function `main':
main.cpp:(.text.startup+0x3a): undefined reference to `fraction::fraction(int, int)'
/usr/bin/ld: main.cpp:(.text.startup+0x4c): undefined reference to `fraction::fraction(int, int)'
/usr/bin/ld: main.cpp:(.text.startup+0x85): undefined reference to `operation::add(fraction, fraction)'
/usr/bin/ld: main.cpp:(.text.startup+0x8d): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.startup+0x95): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.startup+0x9d): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.startup+0xa5): undefined reference to `operation::~operation()'
/usr/bin/ld: main.cpp:(.text.startup+0xad): undefined reference to `fraction::~fraction()'
/usr/bin/ld: main.cpp:(.text.startup+0xb5): undefined reference to `fraction::~fraction()'
collect2: error: ld returned 1 exit status
make[2]: *** [example/CMakeFiles/example1.dir/build.make:97: example/example1] Error 1
make[1]: *** [CMakeFiles/Makefile2:186: example/CMakeFiles/example1.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

If I do #include "fraction.h" inside main.cpp it generates redifinition error. How to link everything properly?

  • 1) Use `#pragma once` in headers; 2) Use PUBLIC in `target_link_libraries` to express transitive usage requirements (users should also link to fraction if they link to operation) – Osyotr Nov 23 '22 at 20:03
  • Globs in CMake are rarely a good idea, I'd replace them with actual filenames. I'd guess you've added files and CMake hasn't noticed (one of the perils of globs) – Alan Birtles Nov 23 '22 at 20:04
  • 2
    Oh, I've noticed that you don't even link to `fraction`/`operation` in `example1` – Osyotr Nov 23 '22 at 20:07

0 Answers0