How does cmake print the path of the header file of the library the project depends on? - cmake

I want cmake to output the path of the header file of the library that the project depends on, and give it to ctags to generate tags.
I have tried to generate tags of all header files of the system directly: ctags -R /usr/include, but the size of the generated tags file is 190MB, which is too large.
For example, if libcurl is used in the project, then let cmake output /usr/include/curl, and then ctags can ctags -R /usr/include/curl.
I looked at cmake --help, but didn't find what I was looking for. How can I achieve this?

Generate compile_commands.json. Parse compile_commands.json, extract all "command": keys, extract all -I<this paths> include paths from compile commands, interpret them relative to build directory. sort -u the list.
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ...
$ jq -r '.[] | .command' "$builddir"/compile_commands.json |
grep -o -- '-I[^ ]*' |
sed 's/^-I//' |
sort -u |
( cd "$builddir" && xargs -d '\n' readlink -f ) |
sort -u

Related

How to call a custom cmake script only when files are updated?

I want to run an JavaScript minifier only when my JavaScript files are updated.
I know how to do this in make, but not in CMakelist.
Currently I have a script that minifies the JavaScript files, but it runs any time any files update.
add_custom_target(MINI-JS ALL DEPENDS ${JS_FILES}
COMMAND rm -f ../projectv/js/all-min.js
COMMAND cat ${JS_FILES} | jsmin >> ../projectv/js/all-min.js
)
Edit: Tsyvarev's comment seems to suggest this:
add_custom_command(OUTPUT ../projectv/js/all-min.js
DEPENDS ${JS_FILES}
COMMAND rm -f ../projectv/js/all-min.js
COMMAND cat ${JS_FILES} | jsmin >> ../projectv/js/all-min.js
)
add_custom_target(MINI-JS ALL DEPENDS ../projectv/js/all-min.js)
This never updates, even when the JavaScript files change. Am I missing something?
The code below should work. In my example I'm using CMake command line tool -E mode. This way the custom command is more portable. My example relies on 3.17 though.
# SUPER IMPORTANT: Always specify absolute paths!
set(foobar "${CMAKE_CURRENT_LIST_DIR}../projectv/js/all-min.js")
add_custom_command(
OUTPUT
# This is the file your custom command will create
${foobar}
DEPENDS
# These are all the files you need to run your custom command
${JS_FILES}
COMMAND
COMMAND ${CMAKE_COMMAND} -E rm ${foobar}
COMMAND ${CMAKE_COMMAND} -E cat ${JS_FILES} | jsmin >> ${foobar}
)
add_custom_target(MINI-JS ALL DEPENDS ${foobar})

Use Fortify sourceanalyzer with CMake

