CMake macro across CMakeLists - macros

I have a c++ project which directory structure like below:
server/
code/
BASE/
Thread/
Log/
Memory/
Net/
cmake/
CMakeList.txt
BASE/
CMakeList.txt
Net/
CMakeList.txt
here is part of /cmake/CMakeList.txt:
MACRO(SUBDIRLIST result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
SET(dirlist "")
FOREACH(child ${children})
IF(IS_DIRECTORY ${curdir}/${child})
SET(dirlist ${dirlist} ${child})
ENDIF()
ENDFOREACH()
SET(${result} ${dirlist})
ENDMACRO()
add_subdirectory(Base)
then use macro in /cmake/Base/CMakeList.txt:
SET(SUBDIR, "")
SUBDIRLIST(SUBDIRS, ${BASE_SRC_DIR})
message("SUBDIRS : " ${SUBDIRS})
output:
SUBDIRS :
I check ${dirlist} by output it's value in macro, I get directory list expected,but when message("result " ${result}) after SET(${result} ${dirlist}),I can not get output expected , what's wrong with my CMakeLists.txt?

There are a couple of minor issues here:
In your macro, SET(dirlist "") could be just SET(dirlist). Likewise, SET(SUBDIR, "") could be just SET(SUBDIRS) (I guess "SUBDIR" is a typo and should be "SUBDIRS". Also you don't want the comma in the set command - probably another typo?)
To output the contents of ${result} in the macro, use message("result: ${${result}}"), since you're not appending ${child} to result each time, but to ${result}. In your example ${result} is SUBDIRS, so ${${result}} is ${SUBDIRS}.
When you call SUBDIRLIST, don't use a comma between the arguments.
When you output the value of SUBDIRS, include ${SUBDIRS} in the quotes, i.e. message("SUBDIRS: ${SUBDIRS}") or you'll lose the semi-colon separators.
Other than those, your macro seems fine.

Related

os.walk() is not throwing any error nor the result in python

I am trying to print the file names which are in a predefined paths where the paths are stored in paths.txt. But when I execute the below code, I'm not getting any error nor the files names printed.
import os
with open('D:\paths.txt', 'r') as file:
data = file.read()
path = data.split(";")
print(path)
for line in path:
for root, dirs, files in os.walk(line):
for name in files:
print(name)
You need to remove the double-quotes from the file (""). Here is why; When the file gets read by Python, after it does the .split(), the double-quote characters are part of the Python string. So instead of passing into os.walk() the path D:\bp1, you were actually passing in "D:\bp1", and there was no path that starts with a " that's why nothing was happening.
You would only need to provide the double quotes if you're writing the name in a terminal/command prompt and don't want to escape the double quotes, or if you're trying to define the string inside Python using the double quote literal, for example path = "D:\\bp1" (notice that in that case you also have to escape the \ with another one.

Doxygen EXCLUDE_PATTERNS regex

I am attempting to exclude certain files from my doxygen generated documentation. I am using version 1.8.14.
My files come in this naming convention:
/Path2/OtherFile.cs
/Path/DAL.Entity/Source.cs
/Path/DAL.Entity/SourceBase.generated.cs
I want to exclude all files that do NOT end in Base.generated.cs, and are located inside of /Path/.
Since it appears doxygen claims to use regex for the exclude_patterns variable, I eventually came up with this:
.*\\Path\\DAL\..{4,15}\\((?<!Base\.generated).)*
Needless to say, it did not work. Nor did multiple other variations. So far a simple wildcard * is the only regex character I have gotten to actually work.
doxygen uses QRegExp for a lot of things, so I assumed that was the library used for this variable as well, but even several variations of a pattern that that library claims to support did not work; granted apparently that library is full of bugs, but I would expect some things to work.
Does doxygen actually use a regex library for this variable?
If so, which library is it?
In either case, is there a method of achieving my goal?
My conclusion is; No... Doxygen Doxyfile does not support real regex. Even though they claim that it do. It's just standard wildcards that work.
We ended up with a really awkward solution to work around this.
What we did is that we added a macro in our CMakeLists.txt that creates a string with everything we want to include in INPUT instead. Manually excluding the parts we don't want.
The sad part is that CMakes regex also is crippled. So we couldn't use advanced regex such as negative lookahead in LIST(FILTER EXLUDE) similar to LIST(FILTER children EXCLUDE REGEX "^((?!autogen/public).)*$")... So even this solution is not really what we wanted.
Our CMakeLists.txt ended up looking something like this
cmake_minimum_required(VERSION 3.9)
project(documentation_html LANGUAGES CXX)
find_package(Doxygen REQUIRED dot)
# Custom macros
## Macro for getting all relevant directories when creating HTML documentain.
## This was created cause the regex matching in Doxygen and CMake are lacking support for more
## advanced syntax.
MACRO(SUBDIRS result current_dir include_regex)
FILE(GLOB_RECURSE children ${current_dir} ${current_dir}/*)
LIST(FILTER children INCLUDE REGEX "${include_regex}")
SET(dir_list "")
FOREACH(child ${children})
get_filename_component(path ${child} DIRECTORY)
IF(${path} MATCHES ".*autogen/public.*$" OR NOT ${path} MATCHES ".*build.*$") # If we have the /source/build/autogen/public folder available we create the doxygen for those interfaces also.
LIST(APPEND dir_list ${path})
ENDIF()
ENDFOREACH()
LIST(REMOVE_DUPLICATES dir_list)
string(REPLACE ";" " " dirs "${dir_list}")
SET(${result} ${dirs})
ENDMACRO()
SUBDIRS(DOCSDIRS "${CMAKE_SOURCE_DIR}/docs" ".*.plantuml$|.*.puml$|.*.md$|.*.txt$|.*.sty$|.*.tex$|")
SUBDIRS(SOURCEDIRS "${CMAKE_SOURCE_DIR}/source" ".*.cpp$|.*.hpp$|.*.h$|.*.md$")
# Common config
set(DOXYGEN_CONFIG_PATH ${CMAKE_SOURCE_DIR}/docs/doxy_config)
set(DOXYGEN_IN ${DOXYGEN_CONFIG_PATH}/Doxyfile.in)
set(DOXYGEN_IMAGE_PATH ${CMAKE_SOURCE_DIR}/docs)
set(DOXYGEN_PLANTUML_INCLUDE_PATH ${CMAKE_SOURCE_DIR}/docs)
set(DOXYGEN_OUTPUT_DIRECTORY docs)
# HTML config
set(DOXYGEN_INPUT "${DOCSDIRS} ${SOURCEDIRS}")
set(DOXYGEN_EXCLUDE_PATTERNS "*/tests/* */.*/*")
set(DOXYGEN_FILE_PATTERNS "*.cpp *.hpp *.h *.md")
set(DOXYGEN_RECURSIVE NO)
set(DOXYGEN_GENERATE_LATEX NO)
set(DOXYGEN_GENERATE_HTML YES)
set(DOXYGEN_HTML_DYNAMIC_MENUS NO)
configure_file(${DOXYGEN_IN} ${CMAKE_BINARY_DIR}/DoxyHTML #ONLY)
add_custom_target(docs
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/DoxyHTML -d Markdown
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Generating documentation"
VERBATIM)
and in the Doxyfile we added the environment variables for those fields
OUTPUT_DIRECTORY = #DOXYGEN_OUTPUT_DIRECTORY#
INPUT = #DOXYGEN_INPUT#
FILE_PATTERNS = #DOXYGEN_FILE_PATTERNS#
RECURSIVE = #DOXYGEN_RECURSIVE#
EXCLUDE_PATTERNS = #DOXYGEN_EXCLUDE_PATTERNS#
IMAGE_PATH = #DOXYGEN_IMAGE_PATH#
GENERATE_HTML = #DOXYGEN_GENERATE_HTML#
HTML_DYNAMIC_MENUS = #DOXYGEN_HTML_DYNAMIC_MENUS#
GENERATE_LATEX = #DOXYGEN_GENERATE_LATEX#
PLANTUML_INCLUDE_PATH = #DOXYGEN_PLANTUML_INCLUDE_PATH#
After this we can run cd ./build && cmake ../ && make docs to create our html documentation and have it include the autogenerated interfaces in our source folder without including all the other directories in the build folder.
Quick description of what actually happens in the CMakeLists.txt
# Macro that gets all directories from current_dir recursively and returns the result to result as a space separated string
MACRO(SUBDIRS result current_dir include_regex)
# Gets all files recursively from current_dir
FILE(GLOB_RECURSE children ${current_dir} ${current_dir}/*)
# Filter files so we only keep the files that match the include_regex (can't be to advanced regex)
LIST(FILTER children INCLUDE REGEX "${include_regex}")
SET(dir_list "")
# Let us act on all files... :)
FOREACH(child ${children})
# We're only interested in the path. So we get the path part from the file
get_filename_component(path ${child} DIRECTORY)
# Since CMakes regex also is crippled we can't do nice things such as LIST(FILTER children EXCLUDE REGEX "^((?!autogen/public).)*$") which would have been preferred (CMake regex does not understand negative lookahead/lookbehind)... So we ended up with this ugly thing instead... Adding all build/autogen/public paths and not adding any other paths inside build. I guess it would be possible to write this expression in regex without negative lookahead. But I'm both not really fluent in regex (who are... right?) and a bit lazy in this case. We just needed to get this one pointer task done... :P
IF(${path} MATCHES ".*autogen/public.*$" OR NOT ${path} MATCHES ".*build.*$")
LIST(APPEND dir_list ${path})
ENDIF()
ENDFOREACH()
# Remove all duplicates... Since we GLOBed all files there are a lot of them. So this is important or Doxygen INPUT will overflow... I know... I tested...
LIST(REMOVE_DUPLICATES dir_list)
# Convert the dir_list to a space seperated string
string(REPLACE ";" " " dirs "${dir_list}")
# Return the result! Coffee and cinnamon buns for everyone!
SET(${result} ${dirs})
ENDMACRO()
# Get all the pathes that we want to include in our documentation ... this is also where the build folders for the different applications are going to be... with our autogenerated interfaces which we want to keep.
SUBDIRS(SOURCEDIRS "${CMAKE_SOURCE_DIR}/source" ".*.cpp$|.*.hpp$|.*.h$|.*.md$")
# Add the dirs we want to the Doxygen INPUT
set(DOXYGEN_INPUT "${SOURCEDIRS}")
# Normal exlude patterns for stuff we don't want to add. This thing does not support regex... even though it should.
set(DOXYGEN_EXCLUDE_PATTERNS "*/tests/* */.*/*")
# Normal use of the file patterns that we want to keep in the documentation
set(DOXYGEN_FILE_PATTERNS "*.cpp *.hpp *.h *.md")
# IMPORTANT! Since we are creating all the INPUT paths our self we don't want Doxygen to do any recursion for us
set(DOXYGEN_RECURSIVE NO)
# Write the config
configure_file(${DOXYGEN_IN} ${CMAKE_BINARY_DIR}/DoxyHTML #ONLY)
# Create the target that will use that config to create the html documentation
add_custom_target(docs
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/DoxyHTML -d Markdown
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Generating documentation"
VERBATIM)
I know this isn't the answer anyone who stumbles in on this question wants... unfortunately it seems to be the only reasonable solution...
... you all have my deepest condolences...

Passing file path with spaces and forward slashes as argument to TCL command

I have a TCL script that is run by Libero using a file path provided as an argument to open a project. The file path is C:\Users\me\Documents\FPGA projects\file.prjx
I am running the script according to Libero TCL Reference Guide (pages 51 - 52) to run the script on the command line. On page 47, the doc outlines how to work with filenames with spaces; using braces or in the case where it is used as an argument use double quotes.
The command I am trying to execute is:
Path\to\libero SCRIPT:export.tcl SCRIPT_ARGS:""C:\Users\me\Documents\FPGA projects\file.prjx""
The outer set of double quotes is to follow the syntax outlined in page 52 of the document for providing arguments and the inner set of double quotes is to handle the white space in the first argument. I had expected $argv 0 to be C:\Users\me\Documents\FPGA projects\file.prjx, but instead $argv 0 is actually C:\Users\me\Documents\FPGA.
I added a print statement to the script to print $argv:
puts $argv
This gives a result of C:\Users\me\Documents\FPGA so the rest of the file path is not being interpreted as even being a second argument.
My assumption is that the conventions outlined in the document are just standard TCL conventions for providing a file path containing forward slashes and spaces as an argument. I have not been able to find an example of passing a similarly formatted argument in TCL. Any ideas?
I'm not sure it's the same issue but, having MagicSplat tclsh.exe and wish.exe linked to *.tcl and *.tk files in Windows (7 to 11), calling scripts this way:
processDIR.tcl "c:\My directory\subdir"
evals arguments only till first space, becoming "c:\My" BUT calling scripts like this:
tclsh.exe processDIR.tcl "c:\My directory\subdir"
trespasses complete argument.
Could you give it a try?

How to assign particular context to --keyword for proper_name?

When using the xgettext tool it is possible to automatically add commenting to assist translators with regards to proper names (as documented).
The documentation suggests to add the following to the command line:
--keyword='proper_name:1,"This is a proper name. See the gettext manual, section Names."'
Which results in proper names being extracted to the .pot file like this:
#. This is a proper name. See the gettext manual, section Names.
#: ../Foo.cpp:18
msgid "Bob"
msgstr ""
The problem with this; is that no particular context has been defined for that string. Here is ideally how the proper name would be extracted:
#. This is a proper name. See the gettext manual, section Names.
#: ../Foo.cpp:18
msgctxt "Proper Name"
msgid "Bob"
msgstr ""
I've tried the following but with no success:
# Hoping that 0 would be the function name 'proper_name'.
--keyword='proper_name:0c,1,"This is a proper name. See the gettext manual, section Names."'
# Hoping that -1 would be the function name 'proper_name'.
--keyword='proper_name:-1c,1,"This is a proper name. See the gettext manual, section Names."'
# Hoping that the string would be used as the context.
--keyword='proper_name:"Proper Name"c,1,"This is a proper name. See the gettext manual, section Names."'
# Hoping that the string would be used as the context.
--keyword='proper_name:c"Proper Name",1,"This is a proper name. See the gettext manual, section Names."'
Is there a way to force a particular msgctxt to be used for all strings extracted with a keyword (such as proper_name from the example above)?
If there is no option to achieve this with xgettext as-is then I considered perhaps using the following:
--keyword='proper_name:1,"<PROPERNAME>"'
Resulting with:
#. <PROPERNAME>
#: ../Foo.cpp:18
msgid "Bob"
msgstr ""
The problem then becomes; how to automatically translate all occurrences of this in the resulting .pot file into the following:
#. This is a proper name. See the gettext manual, section Names.
#: ../Foo.cpp:18
msgctxt "Proper Name"
msgid "Bob"
msgstr ""
If you want to extract a message context, it has to be part of the argument list. And the numerical part in "Nc" has to be a positive integer. All your attempts with 0, -1 are fruitless, sorry.
The signature of your function must look like this:
#define PROPER_NAME "Proper Name"
const char *proper_name(const char *ctx, const char *name);
And then call it like this:
proper_name(PROPER_NAME, "Bob");
That repeats PROPER_NAME all over the code, but it's the only way to get it into the message context.
Maybe file a feature request?
There is also a hack that achieves the same without changing your source code. I assume that you're using C and the standard Makefile (but you can do the same in other languages):
Copy the file POTFILES to POTFILES-proper-names and add a line ./proper_names.pot to POTFILES.in.
Then you have to create proper_names.pot:
xgettext --files-from=POTFILES-proper-names \
--keyword='' \
--keyword='proper_names:1:"Your comment ..."' \
--output=proper_names.pox
This will now only contain the entries that were maked with "proper_names()". Now add the context:
msg-add-content proper_names.pox "Proper Name" >proper_names.pot
rm proper_names.pot
Unfortunately, there is no program called "msg-add-content". Grab one of the zillion po-parsers out there, and write one yourself (or take mine at the end of this post).
Now, update your PACKAGE.pot as usual. Since "proper_names.pox" is an input file for the main xgettext run, all your extracted proper names with the context added, are added to your pot file (and their context will be used).
Short of another script for adding a message context to all your entries in a .pot file, use this one:
#! /usr/bin/env perl
use strict;
use Locale::PO;
die "usage: $0 POFILE CONTEXT" unless #ARGV == 2;
my ($input, $context) = #ARGV;
my $entries = Locale::PO->load_file_asarray($input) or die "$input: failure";
foreach my $entry (#$entries) {
$entry->msgctxt($context) unless '""' eq $entry->msgid;
print $entry->dump;
}
You have to install the Perl library "Locale::PO" for it, either with "sudo cpan install Locale::PO" or use the pre-built version that your vendor may have.

Uppercasing filename in Makefile using sed

I try to convert a filename such as foo/bar/baz.proto into something like foo/bar/Baz.java in my Makefile. For this purpose, I thought I could use sed. However, it seems that the command does not work as expected:
uppercase_file = $(shell echo "$(1)" | sed 's/\(.*\/\)\(.*\)/\1\u\2/')
# generated Java sources
PROTO_JAVA_TARGETS := ${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/$(call uppercase_file,%).java}
When I try to run the sed command on the command line it seems to work:
~$ echo "foo/bar/baz" | sed 's/\(.*\/\)\(.*\)/\1\u\2/'
foo/bar/Baz
Any ideas why this does not work inside the Makefile?
UPDATE:
The java files are generated with the following target:
$(JAVAGEN)/%.java: $(SRCDIR)/%.proto
How can I apply the substitution also for targets?
GNU Make does not replace % character in the replacement part of a substitution reference (which is basically a syntactic sugar for patsubst) if it is part of a variable reference. I have not found this behavior described in the documentation, but you can look it implemented in the source code (the relevant function I believe is find_char_unquote).
I suggest moving the call out of the substitution reference, since uppercase_file obviously works properly on any file path:
PROTO_JAVA_TARGETS := $(call uppercase_file,${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java})
If $(PROTO_SPECS) resolves not to a single element, but rather to a list of elements, you can use foreach to call the function on every elements of a processed list:
PROTO_JAVA_TARGETS := $(foreach JAVA,${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java},$(call uppercase_file,$(JAVA)))
The java files are generated with the following target: $(JAVAGEN)/%.java: $(SRCDIR)/%.proto
How can I apply the substitution also for targets?
Since Make matches targets first, and there is no way to run sed backwards, what you need here is either define an inverse function, or generate multiple explicit rules. I will show the latter approach.
define java_from_proto
$(call uppercase_file,$(1:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java)): $1
# Whatever recipe you use.
# Use `$$#`, `$$<` and so on instead of `$#` or `$<`.
endef
$(foreach PROTO,$(PROTO_SPECS),$(eval $(call java_from_proto,$(PROTO))))
We basically generate one rule per file in $(PROTO_SPEC) using a multiline variable syntax, and then use eval to install that rule. There is also a very similar example on this documentation page that can be helpful.