scons surrounds option with double quotes - scons

I use scons (V1.1.0) for a project that contains a build step that involves the flex tool.
The definition for the flex command in the scons default rules is:
env["LEX"] = env.Detect("flex") or "lex"
env["LEXFLAGS"] = SCons.Util.CLVar("")
env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET"
which I don't want to change.
However, since -t causes #line directives to be created in the output file that refer to the file "<stdout>", this confuses the subsequent gcov processing.
As a solution, I found that -o can be used to override the file name flex produces into the #line directives (it still produces its output on stdout due to the -t option which apparently has precedence).
To achieve that, I added this in the project's SConscript file:
env.AppendUnique(LEXFLAGS = ['-o $TARGET','-c'],delete_existing=1)
I added the -c option (which does nothing) only to show the difference between how it is treated compared to -o.
An according debug print in the SConscript file results in the following (as expected):
repr(env["LEXFLAGS"]) = ['-o $TARGET', '-c']
This results in the following command line, according to the scons log:
flex "-o build/myfile.cpp" -c -t src/myfile.ll > build/myfile.cpp
So the -c option gets into the command line as desired, but the -o option and its filename parameter has double quotes around it, that must have been created by scons when expanding the LEXFLAGS variable.
When I use this definition for LEXFLAGS instead:
env.AppendUnique(LEXFLAGS = ['--outfile=$TARGET','-c'],delete_existing=1)
the resulting command line works as desired:
flex --outfile=build/myfile.cpp -c -t src/myfile.ll > build/myfile.cpp
So one could speculate that the blank in the -o case caused the double quotes to be used, maybe in an attempt to bind the content together into one logical parameter for the command.
So while my immediate problem is solved by using --outfile, my question is still is it possible to rid of the double quotes in the -o case?
Thanks,
Andy

SCons 1.1.0 is extremely old at this point. I'd recommend trying 2.3.0. But your analysis is correct; if an option (a single option, that is) has a space in it, SCons will quote it so it stays a single option. But you don't have a single option; you really have two, '-o' and '$TARGET'. Just break it up like that and it'll work.

Related

How is the -fprofile-prefix-path option supposed to work?

