0

I don't really know how to phrase this question exactly, but this is my best attempt...

I recently started working on a large-ish project, which I wanted to break down into several component parts.

Here's how two of those components work:

  • First component: Is itself an execuable program. Produced by CMake. Takes some source files for library code and compiles and links with a main.cpp file.
  • Second component: Should use the same libraries compiled in the first component, but a different main.cpp file, plus some new libraries.

The "libraries" in this instance are just header and source files containing some classes which themselves are dependent on some other libraries. (SDL, to be exact.)

What I want to do is somehow get CMake to compile the "First component" and also produce one or more object files which I can link to (again using CMake) in the "Second component".

I hope that makes sense, but it probably isn't obvious, so here are some further details:

  • First component uses SDL to render and draw text to an SDL window. The various classes I have written do a bunch of things including loading fonts from file, finding them on the system, as well as managing SDL objects (such as a window and the rendered text objects). The main.cpp file is just for testing purposes. It allows an executable to be compiled which demonstrates that everything works as expected. Consider it a test driven development file if you wish, although it doesn't contain any formal tests.

  • The second component depends on the object files which gcc/g++ would normally produce from compilation of the first component. In other words, I want to be able to take some object files from the first component and use them to compile the second component. I could copy and paste the source files from the first component to the second and compile everything from scratch, but this will mess up my git repository and also isn't particularly efficient.

I am sure it must be possible to get cmake to produce some object files from the first component and then link those when compiling the second component. Of course, it will require two seperate cmake processes, probably, but that is fine.

Please note; I do not want to compile a shared object. I assume what I want is to statically link my object files, however I would assume by default cmake will invoke a dynamic link process. In other words, the SDL libraries should still be dynamically linked. My own libraries should not be dynamically linked because they are not available on linux systems, they are only available on my own linux system.

Minimal working example?

This is the kind of thing which a MWE is a bit pointless for, but here is an attempt none the less.

The first directory is setup like this

first/
    main.cpp
    first.h
    first.cpp
    CMakeLists.txt
    build [d]

The second directory contains only a main.cpp file.

CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)

project(testproject)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)

find_package(SDL2 REQUIRED)
include_directories(testproject ${SDL2_INCLUDE_DIRS})

set(SOURCE_FILES main.cpp first.cpp)

add_executable(testproject ${SOURCE_FILES})

target_link_libraries(testproject ${SDL2_LIBRARIES})


main.cpp:
#include "first.h"

int main()
{

    SDL_Init(SDL_INIT_VIDEO);

    // using first library in main function
    SDL_Window *window = GetWindow();

    // doesn't draw anything, but you can
    // press the window X to quit
    for(bool quit = false; quit == false; )
    {
        SDL_Event event;
        while(SDL_PollEvent(&event) != 0)
        {
            if(event.type == SDL_QUIT)
            {
                quit = true;
            }
        }
    }

    FreeWindow(window);

    SDL_Quit();

    return 0;
}


first.h:
#ifndef FIRST_H
#define FIRST_H

#include <SDL2/SDL.h>

SDL_Window* GetWindow();
void FreeWindow(SDL_Window* &window);

#endif // FIRST_H


first.cpp
#include "first.h"

SDL_Window* GetWindow()
{

    SDL_Window *window =
        SDL_CreateWindow(
            "a test window",
            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
            800, 600, SDL_WINDOW_SHOWN);

    return window;
}


void FreeWindow(SDL_Window *&window)
{
    SDL_DestroyWindow(window);
    window = nullptr;
}

It's a bit of a weird example. I've tried to include some very minimal functions which would produce an object file as well as making use of the external SDL library. The second folder in this case could literally just be a copy of main.cpp, a copy of the header file, or a line in CMakeLists.txt to tell the make system where to find the header, and then it should have some way to find the object files, which currently don't exist, becuase the first CMakeLists.txt doesn't produce them yet.

Things I don't know how to do:

  • Tell cmake in the directory first to produce a directory with (ideally a single) object file containing the library code, but not the object code for main.cpp
  • Tell cmake in the directory second where the header files are inside first
  • Tell cmake in the directory second where the object file for the library code produced by the directory first is, and then how to link that to a new main.cpp file in second

Hopefully that is clear enough but this is presumably a somewhat non-trivial thing.

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225

1 Answers1

0

First component: Is itself an execuable program. Produced by CMake. Takes some source files for library code and compiles and links with a main.cpp file.

Second component: Should use the same libraries compiled in the first component, but a different main.cpp file, plus some new librariess

So just:

find_library(SDL2 REQUIRED)

add_library(common_stuff some_source_files_for_library_code)
target_link_libraries(common_stuff PUBLIC
     SDL2::SDL2    # prefer newer INTERFACE libraries **if available**
)
target_include_directories(common_stuff PUBLIC 
    ${CMAKE_CURRENT_SOURCE_DIR}
)

add_executable(first_component main.cpp)
target_link_libraries(first_component PRIVATE common_stuff)

add_library(second_component different_main.cpp)
target_link_libraries(second_component PUBLIC common_stuff)

Notes:

  • Prefer not to use set(CMAKE_... ...). For example prefer target_compile_features over set(CMAKE_CXX_STANDARD 14).
  • common_stuff could be an OBJECT library (see add_library documentation) but I see no reason to.
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thanks for your answer, this looks like it will work, although I haven't implemented it just yet. Do you recommend any reference for your suggestions in the notes at the end? This is all new stuff I haven't seen before – FreelanceConsultant Aug 17 '21 at 15:52
  • The CMake documentation at https://cmake.org/cmake/help/latest/index.html – Peter Aug 17 '21 at 16:08
  • `Do you recommend any reference for your suggestions in the notes at the end?` Searching for "cmake best practices" online I found: https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1#declare-include-directories-with-target_include_directories etc.. – KamilCuk Aug 17 '21 at 16:35
  • Following up again: I have used your instructions to build the first component and the associated library. I have a libXXX.a file in the same `./build` directory as the executable. If I want to build the second component in a different directory, then how do I tell cmake where it should look for the file libXXX.a ? – FreelanceConsultant Aug 21 '21 at 11:51
  • `If I want to build the second component in a different directory` https://stackoverflow.com/questions/13556885/how-to-change-the-executable-output-directory-for-win32-builds-in-cmake `how do I tell cmake` CMake already knows it, don't tell it to cmake, just `target_link_libraries`. If `libXXX.a` is an external entity that has to be included in the project, in that case use `add_library(... IMPORTED)`. – KamilCuk Aug 21 '21 at 20:05