I have a Makefile generated by CMake. The following path to CMake executable is set in the Makefile:
CMAKE_COMMAND = /home/xyz/opt/cmake/cmake-3.1.1/bin/cmake
How can I integrate Fortify sourceanalyzer with it and run scans?
I had the same challenge but solved it by running it like this:
sourceanalyzer -b project_ID -clean
Go to your build directory and perform make clean or remove all contents including the Makefile
Run cmake by changing CC and CXX variables:
CC="sourceanalyzer -b project_ID gcc" CXX="sourceanalyzer -b project_ID g++" cmake ..
Run make and fortify should be translating files while compilers do their job.
Run sourceanalyzer -b project_ID -scan -f results.fpr
Hope it helps.
I was tasked with integrating our CMake build system with HP Fortify SCA and came across this Thread that gave some insights but lacked specifics as related to HP Fortify so I thought I would share my implementation.
I created a fortify_tools directory at the same level as the source directory. Inside the fortify_tools are a toolchain file and fortify_cc, fortify_cxx, and fortify_ar scripts that will be set as the cmake_compilers via the toolchain file.
fortify_cc
#!/bin/bash
sourceanalyzer -b <PROJECT_ID> gcc $#
fortify_cxx
#!/bin/bash
sourceanalyzer -b <PROJECT_ID> g++ $#
fortify_ar
#!/bin/bash
sourceanalyzer -b <PROJECT_ID> ar $#
NOTE: insert your project name in place of PROJECT_ID
Setting cmake to use the scripts is accomplished in a toolchain file.
fortify_linux_toolchain.cmake
INCLUDE (CMakeForceCompiler)
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 1)
#specify the compilers
SET(CMAKE_C_COMPILER ${CMAKE_SOURCE_DIR}/fortify_tools/fortify_cc)
SET(CMAKE_CXX_COMPILER ${CMAKE_SOURCE_DIR}/fortify_tools/fortify_cxx)
SET(CMAKE_AR_COMPILER ${CMAKE_SOURCE_DIR}/fortify_tools/fortify_ar)
To generate makefiles using the toolchain file
ccmake -DCMAKE_TOOLCHAIN_FILE=../fortify_tools/foritfy_linux_toolchain.cmake ../
configure and generate your makefiles and build your project.
Once the project is built from within the build directory generate a fortify report by
sourceanalyzer -Xmx2400M -debug -verbose -b <PROJECT_ID> -scan -f <PROJECT_ID>.fpr
I understand the last step is outside of CMake but I am pretty confident a cmake_custom_command can be created to perform the scan step as a post build action.
Finally, this is just the linux implementation but the concept scales well to Windows by creating the necessary batch files and windows specific toolchain file
Fortify doesn't support CMake, I received confirmation from Fortify support team.
This answer is late, but might help someone. This is actually easy to fix - you simply need to run cmake inside sourceanalyzer as well. Make a simple build script that calls cmake and then make, and use sourceanalyzer on that instead. I am using fortify 4.21.
Our old Fortify script for building hand-created Makefiles used a build command that looked like this:
$SOURCEANALYZER $MEMORY $LAUNCHERSWITCHES -b $BUILDID make -f Makefile -j12
I was able to get it working for a project that had been converted to CMake by replacing the above line with this, inspired by a couple of the other answers here:
CC="$SOURCEANALYZER $MEMORY $LAUNCHERSWITCHES -b $BUILDID gcc" \
CXX="$SOURCEANALYZER $MEMORY $LAUNCHERSWITCHES -b $BUILDID g++" \
AR="$SOURCEANALYZER $MEMORY $LAUNCHERSWITCHES -b $BUILDID ar" \
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ..
make -f Makefile -j12 VERBOSE=1
This is with cmake 2.8.12.2 on Linux.
Below is the script i use for my example project to generate HP Fortify report for Android JNI C/C++ Code.
#!/bin/sh
# Configure NDK version and CMake version
NDK_VERSION=21.0.6113669
CMAKE_VERSION=3.10.2
CMAKE_VERSION_PATH=$CMAKE_VERSION.4988404
PROJECTID="JNI_EXAMPLE"
REPORT_NAME=$PROJECTID"_$(date +'%Y%m%d_%H:%M:%S')"
WORKING_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BUILD_HOME=${WORKING_DIR}/../hpfortify_build
FPR="$BUILD_HOME/$REPORT_NAME.fpr"
# Following exports need to be configured according to host machine.
export ANDROID_SDK_HOME=/Library/Android/sdk
export ANDROID_CMAKE_HOME=$ANDROID_SDK_HOME/cmake/$CMAKE_VERSION_PATH/bin
export ANDROID_NDK_HOME=$ANDROID_SDK_HOME/ndk/$NDK_VERSION
# E.g. JniExample/app/hpfortify/build/CMakeFiles/3.10.2
export CMAKE_FILES_PATH=${BUILD_HOME}/CMakeFiles/$CMAKE_VERSION
export HPFORTIFY_HOME="/Applications/Fortify/Fortify_SCA_and_Apps_20.1.0/bin"
export PATH=$PATH:$ANDROID_SDK_HOME:$ANDROID_NDK_HOME:$ANDROID_CMAKE_HOME:$HPFORTIFY_HOME
echo "[========Start Android JNI C/C++ HP Fortify scanning========]"
echo "[========Build Dir: $BUILD_HOME========]"
echo "[========HP Fortify report path: $FPR========]"
function create_build_folder {
rm -rf $BUILD_HOME
mkdir $BUILD_HOME
}
# The standalone cmake build command can be found from below file.
# JniExample/app/.cxx/cmake/release/x86/build_command.txt
# This file is generated after running command
# `➜ JniExample git:(master) ✗ ./gradlew :app:externalNativeBuildRelease`
function configure_cmake_files {
cd $BUILD_HOME
$ANDROID_CMAKE_HOME/cmake -H$BUILD_HOME/. \
-DCMAKE_CXX_FLAGS=-std=c++11 -frtti -fexceptions \
-DCMAKE_FIND_ROOT_PATH=$BUILD_HOME/.cxx/cmake/release/prefab/x86/prefab \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_SDK_HOME/ndk/$NDK_VERSION/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=x86 \
-DANDROID_NDK=$ANDROID_SDK_HOME/ndk/$NDK_VERSION \
-DANDROID_PLATFORM=android-16 \
-DCMAKE_ANDROID_ARCH_ABI=x86 \
-DCMAKE_ANDROID_NDK=$ANDROID_SDK_HOME/ndk/$NDK_VERSION \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$BUILD_HOME/intermediates/cmake/release/obj/x86 \
-DCMAKE_MAKE_PROGRAM=$ANDROID_SDK_HOME/cmake/$CMAKE_VERSION_PATH/bin/ninja \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=16 \
-B$BUILD_HOME/.cxx/cmake/release/x86 \
-GNinja ..
}
function build {
cmake --build .
}
function cleanup {
rm -rf $BUILD_HOME/CMakeFiles/native-lib.dir
rm -rf $FPR
$HPFORTIFY_HOME/sourceanalyzer -clean
}
function replace_compiler_paths {
FORTIFY_TOOLS_PATH="$WORKING_DIR"
CLANG_PATH="$ANDROID_SDK_HOME/ndk/$NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang"
CLANGXX_PATH="$ANDROID_SDK_HOME/ndk/$NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++"
HPFORTIFY_CCPATH="$FORTIFY_TOOLS_PATH/fortify_cc"
HPFORTIFY_CXXPATH="$FORTIFY_TOOLS_PATH/fortify_cxx"\"
sed -i '' 's+'$CLANG_PATH'+'$HPFORTIFY_CCPATH'+g' $CMAKE_FILES_PATH/CMakeCCompiler.cmake
sed -i '' 's+'$CLANG_PATH.*[^")"]'+'$HPFORTIFY_CXXPATH'+g' $CMAKE_FILES_PATH/CMakeCXXCompiler.cmake
}
function scan {
$HPFORTIFY_HOME/sourceanalyzer -b $PROJECTID -scan -f $FPR
# copy the file to $WORKING_DIR
cp $FPR $WORKING_DIR
}
create_build_folder
configure_cmake_files
echo "[========Compile C/C++ using normal compiler ========"]
build
echo "[========Replace the compiler with HP Fortify analyser wrapper compilers ========"]
replace_compiler_paths
echo "[========Clean up the build intermediates and the older build ID and fpr file ========"]
cleanup
echo "[========Recompile C/C++ using HP Fortify analyser wrapper compilers ========"]
build
echo "[========Scan the compiled files and generate final report ========"]
scan
echo "[========Change directory to original working dir ========"]
cd $WORKING_DIR
Need to configure below vars before using it. For my case, I use NDK 21 and CMake 3.10.2 and my project ID is "JNI_EXAMPLE"
# Configure NDK version and CMake version
NDK_VERSION=21.0.6113669
CMAKE_VERSION=3.10.2
CMAKE_VERSION_PATH=$CMAKE_VERSION.4988404
PROJECTID="JNI_EXAMPLE"
# Following exports need to be configured according to host machine.
export ANDROID_SDK_HOME=/Library/Android/sdk
export ANDROID_NDK_HOME=$ANDROID_SDK_HOME/ndk/$NDK_VERSION
export HPFORTIFY_HOME="/Applications/Fortify/Fortify_SCA_and_Apps_20.1.0/bin"
Here is a more detailed explanation: Using HP Fortify to Scan Android JNI C/C++ Code
On recent version of CMake one can use:
CMAKE_<LANG>_COMPILER_LAUNCHER='sourceanalyzer;-b;<PROJECT_ID>'
You can add other arguments (like -Xmx2G for instance), semicolon separated, as mentioned on cmake documentation
You need to check if you don't use the compiler launcher for another tool like ccache. We can probably use both with
CCACHE_PREFIX='.../sourceanalyzer -b ID'
Here is what I've used in CMake project:
project(myFortifiedProject LANGUAGES CXX)
set(CMAKE_CXX_COMPILER_LAUNCHER ${FORTIFY_TOOL} -b ${PROJECT_NAME})
So when running cmake (assuming sourceanalyzer is on the path):
cmake <other args> -DFORTIFY_TOOL=sourceanalyzer
So the normal build command works:
make myFortifiedProject
And you can finally collect results with:
sourceanalyzer -b myFortifiedProject -scan

