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
I am trying to generate a CMake target for a C++ project using add_custom_target that copies the contents of the directory in which the CMakeLists.txt resides into ${CMAKE_BINARY_DIR}, but excludes a given list of files.
While it looks like a quite easy task, I'm facing problems here. My attempts so far:
1) Generate a list of all files/dirs, remove the items to be excluded and use the CMake copy command:
file(GLOB files_to_copy LIST_DIRECTORIES TRUE *)
list(REMOVE_ITEM files_to_copy
${CMAKE_CURRENT_SOURCE_DIR}/file_to_exclude.txt
${CMAKE_CURRENT_SOURCE_DIR}/dir_to_exclude
# ...
)
add_custom_target(my-target
COMMAND ${CMAKE_COMMAND} -E copy ${files_to_copy} ${CMAKE_BINARY_DIR}
)
Problems:
Removing items this way works on a string comparison level, which could lead to problems when using symbolic links, for example
The copy command line tool apparently supports copying directories, however it doesn't seem to work on my machine, therefore directories do not get copied.
2) Use the file command to copy the files, excluding some files
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/" DESTINATION ${CMAKE_BINARY_DIR}
PATTERN "file_to_exclude.txt" EXCLUDE
PATTERN "dir_to_exclude" EXCLUDE
)
Problems:
This does exactly what I want, except that the command is not bound to a target and therefore is always executed. But I only need the copy operation to be a target.
Is there any possibility to bind the file(COPY ...) command to a target? Or any other straightforward solution to achieve what I want?
Please note that I only want to use CMake built-in tools and not execute any OS-specific shell commands (working on a cross-platform project).
CMake scripting commands work only in CMake context and are executed immediately.
But a COMMAND in add_custom_command (or add_custom_target) is executed in the context of a build tool (e.g. Makefile), not in CMake context.
However, you may put CMake scripting commands into separate CMake script, and call this script from the COMMAND. This solution has the same platform-independent properties as CMakeLists.txt itself.
You may parameterize separate script either:
With configure_file CMake command.
By passing -D parameters to CMake when call the script.
The first approach is quite simple: you write the script as would you write CMakeLists.txt itself. But it generates additional files for every parametrization set.
The second approach is useful for multi-purpose (or multi-usable) scripts, as it doesn't create additional copy of the script for every usage. But it requires some design of the parameters.
Using 'configure_file'
copy_sources.cmake.in (as if commands are written in CMakeLists.txt):
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/" DESTINATION ${CMAKE_BINARY_DIR}
PATTERN "file_to_exclude.txt" EXCLUDE
PATTERN "dir_to_exclude" EXCLUDE
)
CMakeLists.txt:
# Instantiate the parameterized script in the binary directory.
configure_file(copy_sources.cmake.in copy_sources.cmake)
add_custom_target(my-target
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/copy_sources.cmake
)
Using '-D' parameters
copy_sources.cmake:
# Expect the script to be called from the source directory.
# This saves one parameter.
#
# DEST_DIR denotes the directory for copy to.
file(COPY "${CMAKE_SOURCE_DIR}/" DESTINATION ${DEST_DIR}
PATTERN "file_to_exclude.txt" EXCLUDE
PATTERN "dir_to_exclude" EXCLUDE
)
CMakeLists.txt:
add_custom_target(my-target
COMMAND ${CMAKE_COMMAND}
-DDEST_DIR=${CMAKE_BINARY_DIR} # all '-D' options should preceed '-P' one
-P ${CMAKE_CURRENT_SOURCE_DIR}/copy_sources.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} # The script assumes proper current directory
)
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.
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.
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.
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.