How can CMake be used to generate Makefiles with personalized commands? - makefile

I like to keep my Makefiles flexible and multifunctional. One of the tasks I usually add to make command is tar, for example the following instruction does the job:
tar:
tar -cvf $(PROGNAME).tar $(SRCS) Makefile
My question is: How can CMake be used to generate personalized commands like tar?
I would like to see some code samples.
For the full functionality it would be useful to create project's components and be able to use them as parameters.
(Exempli gratia: archive only header files or some specific library).
Thanks in advance for your answers!

The literal translation of your tar example would be:
ADD_CUSTOM_TARGET(tar
tar -cvf ${CMAKE_CURRENT_BINARY_DIR}/${PROGNAME}.tar ${SRCS} Makefile
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
This adds a new target "tar" that always executes the given command whenever it is requested as a command line target, i.e. whenever you run make tar it will create a new tar file. The WORKING_DIRECTORY argument will ensure that the source files are taken from the source directory, while CMAKE_CURRENT_BINARY_DIR ensures the output goes in the current build directory.
A slightly better iteration would be to replace tar with ${CMAKE_COMMAND} -E tar, as this doesn't depend on the command line tar program being available. So something like this would tar up all the header files when you run make tar:
SET(HEADER_FILES my.h another.h)
SET(PROGNAME myprog)
ADD_CUSTOM_TARGET(tar ${CMAKE_COMMAND} -E tar -czvf
${CMAKE_CURRENT_BINARY_DIR}/${PROGNAME}.tar.gz ${HEADER_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
An even better iteration would be to use the CPack features to create source or binary tar files, but that's quite a bit more work and may not be what you need anyway.

Related

make rebuild target depending on zip file

Why make rebuilds the target (I suppose) if the dependency is a binary file?
To reproduce:
create (and enter it) a new empty directory
download the GameLift SDK (it is just an example: the Makefile content on this question is an example with this file)
create a simple Makefile with the content below
issue more times the make command
all: GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt
GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt: GameLift_12_22_2020.zip
unzip -oq GameLift_12_22_2020.zip
I would have expected to see the unzip command to be executed only first time I issue the make command, but it continue to be executed in next make runs... why?
There are two possibilities, we cannot know which is the case with the information you've provided.
The first is that the file GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt is not present in the zip file, so the second time make runs it looks to see if that file exists and it doesn't, so it re-runs the rule. If, in the same directory you run make, you use ls GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt (after the unzip runs) and you get "file not found" or similar, this is your problem.
If that's not it, then the problem is that the timestamp of the file in the zip file is older than the zip file itself, and when unzip unpacks the file it sets the timestamp to this older time.
So when make goes to build it finds the CMakeLists.txt file but the modification time is older than the zip file, so make unpacks the zip file again to try to update it.
You can use ls -l to see the modification time on that file. If this is the case you should touch the file when you unpack it, so it's newer:
GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt: GameLift_12_22_2020.zip
unzip -oq GameLift_12_22_2020.zip
touch $#

How to extract only if it not extracted already?

I have a makefile which has an action to extract an archive. It does it even when the archive has already been extracted (and there were no changes to it).
all:
tar zxvf soplex-1.7.2.tgz
Is there a way to prevent this? I tried using the k flag to make it keep the existing files but it gives me this
soplex-1.7.2/src/vector.cpp
tar: soplex-1.7.2/src/vector.cpp: Cannot open: File exists
This isn't exactly good make practice but this sort of operation doesn't really fit into the way make does things either (unless you want to use a known sentinel file from inside the tarball as your marker).
all:
tar -df soplex-1.7.2.tgz 2>/dev/null || tar -xvf soplex-1.7.2.tgz
(You can manually supply the z flag to tar if your tar can't figure out that it needs it itself.)
Also note that this is very expensive in the case that one of the later files in the tarball is the one that is missing/modified since it requires two sequential scans of the entire tarball and the related disk activity.

Running a bash command via CMake

I'm trying to have CMake either run three bash commands or a bash script. However, I can't seem to get it to work.
The bash commands are:
cd ${CMAKE_SOURCE_DIR}/dependencies/library
make
cd ${CMAKE_BINARY_DIR}
Essentially, I would like CMake to build the library in that directory if it does not already exist.
Here's the CMake code I tried:
if(NOT "${CMAKE_SOURCE_DIR}/dependencies/library/lib.o")
execute_process(COMMAND cd ${CMAKE_SOURCE_DIR}/dependencies/library)
execute_process(COMMAND make)
execute_process(COMMAND cd ${CMAKE_BINARY_DIR})
endif(NOT "${CMAKE_SOURCE_DIR}/dependencies/library/lib.o")
However, it's not building anything. What am I doing wrong?
Also, while I'm here asking this: should the third command, to move to the binary folder, be included?
Thanks!
execute_process() is executed during configure time. But you want this to run at build time, thus add_custom_command() and add_custom_target() is what you're looking for.
In this special case you want to generate an output file, so you should go for add_custom_command() (both are essentially the same, but command produces one or multiple output files, while target does not.
The cmake snippet for this should look something like the following:
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/dependencies/library/lib.o
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/dependencies/library
COMMAND make
)
You then have to add the output file in another target as dependency, and everything should (hopefully) work as expected.
You can also add DEPENDS statements to the add_custom_command() call to rebuild the object file in case some input sources have changed.

How can I compress a directory, and convert soft to hard links?

I would like to compress a directory.
tar -cvzf mydir.tar.gz mydir
but this retains symlinks so that it is not portable to a new system.
How can I convert symlinks?
I have tried
tar -cvzfh
since man tar says
-h, --dereference
don’t dump symlinks; dump the files they point to
but this results in an error
tar: Error exit delayed from previous errors
and creates a file called "zh"
My files are on a RHEL server.
Your tar.gz file name must follow immediately after the -f flag, merely reordering the flags may work.
tar -cvzhf mydir.tar.gz mydir

Makefile and Files under a directory

I have Makefile under a directory and directory has many .pm ( perl files )
I want to add makefile at this directory level which does perl file syntax checking.
Basically I want to add:
perl -c <filename>
How to get list of files automatically in that directory without hardcoding.
The following worked for me in the GNU makefile (Linux and Windows)
ALL_PM_FILES = $(wildcard *.pm)
Then run a for/foreach loop on them.
You can try the filter command:
PMFILES=$(filter %.pm, $(SRC))
Documentation for filter is hard to find. See here for an example.
This is the normal workaround:
check_pm_syntax:
for file in *.pm; do ${PERL} -c $$file; done
You run 'make check_pm_syntax' and it goes off and runs the shell loop for all the *.pm files it can find. You can simply list check_pm_syntax as a pre-requisite for your all target if you like (but it means you'll always do work when you build all). The only time this causes trouble is if there are no *.pm files in the directory.
Here's a slightly different approach:
.PHONY: check_%.pm
check_%.pm:
perl -c $*.pm
check_all: $(addprefix check_,$(wildcard *.pm))

Resources