Problems with CMAKE vars - visual-c++

I'm using CMAKE to generate VS2008 SLN/VCPROJ files but a few simple things don't appear to work:
1)This works: INCLUDE_DIRECTORIES ($ENV{MCS_OGRE_HOME}/OgreMain/include)
But this doesn't, my VC++ additional include dirs gets totally screwed up when I do this, brackets and all kinds floating around:
SET (OGRE_PATH $ENV{OGRE_HOME}/OgreMain)
INCLUDE_DIRECTORIES (${OGRE_PATH}/include)
2)This works: target_link_libraries( debug $ENV{OGRE_HOME}/lib/OgreMainStatic_d.lib )
But this doesn't, the library path isn't shown under the library paths in VC++:
LINK_DIRECTORIES($ENV{OGRE_HOME}/lib/)
target_link_libraries( debug OgreMainStatic_d.lib )
I figure it must be something simple?

Rather than:
set(OGRE_PATH $ENV{OGRE_HOME}/OgreMain)
Use:
string(REPLACE "\\" "/" OGRE_PATH "$ENV{OGRE_HOME}/OgreMain")
CMake uses all "/" for path separators on all platforms.
Also, it's recommended to use full path names (with "/" separators) as library arguments to target_link_libraries rather than specifying link_directories. CMake then takes the whole set of libraries linked to and figures out the right order to pass linker flags on all platforms.
One more comment: looks like you have a library or an executable named "debug" from your call to target_link_libraries. Is that true? Or are you missing the first argument in the question posted here?
Should be something like:
target_link_libraries(mylib
debug /path/to/DebugLib.lib
optimized /path/to/ReleaseLib.lib)

Related

CMake Precompiled Headers not including correctly for Visual Studio

So I've recently started using the "new" target_precompile_headers command for CMake to generate and include my precompiled headers.
But for some reason it doesn't seem to work at all the way I want it.
I do understand that the usage of this command is to provide the list of headers to include in your pch and that CMake itself generates the actual pch. This seem to work just fine as I can see the pchs generated for each config & project in my solution.
However looking at the project properties, and more specifically looking at the .cpp properties the /FI command is always empty, which means no files are force included.
This also means that none of my cpp catches that I am using pch at all and my compile fails.
Does anyone have any suggestions on what could be wrong?
// Example of a CMakeLists.txt which defines a static lib using PCH
file(GLOB ALL_SOURCE_FILES "*.cpp" "*.h")
add_library(MyLib STATIC ${ALL_SOURCE_FILES})
target_precompile_headers(MyLib PRIVATE MyHeader.h)
Below you see my own lib created with the snippet above. As you can see, no Forced Include file is added, thus no cpp files gets the PCH included.
I found this workaround: Adding the compiler option manually...
if(MSVC)
target_compile_options(YourProject PRIVATE "/FI${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir/$<CONFIG>/cmake_pch.hxx")
endif()
(Currently using Cmake 3.24.1 with cmake_minimum_required(VERSION 3.18) in the file)

how to print the value inside a variable in cmakelist.txt

