├── .gitignore ├── README.md ├── media ├── file0.png ├── file1.png ├── file2.png └── unix-as-ide-cover.png ├── src ├── 01-introduction.md ├── 02-files.md ├── 03-editing.md ├── 04-compiling.md ├── 05-building.md ├── 06-revisions.md ├── 07-conclusion.md └── metadata.yml ├── unix-as-ide.epub └── unix-as-ide.mobi /.gitignore: -------------------------------------------------------------------------------- 1 | commands 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unix as IDE 2 | 3 | ![Unix as IDE](media/unix-as-ide-cover.png) 4 | 5 | The ebook version of the series of posts by Tom Ryder, originally posted on [his website](http://blog.sanctum.geek.nz/series/unix-as-ide/). 6 | 7 | Built with the help of [Beautiful Soup](http://www.crummy.com/software/BeautifulSoup/), [Pandoc](http://pandoc.org/), [kindlegen](https://www.amazon.com/gp/feature.html?docId=1000765211), and [Vim](http://www.vim.org/). 8 | 9 | This book is also available on [Unglue.it](https://unglue.it/work/194054/). 10 | 11 | Cover by [Eric Hellman](https://github.com/eshellman). 12 | 13 | ## License 14 | 15 | © Tom Ryder, CC BY-NC-SA 16 | -------------------------------------------------------------------------------- /media/file0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrzool/unix-as-ide/b8ede7cfb8042b990fbd282f4e109c0ce7c332cd/media/file0.png -------------------------------------------------------------------------------- /media/file1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrzool/unix-as-ide/b8ede7cfb8042b990fbd282f4e109c0ce7c332cd/media/file1.png -------------------------------------------------------------------------------- /media/file2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrzool/unix-as-ide/b8ede7cfb8042b990fbd282f4e109c0ce7c332cd/media/file2.png -------------------------------------------------------------------------------- /media/unix-as-ide-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrzool/unix-as-ide/b8ede7cfb8042b990fbd282f4e109c0ce7c332cd/media/unix-as-ide-cover.png -------------------------------------------------------------------------------- /src/01-introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Newbies and experienced professional programmers alike appreciate the concept of the IDE, or [integrated development environment](http://en.wikipedia.org/wiki/Integrated_development_environment). Having the primary tools necessary for organising, writing, maintaining, testing, and debugging code in an integrated application with common interfaces for all the different tools is certainly a very valuable asset. Additionally, an environment expressly designed for programming in various languages affords advantages such as autocompletion, and syntax checking and highlighting. 4 | 5 | With such tools available to developers on all major desktop operating systems including Linux and BSD, and with many of the best free of charge, there’s not really a good reason to write your code in Windows Notepad, or with `nano` or `cat`. 6 | 7 | However, there’s a minor meme among devotees of Unix and its modern-day derivatives that “Unix is an IDE”, meaning that the tools available to developers on the terminal cover the major features in cutting-edge desktop IDEs with some ease. Opinion is quite divided on this, but whether or not you feel it’s fair to call Unix an IDE in the same sense as Eclipse or Microsoft Visual Studio, it may surprise you just how comprehensive a development environment the humble Bash shell can be. 8 | 9 | ## How is UNIX an IDE? 10 | 11 | The primary rationale for using an IDE is that it gathers all your tools in the same place, and you can use them in concert with roughly the same user interface paradigm, and without having to exert too much effort to make separate applications cooperate. The reason this becomes especially desirable with GUI applications is because it’s very difficult to make windowed applications speak a common language or work well with each other; aside from cutting and pasting text, they don’t share a *common interface*. 12 | 13 | The interesting thing about this problem for shell users is that well-designed and enduring Unix tools already share a common user interface in *streams of text* and *files as persistent objects*, otherwise expressed in the axiom “everything’s a file”. Pretty much everything in Unix is built around these two concepts, and it’s this common user interface, coupled with a forty-year history of high-powered tools whose users and developers have especially prized interoperability, that goes a long way to making Unix as powerful as a full-blown IDE. 14 | 15 | ## The right idea 16 | 17 | This attitude isn’t the preserve of battle-hardened Unix greybeards; you can see it in another form in the way the modern incarnations of the two grand old text editors Emacs and Vi (GNU Emacs and Vim) have such active communities developing plugins to make them support pretty much any kind of editing task. There are plugins to do pretty much anything you could really want to do in programming in both editors, and like any Vim junkie I could spout off at least six or seven that I feel are “essential”. 18 | 19 | However, it often becomes apparent to me when reading about these efforts that the developers concerned are trying to make these text editors into IDEs in their own right. There are posts about [never needing to leave Vim](http://kevinw.github.com/2010/12/15/this-is-your-brain-on-vim/), or [never needing to leave Emacs](http://news.ycombinator.com/item?id=819447). But I think that trying to shoehorn Vim or Emacs into becoming something that it’s not isn’t quite thinking about the problem in the right way. Bram Moolenaar, the author of Vim, appears to agree to some extent, as you can see by reading [`:help design-not`](http://vimdoc.sourceforge.net/htmldoc/develop.html#design-not). The shell is only ever a Ctrl+Z away, and its mature, highly composable toolset will afford you more power than either editor ever could. 20 | 21 | ## About this series 22 | 23 | In this series of posts, I will be going through six major features of an IDE, and giving examples showing how common tools available in Linux allow you to use them together with ease. This will by no means be a comprehensive survey, nor are the tools I will demonstrate the only options. 24 | 25 | - **File and project management** — `ls`, `find`, `grep`/`ack`, `bash` 26 | - **Text editor and editing tools** — `vim`, `awk`, `sort`, `column` 27 | - **Compiler and/or interpreter** — `gcc`, `perl` 28 | - **Build tools** — `make` 29 | - **Debugger** — `gdb`, `valgrind`, `ltrace`, `lsof`, `pmap` 30 | - **Version control** — `diff`, `patch`, `svn`, `git` 31 | 32 | ## What I’m not trying to say 33 | 34 | I don’t think IDEs are bad; I think they’re brilliant, which is why I’m trying to convince you that Unix can be used as one, or at least thought of as one. I’m also not going to say that Unix is always the best tool for any programming task; it is arguably much better suited for C, C++, Python, Perl, or Shell development than it is for more “industry” languages like Java or C\#, especially if writing GUI-heavy applications. In particular, I’m not going to try to convince you to scrap your hard-won Eclipse or Microsoft Visual Studio knowledge for the sometimes esoteric world of the command line. All I want to do is show you what we’re doing on the other side of the fence. 35 | -------------------------------------------------------------------------------- /src/02-files.md: -------------------------------------------------------------------------------- 1 | # Files 2 | 3 | One prominent feature of an IDE is a built-in system for managing files, both the elementary functions like moving, renaming, and deleting, and ones more specific to development, like compiling and checking syntax. It may also be useful to have operations on sets of files, such as finding files of a certain extension or size, or searching files for specific patterns. In this first article, I’ll explore some useful ways to use tools that will be familiar to most Linux users for the purposes of working with sets of files in a project. 4 | 5 | ## Listing files 6 | 7 | Using `ls` is probably one of the first commands an administrator will learn for getting a simple list of the contents of the directory. Most administrators will also know about the `-a` and `-l` switches, to show all files including dot files and to show more detailed data about files in columns, respectively. 8 | 9 | There are other switches to GNU `ls` which are less frequently used, some of which turn out to be very useful for programming: 10 | 11 | - `-t` — List files in order of last modification date, newest first. This is useful for very large directories when you want to get a quick list of the most recent files changed, maybe piped through `head` or `sed 10q`. Probably most useful combined with `-l`. If you want the *oldest* files, you can add `-r` to reverse the list. 12 | - `-X` — Group files by extension; handy for polyglot code, to group header files and source files separately, or to separate source files from directories or build files. 13 | - `-v` — Naturally sort version numbers in filenames. 14 | - `-S` — Sort by filesize. 15 | - `-R` — List files recursively. This one is good combined with `-l` and pipedthrough a pager like `less`. 16 | 17 | Since the listing is text like anything else, you could, for example, pipe the output of this command into a `vim` process, so you could add explanations of what each file is for and save it as an `inventory` file or add it to a README: 18 | 19 | $ ls -XR | vim - 20 | 21 | This kind of stuff can even be automated by `make` with a little work, which I’ll cover in another article later in the series. 22 | 23 | ## Finding files 24 | 25 | Funnily enough, you can get a complete list of files including relative paths with no decoration by simply typing `find` with no arguments, though it’s usually a good idea to pipe it through `sort`: 26 | 27 | $ find | sort 28 | . 29 | ./Makefile 30 | ./README 31 | ./build 32 | ./client.c 33 | ./client.h 34 | ./common.h 35 | ./project.c 36 | ./server.c 37 | ./server.h 38 | ./tests 39 | ./tests/suite1.pl 40 | ./tests/suite2.pl 41 | ./tests/suite3.pl 42 | ./tests/suite4.pl 43 | 44 | If you want an `ls -l` style listing, you can add `-ls` as the action to `find` results: 45 | 46 | $ find -ls | sort -k 11 47 | 1155096 4 drwxr-xr-x 4 tom tom 4096 Feb 10 09:37 . 48 | 1155152 4 drwxr-xr-x 2 tom tom 4096 Feb 10 09:17 ./build 49 | 1155155 4 -rw-r--r-- 1 tom tom 2290 Jan 11 07:21 ./client.c 50 | 1155157 4 -rw-r--r-- 1 tom tom 1871 Jan 11 16:41 ./client.h 51 | 1155159 32 -rw-r--r-- 1 tom tom 30390 Jan 10 15:29 ./common.h 52 | 1155153 24 -rw-r--r-- 1 tom tom 21170 Jan 11 05:43 ./Makefile 53 | 1155154 16 -rw-r--r-- 1 tom tom 13966 Jan 14 07:39 ./project.c 54 | 1155080 28 -rw-r--r-- 1 tom tom 25840 Jan 15 22:28 ./README 55 | 1155156 32 -rw-r--r-- 1 tom tom 31124 Jan 11 02:34 ./server.c 56 | 1155158 4 -rw-r--r-- 1 tom tom 3599 Jan 16 05:27 ./server.h 57 | 1155160 4 drwxr-xr-x 2 tom tom 4096 Feb 10 09:29 ./tests 58 | 1155161 4 -rw-r--r-- 1 tom tom 288 Jan 13 03:04 ./tests/suite1.pl 59 | 1155162 4 -rw-r--r-- 1 tom tom 1792 Jan 13 10:06 ./tests/suite2.pl 60 | 1155163 4 -rw-r--r-- 1 tom tom 112 Jan 9 23:42 ./tests/suite3.pl 61 | 1155164 4 -rw-r--r-- 1 tom tom 144 Jan 15 02:10 ./tests/suite4.pl 62 | 63 | Note that in this case I have to specify to `sort` that it should sort by the 11th column of output, the filenames; this is done with the `-k` option. 64 | 65 | `find` has a complex filtering syntax all of its own; the following examples show some of the most useful filters you can apply to retrieve lists of certain files: 66 | 67 | - `find -name '*.c'` — Find files with names matching a shell-style pattern. Use `-iname` for a case-insensitive search. 68 | - `find -path '*test*'` — Find files with paths matching a shell-style pattern. Use `-ipath` for a case-insensitive search. 69 | - `find -mtime -5` — Find files edited within the last five days. You can use `+5` instead to find files edited *before* five days ago. 70 | - `find -newer server.c` — Find files more recently modified than `server.c`. 71 | - `find -type d` — Find directories. For files, use `-type f`; for symbolic links, use `-type l`. 72 | 73 | Note, in particular, that all of these can be combined, for example to find C source files edited in the last two days: 74 | 75 | $ find -name '*.c' -mtime -2 76 | 77 | By default, the action `find` takes for search results is simply to list them on standard output, but there are several other useful actions: 78 | 79 | - `-ls` — Provide an `ls -l` style listing, as above 80 | - `-delete` — Delete matching files 81 | - `-exec` — Run an arbitrary command line on each file, replacing `{}` with the appropriate filename, and terminated by `\;`; for example: 82 | 83 | $ find -name '*.pl' -exec perl -c {} \; 84 | 85 | You can use `+` as the terminating character instead if you want to put all of the results on one invocation of the command. One trick I find myself using often is using `find` to generate lists of files that I then edit in vertically split Vim windows: 86 | 87 | $ find -name '*.c' -exec vim {} + 88 | 89 | *Earlier versions of Unix as IDE suggested the use of `xargs` with `find` results. In most cases this should not really be necessary, and it’s more robust to handle filenames with whitespace using `-exec` or a `while read -r` loop.* 90 | 91 | ## Searching files 92 | 93 | More often than *attributes* of a set of files, however, you want to find files based on their *contents*, and it’s no surprise that `grep`, in particular `grep -R`, is useful here. This searches the current directory tree recursively for anything matching ‘someVar’: 94 | 95 | $ grep -FR someVar . 96 | 97 | Don’t forget the case insensitivity flag either, since by default `grep` works with fixed case: 98 | 99 | $ grep -iR somevar . 100 | 101 | Also, you can print a list of files that match without printing the matches themselves with `grep -l`: 102 | 103 | $ grep -lR someVar . 104 | 105 | If you write scripts or batch jobs using the output of the above, use a `while` loop with `read` to handle spaces and other special characters in filenames: 106 | 107 | grep -lR someVar | while IFS= read -r file; do 108 | head "$file" 109 | done 110 | 111 | If you’re using version control for your project, this often includes metadata in the `.svn`, `.git`, or `.hg` directories. This is dealt with easily enough by *excluding* (`grep -v`) anything matching an appropriate fixed (`grep -F`) string: 112 | 113 | $ grep -R someVar . | grep -vF .svn 114 | 115 | Some versions of `grep` include `--exclude` and `--exclude-dir` options, which may be tidier. 116 | 117 | With all this said, there’s a very popular [alternative to grep](http://betterthangrep.com/) called `ack`, which excludes this sort of stuff for you by default. It also allows you to use Perl-compatible regular expressions (PCRE), which are a favourite for many hackers. It has a lot of utilities that are generally useful for working with source code, so while there’s nothing wrong with good old `grep` since you know it will always be there, if you can install `ack` I highly recommend it. There’s a Debian package called `ack-grep`, and being a Perl script it’s otherwise very simple to install. 118 | 119 | Unix purists might be displeased with my even mentioning a relatively new Perl script alternative to classic `grep`, but I don’t believe that the Unix philosophy or using Unix as an IDE is dependent on sticking to the same classic tools when alternatives with the same spirit that solve new problems are available. 120 | 121 | ## File metadata 122 | 123 | The `file` tool gives you a one-line summary of what kind of file you’re looking at, based on its extension, headers and other cues. This is very handy used with `find` when examining a set of unfamiliar files: 124 | 125 | $ find -exec file {} \; 126 | .: directory 127 | ./hanoi: Perl script, ASCII text executable 128 | ./.hanoi.swp: Vim swap file, version 7.3 129 | ./factorial: Perl script, ASCII text executable 130 | ./bits.c: C source, ASCII text 131 | ./bits: ELF 32-bit LSB executable, Intel 80386, version ... 132 | 133 | ## Matching files 134 | 135 | As a final tip for this section, I’d suggest learning a bit about pattern matching and brace expansion in Bash, which you can do in my earlier post entitled [Bash shell expansion](http://blog.sanctum.geek.nz/bash-shell-expansion/). 136 | 137 | All of the above make the classic UNIX shell into a pretty powerful means of managing files in programming projects. 138 | 139 | -------------------------------------------------------------------------------- /src/03-editing.md: -------------------------------------------------------------------------------- 1 | # Editing 2 | 3 | The text editor is the core tool for any programmer, which is why choice of editor evokes such tongue-in-cheek zealotry in debate among programmers. Unix is the operating system most strongly linked with two enduring favourites, Emacs and Vi, and their modern versions in GNU Emacs and Vim, two editors with very different editing philosophies but comparable power. 4 | 5 | Being a Vim heretic myself, here I’ll discuss the indispensable features of Vim for programming, and in particular the use of Linux shell tools called from *within* Vim to complement the editor’s built-in functionality. Some of the principles discussed here will be applicable to those using Emacs as well, but probably not for underpowered editors like Nano. 6 | 7 | This will be a very general survey, as Vim’s toolset for programmers is *enormous*, and it’ll still end up being quite long. I’ll focus on the essentials and the things I feel are most helpful, and try to provide links to articles with a more comprehensive treatment of the topic. Don’t forget that Vim’s `:help` has surprised many people new to the editor with its high quality and usefulness. 8 | 9 | ## Filetype detection 10 | 11 | Vim has built-in settings to adjust its behaviour, in particular its syntax highlighting, based on the filetype being loaded, which it happily detects and generally does a good job at doing so. In particular, this allows you to set an indenting style conformant with the way a particular language is usually written. This should be one of the first things in your `.vimrc` file. 12 | 13 | if has("autocmd") 14 | filetype on 15 | filetype indent on 16 | filetype plugin on 17 | endif 18 | 19 | ## Syntax highlighting 20 | 21 | Even if you’re just working with a 16-color terminal, just include the following in your `.vimrc` if you’re not already: 22 | 23 | syntax on 24 | 25 | The colorschemes with a default 16-color terminal are not pretty largely by necessity, but they do the job, and for most languages syntax definition files are available that work very well. There’s a [tremendous array of colorschemes](http://code.google.com/p/vimcolorschemetest/) available, and it’s not hard to tweak them to suit or even to write your own. Using a [256-color terminal](http://vim.wikia.com/wiki/256_colors_in_vim) or gVim will give you more options. Good syntax highlighting files will show you definite syntax errors with a glaring red background. 26 | 27 | ## Line numbering 28 | 29 | To turn line numbers on if you use them a lot in your traditional IDE: 30 | 31 | set number 32 | 33 | You might like to try this as well, if you have at least Vim 7.3 and are keen to try numbering lines relative to the current line rather than absolutely: 34 | 35 | set relativenumber 36 | 37 | ## Tags files 38 | 39 | Vim [works very well](http://amix.dk/blog/post/19329) with the output from the `ctags` utility. This allows you to search quickly for all uses of a particular identifier throughout the project, or to navigate straight to the declaration of a variable from one of its uses, regardless of whether it’s in the same file. For large C projects in multiple files this can save huge amounts of otherwise wasted time, and is probably Vim’s best answer to similar features in mainstream IDEs. 40 | 41 | You can run `:!ctags -R` on the root directory of projects in many popular languages to generate a `tags` file filled with definitions and locations for identifiers throughout your project. Once a `tags` file for your project is available, you can search for uses of an appropriate tag throughout the project like so: 42 | 43 | :tag someClass 44 | 45 | The commands `:tn` and `:tp` will allow you to iterate through successive uses of the tag elsewhere in the project. The built-in tags functionality for this already covers most of the bases you’ll probably need, but for features such as a tag list window, you could try installing the very popular [Taglist plugin](http://vim-taglist.sourceforge.net/). Tim Pope’s [Unimpaired plugin](https://github.com/tpope/vim-unimpaired) also contains a couple of useful relevant mappings. 46 | 47 | ## Calling external programs 48 | 49 | There are two major methods of calling external programs during a Vim session: 50 | 51 | - **`:!`** — Useful for issuing commands from within a Vim context particularly in cases where you intend to record output in a buffer. 52 | - **`:shell`** — Drop to a shell as a subprocess of Vim. Good for interactive commands. 53 | 54 | A third, which I won’t discuss in depth here, is using plugins such as [Conque](http://code.google.com/p/conque/) to emulate a shell within a Vim buffer. Having tried this myself and found it nearly unusable, I’ve concluded it’s simply bad design. From `:help design-not`: 55 | 56 | > Vim is not a shell or an Operating System. You will not be able to run a shell inside Vim or use it to control a debugger. This should work the other way around: Use Vim as a component from a shell or in an IDE. 57 | 58 | ## Lint programs and syntax checkers 59 | 60 | Checking syntax or compiling with an external program call (e.g. `perl -c`, `gcc`) is one of the calls that’s good to make from within the editor using `:!` commands. If you were editing a Perl file, you could run this like so: 61 | 62 | :!perl -c % 63 | 64 | /home/tom/project/test.pl syntax OK 65 | 66 | Press Enter or type command to continue 67 | 68 | The `%` symbol is shorthand for the file loaded in the current buffer. Running this prints the output of the command, if any, below the command line. If you wanted to call this check often, you could perhaps map it as a command, or even a key combination in your `.vimrc` file. In this case, we define a command `:PerlLint` which can be called from normal mode with `\l`: 69 | 70 | command PerlLint !perl -c % 71 | nnoremap l :PerlLint 72 | 73 | For a lot of languages there’s an even better way to do this, though, which allows us to capitalise on Vim’s built-in quickfix window. We can do this by setting an appropriate `makeprg` for the filetype, in this case including a module that provides us with output that Vim can use for its quicklist, and a definition for its two formats: 74 | 75 | :set makeprg=perl\ -c\ -MVi::QuickFix\ % 76 | :set errorformat+=%m\ at\ %f\ line\ %l\. 77 | :set errorformat+=%m\ at\ %f\ line\ %l 78 | 79 | You may need to install this module first via CPAN, or the Debian package `libvi-quickfix-perl`. This done, you can type `:make` after saving the file to check its syntax, and if errors are found, you can open the quicklist window with `:copen` to inspect the errors, and `:cn` and `:cp` to jump to them within the buffer. 80 | 81 | [![Vim quickfix working on a Perl file](http://blog.sanctum.geek.nz/wp-content/uploads/2012/02/vim-quickfix.png "vim-quickfix")](http://blog.sanctum.geek.nz/wp-content/uploads/2012/02/vim-quickfix.png) 82 | Vim quickfix working on a Perl file 83 | 84 | This also works for output from [`gcc`](http://tldp.org/HOWTO/C-editing-with-VIM-HOWTO/quickfix.html), and pretty much any other compiler syntax checker that you might want to use that includes filenames, line numbers, and error strings in its error output. It’s even possible to do this with [web-focused languages like PHP](http://stackoverflow.com/questions/7193547/debugging-php-with-vim-using-quickfix), and for tools like [JSLint for JavaScript](https://github.com/hallettj/jslint.vim). There’s also an excellent plugin named [Syntastic](http://www.vim.org/scripts/script.php?script_id=2736) that does something similar. 85 | 86 | ## Reading output from other commands 87 | 88 | You can use `:r!` to call commands and paste their output directly into the buffer with which you’re working. For example, to pull a quick directory listing for the current folder into the buffer, you could type: 89 | 90 | :r!ls 91 | 92 | This doesn’t just work for commands, of course; you can simply read in other files this way with just `:r`, like public keys or your own custom boilerplate: 93 | 94 | :r ~/.ssh/id_rsa.pub 95 | :r ~/dev/perl/boilerplate/copyright.pl 96 | 97 | ## Filtering output through other commands 98 | 99 | You can extend this to actually filter text in the buffer through external commands, perhaps selected by a range or visual mode, and replace it with the command’s output. While Vim’s visual block mode is great for working with columnar data, it’s very often helpful to bust out tools like `column`, `cut`, `sort`, or `awk`. 100 | 101 | For example, you could sort the entire file in reverse by the second column by typing: 102 | 103 | :%!sort -k2 -r 104 | 105 | You could print only the third column of some selected text where the line matches the pattern `/vim/` with: 106 | 107 | :'<,'>!awk '/vim/ {print $3}' 108 | 109 | You could arrange keywords from lines 1 to 10 in nicely formatted columns like: 110 | 111 | :1,10!column -t 112 | 113 | Really *any kind* of text filter or command can be manipulated like this in Vim, a simple interoperability feature that expands what the editor can do by an order of magnitude. It effectively makes the Vim buffer into a text stream, which is a language that all of these classic tools speak. 114 | 115 | ## Built-in alternatives 116 | 117 | It’s worth noting that for really common operations like sorting and searching, Vim has built-in methods in `:sort` and `:grep`, which can be helpful if you’re stuck using Vim on Windows, but don’t have nearly the adaptability of shell calls. 118 | 119 | ## Diffing 120 | 121 | Vim has a *diffing* mode, `vimdiff`, which allows you to not only view the differences between different versions of a file, but also to resolve conflicts via a three-way merge and to replace differences to and fro with commands like `:diffput` and `:diffget` for ranges of text. You can call `vimdiff` from the command line directly with at least two files to compare like so: 122 | 123 | $ vimdiff file-v1.c file-v2.c 124 | 125 | [![Vim diffing a .vimrc file](http://blog.sanctum.geek.nz/wp-content/uploads/2012/02/vim-diff.png "vim-diff")](http://blog.sanctum.geek.nz/wp-content/uploads/2012/02/vim-diff.png) 126 | Vim diffing a .vimrc file 127 | 128 | ## Version control 129 | 130 | You can call version control methods directly from within Vim, which is probably all you need most of the time. It’s useful to remember here that `%` is always a shortcut for the buffer’s current file: 131 | 132 | :!svn status 133 | :!svn add % 134 | :!git commit -a 135 | 136 | Recently a clear winner for Git functionality with Vim has come up with Tim Pope’s [Fugitive](https://github.com/tpope/vim-fugitive), which I highly recommend to anyone doing Git development with Vim. There’ll be a more comprehensive treatment of version control’s basis and history in Unix in Part 7 of this series. 137 | 138 | ## The difference 139 | 140 | Part of the reason Vim is thought of as a toy or relic by a lot of programmers used to GUI-based IDEs is its being seen as just a tool for editing files on servers, rather than a very capable editing component for the shell in its own right. Its own built-in features being so composable with external tools on Unix-friendly systems makes it into a text editing powerhouse that sometimes surprises even experienced users. 141 | 142 | -------------------------------------------------------------------------------- /src/04-compiling.md: -------------------------------------------------------------------------------- 1 | # Compiling 2 | 3 | There are a lot of tools available for compiling and interpreting code on the Unix platform, and they tend to be used in different ways. However, conceptually many of the steps are the same. Here I’ll discuss compiling C code with `gcc` from the GNU Compiler Collection, and briefly the use of `perl` as an example of an interpreter. 4 | 5 | ## GCC 6 | 7 | [GCC](http://gcc.gnu.org/) is a very mature GPL-licensed collection of compilers, perhaps best-known for working with C and C++ programs. Its free software license and near ubiquity on free Unix-like systems like Linux and BSD has made it enduringly popular for these purposes, though more modern alternatives are available in compilers using the [LLVM](http://llvm.org/) infrastructure, such as [Clang](http://clang.llvm.org/). 8 | 9 | The frontend binaries for GNU Compiler Collection are best thought of less as a set of complete compilers in their own right, and more as *drivers* for a set of discrete programming tools, performing parsing, compiling, and linking, among other steps. This means that while you can use GCC with a relatively simple command line to compile straight from C sources to a working binary, you can also inspect in more detail the steps it takes along the way and tweak it accordingly. 10 | 11 | I won’t be discussing the use of `make` files here, though you’ll almost certainly be wanting them for any C project of more than one file; that will be discussed in the next article on build automation tools. 12 | 13 | ## Compiling and assembling object code 14 | 15 | You can compile object code from a C source file like so: 16 | 17 | $ gcc -c example.c -o example.o 18 | 19 | Assuming it’s a valid C program, this will generate an unlinked binary object file called `example.o` in the current directory, or tell you the reasons it can’t. You can inspect its assembler contents with the `objdump` tool: 20 | 21 | $ objdump -D example.o 22 | 23 | Alternatively, you can get `gcc` to output the appropriate assembly code for the object directly with the `-S` parameter: 24 | 25 | $ gcc -c -S example.c -o example.s 26 | 27 | This kind of assembly output can be particularly instructive, or at least interesting, when printed inline with the source code itself, which you can do with: 28 | 29 | $ gcc -c -g -Wa,-a,-ad example.c > example.lst 30 | 31 | ## Preprocessor 32 | 33 | The C preprocessor `cpp` is generally used to include header files and define macros, among other things. It’s a normal part of `gcc` compilation, but you can view the C code it generates by invoking `cpp` directly: 34 | 35 | $ cpp example.c 36 | 37 | This will print out the complete code as it would be compiled, with includes and relevant macros applied. 38 | 39 | ## Linking objects 40 | 41 | One or more objects can be linked into appropriate binaries like so: 42 | 43 | $ gcc example.o -o example 44 | 45 | In this example, GCC is not doing much more than abstracting a call to `ld`, the GNU linker. The command produces an executable binary called `example`. 46 | 47 | ## Compiling, assembling, and linking 48 | 49 | All of the above can be done in one step with: 50 | 51 | $ gcc example.c -o example 52 | 53 | This is a little simpler, but compiling objects independently turns out to have some practical performance benefits in not recompiling code unnecessarily, which I’ll discuss in the next article. 54 | 55 | ## Including and linking 56 | 57 | C files and headers can be explicitly included in a compilation call with the `-I` parameter: 58 | 59 | $ gcc -I/usr/include/somelib.h example.c -o example 60 | 61 | Similarly, if the code needs to be dynamically linked against a compiled system library available in common locations like `/lib` or `/usr/lib`, such as `ncurses`, that can be included with the `-l` parameter: 62 | 63 | $ gcc -lncurses example.c -o example 64 | 65 | If you have a lot of necessary inclusions and links in your compilation process, it makes sense to put this into environment variables: 66 | 67 | $ export CFLAGS=-I/usr/include/somelib.h 68 | $ export CLIBS=-lncurses 69 | $ gcc $CFLAGS $CLIBS example.c -o example 70 | 71 | This very common step is another thing that a `Makefile` is designed to abstract away for you. 72 | 73 | ## Compilation plan 74 | 75 | To inspect in more detail what `gcc` is doing with any call, you can add the `-v` switch to prompt it to print its compilation plan on the standard error stream: 76 | 77 | $ gcc -v -c example.c -o example.o 78 | 79 | If you don’t want it to actually generate object files or linked binaries, it’s sometimes tidier to use `-###` instead: 80 | 81 | $ gcc -### -c example.c -o example.o 82 | 83 | This is mostly instructive to see what steps the `gcc` binary is abstracting away for you, but in specific cases it can be useful to identify steps the compiler is taking that you may not necessarily want it to. 84 | 85 | ## More verbose error checking 86 | 87 | You can add the `-Wall` and/or `-pedantic` options to the `gcc` call to prompt it to warn you about things that may not necessarily be errors, but could be: 88 | 89 | $ gcc -Wall -pedantic -c example.c -o example.o 90 | 91 | This is good for including in your `Makefile` or in your [`makeprg`](http://vim.wikia.com/wiki/Errorformat_and_makeprg) definition in Vim, as it works well with the quickfix window discussed in the previous article and will enable you to write more readable, compatible, and less error-prone code as it warns you more extensively about errors. 92 | 93 | ## Profiling compilation time 94 | 95 | You can pass the flag `-time` to `gcc` to generate output showing how long each step is taking: 96 | 97 | $ gcc -time -c example.c -o example.o 98 | 99 | ## Optimisation 100 | 101 | You can pass generic optimisation options to `gcc` to make it attempt to build more efficient object files and linked binaries, at the expense of compilation time. I find `-O2` is usually a happy medium for code going into production: 102 | 103 | - `gcc -O1` 104 | - `gcc -O2` 105 | - `gcc -O3` 106 | 107 | Like any other Bash command, all of this can be [called from within Vim](http://blog.sanctum.geek.nz/unix-as-ide-editing/) by: 108 | 109 | :!gcc % -o example 110 | 111 | ## Interpreters 112 | 113 | The approach to interpreted code on Unix-like systems is very different. In these examples I’ll use Perl, but most of these principles will be applicable to interpreted Python or Ruby code, for example. 114 | 115 | ## Inline 116 | 117 | You can run a string of Perl code directly into the interpreter in any one of the following ways, in this case printing the single line “Hello, world.” to the screen, with a linebreak following. The first one is perhaps the tidiest and most standard way to work with Perl; the second uses a [heredoc](http://tldp.org/LDP/abs/html/here-docs.html) string, and the third a classic Unix shell pipe. 118 | 119 | $ perl -e 'print "Hello world.\n";' 120 | $ perl <<<'print "Hello world.\n";' 121 | $ echo 'print "Hello world.\n";' | perl 122 | 123 | Of course, it’s more typical to keep the code in a file, which can be run directly: 124 | 125 | $ perl hello.pl 126 | 127 | In either case, you can check the syntax of the code without actually running it with the `-c` switch: 128 | 129 | $ perl -c hello.pl 130 | 131 | But to use the script as a *logical binary*, so you can invoke it directly without knowing or caring what the script is, you can add a special first line to the file called the “shebang” that does some magic to specify the interpreter through which the file should be run. 132 | 133 | #!/usr/bin/env perl 134 | print "Hello, world.\n"; 135 | 136 | The script then needs to be made executable with a `chmod` call. It’s also good practice to rename it to remove the extension, since it is now taking the shape of a logic binary: 137 | 138 | $ mv hello{.pl,} 139 | $ chmod +x hello 140 | 141 | And can thereafter be invoked directly, as if it were a compiled binary: 142 | 143 | $ ./hello 144 | 145 | This works so well that many of the common utilities on modern Linux systems, such as the `adduser` frontend to `useradd`, are actually Perl or even Python scripts. 146 | 147 | In the next post, I’ll describe the use of `make` for defining and automating building projects in a manner comparable to IDEs, with a nod to newer takes on the same idea with Ruby’s `rake`. 148 | 149 | -------------------------------------------------------------------------------- /src/05-building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | Because compiling projects can be such a complicated and repetitive process, a good IDE provides a means to abstract, simplify, and even automate software builds. Unix and its descendents accomplish this process with a `Makefile`, a prescribed recipe in a standard format for generating executable files from source and object files, taking account of changes to only rebuild what’s necessary to prevent costly recompilation. 4 | 5 | One interesting thing to note about `make` is that while it’s generally used for compiled software build automation and has many shortcuts to that effect, it can actually effectively be used for any situation in which it’s required to generate one set of files from another. One possible use is to generate web-friendly optimised graphics from source files for deployment for a website; another use is for generating static HTML pages from code, rather than generating pages on the fly. It’s on the basis of this more flexible understanding of software “building” that modern takes on the tool like [Ruby’s `rake`](http://rake.rubyforge.org/) have become popular, automating the general tasks for producing and installing code and files of all kinds. 6 | 7 | ## Anatomy of a `Makefile` 8 | 9 | The general pattern of a `Makefile` is a list of variables and a list of *targets*, and the sources and/or objects used to provide them. Targets may not necessarily be linked binaries; they could also constitute actions to perform using the generated files, such as `install` to instate built files into the system, and `clean` to remove built files from the source tree. 10 | 11 | It’s this flexibility of targets that enables `make` to automate any sort of task relevant to assembling a production build of software; not just the typical parsing, preprocessing, compiling proper and linking steps performed by the compiler, but also running tests (`make test`), compiling documentation source files into one or more appropriate formats, or automating deployment of code into production systems, for example, uploading to a website via a `git push` or similar content-tracking method. 12 | 13 | An example `Makefile` for a simple software project might look something like the below: 14 | 15 | all: example 16 | 17 | example: main.o example.o library.o 18 | gcc main.o example.o library.o -o example 19 | 20 | main.o: main.c 21 | gcc -c main.c -o main.o 22 | 23 | example.o: example.c 24 | gcc -c example.c -o example.o 25 | 26 | library.o: library.c 27 | gcc -c library.c -o library.o 28 | 29 | clean: 30 | rm *.o example 31 | 32 | install: example 33 | cp example /usr/bin 34 | 35 | The above isn’t the most optimal `Makefile` possible for this project, but it provides a means to build and install a linked binary simply by typing `make`. Each *target* definition contains a list of the *dependencies* required for the command that follows; this means that the definitions can appear in any order, and the call to `make` will call the relevant commands in the appropriate order. 36 | 37 | Much of the above is needlessly verbose or repetitive; for example, if an object file is built directly from a single C file of the same name, then we don’t need to include the target at all, and `make` will sort things out for us. Similarly, it would make sense to put some of the more repeated calls into variables so that we would not have to change them individually if our choice of compiler or flags changed. A more concise version might look like the following: 38 | 39 | CC = gcc 40 | OBJECTS = main.o example.o library.o 41 | BINARY = example 42 | 43 | all: example 44 | 45 | example: $(OBJECTS) 46 | $(CC) $(OBJECTS) -o $(BINARY) 47 | 48 | clean: 49 | rm -f $(BINARY) $(OBJECTS) 50 | 51 | install: example 52 | cp $(BINARY) /usr/bin 53 | 54 | ## More general uses of `make` 55 | 56 | In the interests of automation, however, it’s instructive to think of this a bit more generally than just code compilation and linking. An example could be for a simple web project involving deploying PHP to a live webserver. This is not normally a task people associate with the use of `make`, but the principles are the same; with the source in place and ready to go, we have certain targets to meet for the build. 57 | 58 | PHP files don’t require compilation, of course, but web assets often do. An example that will be familiar to web developers is the generation of scaled and optimised raster images from vector source files, for deployment to the web. You keep and version your original source file, and when it comes time to deploy, you generate a web-friendly version of it. 59 | 60 | Let’s assume for this particular project that there’s a set of four icons used throughout the site, sized to 64 by 64 pixels. We have the source files to hand in SVG vector format, safely tucked away in version control, and now need to *generate* the smaller bitmaps for the site, ready for deployment. We could therefore define a target `icons`, set the dependencies, and type out the commands to perform. This is where command line tools in Unix really begin to shine in use with `Makefile` syntax: 61 | 62 | icons: create.png read.png update.png delete.png 63 | 64 | create.png: create.svg 65 | convert create.svg create.raw.png && \ 66 | pngcrush create.raw.png create.png 67 | 68 | read.png: read.svg 69 | convert read.svg read.raw.png && \ 70 | pngcrush read.raw.png read.png 71 | 72 | update.png: update.svg 73 | convert update.svg update.raw.png && \ 74 | pngcrush update.raw.png update.png 75 | 76 | delete.png: delete.svg 77 | convert delete.svg delete.raw.png && \ 78 | pngcrush delete.raw.png delete.png 79 | 80 | With the above done, typing `make icons` will go through each of the source icons files in a Bash loop, convert them from SVG to PNG using ImageMagick’s `convert`, and optimise them with `pngcrush`, to produce images ready for upload. 81 | 82 | A similar approach can be used for generating help files in various forms, for example, generating HTML files from Markdown source: 83 | 84 | docs: README.html credits.html 85 | 86 | README.html: README.md 87 | markdown README.md > README.html 88 | 89 | credits.html: credits.md 90 | markdown credits.md > credits.html 91 | 92 | And perhaps finally deploying a website with `git push web`, but only *after* the icons are rasterized and the documents converted: 93 | 94 | deploy: icons docs 95 | git push web 96 | 97 | For a more compact and abstract formula for turning a file of one suffix into another, you can use the `.SUFFIXES` pragma to define these using special symbols. The code for converting icons could look like this; in this case, `$<` refers to the source file, `$*` to the filename with no extension, and `$@` to the target. 98 | 99 | icons: create.png read.png update.png delete.png 100 | 101 | .SUFFIXES: .svg .png 102 | 103 | .svg.png: 104 | convert $< $*.raw.png && \ 105 | pngcrush $*.raw.png $@ 106 | 107 | ## Tools for building a `Makefile` 108 | 109 | A variety of tools exist in the GNU Autotools toolchain for the construction of `configure` scripts and `make` files for larger software projects at a higher level, in particular [`autoconf`](http://en.wikipedia.org/wiki/Autoconf) and [`automake`](http://en.wikipedia.org/wiki/Automake). The use of these tools allows generating `configure` scripts and `make` files covering very large source bases, reducing the necessity of building otherwise extensive makefiles manually, and automating steps taken to ensure the source remains compatible and compilable on a variety of operating systems. 110 | 111 | Covering this complex process would be a series of posts in its own right, and is out of scope of this survey. 112 | 113 | *Thanks to user samwyse for the `.SUFFIXES` suggestion in the comments.* 114 | 115 | # Debugging 116 | 117 | When unexpected behaviour is noticed in a program, Linux provides a wide variety of command-line tools for diagnosing problems. The use of `gdb`, the GNU debugger, and related tools like the lesser-known Perl debugger, will be familiar to those using IDEs to set breakpoints in their code and to examine program state as it runs. Other tools of interest are available however to observe in more detail how a program is interacting with a system and using its resources. 118 | 119 | ## Debugging with `gdb` 120 | 121 | You can use `gdb` in a very similar fashion to the built-in debuggers in modern IDEs like Eclipse and Visual Studio. If you are debugging a program that you’ve just compiled, it makes sense to compile it with its *debugging symbols* added to the binary, which you can do with a `gcc` call containing the `-g` option. If you’re having problems with some code, it helps to also use `-Wall` to show any errors you may have otherwise missed: 122 | 123 | $ gcc -g -Wall example.c -o example 124 | 125 | The classic way to use `gdb` is as the shell for a running program compiled in C or C++, to allow you to inspect the program’s state as it proceeds towards its crash. 126 | 127 | $ gdb example 128 | ... 129 | Reading symbols from /home/tom/example...done. 130 | (gdb) 131 | 132 | At the `(gdb)` prompt, you can type `run` to start the program, and it may provide you with more detailed information about the causes of errors such as segmentation faults, including the source file and line number at which the problem occurred. If you’re able to compile the code with debugging symbols as above and inspect its running state like this, it makes figuring out the cause of a particular bug a lot easier. 133 | 134 | (gdb) run 135 | Starting program: /home/tom/gdb/example 136 | 137 | Program received signal SIGSEGV, Segmentation fault. 138 | 0x000000000040072e in main () at example.c:43 139 | 43 printf("%d\n", *segfault); 140 | 141 | After an error terminates the program within the `(gdb)` shell, you can type `backtrace` to see what the calling function was, which can include the specific parameters passed that may have something to do with what caused the crash. 142 | 143 | (gdb) backtrace 144 | #0 0x000000000040072e in main () at example.c:43 145 | 146 | You can set breakpoints for `gdb` using the `break` to halt the program’s run if it reaches a matching line number or function call: 147 | 148 | (gdb) break 42 149 | Breakpoint 1 at 0x400722: file example.c, line 42. 150 | (gdb) break malloc 151 | Breakpoint 1 at 0x4004c0 152 | (gdb) run 153 | Starting program: /home/tom/gdb/example 154 | 155 | Breakpoint 1, 0x00007ffff7df2310 in malloc () from /lib64/ld-linux-x86-64.so.2 156 | 157 | Thereafter it’s helpful to *step* through successive lines of code using `step`. You can repeat this, like any `gdb` command, by pressing Enter repeatedly to step through lines one at a time: 158 | 159 | (gdb) step 160 | Single stepping until exit from function _start, 161 | which has no line number information. 162 | 0x00007ffff7a74db0 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6 163 | 164 | You can even attach `gdb` to a process that is already running, by finding the process ID and passing it to `gdb`: 165 | 166 | $ pgrep example 167 | 1524 168 | $ gdb -p 1524 169 | 170 | This can be useful for [redirecting streams of output](http://stackoverflow.com/questions/593724/redirect-stderr-stdout-of-a-process-after-its-been-started-using-command-lin) for a task that is taking an unexpectedly long time to run. 171 | 172 | ## Debugging with `valgrind` 173 | 174 | The much newer [valgrind](http://valgrind.org/) can be used as a debugging tool in a similar way. There are many different checks and debugging methods this program can run, but one of the most useful is its Memcheck tool, which can be used to detect common memory errors like buffer overflow: 175 | 176 | $ valgrind --leak-check=yes ./example 177 | ==29557== Memcheck, a memory error detector 178 | ==29557== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 179 | ==29557== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 180 | ==29557== Command: ./example 181 | ==29557== 182 | ==29557== Invalid read of size 1 183 | ==29557== at 0x40072E: main (example.c:43) 184 | ==29557== Address 0x0 is not stack'd, malloc'd or (recently) free'd 185 | ==29557== 186 | ... 187 | 188 | The `gdb` and `valgrind` tools [can be used together](http://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.gdbserver) for a very thorough survey of a program’s run. Zed Shaw’s [Learn C the Hard Way](http://c.learncodethehardway.org/book/) includes a really good introduction for elementary use of `valgrind` with a deliberately broken program. 189 | 190 | ## Tracing system and library calls with `ltrace` 191 | 192 | The `strace` and `ltrace` tools are designed to allow watching system calls and library calls respectively for running programs, and logging them to the screen or, more usefully, to files. 193 | 194 | You can run `ltrace` and have it run the program you want to monitor in this way for you by simply providing it as the sole parameter. It will then give you a listing of all the system and library calls it makes until it exits. 195 | 196 | $ ltrace ./example 197 | __libc_start_main(0x4006ad, 1, 0x7fff9d7e5838, 0x400770, 0x400760 198 | srand(4, 0x7fff9d7e5838, 0x7fff9d7e5848, 0, 0x7ff3aebde320) = 0 199 | malloc(24) = 0x01070010 200 | rand(0, 0x1070020, 0, 0x1070000, 0x7ff3aebdee60) = 0x754e7ddd 201 | malloc(24) = 0x01070030 202 | rand(0x7ff3aebdee60, 24, 0, 0x1070020, 0x7ff3aebdeec8) = 0x11265233 203 | malloc(24) = 0x01070050 204 | rand(0x7ff3aebdee60, 24, 0, 0x1070040, 0x7ff3aebdeec8) = 0x18799942 205 | malloc(24) = 0x01070070 206 | rand(0x7ff3aebdee60, 24, 0, 0x1070060, 0x7ff3aebdeec8) = 0x214a541e 207 | malloc(24) = 0x01070090 208 | rand(0x7ff3aebdee60, 24, 0, 0x1070080, 0x7ff3aebdeec8) = 0x1b6d90f3 209 | malloc(24) = 0x010700b0 210 | rand(0x7ff3aebdee60, 24, 0, 0x10700a0, 0x7ff3aebdeec8) = 0x2e19c419 211 | malloc(24) = 0x010700d0 212 | rand(0x7ff3aebdee60, 24, 0, 0x10700c0, 0x7ff3aebdeec8) = 0x35bc1a99 213 | malloc(24) = 0x010700f0 214 | rand(0x7ff3aebdee60, 24, 0, 0x10700e0, 0x7ff3aebdeec8) = 0x53b8d61b 215 | malloc(24) = 0x01070110 216 | rand(0x7ff3aebdee60, 24, 0, 0x1070100, 0x7ff3aebdeec8) = 0x18e0f924 217 | malloc(24) = 0x01070130 218 | rand(0x7ff3aebdee60, 24, 0, 0x1070120, 0x7ff3aebdeec8) = 0x27a51979 219 | --- SIGSEGV (Segmentation fault) --- 220 | +++ killed by SIGSEGV +++ 221 | 222 | You can also attach it to a process that’s already running: 223 | 224 | $ pgrep example 225 | 5138 226 | $ ltrace -p 5138 227 | 228 | Generally, there’s quite a bit more than a couple of screenfuls of text generated by this, so it’s helpful to use the `-o` option to specify an output file to which to log the calls: 229 | 230 | $ ltrace -o example.ltrace ./example 231 | 232 | You can then view this trace in a text editor like Vim, which includes syntax highlighting for `ltrace` output: 233 | 234 | [![Vim session with ltrace output](http://blog.sanctum.geek.nz/wp-content/uploads/2012/02/ltrace-vim.png "ltrace-vim")](http://blog.sanctum.geek.nz/wp-content/uploads/2012/02/ltrace-vim.png) 235 | Vim session with ltrace output 236 | 237 | I’ve found `ltrace` very useful for debugging problems where I suspect improper linking may be at fault, or the absence of some needed resource in a `chroot` environment, since among its output it shows you its search for libraries at dynamic linking time and opening configuration files in `/etc`, and the use of devices like `/dev/random` or `/dev/zero`. 238 | 239 | ## Tracking open files with `lsof` 240 | 241 | If you want to view what devices, files, or streams a running process has open, you can do that with `lsof`: 242 | 243 | $ pgrep example 244 | 5051 245 | $ lsof -p 5051 246 | 247 | For example, the first few lines of the `apache2` process running on my home server are: 248 | 249 | # lsof -p 30779 250 | COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME 251 | apache2 30779 root cwd DIR 8,1 4096 2 / 252 | apache2 30779 root rtd DIR 8,1 4096 2 / 253 | apache2 30779 root txt REG 8,1 485384 990111 /usr/lib/apache2/mpm-prefork/apache2 254 | apache2 30779 root DEL REG 8,1 1087891 /lib/x86_64-linux-gnu/libgcc_s.so.1 255 | apache2 30779 root mem REG 8,1 35216 1079715 /usr/lib/php5/20090626/pdo_mysql.so 256 | ... 257 | 258 | Interestingly, another way to list the open files for a process is to check the corresponding entry for the process in the dynamic `/proc` directory: 259 | 260 | # ls -l /proc/30779/fd 261 | 262 | This can be very useful in confusing situations with file locks, or identifying whether a process is holding open files that it needn’t. 263 | 264 | ## Viewing memory allocation with `pmap` 265 | 266 | As a final debugging tip, you can view the memory allocations for a particular process with `pmap`: 267 | 268 | # pmap 30779 269 | 30779: /usr/sbin/apache2 -k start 270 | 00007fdb3883e000 84K r-x-- /lib/x86_64-linux-gnu/libgcc_s.so.1 (deleted) 271 | 00007fdb38853000 2048K ----- /lib/x86_64-linux-gnu/libgcc_s.so.1 (deleted) 272 | 00007fdb38a53000 4K rw--- /lib/x86_64-linux-gnu/libgcc_s.so.1 (deleted) 273 | 00007fdb38a54000 4K ----- [ anon ] 274 | 00007fdb38a55000 8192K rw--- [ anon ] 275 | 00007fdb392e5000 28K r-x-- /usr/lib/php5/20090626/pdo_mysql.so 276 | 00007fdb392ec000 2048K ----- /usr/lib/php5/20090626/pdo_mysql.so 277 | 00007fdb394ec000 4K r---- /usr/lib/php5/20090626/pdo_mysql.so 278 | 00007fdb394ed000 4K rw--- /usr/lib/php5/20090626/pdo_mysql.so 279 | ... 280 | total 152520K 281 | 282 | This will show you what libraries a running process is using, including those in shared memory. The total given at the bottom is a little misleading as for loaded shared libraries, the running process is not necessarily the only one using the memory; [determining “actual” memory usage for a given process](http://stackoverflow.com/questions/118307/a-way-to-determine-a-processs-real-memory-usage-i-e-private-dirty-rss) is a little more in-depth than it might seem with shared libraries added to the picture. 283 | 284 | -------------------------------------------------------------------------------- /src/06-revisions.md: -------------------------------------------------------------------------------- 1 | # Revisions 2 | 3 | Version control is now seen as an indispensable part of professional software development, and GUI IDEs like Eclipse and Visual Studio have embraced it and included support for industry standard version control systems in their products. Modern version control systems trace their lineage back to Unix concepts from programs such as `diff` and `patch` however, and there are plenty of people who will insist that the best way to use a version control system is still at a shell prompt. 4 | 5 | In this last article in the [Unix as an IDE series](http://blog.sanctum.geek.nz/series/unix-as-ide/), I’ll follow the evolution of common open-source version control systems from the basic concepts of `diff` and `patch`, among the very first version control tools. 6 | 7 | ## `diff`, `patch`, and RCS 8 | 9 | A central concept for version control systems has been that of the *unified diff*, a file expressing in human and computer readable terms a set of changes made to a file or files. The `diff` command was first released by Douglas McIlroy in 1974 for the 5th Edition of Unix, so it’s one of the oldest commands still in regular use on modern systems. 10 | 11 | A *unified diff*, the most common and interoperable format, can be generated by comparing two versions of a file with the following syntax: 12 | 13 | $ diff -u example.{1,2}.c 14 | --- example.c.1 2012-02-15 20:15:37.000000000 +1300 15 | +++ example.c.2 2012-02-15 20:15:57.000000000 +1300 16 | @@ -1,8 +1,9 @@ 17 | #include 18 | +#include 19 | 20 | int main (int argc, char* argv[]) { printf("Hello, world!\n"); 21 | - return 0; 22 | + return EXIT_SUCCESS; } 23 | 24 | In this example, the second file has a header file added, and the call to `return` changed to use the standard `EXIT_SUCCESS` rather than a literal `0` as the return value for `main()`. Note that the output for `diff` also includes metadata such as the filename that was changed and the last modification time of each of the files. 25 | 26 | A primitive form of version control for larger code bases was thus for developers to trade `diff` output, called *patches* in this context, so that they could be applied to one another’s code bases with the `patch` tool. We could save the output from `diff` above as a patch like so: 27 | 28 | $ diff -u example.{1,2}.c > example.patch 29 | 30 | We could then send this patch to a developer who still had the old version of the file, and they could automatically apply it with: 31 | 32 | $ patch example.1.c < example.patch 33 | 34 | A patch can include `diff` output from more than one file, including within subdirectories, so this provides a very workable way to apply changes to a source tree. 35 | 36 | The operations involved in using `diff` output to track changes were sufficiently regular that for keeping in-place history of a file, the [Source Code Control System](http://en.wikipedia.org/wiki/Source_Code_Control_System) and the [Revision Control System](http://en.wikipedia.org/wiki/Revision_Control_System) that has pretty much replaced it were developed. RCS enabled “locking” files so that they could not be edited by anyone else while “checked out” of the system, paving the way for other concepts in more developed version control systems. 37 | 38 | RCS retains the advantage of being very simple to use. To place an existing file under version control, one need only type `ci ` and provide an appropriate description for the file: 39 | 40 | $ ci example.c 41 | example.c,v <-- example.c 42 | enter description, terminated with single '.' or end of file: 43 | NOTE: This is NOT the log message! 44 | >> example file 45 | >> . 46 | initial revision: 1.1 47 | done 48 | 49 | This creates a file in the same directory, `example.c,v`, that will track the changes. To make changes to the file, you *check it out*, make the changes, then *check it back in*: 50 | 51 | $ co -l example.c 52 | example.c,v --> example.c 53 | revision 1.1 (locked) 54 | done 55 | $ vim example.c 56 | $ ci -u example.c 57 | example.c,v <-- example.c 58 | new revision: 1.2; previous revision: 1.1 59 | enter log message, terminated with single '.' or end of file: 60 | >> added a line 61 | >> . 62 | done 63 | 64 | You can then view the history of a project with `rlog`: 65 | 66 | $ rlog example.c 67 | 68 | RCS file: example.c,v 69 | Working file: example.c 70 | head: 1.2 71 | branch: 72 | locks: strict 73 | access list: 74 | symbolic names: 75 | keyword substitution: kv 76 | total revisions: 2; selected revisions: 2 77 | description: 78 | example file 79 | ---------------------------- 80 | revision 1.2 81 | date: 2012/02/15 07:39:16; author: tom; state: Exp; lines: +1 -0 82 | added a line 83 | ---------------------------- 84 | revision 1.1 85 | date: 2012/02/15 07:36:23; author: tom; state: Exp; 86 | Initial revision 87 | ============================================================================= 88 | 89 | And get a patch in unified `diff` format between two revisions with `rcsdiff -u`: 90 | 91 | $ rcsdiff -u -r1.1 -r1.2 ./example.c 92 | =================================================================== 93 | RCS file: ./example.c,v 94 | retrieving revision 1.1 95 | retrieving revision 1.2 96 | diff -u -r1.1 -r1.2 97 | --- ./example.c 2012/02/15 07:36:23 1.1 98 | +++ ./example.c 2012/02/15 07:39:16 1.2 99 | @@ -4,6 +4,7 @@ 100 | int main (int argc, char* argv[]) 101 | { 102 | printf("Hello, world!\n"); 103 | + printf("Extra line!\n"); 104 | return EXIT_SUCCESS; 105 | } 106 | 107 | It would be misleading to imply that simple patches were now in disuse as a method of version control; they are still very commonly used in the forms above, and also figure prominently in both centralised and decentralised version control systems. 108 | 109 | ## CVS and Subversion 110 | 111 | To handle the problem of resolving changes made to a code base by multiple developers, *centralized version systems* were developed, with the [Concurrent Versions System (CVS)](http://en.wikipedia.org/wiki/Concurrent_Versions_System) developed first and the slightly more advanced [Subversion](http://en.wikipedia.org/wiki/Apache_Subversion) later on. The central feature of these systems are using a *central server* that contains the repository, from which authoritative versions of the codebase at any particular time or revision can be retrieved. These are termed *working copies* of the code. 112 | 113 | For these systems, the basic unit of the systems remained the *changeset*, and the most common way to represent these to the user was in the archetypal `diff` format used in earlier systems. Both systems work by keeping records of these changesets, rather than the actual files themselves from state to state. 114 | 115 | Other concepts introduced by this generation of systems were of *branching* projects so that separate instances of the same project could be worked on concurrently, and then merged into the mainline, or *trunk* with appropriate testing and review. Similarly, the concept of *tagging* was introduced to flag certain revisions as representing the state of a codebase at the time of a release of the software. The concept of the `merge` was also introduced; reconciling conflicting changes made to a file manually. 116 | 117 | ## Git and Mercurial 118 | 119 | The next generation of version control systems are *distributed* or *decentralized* systems, in which working copies of the code themselves contain a complete history of the project, and are hence not reliant on a central server to contribute to the project. In the open source, Unix-friendly environment, the standout systems are Git and Mercurial, with their client programs `git` and `hg`. 120 | 121 | For both of these systems, the concept of communicating changesets is done with the operations `push`, `pull` and `merge`; changes from one repository are accepted by another. This decentralized system allows for a very complex but tightly controlled ecosystem of development; Git was originally developed by Linus Torvalds to provide an open-source DVCS capable of managing development for the Linux kernel. 122 | 123 | Both Git and Mercurial differ from CVS and Subversion in that the basic unit for their operations is not changesets, but complete files (blobs) saved using compression. This makes finding the log history of a single file or the differences between two revisions of a file slightly more expensive, but the output of `git log --patch` still retains the familiar unified `diff` output for each revision, some forty years after `diff` was first being used: 124 | 125 | commit c1e5559ddb09f8d02b989596b0f4100ad1aab422 126 | Author: Tom Ryder 127 | Date: Thu Feb 2 01:14:21 2012 128 | 129 | Changed my mind about this one. 130 | 131 | diff --git a/vim/vimrc b/vim/vimrc index cfbe8e0..65a3143 100644 132 | --- a/vim/vimrc 133 | +++ b/vim/vimrc 134 | @@ -47,10 +47,6 @@ 135 | set shiftwidth=4 136 | set softtabstop=4 137 | set tabstop=4 138 | 139 | -" Heresy 140 | -inoremap 141 | -inoremap 142 | - 143 | " History 144 | set history=1000 145 | 146 | The two systems have considerable overlap in functionality and even in command set, and the question of which to use provokes [considerable debate](http://stackoverflow.com/questions/35837/what-is-the-difference-between-mercurial-and-git). The best introductions I’ve seen to each are [Pro Git](http://progit.org/) by Scott Chacon, and [Hg Init](http://hginit.com/) by Joel Spolsky. 147 | 148 | -------------------------------------------------------------------------------- /src/07-conclusion.md: -------------------------------------------------------------------------------- 1 | # Conclusion 2 | 3 | This is the last post in the [Unix as IDE series](http://blog.sanctum.geek.nz/series/unix-as-ide/); I’ve tried to offer a rapid survey of the basic tools available just within a shell on Linux for all of the basic functionality afforded by professional IDEs. At points I’ve had to be not quite as thorough as I’d like in explaining certain features, but to those unfamiliar to development on Linux machines this will all have hopefully given some idea of how comprehensive a development environment the humble shell can be, and all with free, highly mature, and standard software tools. 4 | 5 | -------------------------------------------------------------------------------- /src/metadata.yml: -------------------------------------------------------------------------------- 1 | --- 2 | title: Unix as IDE 3 | creator: 4 | - role: author 5 | text: Tom Ryder 6 | lang: en-US 7 | rights: | 8 | 9 | © 2015 Tom Ryder, CC BY-NC-SA 10 | 11 | Originally posted on [Arabesque](http://blog.sanctum.geek.nz/series/unix-as-ide/) 12 | Ebook version built by [mrzool](http://mrzool.cc) 13 | --- 14 | 15 | -------------------------------------------------------------------------------- /unix-as-ide.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrzool/unix-as-ide/b8ede7cfb8042b990fbd282f4e109c0ce7c332cd/unix-as-ide.epub -------------------------------------------------------------------------------- /unix-as-ide.mobi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrzool/unix-as-ide/b8ede7cfb8042b990fbd282f4e109c0ce7c332cd/unix-as-ide.mobi --------------------------------------------------------------------------------