terminal mkdir with variable and subfolders

I have a text file "modules.txt" containing (individual module names):
dashboard
editor
images
inspector
loader
navigation
sharing
tags
toolbar
I want to create a folder structure for each module like:
dashboard/templates
editor/templates
flash/templates
images/templates
etc ...
I'm fiddling around in the area of:
cat modules.txt | xargs mkdir -p $1/templates
But this creates the first level of folders, ignoring the /templates part and giving and error:
mkdir: /templates: File exists
Which it does not.
I've tried all sort of combinations of:
cat modules.txt | xargs mkdir -p $1/{templates}
cat modules.txt | xargs mkdir -p $1{templates}
cat modules.txt | xargs mkdir -p $1/{templates}
cat modules.txt | xargs mkdir -p $1\/{templates}
(yes, pretty much guessing here)
I've also tried to add the /templates to each line in the text file, but that makes the whole thing crash.
Any ideas how to go about doing this?
Turns out, this did work when adding /templates to the text file. Must have been to tired (or stupid) when fiddling with this.
cat file_containing_folder_structure.txt | xargs mkdir -p

Issue with genstrings for Swift file

genstrings works well to extract localizable content from .m file as,
find . -name \*.m | xargs genstrings -o en.lproj
But, not working for .swift file as,
find . -name \*.swift | xargs genstrings -o en.lproj
The genstrings tool works fine with swift as far as I am concerned. Here is my test:
// MyClass.swift
let message = NSLocalizedString("This is the test message.", comment: "Test")
then, in the folder with the class
# generate strings for all swift files (even in nested directories)
$ find . -name \*.swift | xargs genstrings -o .
# See results
$ cat Localizable.strings
/* Test */
"This is the test message." = "This is the test message.";
$
I believe genstrings works as intended, however Apple's xargs approach to generate strings from all your project's files is flawed and does not properly parse paths containing spaces.
That might be the reason why it's not working for you.
Try using the following:
find . -name \*.swift | tr '\n' '\0' | xargs -0 genstrings -o .
We wrote a command line tool that works for Swift files and merges the result of apples genstrings tool.
It allows for key and value in NSLocalizedString
https://github.com/KeepSafe/genstrings_swift
There's an alternative tool called SwiftGenStrings
Hello.swift
NSLocalizedString("hello", value: "world", comment: "Hi!")
SwiftGenStrings:
$ SwiftGenStrings Hello.swift
/* Hi! */
"hello" = "world";
Apple genstrings:
$ genstrings Hello.swift
Bad entry in file Hello.swift (line = 1): Argument is not a literal string.
Disclaimer: I worked on SwiftGenStrings.
There is a similar question here:
How to use genstrings across multiple directories?
find ./ -name "*.m" -print0 | xargs -0 genstrings -o en.lproj
The issue I was having with find/genstrings was twofold:
When it reached folder names with spaces (generated by the output of find), it would exit with an error
When it reached the file where I had my custom routine defined, it was giving me an error when trying to parse my actual function definition
To fix both those problems I'm using the following:
find Some/Path/ \( -name "*.swift" ! -name "MyExcludedFile.swift" \) | sed "s/^/'/;s/$/'/" | xargs genstrings -o . -s MyCustomLocalizedStringRoutine
To summarize, we use the find command to both find and exclude your Swift files, then pipe the results into the sed command which will wrap each file path in quotes, then finally pipe that result into the genstrings command
Xcode now includes a powerful tool for extracting localizations.
Just select your project on the left then Editor menu >> Export localizations.
You'll get a folder with all the text in your files as well as the Localizable.strings and InfoPlist.strings
More details here:
https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LocalizingYourApp/LocalizingYourApp.html

