Define CMake target that acts as intermediate abstraction layer for another - cmake

I have two projects sharing some code. Both have their own tools for generating almost the same boilerplate code, which are defined as a CMake target that runs everything when imported (or used as dependency). Let's assume that it is called generator_A in project A and generator_B in project B.
There are components that are shared "cloned" in both projects that use this boilerplate code, so for component_1 the CMakeLists.txt on each project would have something like:
# In project A
find_package(generator_A)
add_library(component_1 ...)
target_link_libraries(component_1 generator_A)
# In project B
find_package(generator_B)
add_library(component_1 ...)
target_link_libraries(component_1 generator_B)
Apart from this and other small differences regarding the generated code, component_1 would work in both projects.
I would like to define some intermediate layer for the generators on each project, so that its usage is independent of the project and look like:
find_package(generator_unified)
add_library(component_1 ...)
target_link_libraries(component_1 generator_unified)
For reasons not under my control, I cannot change anything about the generators (e.g. names, how they work, generated code/products).
I have no idea what is the best way to do this. Some ideas that I've found searching docs and the internet:
Create a Findgenerator_unified.cmake file that defines a generator_unified::generator_unified from the products of generator_X in project_X (library, headers, other properties?). I am not really how this can be achieved, though.
Does generator_unified needs its own CMakeLists.txt file, or is it enough with the Find<>.cmake.
How can I make sure that the Find<>.cmake file is available for others?
Create some kind of alias that can be used project-wide. This doesn't look possible according to docs .
Is this a better way to achieve this? If 1st alternative is the correct,

