How to collect source files with CMake without globbing? - build

The CMake documentation explicitly states that file(GLOB ...) is not
recommended to collect source files for a build, but it doesn't
mention what the recommended method actually is.
Specifying every source file manually sounds a little bit too manually
to me. So, what is the right method to collect source files, if not
file(GLOB ...)?

Manual is indeed the recommended method. By recommending against using GLOB, the documentation is simply warning against a build system that depends on files present. For example, you want to add a test executable, so you create mytest.cpp. Oops. Now your library compilation breaks. The documentation for AUX_SOURCE_DIRECTORY (similar purpose as globbing for for source files) gives the following warning:
It is tempting to use this command to avoid writing the list of source
files for a library or executable target. While this seems to work,
there is no way for CMake to generate a build system that knows when a
new source file has been added. Normally the generated build system
knows when it needs to rerun CMake because the CMakeLists.txt file is
modified to add a new source. When the source is just added to the
directory without modifying this file, one would have to manually
rerun CMake to generate a build system incorporating the new file.
If you're certain that you want all the contents of a directory, and don't plan on adding new ones, then by all means use a GLOB.
Also, don't forget listing files manually doesn't have to involve typing all the filenames. You could do, for example, ls *.cpp >> CMakeLists.txt, then use your editor to move the list of files to the correct place in the file.

I use GLOB for exactly that and every time I add a file I run
touch ../src/CMakeLists.txt
The next make command will re-scan the directories.
"There is no way for CMake to generate a build system that knows when a new source file has been added" Really? Okay, so tell it!
It's not 100% automatic but a damn sight easier than adding files manually.

I use cog, a python module. Here is a sample to collect .cpp file:
The CMakeLists.txt:
set(SRC_FILES "")
# [[[cog
# import cog, glob
# for src in glob.glob('*.cpp'):
# if "skeleton" in src: continue
# cog.outl("SET(SRC_FILES ${SRC_FILES} %s)" % src)
# ]]]
# [[[end]]]
add_library(mylib STATIC ${SRC_FILES})
And then, run:
python -m cogapp -r CMakeLists.txt
The CMakeLists.txt file will be updated in place.
For how to install cog and other usage, please read the article from the author.

I use a conventional CMakeLists.txt and a python script to update it. I run the python script manually after adding files.
import os
import re
def relFiles(base, sub):
fullSub = os.path.join(base,sub)
abs = [os.path.join(dp, f) for dp, dn, fn in os.walk(fullSub) for f in fn]
return [os.path.relpath(f, base) for f in abs]
def updateAddLibrary(cmakelistsDir, subs):
cmakelists = os.path.join(cmakelistsDir, "CMakeLists.txt")
listings = [relFiles(cmakelistsDir, sub) for sub in subs]
files = [f for listing in listings for f in listing] #flatten
with open(cmakelists, 'r') as file:
text = file.read()
sources = "".join([" %s\n" % f.replace('\\', '/') for f in files])
text = re.sub(r"add_library\s*\(\s*([^\s\)]+).*?\)",
r"add_library(\1\n%s)" % sources,
text, 1, re.DOTALL)
with open(cmakelists, "w") as file:
file.write(text)
dir = os.path.dirname(os.path.abspath(__file__))
updateAddLibrary(dir, ['inc','src'])
Example before:
...
add_library(MyLib
inc/a.h
)
...
after:
...
add_library(MyLib
inc/a.h
inc/sub/b.h
src/a.cpp
)
...