NetBeans doesn't recognize Makefile.am

I'm coming from a Objective-C/Xcode background.
I'm used to working with C projects already imported into XCode, but now I want to analyse an existing implementation of an algorithm I'm interested in integrating with my project.
Only that this project is written completely in C and has nothing to do with Objective-C/Xcode etc.
I'm not sure what is the best way to view a purely C project on Mac, so I installed NetBeans for C/C++.
The problem is that when I try to create a New Project on NetBeans and select C/C++ Project with Existing Sources it complains that
no make files or configure scripts were found
in the root directory.. although it clearly has a Makefile.am
I know that the Balsa project is written for linux, but I'm not interested in building the binary I just want to look at the source code in a IDE kinda way (ie I can click on a function call and see where it's implemented etc etc).
So in short my question is why isn't NetBeans recognising my Makefile.am?
and just for reference here is the content of the Makefile.am
#intl dir needed for tarball --disable-nls build.
DISTCHECK_CONFIGURE_FLAGS=--disable-extra-mimeicons --without-gnome --without-html-widget
SUBDIRS = po sounds images doc libbalsa libinit_balsa src
# set tar in case it is not set by automake or make
man_MANS=balsa.1
pixmapdir = $(datadir)/pixmaps
pixmap_DATA = gnome-balsa2.png
desktopdir = $(datadir)/applications
desktop_in_files = balsa.desktop.in balsa-mailto-handler.desktop.in
desktop_DATA = balsa.desktop balsa-mailto-handler.desktop
#INTLTOOL_DESKTOP_RULE#
balsa_extra_dist = \
GNOME_Balsa.server.in \
HACKING \
balsa-mail-style.xml \
balsa-mail.lang \
balsa.1.in \
balsa.spec.in \
bootstrap.sh \
docs/mh-mail-HOWTO \
docs/pine2vcard \
docs/vconvert.awk \
$(desktop_in_files) \
gnome-balsa2.png \
intltool-extract.in \
intltool-merge.in \
intltool-update.in \
mkinstalldirs
if BUILD_WITH_G_D_U
balsa_g_d_u_extra_dist = gnome-doc-utils.make
endif
if !BUILD_WITH_UNIQUE
serverdir = $(libdir)/bonobo/servers
server_in_files = GNOME_Balsa.server
server_DATA = $(server_in_files:.server.in=.server)
$(server_in_files): $(server_in_files).in
sed -e "s|\#bindir\#|$(bindir)|" $< > $#
endif
EXTRA_DIST = \
$(balsa_extra_dist) \
$(balsa_g_d_u_extra_dist)
if BUILD_WITH_GTKSOURCEVIEW2
gtksourceviewdir = $(BALSA_DATA_PREFIX)/gtksourceview-2.0
gtksourceview_DATA = balsa-mail.lang \
balsa-mail-style.xml
endif
DISTCLEANFILES = $(desktop_DATA) $(server_DATA) \
intltool-extract intltool-merge intltool-update \
gnome-doc-utils.make
dist-hook: balsa.spec
cp balsa.spec $(distdir)
#MAINT#RPM: balsa.spec
#MAINT# rm -f *.rpm
#MAINT# $(MAKE) distdir="$(PACKAGE)-#BALSA_VERSION#" dist
#MAINT# cp $(top_srcdir)/rpm-po.patch $(top_builddir)/rpm-po.patch
#MAINT# rpm -ta "./$(PACKAGE)-#BALSA_VERSION#.tar.gz"
#MAINT# rm $(top_builddir)/rpm-po.patch
#MAINT# -test -f "/usr/src/redhat/SRPMS/$(PACKAGE)-#VERSION#-#BALSA_RELEASE#.src.rpm" \
#MAINT# && cp -f "/usr/src/redhat/SRPMS/$(PACKAGE)-#VERSION#-#BALSA_RELEASE#.src.rpm" .
#MAINT# -for ping in /usr/src/redhat/RPMS/* ; do \
#MAINT# if test -d $$ping ; then \
#MAINT# arch=`echo $$ping |sed -e 's,/.*/\([^/][^/]*\),\1,'` ; \
#MAINT# f="$$ping/$(PACKAGE)-#VERSION#-#BALSA_RELEASE#.$$arch.rpm" ; \
#MAINT# test -f $$f && cp -f $$f . ; \
#MAINT# fi ; \
#MAINT# done
#MAINT#snapshot:
#MAINT# $(MAKE) distdir=$(PACKAGE)-`date +"%y%m%d"` dist
#MAINT#balsa-dcheck:
#MAINT# $(MAKE) BALSA_DISTCHECK_HACK=yes distcheck
## to automatically rebuild aclocal.m4 if any of the macros in
## `macros/' change
bzdist: distdir
#test -n "$(AMTAR)" || { echo "AMTAR undefined. Run make bzdist AMTAR=gtar"; false; }
-chmod -R a+r $(distdir)
$(AMTAR) chojf $(distdir).tar.bz2 $(distdir)
-rm -rf $(distdir)
# macros are not used any more by current configure.in, see also
# post by Ildar Mulyukov to balsa-list, 2006.06.27
# ACLOCAL_AMFLAGS = -I macros
UPDATE
I tried this answer.. but I got the following:
autoreconf --install
configure.in:250: warning: macro `AM_GLIB_GNU_GETTEXT' not found in library
glibtoolize: putting auxiliary files in `.'.
glibtoolize: copying file `./ltmain.sh'
glibtoolize: putting macros in AC_CONFIG_MACRO_DIR, `m4'.
glibtoolize: copying file `m4/libtool.m4'
glibtoolize: copying file `m4/ltoptions.m4'
glibtoolize: copying file `m4/ltsugar.m4'
glibtoolize: copying file `m4/ltversion.m4'
glibtoolize: copying file `m4/lt~obsolete.m4'
glibtoolize: Remember to add `LT_INIT' to configure.in.
glibtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.
glibtoolize: `AC_PROG_RANLIB' is rendered obsolete by `LT_INIT'
configure.in:250: warning: macro `AM_GLIB_GNU_GETTEXT' not found in library
configure.in:249: error: possibly undefined macro: AC_PROG_INTLTOOL
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.
configure.in:250: error: possibly undefined macro: AM_GLIB_GNU_GETTEXT
configure.in:301: error: possibly undefined macro: AC_MSG_ERROR
autoreconf: /usr/bin/autoconf failed with exit status: 1
I'm looking into using the suggestions in the output..
Interesting. I just tried downloading "balsa" and noticed that they distributed the Makefile.am and configure.in files instead of a ready to run configure script. You could let the package maintainers know they aren't doing anyone any favors by not precompiling their own autotools sources.
Makefile.am is not a real Makefile. It's the thing that generates Makefile.in, which in turn gets translated into a real Makefile by a configure script.
Try the following steps:
Download the sources to balsa again clean. Then from the command prompt type the following:
autoreconf --install
(If you don't have autoreconf, you likely need to install the autotools packages - ughh...)
That should generate the configure script. Then type:
./configure
It complained about some missing GMime dependencies, so I didn't see it actually generate a Makefile. Once you get to the point in which a Makefile is generated, you should be able to point Netbeans to "open project from existing sources".
As per abbood's request...
Netbeans is not very good for C development. One approach would be to build an XCode project around the source base. The maintainers of the project may even accept the XCode project as a contribution.