Can CMake build subprojects (with benefits)? - makefile

I have some huge project that is being compiled in CMake.
It is developed for quite a long time, have 8000+ source/header files (over 500Mbytes, over 500 CMakefile.txt files).
They use directory structure like this
PROJECT_NAME
src
/ subdir_name
/ other_dir_name
/ some_different_dir
/ MY_SPECIFIC_DIR <---
/ yet_another_dir
build
and build it out-source, like this:
name#host:~/PROJECT_NAME/build> cmake ../src
name#host:~/PROJECT_NAME/build> make all
then it's build as one BIG binary (details are not important).
I cannot touch anything else, just content of MY_SPECIFIC_DIR - it's source and CMake files.
So, I have source code in MY_SPECIFIC_DIR tweak CMakefile.txt files somehow and would like to build it like this:
name#host:~/PROJECT_NAME/build_specific> cmake ../src/MY_SPECIFIC_DIR
name#host:~/PROJECT_NAME/build_specific> make all
This should build things in MY_SPECIFIC_DIR into single binary with some few links to other subprojects. But also (obviously) don't change anything about how whole project is compiled.
My question is:
Is my desired setup
posible
using CMake?
Can I somehow test in CMakeFile.txt that it is root project and build it in different way then when it is builded as a whole?
Unless, I have to resort to different means and use standard make for this.
I don't know CMake so I'm hoping for YES/NO anwer, preferable even for technique how to achieve this. And then learn the CMake and do it.
Also, I must use CMake version 2.6.
Thanks

Basic concept is to use
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
... code for stand-alone app
else()
... what was in this file before
endif()

Related

Where should I install myproj-config.cmake and myproj-version-config.cmake?

Suppose you're developing some library, myproj, using CMake for build configuration; supporting the cmake --install (using install() commands); and supporting use of myproj with CMake config mode, i.e. by making relevant .cmake files accessible to dependent projects.
Now, ,given an install root directory - where should I install my project's configuration .cmake files? Is there an idiomatic standard(ish) location?
Sorush Khajepor's R&D blog suggests ${LIB_INSTALL_DIR}/cmake/myproj - and it's the newest.
Foonathan's blog suggests placing the config .cmake files in ${LIB_INSTALL_DIR}/. So does Falkor's blog.
The documentation page for the CMakePackageConfigHelpers module suggests: ${LIB_INSTALL_DIR}/myproj/cmake.
What's the most popular/idiomatic choice? And what are its pros and cons relative to the other ones?
I advocate for setting a cache variable to override this and defaulting it to <LIBDIR>/cmake/ProjName (as you suggest in your answer):
cmake_minimum_required(VERSION 3.21) # for saner CACHE variables
project(ProjName VERSION 0.1.0)
# ...
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(ProjName_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/ProjName"
CACHE STRING "Path to ProjName CMake files")
install(EXPORT ProjName_Targets
DESTINATION "${ProjName_INSTALL_CMAKEDIR}"
NAMESPACE ProjName::
FILE ProjNameConfig.cmake
COMPONENT ProjName_Development)
write_basic_package_version_file(
ProjNameConfigVersion.cmake
COMPATIBILITY SameMajorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/ProjNameConfigVersion.cmake"
DESTINATION "${ProjName_INSTALL_CMAKEDIR}"
COMPONENT ProjName_Development)
I wrote a blog post with an expanded version of this a while back: https://alexreinking.com/blog/building-a-dual-shared-and-static-library-with-cmake.html
In general, setting an install() destination to anything other than "${SOME_CACHE_VARIABLE}" is bound to cause headaches for some package maintainer. Where GNUInstallDirs doesn't provide a valid configuration point, you must create your own.
I'll argue in favor of ${LIB_INSTALL_DIR}/cmake/myproj.
If you're installing to some library-specific install location, e.g. /opt/myproj - then it doesn't really matter all that much anyway. But think about what happens when you install to, say, /usr/local.
If you place the scripts in ${LIB_INSTALL_DIR}, that library now becomes full of foo-config.cmake and foo-version-config.cmake, instead of just library files (and some subdirs). Less fun for browsing and searching.
If you place the scripts in ${LIB_INSTALL_DIR}/myproj/cmake, then - the same thing happens, but with per-project subdirs instead of sets of files. Better, perhaps, but instead - why don't we just replace the path elements of myproj and cmake, and that way we would get a cmake/ directory with many subdirs, instead. That's cleaner and more convenient IMHO.

CMake: Use variables from existing Makefile of 3rdparty library

I'm facing the following scenario:
Existing project which uses cmake
External 3rdparty library which only comes with Makefiles
The difference of my situation compared to existing questions is that I don't need to have cmake to build the 3rdparty library via the Makefile. Instead, the 3rdparty library provides a library.mk Makefile which has variables like LIB_SRCS and LIB_INCS containing all source and header files required to compile the library.
My idea is to include the library.mk into the project's CMakeLists.txt and then adding those $(LIB_SRCS) and $(LIB_INCS) to target_sources().
My question: How can I include library.mk into the existing CMakeLists.txt to get access to the $(LIB_SRCS) and $(LIB_INCS) for adding them to target_sources()? I'm looking for something like this:
include("/path/to/library.mk") # Somehow include the library's `library.mk` to expose variables to cmake.
add_executable(my_app)
target_sources(
my_app
PRIVATE
main.c
$(LIB_SRCS) # Add 3rd-party library source files
$(LIB_INCS) # Add 3rd-party library header files
)
Using include() does not work as the library.mk is not a CMake list/file.
Since you can't be sure that your target system will even have Make on it, the only option is to parse the strings out of the .mk file, which might be easy if the variables are set directly as a list of filenames, or really hard if they are set with expansions of other variables, conditionals, etc. Do this with FILE(STRINGS) cmake doc.
Your plan will only work if the Makefiles are trivial, and do not set important compiler flags, define preprocessor variables, modify the include directory, etc. And if they really are trivial, skip the parsing, and just do something like aux_source_directory(<dir> <variable>) to collect all the sources from the library directory.
You might also consider building and maintaining a CMakeLists.txt for this third-party library. Do the conversion once, and store it as a branch off of the "vendor" main branch in your version control system. Whenever you update, update the vendor branch from upstream, and merge or rebase your modifications. Or just store it in your existing project, referring to the source directory of the 3rd-party stuff.