Following douyw's answer, thank you for your answer.
Not a Cmake expert, don't want to be one, I spent a fxxking 3 hours trying to deal with GLOB(disabled) and aux_source_directory(Not even close to GLOB), and douyw save my life.
I add the recursive file walking, and it's working in my project:
Firstly, install the cogapp python module (python -m pip install cogapp)
set(SRC_FILES "")
# [[[cog
# import cog, os
# for root, _, files in os.walk(".", topdown=False):
# for f in files:
# if not "_unittest" in f: continue
# if not f.endswith(".cpp"): continue
# cog.outl('SET(SRC_FILES ${SRC_FILES} "%s")' % os.path.join(root, f).replace('\\', '/'))
# ]]]
# [[[end]]]
run: python -m cogapp -r CMakeLists.txt
The upper lines add all "*_unittest.cpp" to the list
You can change the middle lines to make your own rule, just plain python.
Using regex is better, but simple string searching can do the job on the above situation.
Notice the last line, it needs to replace // to change to the usable universal separator. You may generate your CMakeList.txt in windows.
You may replace SRC_FILES with whatever you want.
The python command may be run under Jenkins/TeamCity, triggered by svn/git commit. Then we can automatically add new files.

Related

what is the alternative of cmake file(GLOB_RECURSE)? [duplicate]

The CMake documentation explicitly states that file(GLOB ...) is not
recommended to collect source files for a build, but it doesn't
mention what the recommended method actually is.
Specifying every source file manually sounds a little bit too manually
to me. So, what is the right method to collect source files, if not
file(GLOB ...)?
Manual is indeed the recommended method. By recommending against using GLOB, the documentation is simply warning against a build system that depends on files present. For example, you want to add a test executable, so you create mytest.cpp. Oops. Now your library compilation breaks. The documentation for AUX_SOURCE_DIRECTORY (similar purpose as globbing for for source files) gives the following warning:
It is tempting to use this command to avoid writing the list of source
files for a library or executable target. While this seems to work,
there is no way for CMake to generate a build system that knows when a
new source file has been added. Normally the generated build system
knows when it needs to rerun CMake because the CMakeLists.txt file is
modified to add a new source. When the source is just added to the
directory without modifying this file, one would have to manually
rerun CMake to generate a build system incorporating the new file.
If you're certain that you want all the contents of a directory, and don't plan on adding new ones, then by all means use a GLOB.
Also, don't forget listing files manually doesn't have to involve typing all the filenames. You could do, for example, ls *.cpp >> CMakeLists.txt, then use your editor to move the list of files to the correct place in the file.
I use GLOB for exactly that and every time I add a file I run
touch ../src/CMakeLists.txt
The next make command will re-scan the directories.
"There is no way for CMake to generate a build system that knows when a new source file has been added" Really? Okay, so tell it!
It's not 100% automatic but a damn sight easier than adding files manually.
I use cog, a python module. Here is a sample to collect .cpp file:
The CMakeLists.txt:
set(SRC_FILES "")
# [[[cog
# import cog, glob
# for src in glob.glob('*.cpp'):
# if "skeleton" in src: continue
# cog.outl("SET(SRC_FILES ${SRC_FILES} %s)" % src)
# ]]]
# [[[end]]]
add_library(mylib STATIC ${SRC_FILES})
And then, run:
python -m cogapp -r CMakeLists.txt
The CMakeLists.txt file will be updated in place.
For how to install cog and other usage, please read the article from the author.
I use a conventional CMakeLists.txt and a python script to update it. I run the python script manually after adding files.
import os
import re
def relFiles(base, sub):
fullSub = os.path.join(base,sub)
abs = [os.path.join(dp, f) for dp, dn, fn in os.walk(fullSub) for f in fn]
return [os.path.relpath(f, base) for f in abs]
def updateAddLibrary(cmakelistsDir, subs):
cmakelists = os.path.join(cmakelistsDir, "CMakeLists.txt")
listings = [relFiles(cmakelistsDir, sub) for sub in subs]
files = [f for listing in listings for f in listing] #flatten
with open(cmakelists, 'r') as file:
text = file.read()
sources = "".join([" %s\n" % f.replace('\\', '/') for f in files])
text = re.sub(r"add_library\s*\(\s*([^\s\)]+).*?\)",
r"add_library(\1\n%s)" % sources,
text, 1, re.DOTALL)
with open(cmakelists, "w") as file:
file.write(text)
dir = os.path.dirname(os.path.abspath(__file__))
updateAddLibrary(dir, ['inc','src'])
Example before:
...
add_library(MyLib
inc/a.h
)
...
after:
...
add_library(MyLib
inc/a.h
inc/sub/b.h
src/a.cpp
)
...
Following douyw's answer, thank you for your answer.
Not a Cmake expert, don't want to be one, I spent a fxxking 3 hours trying to deal with GLOB(disabled) and aux_source_directory(Not even close to GLOB), and douyw save my life.
I add the recursive file walking, and it's working in my project:
Firstly, install the cogapp python module (python -m pip install cogapp)
set(SRC_FILES "")
# [[[cog
# import cog, os
# for root, _, files in os.walk(".", topdown=False):
# for f in files:
# if not "_unittest" in f: continue
# if not f.endswith(".cpp"): continue
# cog.outl('SET(SRC_FILES ${SRC_FILES} "%s")' % os.path.join(root, f).replace('\\', '/'))
# ]]]
# [[[end]]]
run: python -m cogapp -r CMakeLists.txt
The upper lines add all "*_unittest.cpp" to the list
You can change the middle lines to make your own rule, just plain python.
Using regex is better, but simple string searching can do the job on the above situation.
Notice the last line, it needs to replace // to change to the usable universal separator. You may generate your CMakeList.txt in windows.
You may replace SRC_FILES with whatever you want.
The python command may be run under Jenkins/TeamCity, triggered by svn/git commit. Then we can automatically add new files.