I'm using CMake for my recent project and I want to confirm that the right path is set
set(_protobuf_include_path -I . -I ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR})
I used message to check the value of _gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR but not printing anything in terminal
message( STATUS ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR} ) # no output for this
My intention is to build Grpc1.14.1, bit it is having dependency with protobuf, so I downloaded protobuf 3.6.01 and both grpc and protobuf kept under same directory (home/user/src).
While building I got this error
google/protobuf/stubs/common.h: No such file or directory
#include <google/protobuf/stubs/common.h>
So I opened protobuff.cmake in directory gRPC/cmake and changed the protobuf root path
if(NOT PROTOBUF_ROOT_DIR)
#set(PROTOBUF_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf)
set(PROTOBUF_ROOT_DIR ../../ThirdParty/protobuf)
[...]
After doing this, I'm still getting the same issue.
First, something a bit irrelevant to the question, but related to modern CMake techniques and usage.
In order to set the include path, it is preferred to use the target_include_directories command, e.g.
target_include_directories(mytarget
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR}
)
Now, for the actual question, it would help if you could show the actual setting of ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR}, (is it through the command line via `-D) options?)but you could print it like this:
message(STATUS "include dir: ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR}")
I'm suspecting spurious whitespace or something along those lines in the actual variable contents.

How to include an additional CMakeLists.txt

Say we have a command call foo in CMakeLists.txt which is in folder /A.
foo is defined in antother CMakeLists.txt which is in folder /B.
How can one reference to /B/CMakeLists.txt from within /A/CMakeLists.txt in order to call foo?
I tried to set search paths to /B/CMakeLists.txt via:
CMAKE_INCLUDE_PATH
CMAKE_MODULE_PATH
CMAKE_SOURCE_DIR
but none of them worked.
CMake still complaines Unknown CMake command "foo".
That can be done with include, here's some simple example:
Content of A/CMakeLists.txt
function(foo)
message(STATUS "heya")
endfunction()
Content of B/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
include(${CMAKE_CURRENT_SOURCE_DIR}/../A/CMakeLists.txt)
foo()
Now, including another CMakeLists.txt will also run everything in that file, so you may want to avoid doing that if there are targets in B/CMakeLists.txt
If you can modify your code, it would be better to define your functions in a "top level" CMakeLists.txt before calling add_subdirectory.
If this files just contains functions definitions you should not call it CMakeLists.txt, but some_adequate_name.cmake
Than you can include it where you need those functions definitions using include.

Reusing custom makefile for static library with cmake

I guess this would be a generic question on including libraries with existing makefiles within cmake; but here's my context -
I'm trying to include scintilla in another CMake project, and I have the following problem:
On Linux, scintilla has a makefile in (say) the ${CMAKE_CURRENT_SOURCE_DIR}/scintilla/gtk directory; if you run make in that directory (as usual), you get a ${CMAKE_CURRENT_SOURCE_DIR}/scintilla/bin/scintilla.a file - which (I guess) is the static library.
Now, if I'd try to use cmake's ADD_LIBRARY, I'd have to manually specify the sources of scintilla within cmake - and I'd rather not mess with that, given I already have a makefile. So, I'd rather call the usual scintilla make - and then instruct CMAKE to somehow refer to the resulting scintilla.a. (I guess that this then would not ensure cross-platform compatibility - but note that currently cross-platform is not an issue for me; I'd just like to build scintilla as part of this project that already uses cmake, only within Linux)
So, I've tried a bit with this:
ADD_CUSTOM_COMMAND(
OUTPUT scintilla.a
COMMAND ${CMAKE_MAKE_PROGRAM}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scintilla/gtk
COMMENT "Original scintilla makefile target" )
... but then, add_custom_command adds a "target with no output"; so I'm trying several approach to build upon that, all of which fail (errors given as comment):
ADD_CUSTOM_TARGET(scintilla STATIC DEPENDS scintilla.a) # Target "scintilla" of type UTILITY may not be linked into another target.
ADD_LIBRARY(scintilla STATIC DEPENDS scintilla.a) # Cannot find source file "DEPENDS".
ADD_LIBRARY(scintilla STATIC) # You have called ADD_LIBRARY for library scintilla without any source files.
ADD_DEPENDENCIES(scintilla scintilla.a)
I'm obviously quote a noob with cmake - so, is it possible at all to have cmake run a pre-existing makefile, and "capture" its output library file, such that other components of the cmake project can link against it?
Many thanks for any answers,
Cheers!
EDIT: possible duplicate: CMake: how do i depend on output from a custom target? - Stack Overflow - however, here the breakage seems to be due to the need to specifically have a library that the rest of the cmake project would recognize...
Another related: cmake - adding a custom command with the file name as a target - Stack Overflow; however, it specifically builds an executable from source files (which I wanted to avoid)..
You could also use imported targets and a custom target like this:
# set the output destination
set(SCINTILLA_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/scintilla/gtk/scintilla.a)
# create a custom target called build_scintilla that is part of ALL
# and will run each time you type make
add_custom_target(build_scintilla ALL
COMMAND ${CMAKE_MAKE_PROGRAM}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scintilla/gtk
COMMENT "Original scintilla makefile target")
# now create an imported static target
add_library(scintilla STATIC IMPORTED)
# Import target "scintilla" for configuration ""
set_property(TARGET scintilla APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
set_target_properties(scintilla PROPERTIES
IMPORTED_LOCATION_NOCONFIG "${SCINTILLA_LIBRARY}")
# now you can use scintilla as if it were a regular cmake built target in your project
add_dependencies(scintilla build_scintilla)
add_executable(foo foo.c)
target_link_libraries(foo scintilla)
# note, this will only work on linux/unix platforms, also it does building
# in the source tree which is also sort of bad style and keeps out of source
# builds from working.
OK, I think I have it somewhat; basically, in the CMakeLists.txt that build scintilla, I used this only:
ADD_CUSTOM_TARGET(
scintilla.a ALL
COMMAND ${CMAKE_MAKE_PROGRAM}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scintilla/gtk
COMMENT "Original scintilla makefile target" )
... and then, the slightly more complicated part, was to find the correct cmake file elsewhere in the project, where the ${PROJECT_NAME} was defined - so as to add a dependency:
ADD_DEPENDENCIES(${PROJECT_NAME} scintilla.a)
... and finally, the library needs to be linked.
Note that in the commands heretofore, the scintilla.a is merely a name/label/identifier/string (so it could be anything else, like scintilla--a or something); but for linking - the full path to the actual `scintilla.a file is needed (which in this project ends up in a variable ${SCINTILLA_LIBRARY}). In this project, the linking basically occurs through a form of a
list(APPEND PROJ_LIBRARIES ${SCINTILLA_LIBRARY} )
... and I don't really know how cmake handles the actual linking afterwards (but it seems to work)
For consistency, I tried to use ${SCINTILLA_LIBRARY} instead of scintilla.a as identifier in the ADD_CUSTOM_TARGET, but got error: "Target names may not contain a slash. Use ADD_CUSTOM_COMMAND to generate files". So probably this could be solved smarter/more correct with ADD_CUSTOM_COMMAND - however, I read that it "defines a new command that can be executed during the build process. The outputs named should be listed as source files in the target for which they are to be generated."... And by now I'm totally confused so as to what is a file, what is a label, and what is a target - so I think I'll leave at this (and not fix it if it ain't broken :) )
Well, it'd still be nice to know a more correct way to do this eventually,
Cheers!

How to work around "scons: warning: Two different environments were specified for target"

Suppose I have an SConstruct file that looks like this:
env = Environment()
env.Program("a", ["a.c", "util.c"])
env.Program("b", ["b.c", "util.c"])
This build works properly with no SCons warning messages. However, if I modify this to specify different libraries for each Program build (the actual libraries are not relevant):
env.Program("a", ["a.c", "util.c"], LIBS="m")
env.Program("b", ["b.c", "util.c"], LIBS="c")
then I get the warning:
scons: warning: Two different environments were specified for target util.o,
but they appear to have the same action: $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES
This appears to be caused by the Program builder automatically creating a new environment for building the sources, even though it is just the LIBS variable that is different (and so only the link step needs to have a different environment). I can work around this by doing something like:
util = env.Object("util.c")
env.Program("a", ["a.c"] + util, LIBS="m")
env.Program("b", ["b.c"] + util, LIBS="c")
This uses a single Object builder for building util.c, then using the precompiled object file in each Program build, thus avoiding the warning. However, this should not really be necessary. Is there a more elegant way to work around this problem? Or is this actually a bug in SCons that should be fixed?
Context: I have nearly 2000 C source files compiled into about 20 libraries and 120 executables with lots of shared sources. I created the SConstruct file from the previous proprietary build system using a conversion script I wrote. There are about 450 "Two different environments" warning messages produced by SCons for a full build using my current SConstruct.
I found a workaround that doesn't involve creating extra variables to hold the object file nodes:
env.Program("a", ["a.c", env.Object("util.c")], LIBS="m")
env.Program("b", ["b.c", env.Object("util.c")], LIBS="c")
This isolates the build of util.c within a single environment. Although it is specified twice, once for each Program, SCons doesn't warn about this because it's the same source built with the same env object. Of course SCons only compiles the source once in this case.
You may use the Split function and a custom helper to simplify the build process for large projects:
def create_objs(SRCS, path=""):
return [env.Object(path+src+".cpp") for src in SRCS]
prg1 = Split("file_1 file_2 file_N")
prg2 = Split("file_2 file_5 file_8")
env.Program("a", create_objs(prg1), LIBS="x")
env.Program("b", create_objs(prg2), LIBS="y")
The object files are created only once, and they can be used in multiple builds. Hope this helps...
One issue I found in my code was that I was not using the target object path correctly. Or in otherwords I had a variant dir directive, but instead of using BUILDPATH i ended up using my original source code path. This way Scons was finding the object generated in target BUILDPATH and source path.
Creating a static library out of the first set of files and linking the library to the next set of files (which have some files in common with the first set) to create a target works as well.
env.StaticLibrary ("a", ["a.c","util.c"], LIBS = "m")
env.Program ("b", ["b.c","util.c"], LIBS = ["c","a"])

Resources