How to link with a library built using Meson custom_target()? - meson-build

I'm using Meson custom_target() to build a 3rd party foobar library:
foobar_ct = custom_target('foobar_build',
command: `script.sh`,
output: 'foobar.a')
The custom target calls my script, which in turn calls Make to use existing Makefiles to build the lib. The custom_target() is used as a subproject, and the resulting output file ends up located at ./subprojects/foobar/foobar.a.
When I try to link with it
foobar_dep = dependency('foobar', fallback:...)
exe = executable(
'test',
'main.c',
dependencies: [foobar_dep]
...)
Meson gives me error:
builddir/subprojects/foobar/foobar.a: No such file or directory
How would I link with the foobar.a? Meson expects it at the build directory ./builddir/subprojects/foobar/foobar.a - would it be a proper solution just to add a copy step to my script which would copy foobar.a from the place where Make built it, into the builddir?

Related

How to correctly link static library build and installed previously

There is a static library called revolta which is being built and then installed into a sysroot:
set( CMAKE_INSTALL_PREFIX <path to sysroot> )
# ReVolta c++ library name
set( TARGET_LIBREVOLTA "revolta" )
add_library( ${TARGET_LIBREVOLTA} STATIC )
target_include_directories( ${TARGET_LIBREVOLTA}
PUBLIC
# Once the librevolta targets are being exported, this include directory in which the lib is installed is used
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
target_sources( ${TARGET_LIBREVOLTA}
PUBLIC
...
)
Later then once the librevolta is built, it is installed into the sys root using:
# Install all the revolta headers into include directory and copy the built library
install( TARGETS ${TARGET_LIBREVOLTA} EXPORT ${TARGET_LIBREVOLTA}
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
and the connected custom command:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
So far so good. This works as intended, once CMake builds the "revolta" target, it is built and installed into the sysroot as installed using the ${CMAKE_INSTALL_PREFIX}.
My problem is once I try to add the target as the linked one in other lib/executable, it includes somehow automatically the librevolta source path into includes and links the library using the relative path in the build directory rather than the one installed into sysroot as performed in the step right after the librevolta build.
Some other lib/executable:
target_link_libraries( ${APP_EXECUTABLE}
PRIVATE
revolta
)
Once being built, the include path -I/home/martin/git/revolta/source/librevolta is added (the source location) even though it is stated as PRIVATE in the snipped above:
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
and only the ${CMAKE_INSTALL_PREFIX}/include is made public...
Additionally, the library is taken from the build tree rather than from the location where it is installed:
../../librevolta/librevolta.a
instead of
/home/martin/git/revolta/sysroot/lib/librevolta.a
Could you please advice me how to correctly set the revolta target the way it correctly uses its sources for building itself but once used elsewhere it provides the sysroot installed headers and built library from the same location (respecting the standard locations)?
HINT: I also tried to remove the revolta target from the app completely, specifying only to use the sys root (gcc option --sysroot=/home/martin/git/revolta/sysroot), it works fine correct headers and lib is used BUT once the librevolta is not built and installed, the target is not run prior to app build as the dependency is not defined then...
TL;DR: You need to do what's done here:
How to create a ProjectConfig.cmake file
I see a few issues with these CMakeLists.txt files but they aren't related to your problem, because if I understand correctly what you are trying to do here, then there is no problem and it is used as intended.
Let me clarify:
You have a library project that has it's own CMakeLists.txt, where you define the target revolta
You have an executable project that has it's own CMakeLists.txt, where you define your executable target and then you add the revolta target via add_subdirectory() and target_link_libraries(my_executable revolta)
If that's the case then this is just bad:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
Forcing your build to automatically install this library is not the way to go, ever (you for example, need elevated privileges to build it in the first place, because of this command and that poses a security risk).
That being said what is happening is perfectly fine, because from the perspective of the my_executable's CMakeLists.txt you are still building i.e. you use the BUILD_INTERFACE. It is however something you do not want to do.
What instead you want to do is:
Create generator files for a revoltaConfig.cmake file. For that I will refer you to this tutorial:
How to create a ProjectConfig.cmake file
After you create such file, i.e. after building and installing revolta. You will (in the process) also create a revoltaConfig.cmake file. Which helps you populate the my_executable project via find_package(revolta).
The above is probably what you are interested in.
The generator expressions that you use to distinguish BUILD_INTERFACE and INSTALL_INTERFACE are mainly for header file locations (or other linked libraries). Because when you build the library the header files can have a different structure then when you install it (as you already know). And as such work perfectly fine in your CMakeLists.txt, because when you think about it:
You don't want to copy changes to your library files (into the install directory) just to test ongoing development (features/bugfixes) in your executable.
And during the build of the executable if your building another target then IT IS NOT INSTALLED but rather BEING BUILT. And you are most likely adding it as a built target.
So to sum up what would most likely happen here (using your old CMakeLists.txt) is that
The moment you start building the executable which adds the target library as a dependency via add_subdirectory you are implicitly using BUILD_INTERFACE because you are building.
If you were to then install both the executable and the library it would again use the correct install paths, i.e. you would then implicitly start using INSTALL_INTERFACE.
You could hack it without the projectConfig file using the same generator expressions by mixing them up, but I don't recommend it, because then the CMakeLists.txt wouldn't work without doing some weird steps beforehand.

"a bin target must be available for 'cargo run'"

While building a new Rust "Project from other sources", in Intellij IDEA 2017, I was unable to run the project through its UI.
C:/Users/sjsui/.cargo/bin/cargo.exe run error: a bin target must be
available for cargo run
Process finished with exit code 101
I noticed that no --bin target was provided by my build configuration so I placed the path to the projects target folder; same result.
C:/Users/sjsui/.cargo/bin/cargo.exe run --bin C:\Users\sjsui\exercism\rust\hello-world\target\debug
error: no bin target named C:\Users\sjsui\exercism\rust\hello-world\target\debug
I tried creating a fresh Rust project through the Cargo command line interface, and received this error when running it:
error: could not exec the linker link.exe: The system cannot find the file specified. (os error 2)
note: the msvc targets depend on the msvc linker but link.exe was
not found
note: please ensure that VS 2013 or VS 2015 was installed with the
Visual C++ option
Evidently I must install Visual C++ build tools 2017 and am in the process of doing so. Are these errors related, or different issues?
By default, Cargo will consider the file src/main.rs to be the main binary target for the package. If this file doesn't exist, and there are no other binary targets defined in Cargo.toml, you'll get this error.
According to the documentation, when you create a Rust project in IntelliJ IDEA, you get an option to Use a binary (application) template. This should give you a src/main.rs instead of a src/lib.rs (which is the default root file for a library target). Using Cargo on the command line, you can also create an application package with cargo new hello.
Cargo defaults to --bin to make a binary program. To make a library, we'd pass --lib.
When you use --bin on the cargo run command, the argument refers to one of the [[bin]] sections in Cargo.toml, or files following the pattern src/bin/*.rs (the argument replaces the *) if there are no [[bin]] sections in Cargo.toml. For example, cargo run --bin foo will either compile and run src/bin/foo.rs or the [[bin]] section with name = "foo" in Cargo.toml.

CMake install dynamic generated list of files

I have a cmake project that upon installing it will invoke a python script which will output for me a list of files that I must also install.
I use this output and pass it to FILE(INSTALL ${output}) but this command doesnt put the files at the right place.
How can I install a list of file that is a output of a command? (Note that the command depends on the built target)
Make a target which generates this list to get it during the build step. With the list of files written somewhere, use install(SCRIPT <file>) to run a CMake script which uses the file to copy/modify/whatever the files into the right place. The script itself will likely need to have configure_file used to get the install directory and the generated file path correct (I don't see a way to pass arguments to the cmake command which runs the script).

Use external DLL in cmake build

I'm working on the cmake scripts for my project and I've run into a problem:
My project uses a 3rd party library (FreeImage), which has its own Makefile-based build system. I can build FreeImage just fine by simply running "make" (I'm using gnuwin32), which will build FreeImage using MinGW and produce:
FreeImage.lib
FreeImage.dll
Now my problem is twofold:
I want to execute "make" from my cmake script.
I want to link to the import lib (FreeImage.lib), and also make sure the DLL gets copied to the correct place so the EXE will run.
I know how to link to the LIB file, but I'm lost on the rest.
The folder structure is like this:
MyProject # main directory
MyProject/Libs/FreeImage # FreeImage root directory
MyProject/Libs/FreeImage/Dist # This is where FreeImage outputs go (LIB and DLL)
BTW: I'm running on Windows 7. I plan to build my project both with MSVC and MinGW.
Thanks!
EDIT:
I'm now trying to use ExternalProject_Add like so:
ExternalProject_Add(
FreeImage
PREFIX ./Libs/FreeImage
URL ./Libs/FreeImage
BUILD_COMMAND make
)
This gets me part of the way there, but doesn't totally work... it tries to configure things for me and tries to use nmake... ugh
In my opinion, there are two options:
In case you have put your FreeImage sources in your projects' source-tree, the easiest option may be to use the execute_process() command. Assuming FreeImage is in your projects' source-tree in "3rdparty/FreeImage/" you can do something like,
execute_process( COMMAND make WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/3rdParty/FreeImage )
Optionally, you can copy the dll from 3rdParty/FreeImage/bin into you own bin directory. And then you can write a FreeImageConfig.cmake for importing the library:
add_library( FreeImage IMPORTED )
set_target_properties( FreeImage PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/3rdParty/FreeImage/lib )
...
The other option is to make use of the ExternalProject module. You can also take a look at this article from Kitware for an overview of this module. In essence, you specify the full chain of commands needed to get the source, configure the build, build the source and install it. All in your own CMakeLists.txt

linking 3rd party libraries

I have created a simple application that works ok. However, now I need to link with some libraries in the following directory.
/opt/norton/lib
In my make file I have the following with works, but I need to use cmake
LIBS_PATH = -L/opt/norton/lib
INC_PATH = -I/opt/norton/inc
LIBS = -lntctrl
In my CMakeList.txt I have this but doesn't work I keep gettng the following error:
undefined reference to `nt_init'
This is my CMakeList.txt
# Includes files
INCLUDE_DIRECTORIES(/opt/norton/inc)
# Link libraries
LINK_DIRECTORIES(/opt/norton/lib)
# Add the library that is used by nt_init
TARGET_LINK_LIBRARIES(-lntctrl)
ADD_LIBRARY(application initialize_nw)
Many thanks for any advice,
Try out TARGET_LINK_LIBRARIES(ntctrl), the -l flag should not be used there (guess from what I have in mind)
This is how I would write the cmake file:
include_directories(/opt/norton/inc)
link_directories(/opt/norton/lib)
add_executable(application initialize_nw)
target_link_libraries(application ntctrl)
To show what are the actual command lines run during a make, use:
make VERBOSE=1
Maybe this shows you the difference between what you ran manually and the cmake generated commands.