Compile a third-party library also using SCons from SCons build script

I'm using SCons to build my project.
A third-party library I've integrated also uses SCons, but it can be updated from Git at any time and I've got no control over the contents of its SConstruct file.
When compiled on its own, the library's SConstruct file accepts the parameters bits=32/64 and target=debug/release
I tried building it with env.SConscript(), but this doesn't pass the parameters in a form that the target SConstruct file accepts (without using SCons' Import() function):
# Compile Godot-CPP, a wrapper library we depend on
if nuclex._is_debug_build(environment):
compile_godot_cpp = environment.SConscript(
'addons/godot-cpp/SConstruct', export='bits=64 target=debug'
)
else:
compile_godot_cpp = environment.SConscript(
'addons/godot-cpp/SConstruct', export='bits=64 target=release'
)
Can I compile another SConstruct file and pass parameters to it as if SCons had been invoked from the command line on its own?
I'm aware that I could just use env.Command() to start another SCons process, but then SCons couldn't parallelize the build (i.e. scons -j16) like it does in the case of env.SConscript().
There's not a good way to do this beyond Command().
You might ask the godot project if they could move the bulk of their logic into a SConscript at the top level which you could then import and somehow pass the needed parameters to.

Scons: how to specify file dependency for 3rd party compile result?

It seem to me that scons targets are being generated not in declaration sequence. My problem is, I need to generate some code first, I'm using protoc to process a my.proto file into .h and .cc file, I need some pseudo code like this(what should the working code look like?)
import os
env=Environment(ENV=os.environ,LIBPATH='/usr/local/lib')
env.ShellExecute('protoc', '--outdir=. --out-lang=cpp', 'my.proto')//produces my.cc
myObj=Object('my.cc')//should wait until 'my.cc' is generated by protoc
Dependency(myObj, 'my.cc')
mainObj=Object('main.cpp')
My question is:
How to specify this ShellExecution of protoc in SConstruct/SConscript?
How to make sure that the compilation of 'main.cpp' depends on the existence of 'my.cc', in another word, wait until 'my.cc' is generated and then execute?
Your observations and assumptions are correct, SCons will not execute the single build commands in the order that you list them in the SConstruct files. It will run them based on the dependencies of the targets and source files in your build, either defined implicitly (header includes in C++, for example) or explicitly (via the Depends() method).
So you have to define and setup your dependencies correctly, such that SCons delivers the output that you want. For the special protoc case in your example, a special Builder exists that will help you to get the dependency graph right. It is available in our ToolsIndex, where also support for a variety of other languages and dialects can be found.
These special builders will emit the correct target nodes, e.g. when given a *.proto input file, and SCons is then able to automatically detect the dependency between the protoc input file and your main program if you say something like:
env=Environment(tools=['default','protoc'])
env.Protoc([], "test.proto")
env.Program('main', ['main.cpp'] + Glob('*.cc'))
The Glob('*.cc') will detect your *.cc files, coming out of the protoc Tool, and include them as dependencies for your final target main.
You can always write your own Builders and Emitters in SCons, which is the canonical way of making new tools/toolchains known to SCons dependency analysis. In the UserGuide, sect. "18 Writing Your Own Builders", and especially our ToolsForFools Guide you can find more infos about this.

Autoconf compilation of dependency before application

I have been tooling around with autotools for the past couple of days, and finally have made significant progress. One problem I am having is that I have two libraries that need to be compiled before the main application code. I'm not quite sure how to do this. My directory structure is below and a snippet from my configure.ac as well.
AC_CONFIG_FILES([Makefile
src/Makefile
gtkworkbook/Makefile
csv/Makefile])
AC_OUTPUT
I need the csv/Makefile and gtkworkbook/Makefile to both be compiled before src/Makefile; is there any way to specify this? Right now I am getting an error about the library (csv) not existing during the application compile process.
The order of items in AC_CONFIG_FILES() does not affect the build order. If you're using automake, which I assume you are, it will traverse your directory tree in the order that you list directories in each Makefile.am's SUBDIRS list.
That being said, you should have the order of items in AC_CONFIG_FILES() mirror the build order, for consistency/maintainability.
Example of how your toplevel Makefile.am's SUBDIRS to build in the desired order:
SUBDIRS = csv gtkworkbook src
Also, for this simple case you don't need both AC_CONFIG_FILES() and AC_OUTPUT(). You can pass your list directory to AC_OUTPUT():
AC_OUTPUT([
Makefile
src/Makefile
gtkworkbook/Makefile
csv/Makefile
])

Resources