How to import Tensorflow source codes correctly with Clion or Netbeans

I intend to read the core module of Tensorflow(TF) source codes
My problem is I do not have experience reading C/C++ source codes like TF in IDE. Could anyone give me some instructions regarding how to read TF source codes (core module) efficiently in a IDE. I have Clion and Netbeans on my Macbook but I don't know how to import TF correctly(also which part to import?;how to build it?) such that when I want to know the declaration of one C++ class I can jump to its signature/declaration directly.
I will appreciate any advice/recommended tools for reading TF source codes efficiently. BTW, I am assuming reading TF codes with a IDE is efficient. If it is not true, I can stop using them and turn to tools like VIM.
The original Tensorflow repository(GitHub) does not contain project information file for any specific IDE, which means you cannot just import the whole project unless you are using something which can import files based on the project folder (Atom, Visual Studio Code, Sublime etc.) I would suggest using one of those if your aim is just to read an navigate inside the code base.
Unfortunately you wont be able to build the code with any of those editors. Tensorflow uses Bazel as its build tool which has support at the moment for Eclipse and Xcode. Honestly, I'm not sure if you can import the the code base in one of this IDEs either.
I solved this problem doing this:
Create CMakeLists.txt file inside TF repo. ls -la output should look like this:
tensorflow
third-party
tools
CMakeLists.txt
some
other
files
CMakeLists.txt should look like this:
NOTE!!! This is not a complete file because a complete file should be 800+ lines long. This is just an example for you to get the idea. You can get the complete file here:
https://github.com/oleg-ostanin/tf_related_staff/blob/master/CMakeLists.txt
but it will probably become outdated next week so don't even bother.
cmake_minimum_required(VERSION 3.14)
project(tensorflow)
set(CMAKE_CXX_STANDARD 14)
# git clone https://github.com/abseil/abseil-cpp.git
include_directories(/home/oostanin/tf_related_repos/abseil-cpp)
# git clone https://github.com/protocolbuffers/protobuf.git
include_directories(/home/oostanin/tf_related_repos/protobuf/src/)
include_directories(/home/oostanin/tensorflow)
# you can get this directory only by building TF from sources using Bazel
include_directories(/home/oostanin/tensorflow/bazel-genfiles)
file(GLOB tensorflow_contrib_tensorrt_shape_fn "${CMAKE_SOURCE_DIR}/tensorflow/contrib/tensorrt/shape_fn/*.cc")
file(GLOB tensorflow_compiler_xla_service_llvm_ir "${CMAKE_SOURCE_DIR}/tensorflow/compiler/xla/service/llvm_ir/*.cc")
file(GLOB tensorflow_lite_delegates_gpu_gl "${CMAKE_SOURCE_DIR}/tensorflow/lite/delegates/gpu/gl/*.cc")
# here should be much more lines
add_library(
tensorflow
SHARED
${tensorflow_contrib_tensorrt_shape_fn}
${tensorflow_compiler_xla_service_llvm_ir}
${tensorflow_lite_delegates_gpu_gl}
# here should be much more lines too
)
target_link_libraries(
tensorflow
)
Some explanations:
TF source files depend on many other projects so you will need to clone and place those projects somewhere near and tell your Cmake where to find them. This is what those 2 lines do:
# git clone https://github.com/abseil/abseil-cpp.git
include_directories(/home/oostanin/tf_related_repos/abseil-cpp)
# git clone https://github.com/protocolbuffers/protobuf.git
include_directories(/home/oostanin/tf_related_repos/protobuf/src/)
There is probably more than 2 repos, but that will get you started.
TF source files depend on compiled protobuf headers and the easiest way for me to compile them turned out to build TF from sources using Bazel. This is another painful story but I did it and I'm sure you can do it too. Than you should tell your Cmake where to find those compiled protobuf headers. This is what that line do:
include_directories(/home/oostanin/tensorflow/bazel-genfiles)
If you can't build TF using Bazel, just skip that part, you will see more red errors caused by unsatisfied includes, but you will be able to read and navigate most of the code.
You should tell Cmake where to find TF source files and the easiest way for me was to generate 300+ lines long list of all directories containing .cc files like this:
file(GLOB tensorflow_contrib_tensorrt_shape_fn "${CMAKE_SOURCE_DIR}/tensorflow/contrib/tensorrt/shape_fn/*.cc")
file(GLOB tensorflow_compiler_xla_service_llvm_ir "${CMAKE_SOURCE_DIR}/tensorflow/compiler/xla/service/llvm_ir/*.cc")
file(GLOB tensorflow_lite_delegates_gpu_gl "${CMAKE_SOURCE_DIR}/tensorflow/lite/delegates/gpu/gl/*.cc")
# here should be much more lines
and then generate another 300+ lines long list to include them as sources like this:
add_library(
tensorflow
SHARED
${tensorflow_contrib_tensorrt_shape_fn}
${tensorflow_compiler_xla_service_llvm_ir}
${tensorflow_lite_delegates_gpu_gl}
# here should be much more lines too
)
There is no way you can build TF using that CMakeLists.txt, don't even think about it. But it will allow you to import TF source code into CLion and read and navigate and edit much easier than using VIM.

