The Linux Page

Writing a Shell Script from cmake

In cmake, you have a cool feature which allows you to create a file.

First you use a WRITE, then you use the APPEND to add more lines to the file.

That file may be a shell script, though. In that case, you run in a problem whenever you want to write a variable which uses the curly brackets (as in ${TEST}), because cmake sees those as its own variables.

There is an example of script that we use in our Snap! development.

file( WRITE  ${lint_script} "#!${BASH}\n"                                                            )
file( APPEND ${lint_script} "if test \${3##*.} = 'dtd'; then\n" )
file( APPEND ${lint_script} "  ${XMLLINT} --dtdvalid $3 --output $2 $1 && exit 0 || (rm $2; exit 1)\n" )
file( APPEND ${lint_script} "else\n" )
file( APPEND ${lint_script} "  ${XMLLINT} --schema $3 --output $2 $1 && exit 0 || (rm $2; exit 1)\n" )
file( APPEND ${lint_script} "fi\n" )

As we can see, we have many variables defined and used here.

The $1, $2, and $3 are the usual first, second and third parameters specified on the command line when calling the script.

The ${XMLLINT} variable is set by cmake with a find_program() call:

find_program( XMLLINT xmllint /usr/bin )

The ${BASH} variable is similar, we look for the location of the bash shell script and use the variable to generate the script from cmake.

However, $3 represents a path and a filename with an extension. We expect .dtd or .xsd as the extension of that file. Since it is dynamic and various calls to the script may use various setup, the script extracts the extension and then compares it with the 'dtd' string. To extract the string, we use the special shell syntax:


Which says: "Remove everything up to the last period and the last period."

Here we have a problem because the cmake interpretor wants to convert the ${3##*.} with the contents of the variable named "3##*." and that's not considered a valid name as per cmake so it generates an error and bails out.

To palliate to that problem, we have to escape the { character as we have shown in our first example:


and then cmake will write "${3##*.}", without the backslash (\), to the output file.