When compiling code for coverage instrumentation (to use with lcov later on), we're compiling from a base directory tree (let's call it A), and we want the .gcda files to be produced at a different place (because the target directory tree is different - let's call it B).
So, the compilation command looked like this:
gcc -O0 -g --coverage -fprofile-dir=B -c -fPIC -Wall -o A/otherpath/to/mySourceFile.o A/path/to/mySourceFile.c
When checking the contents of mySourceFile.o (with the strings command), I saw that the mySourceFile.gcda file was set to be generated in B/A/otherpath/to/mySourceFile.gcda
Which is the mangling of the path given through the -fprofile-dir option with the exact absolute path of the object file created - just as written in the documentation. So far, no problem - except that what I want would be the mySourceFile.gcda file to be generated from the B directory, WITHOUT the A part.
So, the documentation also mentions the -fprofile-prefix-path option, which is supposed to allow you to remove part of the path, so that the mangling doesn't add the old path to the new.
I tried using it in the following way:
gcc -O0 -g --coverage -fprofile-dir=B -fprofile-prefix-path=A -c -fPIC -Wall -o A/otherpath/to/mySourceFile.o A/path/to/mySourceFile.c
However, after checking through strings, once again, in the generated object file, the path was still B/A/otherpath/to/mySourceFile.gcda, whereas I expected it to be B/otherpath/to/mySourceFile.gcda (that is, I expected the A part to have been stripped by the -fprofile-prefix-path option.)
Obviously, it didn't work. Any insight why ?
( Compiler used is GCC 11.2.1, which is a version recent enough to know about the option. )
Ok, after some tinkering, I got results. Maybe not exactly what I was expecting, but close enough.
Let me start by saying that the A and B "directories" I mentioned in my question were absolute paths. And it didn't work well.
However, while keeping the absolute B (target) path, I tried not using the full A (source) path while compiling. More precisely, I didn't use it to specify the OUTPUT file name, for the object. Instead, I went to the base directory (the A path), and then, ran the command by specifying the output file path relative to the current (A) directory
Which would give the following command:
(From directory A)
gcc -O0 -g --coverage -fprofile-dir=B -fprofile-prefix-path=A -c -fPIC -Wall -o otherpath/to/mySourceFile.o path/to/mySourceFile.c
This time, the source command did show an interesting result, for the mySourceFile.gcda file:
B#otherpath#to#mySourceFile.gcda
As you can see, it's not exactly what I wanted (there are # instead of /), but mentions to A disappeared, and overall, I'm confident it should work as intended. Not utterly sure yet (I still have to test it on the target platform, which will need tinkering with the way the makefiles currently work), but confident nonetheless.
Also, if I didn't use the -fprofile-prefix-path in the command, then the string would mention the A path, like this (with the '/' inside the A path being replaced with '#' characters, obviously):
B#A#otherpath#to#mySourceFile.gcda
So, the option works, but only when using relative paths, not when using absolute ones, for the object file. Hope that helps people.
PS: I checked by changing the path to the source (.c) file. Whether specified using absolute, or relative, paths, it didn't change the outcome. What matters is specifying the path to the object file in a relative manner.

Is there a way to define custom implicit GNU Make rules?

I'm often creating png files out of dot (graphviz format) files. The command to do so is the following:
$ dot my_graph.dot -o my_graph.png -Tpng
However, I would like to be able to have a shorter command format like $ make my_graph.dot to automatically generate my png file.
For the moment, I'm using a Makefile in which I've defined the following rule, but the recipe is only available in the directory containing the Makefile
%.eps: %.dot
dot $< -o $# -Teps
Is it possible to define custom implicit GNU Make recipes ? Which would allow the above recipe to be available system-wide
If not, what solution do you use to solve those kind of problem ?
Setup:
Fedora Linux with ZSH/Bash
You could define shell functions in your shell's startup files, e.g.
dotpng()
{
echo dot ${1%.dot}.dot -o ${1%.dot}.png -Tpng;
}
This function can be called like
dotpng my_graph.dot
or
dotpng my_graph
The code ${1%.dot}.dot strips .dot from the file name if present and appends it (again) to allow both my_graph.dot and my_graph as function argument.
Is it possible to define custom implicit GNU Make recipes ?
Not without modifying the source code of GNU Make.
If not, what solution do you use to solve those kind of problem ?
I wouldn't be a fan o modyfying the system globally, but you could do:
Create a file /usr/local/lib/make/myimplicitrules.make with the content
%.eps: %.dot
dot $< -o $# -Teps
Use include /usr/local/lib/make/myimplicitrules.make in your Makefile.
I would rather use a git submodule or similar to share common configuration between projects, rather than depending on global configuration. Depending on global environment will make your program hard to test and non-portable.
I would rather go with a shell function, something along:
mymake() {
make -f <(cat <<'EOF'
%.eps: %.dot
dot $< -o $# -Teps
EOF
) "$#"
}
mymake my_graph.dot
GNU Make lets you specify extra makefiles to read using the MAKEFILES
environment variable. Quoting from info '(make)MAKEFILES Variable':
the default goal is never taken from one of these makefiles (or any
makefile included by them) and it is not an error if the files listed
in 'MAKEFILES' are not found
if you are running 'make' without a specific makefile, a makefile
in 'MAKEFILES' can do useful things to help the built-in implicit
rules work better
As an example, with no makefile in the current directory and the
following .mk files in make's include path (e.g. via
MAKEFLAGS=--include-dir="$HOME"/.local/lib/make/) you can create
subdir gen/ and convert my_graph.dot or dot/my_graph.dot by
running:
MAKEFILES=dot.mk make gen/my_graph.png
To further save some typing it's tempting to add MAKEFILES=dot.mk
to a session environment but defining MAKEFILES in startup files
can make things completely nontransparent. For that reason I prefer
seeing MAKEFILES=… on the command line.
File: dot.mk
include common.mk
genDir ?= gen/
dotDir ?= dot/
dotFlags ?= $(if $(DEBUG),-v)
Tvariant ?= :cairo:cairo
vpath %.dot $(dotDir)
$(genDir)%.png $(genDir)%.svg $(genDir)%.eps : %.dot | $(genDir).
dot $(dotFlags) $< -o $# -T'$(patsubst .%,%,$(suffix $#))$(Tvariant)'
The included common.mk is where you'd store general definitions to
manage directory creation, diagnostics etc., e.g.
.PRECIOUS: %/. ## preempt 'unlink: ...: Is a directory'
%/. : ; $(if $(wildcard $#),,mkdir -p -- $(#D))
References:
?= = := … - info '(make)Reading Makefiles'
vpath - info '(make)Selective Search'
order-only prerequisites (e.g. | $(genDir).) - info '(make)Prerequisite Types'
.PRECIOUS - info '(make)Chained Rules'

Standard error file when there is no error

I'm new to Linux & shell and I'm struggling with checking if the compilation is successful.
g++ code.cpp -o code.o 2>error.txt
if [ ! -e error.txt ]
then
do something
else
echo "Failed to compile"
I guess an error file is created even if the compilation is successful. What is the content of the error file when there is no error? I need to change the if condition to check if the compilation is successful.
It's just the order of things. What happens when the shell parses the string g++ code.cpp -o code.o 2>error.txt is:
The shell creates error.txt, truncating the file if that name already exists.
g++ is called with its error output redirected to the new file.
If g++ does not write any data, then the file remains as it was (empty) at the end of step 1.
You probably aren't so much interested in the error file as you are the return value. You probably ought to just do:
if g++ code.cpp -o code; then : do something; done
or even just:
g++ code .cpp -o code && : do something
but if really want to do something else with the errors, you can do:
if g++ code.cpp -o code.o 2> error.txt; then
rm error.txt
: do something
else
echo >&2 Failed to compile code.cpp.\ See "$(pwd)"/error.txt for details.
fi
Make sure you escape at least one of the spaces after the . so that you get 2 spaces after the period (or just quote the whole argument to echo). Although it's become fashionable lately to claim that you only need one space, all of those arguments rely on the use of variable width fonts and any command line tool worth using will be used most often in an environment where fixed width fonts are still dominant. This last point is totally unrelated to your question, but is worth remembering.

How to let MAKEFILE retain the backslash sequences within a string when used in a make rule?

This is my first question on Stackoverflow so forgive me if I ask anything ridiculous :D.
Problem:
Suppose I want to compile a program that is in the directory "my dir/" with a space in it. Say the pathname of the program is "my dir/test.c".
Here is the sample makefile that I was trying out:
CC = gcc
DIR = my\ dir
$(DIR)/test.out: $(DIR)/test.c
# $(CC) $< -o $#
$(CC) $(DIR)/test.c -o $(DIR)/test.out
As you can see that in the last line(line-5) I have written the pathnames of the source and the output files directly as written in the prerequisite and the target, respectively. Doing this works fine because it yields the command:gcc my\ dir/test.c -o my\ dir/test.outwhich a syntactically correct way of passing filenames(with spaces) to gcc or any other shell command.
The second last line(line-4) is where the problem is(commented line). I've used automatic variables $# (Target) and $< (First and the only Prerequisite) to produce the filename arguments for gcc which I expected to bemy\ dir/test.out and my\ dir/test.c, respectively. But here, for some reason, the produced filenames are my dir/test.out and my dir/test.c and hence the yielded command is: gcc my dir/test.c -o my dir/test.out
Now here, gcc considers my and dir/test.c as different two different input filenames and the command generates errors.
Here is a screenshot of the generated error output when I uncomment line-4 and comment line-5 of the above Makefile:
My Question:
Is there any way to retain those backslashes even by using automatic variables the way I did? Or is there any alternative that will achieve the same goal as using automatic variables and also solve my problem? Because flexibility is important here.
Thanks in advance for your help!!!
Use double or single quotes for the automatic variables.
Use single quotes, if you want to avoid shell expansion of the values referenced by the automatic variables:
$(DIR)/test.out: $(DIR)/test.c
$(CC) '$<' -o '$#'
Double quotes allow shell expansion. For example, if there was a dollar sign in DIR:
DIR := $$my\ dir
then "$#" would expand to "$my dir", and the shell would interpret $my as variable.

help - change diff symbol "<", "|" or ">" to a desired one?

diff -w command is used to create a side by side comparison diff file (instead of parallel)
i then view them using vi via ssh terminal
the changes are indicated by either "<" or "|" or ">"
Since the file i am viewing is a source code, navigating to changes alone
using above symbols is difficult since they are also in C source code.
How can i change these default symbols to desired ones ?
Kindly help. Thanks.
Instead of viewing the output of diff -w in vim, you can use vim's built-in diff:
vim -d file1 file2
This opens vim in a vertical split with both files open, and diff markings in the code. This is what it looks like:
And it works in a terminal too:
You can find a short tutorial here
According to my version of diff (2.8.1 from the GNU diffutils by the FSF) -w is used to change the width of the output; The -y parameter outputs side by side comparison. In combination, the two show no further effect than the -y parameter used alone, which means you may have an alias in your terminal profile or in the global terminal profile that aliases diff to diff -y.
I say all this because all options to change the symbols ("<", "|", and ">") conflict with the -y option. If you can live without side-by-side, you have the option of two other included output styles or defining your own. The two output styles are -c (context) and -u (unified). (For more information on what they do see the diff Wikipedia page. For more information on the options see the diff man page.)
A more in depth fix would be to use the following options:
diff --old-group-format="(deleted)---" \
--new-group-format="(added)---" \
--changed-group-format="(updated)---" \
--unchanged-group-format="(nodiff)---" \
old_file.c new_file.c
Now the old file's lines that are not present in the new file are represented by (deleted)---
The new file's lines that are not present in the old file are represented by (added)---
Lines that have been changed are represented by (updated)---
Lines common to both files are represented by (nodiff)---
Since you seem to do this often enough, you have the option of making it an alias in your terminal profile or writing a small shell script to handle it. For more options, see the manual's section on options and specifically see the section on line group formats for information on what you can put between the quotes in the format definitions.
Of course, if you must have side-by-side, try Nathan Fellman's idea above. Otherwise, there's the option of using a dedicated GUI tool for it such as Kompare.

Resources