How to transfer version number from a git tag to the CPack-generated source package?

In a CMake project, I am using git tags for version numbers. Using the approach from https://github.com/iPenguin/version_git, I am able to get the version number into the source codes (that is, CMake creates a version.cpp file with a const string containing the version number that I can then use in my C++ source codes). The problem is that when I create a source package using CPack, I exclude the .git directory and when compiling the sources extracted from the package, the version number is not available.
How to make CPack put the version number into the source package?
I have the following requirements:
I do not want the build directory to be included in my source package. It is important to note that I do out-of-source builds from a directory under the project root. That is, if my toplevel CMakeLists.txt is in directory my_project, then I run "cmake .." and "make" in directory my_project/build.
I do not want to include the .git directory in my source package.
I want all the files generated by cmake and "make package_source" to be inside the build directory. Additionally, I don't want cmake to modify or delete any file outside of the build directory.
I do not want to use undocumented behavior of cmake and cpack, in particular the paths used by cpack.
So far I was not able to find a solution that would satisfy all my requirements. Here is what I have tried:
I let cmake create a file versionForSourcePackage.cmake, which, when later included by cmake, will set the VERSION variable. Then I want to put this file into the source package, so that when cmake is run in the extracted package, it will have the VERSION variable set. This file is created under the build directory, but I do not know how to properly let CPack copy it to the source packages.
The first possibility is a slight modification of https://github.com/iPenguin/version_git, but it does not satisfy my requirement number 4. Full example is at https://github.com/josefcibulka/version_git.
When building from repository, I get the version number from git and save it in ${PROJECT_BINARY_DIR}/versionForSourcePackage.cmake. Then I use CPACK_INSTALL_COMMANDS to let CPack copy this file to ${PROJECT_BINARY_DIR}/_CPack_Packages/Linux-Source/${CPACK_SOURCE_GENERATOR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}/ so that it is included in the package. This violates the requirement number 4 and moreover, if I want to create both TGZ and ZIP packages in the future, I need to make some changes. Is there some variable that I could use within CPACK_INSTALL_COMMANDS to get the path to the directory where CPack is preparing the contents of the package?
CMakeLists.txt looks like:
cmake_minimum_required(VERSION 2.8)
project("version_from_git")
# Appends the cmake/modules path to MAKE_MODULE_PATH variable.
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
# When in the extracted package, use the previously generated file, otherwise get the current version from git.
if(EXISTS "${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake")
include("${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake")
else()
include(GetGitRevisionDescription)
git_describe(VERSION --tags --dirty=-dirty)
endif()
# Parse the version information into pieces.
string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+(.*)" "\\1" VERSION_SHA1 "${VERSION}")
set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}")
# The following will copy the version file to the source package.
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_SHORT}-Source")
set(CPACK_INSTALL_COMMANDS "${CMAKE_COMMAND} -E make_directory \
${PROJECT_BINARY_DIR}/_CPack_Packages/Linux-Source/${CPACK_SOURCE_GENERATOR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}/"
"${CMAKE_COMMAND} -E copy \
${PROJECT_BINARY_DIR}/versionForSourcePackage.cmake \
${PROJECT_BINARY_DIR}/_CPack_Packages/Linux-Source/${CPACK_SOURCE_GENERATOR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}/")
# Exclude the build and .git directory from the source package.
set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_SOURCE_DIR}/.git/;${PROJECT_BINARY_DIR}/;${CPACK_SOURCE_IGNORE_FILES}")
include (CPack)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/version.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
# Prepare the versionForSourcePackage.cmake file that will be included in the source package.
configure_file(
${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake.in
${PROJECT_BINARY_DIR}/versionForSourcePackage.cmake #ONLY)
set(version_file "${CMAKE_CURRENT_BINARY_DIR}/version.cpp")
set(source_files "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")
#Add the version_file to the project build.
add_executable(${PROJECT_NAME} ${source_files} ${version_file})
The file versionForSourcePackage.cmake.in:
set(VERSION "v#VERSION_SHORT#")
The second possibility is the approach used at https://github.com/lcw/cmake_git_version. Full example is in the second_possibility branch in https://github.com/josefcibulka/version_git. The file versionForSourcePackage.cmake is placed to the subdirectory version_file of the build directory and this directory is added among CPACK_SOURCE_INSTALLED_DIRECTORIES. This works well when the project and build directory are at the same level.
The problem is that if the build directory is under the project directory, I add the build directory to CPACK_SOURCE_IGNORE_FILES to satisfy requirement 1. Then, even when I set CPACK_SOURCE_INSTALLED_DIRECTORIES to include the version_file directory, it will be ignored. It can be done so that I ignore only everything in the build directory except for the version_file directory. Then the build directory in the source package contains only the version_file directory, which is better than the whole build directory, but still not perfect.
Update: It seems that requirement 4 is not satisfied either, because I cannot find CPACK_SOURCE_INSTALLED_DIRECTORIES in the CMake documentation.
The differences from the first possibility are, in CMakeLists.txt:
# The following will transfer the version from git to the source package.
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${PROJECT_SOURCE_DIR};/;${PROJECT_BINARY_DIR}/version_file;/")
# Exclude the build and .git directory from the source package.
set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_SOURCE_DIR}/.git/;${PROJECT_BINARY_DIR}/([^v].*|v[^e].*|ve[^r].*|ver[^s].*|vers[^i].*|vers[^i].*|versi[^o].*|versio[^n].*|version[^_].*|version_[^f].*|version_f[^i].*|version_fi[^l].*|version_fil[^e].*);${CPACK_SOURCE_IGNORE_FILES}")
include (CPack)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/version.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
configure_file(
${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake.in
${PROJECT_BINARY_DIR}/version_file/versionForSourcePackage.cmake #ONLY)
At the end, I am using a modification of the first possibility, but the issues are solved only partially.
Instead of CPACK_INSTALL_COMMANDS, I use CPACK_INSTALL_SCRIPT and a CMake script. This script is executed for every package that is being created and the nice thing is that inside of this script, CMAKE_CURRENT_BINARY_DIR points to the directory where the contents of the package are being gathered. That is, whatever I copy to that directory will get inside the package.
This, however, looks to be an undocumented behavior of CMAKE_CURRENT_BINARY_DIR. For example, the CMAKE_BINARY_DIR and PROJECT_BINARY_DIR are empty when used in a CMake script executed by CPack. So maybe in some other version of CMake, CMAKE_CURRENT_BINARY_DIR will also be empty when executed by CPack.
Another issue is that I do want the version file to be copied only to source packages and not to binary packages. The way how I do the distinction is that if CMAKE_CURRENT_BINARY_DIR ends with "Source", I assume that the script is running during the preparation of a source package.
Full example is in the using_cpack_install_script branch of https://github.com/josefcibulka/version_git. The changed parts of CMakeLists.txt:
# The following will transfer the version from git to the source package.
set(CPACK_INSTALL_SCRIPT "${CMAKE_BINARY_DIR}/versionNumberToPackage.cmake")
configure_file(${CMAKE_SOURCE_DIR}/versionNumberToPackage.cmake.in
${CMAKE_BINARY_DIR}/versionNumberToPackage.cmake #ONLY)
The contents of versionNumberToPackage.cmake.in are:
# Copies VersionForSourcePackage.cmake to the source package (it should not be copied to binary package).
# Detection if this is the source package is that the path ends with "Source" and an optional slash.
if("${CMAKE_CURRENT_BINARY_DIR}" MATCHES "Source/?$")
file(COPY #CMAKE_BINARY_DIR#/versionForSourcePackage.cmake DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()

Can I create a single file that mentions all files I want to build : c++

In React, I can write code like this for my containers/components:
export App from './App/App';
export Chat from './Chat/Chat';
export Home from './Home/Home';
This allows me to specify the stuff that I am exporting from that dir. Can I do something like that in CMake?
What I want is to be able to create a header file that includes the files that I want to build. I dont want to list them out in CMakeLists.txt as it becomes too cluttered. I also dont want to GLOB_RECURSE as it doesnt allow me to select files. How do I do this?
Just create file which lists sources:
sources.list:
foo.c
bar/baz.c
and read it using file(STRINGS) command into the variable:
CMakeLists.txt:
# Load list of sources into 'sources' variable
file(STRINGS "sources.list" sources)
# Use the variable
add_executable(my_exe ${sources})
As pointed by #wasthishelpful, a file used in file(STRINGS) is not tracked by CMake. That is, if content of the file will be modified(e.g., new sources will be added), explicit cmake call is needed to reflect this modification. (That is, simple make doesn't cause cmake to rerun).
Alternative to file(STRINGS), which force CMake to track file with sources. is include():
sources.cmake:
set(sources
"foo.c"
"bar/baz.c"
)
CMakeLists.txt:
# Run additional script, which fills 'sources' variable with list of sources
include(sources.cmake)
# Use the variable
add_executable(my_exe ${sources})
Such a way, if "sources.cmake" will be changed, cmake will be automatically rerun on make call.

How to integrate QT internationalization to CMake?

Greetings all,
I am trying to use QT internationalization with CMake.
I have configured my cmake file as follows :
#Internalization - this should generate core_jp.ts ?
SET(rinzo_core_TRANSLATIONS
i18n/core_jp.ts
)
#these are my source files in the project
SET(FILES_TO_TRANSLATE
${rinzo_core_srcs}
${rinzo_core_moh_srcs}
)
QT4_CREATE_TRANSLATION(QM_FILES ${FILES_TO_TRANSLATE} ${rinzo_core_TRANSLATIONS})
QT4_ADD_TRANSLATION(QM ${rinzo_core_TRANSLATIONS})
But it doesnt genereate any TS nor QM files.
My questions -
1.Does Cmake(by using QT tools) generate TS files automatically extracting "tr()" methods from the source ? (that means I dont have to create any TS file and above i18n/core_jp.ts will be genereated automatically)
2.What exacly are QM files ?
Thanks in advance
In CMake documentation see QT4_CREATE_TRANSLATION and QT4_ADD_TRANSLATION macros.
So you should do the followings:
SET(Lang_files
example.ts
)
...
QT4_CREATE_TRANSLATION(LangSrcs ${Lang_files})
...
ADD_EXECUTABLE(project_name ... others sources ... ${LangSrcs})
Translation binary files (*.qm) according to http://itk.org/Wiki/CMake:How_To_Build_Qt4_Software
Also from the bottom of that website
Usage - Updating the .ts files
When you want it to process all your
source files (looking for new texts to
translate), configure cmake to turn on
UPDATE_TRANSLATIONS, and then make
your project. CMake will modify your
.ts files in your SOURCE folders in
addition to generating the .qm files.
WARNING: Be aware that CMake will be updating the source .ts files, which means that if > you do a make clean, it will DELETE your source .ts files!!! So it would be a good idea > to switch off UPDATE_TRANSLATIONS as soon as possible.
My solution relies on manually invoked lupdate and lrelease tools via add_custom_target, so the generated files are not removed on make clean and put into the source directory.
I defined a function that scans provided directory, generates/updates ts files, and compiles them into qm files in the same directory, so they can be added to the app via .qrc file
cmake_minimum_required(VERSION 3.5)
project(l10n LANGUAGES CXX)
find_package(Qt5 COMPONENTS Core LinguistTools REQUIRED)
# genearats ts and qm file searching recursively SRC_DIR
function(generate_translations CUSTOM_TARGET TS_DIR TS_FILES SRC_DIR)
set(UPADTE_TS_TARGET_NAME ${CUSTOM_TARGET}_ts)
set(UPADTE_QM_TARGET_NAME ${CUSTOM_TARGET}_qm)
add_custom_target(${UPADTE_TS_TARGET_NAME}
COMMAND ${Qt5_LUPDATE_EXECUTABLE} -recursive ${SRC_DIR} -ts ${TS_FILES}
WORKING_DIRECTORY ${TS_DIR})
add_custom_target(${UPADTE_QM_TARGET_NAME}
COMMAND ${Qt5_LRELEASE_EXECUTABLE} ${TS_FILES}
WORKING_DIRECTORY ${TS_DIR})
add_dependencies(${UPADTE_QM_TARGET_NAME} ${UPADTE_TS_TARGET_NAME} )
add_dependencies(${CUSTOM_TARGET} ${UPADTE_QM_TARGET_NAME})
endfunction()
add_executable(l10n main.cxx)
target_link_libraries(l10n Qt5::Core)
set(MY_TS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/translate)
set(MY_TS_FILES foo_en.ts foo_en.ts)
set(MY_SOURCE_LOOKUP_DIR ${CMAKE_CURRENT_SOURCE_DIR})
generate_translations(l10n "${MY_TS_DIR}" "${MY_TS_FILES}" "${MY_SOURCE_LOOKUP_DIR}")
can you use lupdate.exe, linguist.exe and lrelease.exe from qt/[qt_version]/[msvc|mingw|...]/bin/ ?
you can use it like that:
Usage:
lupdate [options] [project-file]
lupdate [options] [source-file|path]... -ts ts-files
Options:
-help Display this information and exit.
-noobsolete
Drop all obsolete strings.
-extensions [,]...
Process files with the given extensions only.
The extension list must be separated with commas, not with whitespace.
Default: 'ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx'.
-pluralonly
Only include plural form messages.
-silent
Do not explain what is being done.
-version
Display the version of lupdate and exit
so, [source-file|path] - is you option, like i think.
try to call it with list of source files names.