How to execute custom command before each compile command in CMake - cmake

I'm trying to create a CMake script that will 'bind' so to speak "my custom command" action to every compile command.
Example scenario
I have 3 sources:
source1.c
source2.c
main.c
This is what I expect to happen:
"my custom command" -> compile: source1.c ->
"my custom command" -> compile: source2.c ->
"my custom command" -> compile: main.c
I want this custom command to be executed only if particular object file needs rebuilding.
It would be nice if I could also pass compiled 'source path' as an argument to "my custom command" using generated expressions somehow.
Is it possible?

Related

Visual Studio says Acces is Denied when trying to run with F5 or Ctrl-F5 [duplicate]

I am using CMake to generate Visual Studio projects. Everything works fine except one thing.
The startup project in the solution is always ALL_BUILD. How do I change the startup project to the real project I want via CMake?
CMake now supports this with versions 3.6 and higher through the VS_STARTUP_PROJECT directory property:
cmake_minimum_required(VERSION 3.6)
project(foo)
# ...
add_executable(bar ${BAR_SOURCES})
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT bar)
This will set bar as the startup project for the foo.sln solution.
You can't. The startup-project is stored in a binary file, which is NOT generated by CMake. Without that binary file, visual studio will default to the first project in the solution file and the ALL_BUILD project is always first...
Update: this answer is "out-of-date" since it is now feasible with CMake 3.6. See the answer by ComicSansMS.
Since Visual 2005, the configuration is stored in a file name projectname.vc(x)proj.user, which is plain xml.
I don't know about a way to change the startup project, but you certainly can set ALL_BUILD to run the desired executable instead of displaying the stupid popup :
create_default_target_launcher(
your_desired_target_name
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/desired_path/"
# or ${CMAKE_CURRENT_BINARY_DIR}, depending on your setup
)
This module is available on rpavlik's github. You simply need to add this in your topmost CMakeLists.txt :
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external/rpavlik-cmake-modules-1c73e35") # or whichever path you put the module in.
include(CreateLaunchers)
Examples available here.
If you can't allow a perl dependency like me, I just wrote a little command line utility for windows called slnStartupProject to solve this. It sets the Startup Project automatically like this:
slnStartupProject slnFilename projectName
I personally use it to set the project after generating the solution with cmake that always sets a dummy ALL_BUILD project as the first project in the solution.
The source is on github:
https://github.com/michaKFromParis/slnStartupProject
Forks and feedbacks are welcome.
Hope this helps!
It is correct that the explicit choice the user makes when hitting "Set as startup project" in IDE is stored in a binary file. But I found somewhere else that Visual Studio takes the first Project in the solution as an implicit Startup Project when first opening a solution, so CMake does have an influence on this.
Our problem now: ALL_BUILD is always the first project. To change this, I am running a short perl script after CMake that cuts the desired project definition out of the file and pastes it into the front. Path to solution file in first parameter, project name in second:
use strict;
use File::Spec;
# variables
my $slnPath = File::Spec->rel2abs($ARGV[0]);
my $projectName = $ARGV[1];
my $contents;
my $header;
my $project;
my $GUID = "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}";
my $fh;
# read file content (error if not found)
print "Setting \"$projectName\" as Startup Project in \"$slnPath\"...\n";
die "Error: path \"$slnPath\" not found!\n" if not -f $slnPath;
open($fh, "<", $slnPath) or die "Error: cannot read $slnPath: $!";
$contents = do { local $/; <$fh> };
close($fh) or warn "close failed: $!";
# extract part before Projects definition section (the first mention of "Project([GUID])")
$header = $1 if $contents =~ s{(.*?(?=Project\("\{${GUID}\}"\)))}{}si;
# extract definition of the project specified (error if not found)
$project = $1 if $contents =~ s{(Project\("\{${GUID}\}"\) = \"${projectName}\".*?EndProject\s)}{}si;
die "Error: Project not found!\n" if not defined $project or not length $project;
# write header, project definition and remaining content back into the file
`attrib -R "$slnPath"`;
open($fh, ">", $slnPath) or die "Error: cannot write to $slnPath: $!";
print $fh $header, $project, $contents;
close($fh) or warn "close failed: $!";
print "Successfully done.\n";
Once the solution has been opened, the implicit startup project is saved in the binary file and thus becomes explicit, so this even survives a CMake rerun (e.g. triggered by ZERO-CHECK, which doesn't allow post-execution). In the same way, anm explicit user choice is also preserved.
(Written and tested on Win7 machine with ActiveState Perl)
With cmake 3.5, the startup project (for VS 2010) can be changed with
SET(CMAKE_DEFAULT_STARTUP_PROJECT myFavoriteProject)
in the main CMakeLists.txt of the project.
By default, it is set to ALL_BUILD.

how to make Cmake output relative path?

In my last deleted question about CMake, I raised a problem about how to make CMake output relative path because vscode's problem panel only
understanding Windows directories or relative paths under workspace.
This won't happen unless some specific situation. If you use vsocde to edit source files in Windows and use WSL as terminal to compile using CMake, you will find vscode cannot work perfectly.
the problem panel will tell you it could not find the error or warning's source code after compilation completes.
gdb could not find source file when debugging.
(1)Solution to the first problem:Add the following code to your outermost CMakeLists.txt:
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_SOURCE_DIR}/custom_output.sh")
The global RULE_LAUNCH_COMPILE property is set to a custom launch script named custom_output.sh which needs to be added to the root of the CMake source tree:
#!/bin/bash
# shell script invoked with the following arguments
# $(CXX) $(CXX_DEFINES) $(CXX_FLAGS) -o OBJECT_FILE -c SOURCE_FILE
# extract parameters
SOURCE_FILE="${#: -1:1}"
OBJECT_FILE="${#: -3:1}"
# invoke compiler
{ "$#" 2> >(sed 's#^/mnt/d/demo/##'|sed "s/warning/${esc}[32m&${esc}[0m/g"|sed "s/error/${esc}[31m&${esc}[0m/g" >&3); } 3>&2
it would output stderr messages also on stderr and transform it to relative path.Where "/mnt/d/demo/" is the string to be deleted. What to delete depends on the source code relative path.
(2)You can use the following command from gdb for remapping to solve the second problem:
set substitute-path old_path new_path
You need add follow code to you launch.json
"customLaunchSetupCommands": [
{
"text": "set substitute-path /mnt/d d:/",
"description": "change directory to workspace",
"ignoreFailures": false
}
]
If you debug with Cortex Debug you should add follw code to you launch.json
"postLaunchCommands": ["set substitute-path /mnt/d d:/"]

Does CMake provide a way to run custom commands from the command line?

add_custom_command can be used to execute custom command, however CMake is responsible to call these commands when eg before the build or after the build.
What I am looking for is a way to launch a custom command from the command line, such as :
cmake --build
cmake --my_custom_command
Or
cmake --build
make --my_custom_command
The reason I want to do this is because I would like to :
Create a command to launch by executable.
Create a command which gathers all the necessary include paths from my executable dependencies.
Doing so would allow me to configure my editor without having to manually set these things per project.

Get list arguments/flag cmake

I want write cmake ... -variable=value.
I want use this variable in code.
example:
cmake CMakeLists.txt -TEST_MESSAGE=Hello
and
cmake_minimum_required (VERSION 2.6)
set (PROJECT hello_world)
project (${PROJECT})
message (${TEST_MESSAGE})
How to?
A) Create a wrapper (batch, bash script, Python script or whatever) called "cmake" to parse arguments and inside the wrapper call
cmake ... -Dvariable=value
B) Or get source of CMAKE, modify parsing of command line arguments code, recompile it yourself and use.
But I would strongly suggest to just use -D option as everyone else

Cmake : post-package step

I'm looking for a way to execute a code after the packaging is done.
I tried to add a custom target that was depending on the PACKAGE target generated.
That looks like it does not work, here's cmake error:
CMake Error: The inter-target dependency graph contains the following strongly connected component (cycle):
"ALL_BUILD" of type UTILITY
depends on "UPLOAD" (strong)
"PACKAGE" of type GLOBAL_TARGET
depends on "ALL_BUILD" (strong)
"UPLOAD" of type UTILITY
depends on "PACKAGE" (strong)
At least one of these targets is not a STATIC_LIBRARY. Cyclic dependencies are allowed only among static libraries.
To do this I used to following code:
add_custom_target(UPLOAD ALL
COMMAND cmake -E echo "Should be post packging!"
)
add_dependencies(UPLOAD PACKAGE)
Is there some way to have the target to UPLOAD the PACKAGEd file?
Create your own package target.
add_custom_target(mypackage
COMMAND ${CMAKE_CPACK_COMMAND}
COMMAND ${CMAKE_COMMAND} -E echo "after packaging"
)
Upd: Since CMake 3.19 there is CPACK_POST_BUILD_SCRIPTS to do any tasks after CPack produced packages.