James
James

Reputation: 138

Linking to Armadillo with CMake

I have been trying to create a small C++ project using the Armadillo library as part of learning C++ and CMake - I'm new to both. I'm using a Windows computer with the MinGW compiler. After more hours than I'd care to admit, looking at a variety of answers to questions online, for example here and here, I finally got the following to work:

File / Folder structure

ProjectDIR
 |
 +-- CMakeLists.txt
 +-- test.cxx
 +-- testConfig.h.in
 +-- thirdParty
     |
     +-- armadillo
         |
         +-- include
         +-- lib_win64

test.cxx

#include <iostream>
#include <armadillo>

#include "testConfig.h"

int main()
{
    arma::mat A = {{1.0, 2.0, 3.0},{4.0, 5.0, 6.0},{7.0, 8.0, 9.0}};
    std::cout << "A:\n" << A << std::endl;

    std::cout << "A.t()\n" << A.t() << std::endl;

    std::cout << "A*A.t()\n" << A*A.t() << std::endl;

    return 0;
}

testConfig.h.in

#define MyLatestFrustration_VERSION_MAJOR @MyLatestFrustration_VERSION_MAJOR@
#define MyLatestFrustration_VERSION_MINOR @MyLatestFrustration_VERSION_MINOR@

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(MyLatestFrustration VERSION 0.1)

add_library(CompilerFlags INTERFACE)
target_compile_features(CompilerFlags INTERFACE cxx_std_11)

configure_file(testConfig.h.in testConfig.h)

add_executable(MyLatestFrustration test.cxx)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/armadillo/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/armadillo/lib_win64)

#add_subdirectory(custom)

target_link_libraries(MyLatestFrustration PUBLIC
                        CompilerFlags
                        ${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/armadillo/lib_win64/libopenblas.lib
                    )

target_include_directories(MyLatestFrustration PUBLIC "${PROJECT_BINARY_DIR}")

In which the armadillo/include folder is copied directly from the armadillo-14.0.3 download and armadillo/lib_win64 folder is copied from the examples provided with armadillo-14.0.3.

While this solution works for this basic implementation, I get the feeling that it isn't how it should be done. I confirmed my suspicions when I attempted to create and link a custom library which uses armadillo by adding a folder called custom to ProjectDiR containing

matrices.h

#pragma once

#include <armadillo>

arma::mat f1(arma::mat A);

matrices.cxx

#include "matrices.h"

arma::mat f1(arma::mat A)
{
   arma::mat B = A*A.t();
   return B;
}

CMakeLists.txt

add_library(MatrixFunctions matrices.cxx)

target_include_directories(MatrixFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(MatrixFunctions PUBLIC CompilerFlags)

as well as uncommenting add_subdirectory(custom) and adding MatrixFunctions into the target_link_libraries in the top level CMakeLists.txt file, then modifying test.cxx to include matrices.h and make a call to f1. This builds fine, but when attempting to run it produces the following error "The code execution cannot proceed because the libopenblas.dll was not found. Reinstalling the program may fix this problem."

So, I'd like to understand how to properly link to Armadillo using CMake and if it can be done while using the provided precompiled libopenblas.lib, or if I have to install BLAS and LAPACK or OPENBLAS; I have previously attempted to follow this route and ran into even more issues having clearly done something wrong.

Upvotes: 2

Views: 117

Answers (2)

Mizux
Mizux

Reputation: 9291

Since Armadillo is a CMake based build project you should use:

find_package(Armadillo)

ref: https://gitlab.com/conradsnicta/armadillo-code/-/blob/14.0.x/CMakeLists.txt?ref_type=heads#L664-665

or unzip the source tarball archive and use add_subdirectory(thirdParty/armadillo).

(note: or use FetchContent() so cmake will download and add_subdirectory() for you...)

Then now, you have an amarillo CMake Target so you can:

target_link_libraries(MyLatestFrustration PRIVATE armadillo)

ref: https://gitlab.com/conradsnicta/armadillo-code/-/blob/14.0.x/CMakeLists.txt?ref_type=heads#L579-583

note: This will also provide the necessary target_include_directory see: https://gitlab.com/conradsnicta/armadillo-code/-/blob/14.0.x/CMakeLists.txt?ref_type=heads#L586

Upvotes: 0

James
James

Reputation: 138

Following the suggestions in the comments, I solved the problem by using CMake to copy the required file. In particular, I copied the libopenblas.dll to the build directory using the solution here. My CMakeLists.txt file is now

cmake_minimum_required(VERSION 3.15)

project(MyLatestFrustration VERSION 0.1)

add_library(CompilerFlags INTERFACE)
target_compile_features(CompilerFlags INTERFACE cxx_std_11)

configure_file(testConfig.h.in testConfig.h)

add_executable(MyLatestFrustration test.cxx)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/armadillo/include)

add_custom_command(TARGET MyLatestFrustration POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        "${PROJECT_SOURCE_DIR}/thirdParty/armadillo/lib_win64/libopenblas.dll"
        $<TARGET_FILE_DIR:MyLatestFrustration>)

target_link_libraries(MyLatestFrustration PUBLIC
                        CompilerFlags
                        ${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/armadillo/lib_win64/libopenblas.lib
                    )

target_include_directories(MyLatestFrustration PUBLIC "${PROJECT_BINARY_DIR}")

in which the custom command copies the .dll file post build.

This solution doesn't feel ideal, but hopefully it will be useful to anyone facing a similar problem.

Upvotes: 2

Related Questions