Friday, September 23, 2011

Plotting a dependency graph

While working with building loads of packages, it is often necessary to visualise dependencies between them. Two years ago I suffered a lot trying to do just that and at best I came up with a python script that gives a textual list of deps. Today, I found an awesome tool to plot dependency graphs. Although useless for most of the people, it is quite a nifty trick for the few who work with thousands of packages.

Two RPMs need to be installed before anything else

# yum install rpmorphan graphviz

Now, assume you need all the deps for rebuilding "rpm" (The actual RPM rpm)
First, generate a textual dep graph using:

# rpmdep -dot rpm.dot rpm

The "dot" is a file-format which will be used by the "dot" program to plot a graph. To plot the graph

# dot -Tsvg rpm.dot -o rpm.svg

The command takes rpm.dot and plots the graph in an svg format.

Simple!!

A sample image I generated is here:

Tuesday, August 9, 2011

vim: How to setup awesome autocomplete

Recently I moved to a company which has a huge codebase and loves long names! All vim/emacs users will sympathize with such a situation. Eclipse would autocomplete names in a jiffy. I was sorely missing good autocomplete on my favourite editor: vim.

Typically by setting the "dict" variable appropriately, one can autocomplete english words, but what about C/C++ code?
One fine morning, I had an epiphany. Why don't people use the ctags/etags database to autocomplete. The database already has lots of semantic information about the code and it would have all the project specific keywords too. Now, I am not a guy who has too many epiphanies, so I was sure someone had implemented it even before I could spell "vim". With this in mind, I googled the specific terms "vim autocomplete using ctags". The first stackoverflow result had a brilliant suggestion: omniautocomplete.

Before I go into what omniautocomplete (a plug-in for vim) does, let me first list down my requirements from an autocomplete tool
1) Autocomplete sensibly: Use the knowledge that I am editing "c++" code and give sensible suggestions.
2) It should *not* be enabled by default when I run vim i.e. do not blindly putt crao in my .vim. The reason is that I work on multiple projects at the same time and I don't want suggestions from one project to be mixed with other.
3) No lag in autocompleting.
4) Provide a quick way to update the autocomplete "database".

Some vim scripting + omnicomplete provides all that and more. What more, you might ask. Well, it provides an eclipse-like pop-up when asked to autocomplete. It can also fire when I enter a "." or "->". It also has an option to show "function prototypes" alongwith autocomplete suggestions. This is awesome!!!

To get you excited, here is a screenshot. It looks exactly the same.

Setting up omnicomplete is super-easy. Installation instructions are outlined here. All it involves is unzipping a zip file to ~/.vim

The next part is more interesting:
Since ctags database is used, ctags needs to be compiled with some specific options. The commandline I picked up from their documentation is this:

$ ctags -R --c++-kinds=+p --fields=+iaS --extra=+q .

Now, the "tags" variable in vim needs to point to the tags of your project. Setting this up each time you open vim can be irritating. Very irritating. So, I wrote the following vim function to alleviate the issue:

function! SetProj(proj)
        if a:proj == "mythread"
                map <C-F12> :!ctags -R --c++-kinds=+p --fields=+iaS --extra=+q -f /home/jitesh/repos/mythread_lib/tags /home/jitesh/repos/mythread_lib<CR>
                set tags+=/home/jitesh/repos/mythread_lib/tags
                let OmniCpp_ShowPrototypeInAbbr = 1
                let OmniCpp_MayCompleteDot = 1
        endif
endfunction


Now, when I open vim, all I have to do is:

:call SetProj("mythread")

Note - Re-generating the ctags database is also pretty easy, Just press Ctrl+F12. Notice the keymapping in the vim function above.
So, when I define a new function in my code, I just do a Ctrl+F12 and the tags are updated and ready to be auto-completed.

Pressing ctrl+P or ctrl+N invokes the autocompletion.

When I  start vim, none of this stuff is loaded, so there is no load-time penalty/bloat + I can work on multiple projects just by calling "SetProj" with the appropriate parameter.

I sign-off a happy user! vim FTW

Friday, April 22, 2011

Block processing and shell-scripts

Feature 1:
Shell-scripts can easily process files/records line-by-line.
A typical construct to do so is:

ps aux |
while read line do;
        echo $line
done



But, this kind of a thing isn't very easy to process outputs of
commands like ifconfig. ifconfig has output in blocks instead of
lines. Recently I had to do it and I was really surprised that I had
no idea of how to parse blocks of data in shell-script. After some
time, I figured it out myself. The pipe command pipes the output to a
shell. So what if that shell contains another shell-script instead of
a single command?
Here's how to parse ifconfig command. The script prints out a list of
interfaces and corresponding IP address (NIL if no IP): (Note:
Brackets fork a new shell)


ifconfig -a |
(
#Read the first line
while read line; do
    INTERFACE_ADDR=`echo $line | awk '{print $1}'`



    # Read second line in the block and store the IP address, if any
    read line;
    IP=`echo $line | grep "inet addr" | awk '{print $2}' | cut -d':' -f2`



    while true; do
        # Skip all other lines from the block
        read line;



        # End of block is a new line. Start parsing new block then
        if [ -z "$line" ]; then
            break;
        fi
    done
    echo $INTERFACE_ADDR $IP
done
)



There. This is albeit a simple example. You can construct truly
awesome block parsing constructs using the sub-shell (i.e. the
brackets '()' )

Feature 2:
Wonder why a shell-script doesn't have a block commenting feature?
Wonder no more.
Here is a cool trick using here-documents to comment out a block of shell script


: <<'END1'
"The commented block goes here"
END1


Notes:
1) ":" means "true". /dev/null equivalent for a shell script.
2) Make sure that the string "END1" is the same at both the places,
else, this thing is not going to work.
Happy scripting :-)