Something like this ought to work (your first alternative):
# Findgenerator_unified.cmake
cmake_minimum_required(VERSION 3.23)
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
set(quiet QUIET)
else ()
set(quiet "")
endif ()
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
set(required REQUIRED)
else ()
set(required "")
endif ()
set(known_backends generator_A generator_B)
set(GENERATOR_UNIFIED_BACKEND "generator_A"
CACHE STRING "Which generator backend package to use.")
set_property(CACHE GENERATOR_UNIFIED_BACKEND
PROPERTY STRINGS "${known_backends}")
foreach (backend IN LISTS known_backends)
if (backend IN_LIST ${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS)
set(GENERATOR_UNIFIED_BACKEND "${backend}")
endif ()
endforeach ()
if (GENERATOR_UNIFIED_BACKEND IN_LIST known_backends)
find_package("${GENERATOR_UNIFIED_BACKEND}" ${quiet} ${required})
set("${CMAKE_FIND_PACKAGE_NAME}_${GENERATOR_UNIFIED_BACKEND}_FOUND"
"${${GENERATOR_UNIFIED_BACKEND}_FOUND}")
endif ()
unset(known_backends)
unset(quiet)
unset(required)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
generator_unified
HANDLE_COMPONENTS
REQUIRED_VARS "${GENERATOR_UNIFIED_BACKEND}_FOUND"
VERSION_VAR "${GENERATOR_UNIFIED_BACKEND}_VERSION"
)
if (generator_unified_FOUND AND NOT TARGET generator::unified)
add_library(generator::unified ALIAS "${GENERATOR_UNIFIED_BACKEND}")
endif ()
This is mostly boilerplate, but the key is in the last few lines... the generator::unified target is set up as an alias to whichever backend was selected.
When a consuming project goes to install a target using this, they will need to generate code that forces the backend to be the same. The HANDLE_COMPONENTS flag naturally handles this because the component name matches the sub-package name. Users will write the following snippet in their project config files:
# proj-config.cmake.in
# ...
include(FindDependencyMacro)
find_dependency(generator_unified COMPONENTS #GENERATOR_UNIFIED_BACKEND#)
# ...

Related

Stopping CMake prepend 'lib' to libs makes the lib not foundable

I want my libraries to be generated as lib-something.so.1.0.0 and not liblib-something.so.1.0.0. For this, I used the proposals from this link: stop_preppending_'lib'_to_libs. But it seems that CMake is not able to find the library anymore.
This is what I did:
lib-something/src/CMakeLists.txt:
add_library(lib-something SHARED
${CMAKE_CURRENT_SOURCE_DIR}/Something.cpp )
set_target_properties(lib-something PROPERTIES PREFIX "")
lib-another/src/CMakeLists.txt:
add_dependencies(lib-another lib-something)
It is able to find it if I do:
add_dependencies(lib-another something)
altough my target is named 'lib-something'. I don't want that. If I were okey with having to add dependencies as 'something' I would have removed the 'lib' from the target name and it would have been enough. I find this an easy way to be identified by the users as libraries and not executables.
Any ideas?
Edited:
I also tried this:
add_library(something SHARED
${CMAKE_CURRENT_SOURCE_DIR}/Something.cpp )
set_target_properties(something PROPERTIES OUTPUT_NAME "lib-something")
In other projects:
add_dependencies(lib-another lib-something)
It works, but it looks that the generation of the libraries is also liblib...
As far as I have seen, there is no way to avoid this 'liblib' thing without changing the prefix into "", and if this prefix is changed then the library is not found no matter what.

Specify namespace/class in VS2019 Test Explorer while using CTest/CMake

I'm using CMake + VS2019. In my test definition I have something like:
add_test(NAME "common/base64" COMMAND my_unit_test "common/base64")
VS2019 displays this like:
In project I have hundreds of tests and it is very inconvenient to search through unclear randomly generated names, expanding each items. So my questions:
How can I specify recognizable test name (instead of EBF.Tests.52488745200006951440) ?
How to specify namespace / class?
Also I can see Vs2019 Test Explorer can group by "Traits" - may be I can specify it over this feature?
It seems that the "EBF.Tests" prefix is the "project name".
You can disable the test prefix or set a new one by altering the TEST_PREFIX setting in your test autodiscovery method or the test naming itself.
For CTest, by altering the test naming:
# use [namespace].[class].[testname] naming
add_test("lib.namespace.Tests.SomeTest" test_exe)
For Catch2, by altering the test name prefixing by changing TEST_PREFIX in the auto-discovery method:
catch_discover_tests(
TagTestsTarget
TEST_SPEC "[tag]" # select tests by tag name
TEST_PREFIX "lib.namespace.Tests." # last dot required !
)
Fast Solution:
Set the Group By option in the Test Explorer to visualize by Class.
Then name each test in your CMake files as: mytestname.mytestname
As an example: add_test(NAME mytest.mytest COMMAND mytest)
(Not a perfect solution but it works well enough.)
More details:
When naming tests for the Team Explorer, if there is one dot between the name, will be interpreted as class name then function name. If there are two dots it will be interpreted as namespace, class, function. Any more dots are appended in the function string.
You could also display on the Team Explorer by namespace and then use these naming features to group your tests in at most two levels of hierarchy.
As example you could name all the tests related to a given feature with the same class or namespace and they will display nicely in the same set in the Team Explorer window.

How to access modules from parent or sibling folder modules?

I am trying to access modules from the parser.rs and another.rs in the solve.rs. How to include those modules and use the "use statements"? If that is not possible what should be the code structure?
Here is the application folder tree:
app/src
--- main.rs
--- another.rs
--- mod.rs
--- parser/
-------- parser.rs
-------- mod.rs
--- solver/
-------- solve.rs
-------- mod.rs
Your first option is absolute paths:
use crate::parser::Whatever;
use crate::solver::Another;
crate here is a keyword representing the crate root.
You can also use relative paths for advanced scenarios. Both solutions are discussed very nicely in the relevant Rust Documentation
Also, don't forget that you need to make the modules public. They will be private by default and not accessible from parents or siblings.
To access parser/parser.rs and another.rs from anywhere in your crate, you can use absolute paths (here I am also using nested paths, which is not required but makes the structure of modules more clear):
use crate::{
parser::parser,
another,
};
You can also use relative paths with super, which refers to the parent module. More information is avaiable in #Ishmaeel's answer.
Regarding your code structure, it seems a little strange why you have mod.rs (not wrong, but just strange, especially 0; you can totally leave 1 and 2 if you like it, but 0 might confuse you):
app/src
main.rs
another.rs
mod.rs // 0
parser/
parser.rs
mod.rs // 1
solver/
solve.rs
mod.rs // 2
Regarding 1 and 2:
mod.rs was used in the 2015 edition for being able to create nested modules, but is no longer needed in the 2018 edition (assuming that you are using the currently newest and default for cargo 2018 edition, see What are editions?):
A foo.rs and foo/ subdirectory may coexist; mod.rs is no longer needed when placing submodules in a subdirectory.
Regarding 0:
The module you are defining via this is actually named mod (not src as you may have expected, though I'm not sure at all what you expected here), I'm unsure if you meant to do that. However if you did, there is still a way to access it via r# - raw identifiers, available since Rust 1.30:
use crate::r#mod;
If you don't want to write r#mod all over the place, you can use as like this:
use crate::r#mod as new_name;
Then you can refer to the module via new_name.

Dynamic struct member names like in javascript in golang

I am writing a multi-lang website.
I read the language info from users cookies, and I have several translation modules such as en.go gr.go etc.
The modules are of type map[string]string.The problem here is in javascript I can do something like lang[cookies.lang]["whatever message"].'But go does not support accessing struct members in this way.
I could make switch case ormap[string]map[string]string` and map all possible languages, but this is much extra work.
So is there any way golang provides some way to access members like js brackets notation?
Not: There was a similar question on the stack, and somebody wrote to use "reflect" package, but I could not quite understand how it works and failed to reproduce by myself works and failed to reproduce by myself.
One possible route would be to use a map[string]map[string]string.
You could then have a base package in which you declare your base translation variable and in your translation modules, you can use an init function to populate the relevant sub-map. It's essentially optional if you want to do this as separate packages or just separate files (doing it as packages means you have better compile-time control of what languages to include, doing it as files is probably less confusing).
If you go the packages root, I suggest the following structure:
translation/base This is where you export from
translation/<language> These are "import only" packages
Then, in translation/base:
package "base"
var Lang map[string]map[string]string
And in each language-specific package:
package "<language code>"
import "language/base"
var code = "<langcode>"
func init() {
d := map[string]string{}
d[<phrase1>] = "your translation here"
d[<phrase2>] = "another translation here"
// Do this for all the translations
base.Lang[code] = d
}
Then you can use this from your main program:
package "..."
import (
"language/base"
_ "language/lang1" // We are only interested in side-effects
_ "language/lang2" // Same here...
)
Not using separate packages is (almost) the same. You simply ensure that all the files are in the same package and you can skip the package prefix for the Lang variable.
A toy example on the Go Playground, with all the "fiddly" bits inlined.

empty default component name is displayed as 'Unspecified'

I'm trying to make two packages using cmake 2.8.12: the first one contains a shared library, the second one contains headers files. Apparently, I want two packages with names libname and libname-dev respectively, so I used command install with no specified component for the shared library and the same command with COMPONENT dev for the headers files. Before this I had defined an empty default component name. So I have a code as follows:
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "")
install(TARGETS libname DESTINATION /usr/local/lib)
install(FILES ${header_files} DESTINATION /usr/local/include COMPONENT dev)
But as a result, despite the empty default component name libname-Unspecified and libname-dev are generated. I find this behavior quite weird, because it seems I have no way to define two packages with previously described names. Are there any ideas how to do this in any other way?
In case of debian:
Give your main component some temporary name, e.g. "runtime".
install(TARGETS my-target
COMPONENT runtime
DESTINATION ${INSTALLDIR}
)
Then remove the postfixed component name as follows:
set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME "${CPACK_PACKAGE_NAME}")
Might work with other generators, too if they provide something equivalent to "CPACK_DEBIAN_RUNTIME_PACKAGE_NAME".