• Stars
    star
    171
  • Rank 222,266 (Top 5 %)
  • Language
    Go
  • License
    MIT License
  • Created about 9 years ago
  • Updated about 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Roughly extract dependency relation from source code

rexdep

CI Status MIT License release

Roughly extract dependency relation from source code

The rexdep command is a tool for extracting dependency relation from software. The command enables us to see the dependency relation among files, modules or packages written in any programming languages.

rexdep

When we see a large project, we want to understand the code structure intuitively. We sometimes join a project at work, the project has lots of source code. We sometimes want to make pull requests to famous OSS software, but the software is too large. How can we understand the file structure of a huge project without reading through the software?

It is a good solution to check the module dependency relations among the software. A module in a software depends on other modules in that software. Some module depends on many other modules or another module does not depend on others. Extracting the module dependency enables us to understand the relationship among the modules. We can use the dependency graph to read the software top down or bottom up. We sometimes find the core modules in the project because such modules are depended by many other modules.

So, how can we extract dependency relation from a code base of software?

The idea of rexdep is very simple; in many cases, we can extract the module names by a regular expression. Let me explain by a simple example in C language, where we want to extract the dependency relations between the files, rather than modules. Consider a situation that test1.c includes test2.c and test3.c.

/* This is test1.c */
#include "test2.c"
#include "test3.c"

int main...

The file test1.c depends on test2.c and test3.c. This relation can be easily extracted using a simple regular expression.

 $ grep '^#include ".*"' test1.c | sed 's/^#include *"\(.*\)"/\1/'
test2.c
test3.c

This way can be applied for many languages. For example, import keyword is used in Python and Haskell and require in Ruby.

The rexdep command enables us to specify the pattern, the regular expression to extract the module dependency from source codes. For the above example, we can use rexdep to extract the dependency between the C source codes.

 $ rexdep --pattern '^\s*#include\s*"(\S+)"' test1.c
test1.c test2.c
test1.c test3.c

The captured string is regarded as the file names included by the source code. We can of course specify multiple files. We can also specify directories and rexdep recursively investigate the source files under the subdirectories. Allowing the user to specify by regular expression, it can be used for various languages; ^\s*#include\s*[<"](\S+)[>"] for C language or ^\s*import +(?:qualified +)?([[:alnum:].]+) for Haskell language.

There are some other tools targeting on specific languages. For example, there are some UML class diagram generating tools for object-oriented languages. They investigate source codes at the level of abstract syntax tree and therefore much powerful. The rexdep command, on the other hand, simply checks the source code by a regular expression given by the user. It may not as powerful as AST-level tools, but the idea is very simple and can be used for many languages. Now you understand what rexdep stands for; roughly extract dependency relation.

Installation

Homebrew

brew install itchyny/tap/rexdep

Build from source

go install github.com/itchyny/rexdep@latest

Usage

Basic usage: --pattern, --format

Consider the following sample file.

 $ cat test1
import test2
import test3

We want to extract the dependency relation from this file test1. Specify --pattern the regular expression to extract the module names it imports.

 $ rexdep --pattern 'import +(\S+)' test1
test1 test2
test1 test3

The output result shows that test1 depends on test2 and test3. Each line contains the space separated module names. The captured strings in the --pattern argument are interpreted as the module names imported by each file. The regular expression is compiled to Regexp type of Go language, so refer to the document for the regexp syntax or try go doc regexp/syntax.

Note that on Windows environment, use double quotes instead of single quotes (for example: rexdep --pattern "import +(\S+)" test1).

We can use the rexdep command to output in the dot language

 $ rexdep --pattern 'import +(\S+)' --format dot test1
digraph "graph" {
  "test1" -> "test2";
  "test1" -> "test3";
}

and it works perfectly with graphviz (dot command).

 $ rexdep --pattern 'import +(\S+)' --format dot test1 | dot -Tpng -o test.png

example

Also, it works for multiple files.

 $ rexdep --pattern 'import +(\S+)' --format dot test{1,2,3,4}
digraph "graph" {
  "test1" -> "test2";
  "test1" -> "test3";
  "test2" -> "test4";
  "test3" -> "test4";
}
 $ rexdep --pattern 'import +(\S+)' --format dot test{1,2,3,4} | dot -Tpng -o test.png

example

This is a basic example of rexdep.

You can also change the output format to JSON,

 $ rexdep --pattern 'import +(\S+)' --format json test{1,2,3,4}
{
  "test1": [
    "test2",
    "test3"
  ],
  "test2": [
    "test4"
  ],
  "test3": [
    "test4"
  ]
}

which may be piped to jq command.

Module name: --module

The rexdep command uses the filenames to identify each file. This is useful for C language but not for many other languages. For example, we write module ModuleName where in Haskell. Consider the following two files.

 $ cat Foo.hs
module Foo where
import Bar
import System.Directory
 $ cat Bar.hs
module Bar where
import System.IO

Firstly, we try the following command.

 $ rexdep --pattern 'import (\S+)' Foo.hs Bar.hs
Foo.hs Bar
Foo.hs System.Directory
Bar.hs System.IO

The result looks bad and we will not be able to obtain the dependency graph we really want.

We have to tell rexdep to extract the module name as well. The rexdep command enables us to specify the module name pattern. We specify a regular expression to the argument of --module.

 $ rexdep --pattern 'import (\S+)' --module 'module (\S+)' Foo.hs Bar.hs
Foo Bar
Foo System.Directory
Bar System.IO

Now it looks fine.

A file sometimes contains multiple modules. When rexdep finds a new module in the file, the following packages will be regarded as imported by the new module. For example, let us consider the following sample file.

 $ cat sample1
module A
import B
import C

module B
import C

module C
import D

In this example, three modules exist in one file. The following command works nice for this sample.

 $ rexdep --pattern 'import (\S+)' --module 'module (\S+)' sample1
A B
A C
B C
C D

Let me show another example.

 $ cat sample2
A depends on B, C and D.
B depends on C and D.
C depends on D.
D does not depend on other modules.

Can we extract the relations between them? With rexdep, the answer is yes.

 $ rexdep --pattern 'depends on ([A-Z]+)(?:, ([A-Z]+))?(?:, ([A-Z]+))?(?: and ([A-Z]+))?' --module '^([A-Z]+) depends on ' sample2
A B
A C
A D
B C
B D
C D

This example does not look like a practical use case. However, consider the following Makefile.

 $ cat Makefile
all: clean build

build: deps
	go build

install: deps
	go install

deps:
	go get -d -v .

clean:
	go clean

.PHONY: all build install deps clean

Now rexdep can extract the target dependency relation from this Makefile.

 $ rexdep --pattern '^[^.][^:]+: +(\S+)(?: +(\S+))?(?: +(\S+))?' --module '^([^.][^:]+):' Makefile
all build
all clean
build deps
install deps

The rexdep command enables the user to specify a regular expression and still seems to be useful for pretty complicated pattern like the above example.

Extraction range: --start, --end

The rexdep command checks the regular expression of --pattern against each line of files. However, it is sometimes difficult to extract from multiline syntax. Here's an example.

 $ cat sample.go
package main

// "foo"
import (
	"fmt"
	"os"

	"github.com/urfave/cli"
)

func main() {
	fmt.Printf("Hello")
	// ...

The rexdep enables you to specify the range to extract from.

 $ rexdep --pattern '"(\S+)"' --module '^package +(\S+)' --start '^import +\($' --end '^\)$' sample.go
main fmt
main github.com/urfave/cli
main os

When the argument of --start is specified, rexdep finds the line which matches to the regular expression. After it hits the starting line, it turns the internal enabled flag on and starts the extraction with the regular expression of --patern. Then it finds the ending line, it turns the enabled flag off and stops the extraction procedure.

Both starting and ending lines are inclusive. For example, see the following example.

object Foo extends Bar with Baz with Qux {
  // random comment: X extends Y
  // random comment: Y with Z
}

object Qux
  extends Quux
     with Quuy
     with Quuz {
  // with Qyyy
}

Firstly, we try with only --pattern and --module to extract the inheritance relation.

 $ rexdep --pattern '(?:(?:extends|with) +([[:alnum:]]+))(?: +(?:extends|with) +([[:alnum:]]+))?(?: +(?:extends|with) +([[:alnum:]]+))?' --module '^object +(\S+)' sample.scala
Foo Bar
Foo Baz
Foo Qux
Foo Y
Foo Z
Qux Quux
Qux Quuy
Qux Quuz
Qux Qyyy

This result is a failure; it keeps the extraction procedure inside bodies of the objects. For this example, --start and --end work well.

 $ rexdep --pattern '(?:(?:extends|with) +([[:alnum:]]+))(?: +(?:extends|with) +([[:alnum:]]+))?(?: +(?:extends|with) +([[:alnum:]]+))?' --module '^object +(\S+)' --start '^object' --end '{' sample.scala
Foo Bar
Foo Baz
Foo Qux
Qux Quux
Qux Quuy
Qux Quuz

The rexdep command stops the extraction when it finds the line matches against the regular expression of --end.

Output: --format, --output

We can change the output format with --format option. The rexdep command outputs the module names each line with a space by default. In order to see the dependency graph, which is a very common case, you can specify --format dot and pipe the output to the dot command. You can also use --format json and pipe to the jq command.

The rexdep uses the standard output to print the result. When you specify a filename to the --output option, the rexdep command outputs the result to the specified file.

Examples

Git

 $ git clone --depth 1 https://github.com/git/git
 $ rexdep --pattern '^\s*#include\s*[<"](\S+)[>"]' --format dot ./git/*.h | dot -Tpng -o git.png

git The code base of Git is large and the above example checks only header files. The above image (click to see the full image) reveals the relationship between the header files.

Vim

 $ git clone --depth 1 https://github.com/vim/vim
 $ rexdep --pattern '^\s*#include\s*[<"](\S+)[>"]' --format dot ./vim/src/*.{c,h} | dot -Tpng -o vim.png

vim We notice that the structure is flat and many files include vim.h.

consul

 $ git clone --depth 1 https://github.com/hashicorp/consul
 $ rexdep --pattern '"github.com/(?:hashicorp/consul/(?:\S+/)*)?(\S+)"' --module '^package +(\S+)' --start '^import +["(]' --end '^\)$|^import +"' --format dot $(find ./consul/ -name '*.go' | grep -v '_test') | dot -Tpng -o consul.png

consul It is difficult to extract dependency relation between files from Go source codes. We can use functions from the other files at the same directory without writing import. Instead, we can extract dependency relation between packages. The rexdep command extract the imported packages between the lines matched by the start and end arguments.

pandoc

 $ git clone --depth 1 https://github.com/jgm/pandoc
 $ rexdep --pattern '^\s*import +(?:qualified +)?([[:alnum:].]+Pandoc[[:alnum:].]*)' --module '^module +([[:alnum:].]+Pandoc[[:alnum:].]*)' --format dot --recursive ./pandoc/src/ | dot -Tpng -o pandoc.png

pandoc We can flexibly limit the modules by specific words.

lens

 $ git clone --depth 1 https://github.com/ekmett/lens
 $ rexdep --pattern '^\s*import +(?:qualified +)?(\S+Lens\S*)' --module '^module +(\S+Lens\S*)' --format dot --recursive ./lens/src/ | dot -Tpng -o lens.png

lens It is very fun to see the dependency graph of cool libraries like lens, isn't it?

Bug Tracker

Report bug at Issues・itchyny/rexdep - GitHub.

Author

itchyny (https://github.com/itchyny)

License

This software is released under the MIT License, see LICENSE.

More Repositories

1

lightline.vim

A light and configurable statusline/tabline plugin for Vim
Vim Script
6,540
star
2

gojq

Pure Go implementation of jq
Go
3,016
star
3

calendar.vim

A calendar application for Vim
Vim Script
1,904
star
4

bed

Binary editor written in Go
Go
1,134
star
5

mmv

rename multiple files with editor
Go
735
star
6

vim-cursorword

Underlines the word under the cursor
Vim Script
581
star
7

sjsp

Simple JavaScript Profiler
Haskell
234
star
8

vim-gitbranch

Provides the branch name of the current git repository
Vim Script
199
star
9

timefmt-go

Efficient time formatting library (strftime, strptime) for Golang
Go
177
star
10

fillin

fill-in your command and execute
Go
142
star
11

landscape.vim

A colorscheme for Vim
Vim Script
137
star
12

maze

A maze command written in Go
Go
115
star
13

base58-go

Base58 encoding/decoding package and command written in Go
Go
106
star
14

vim-haskell-indent

If the plugin does not work for some syntax, feel free to report to the issue tracker!
Vim Script
105
star
15

thumbnail.vim

A thumbnail-style buffer selector for Vim
Vim Script
101
star
16

volume-go

Cross-platform audio volume control library for Go
Go
72
star
17

rassemble-go

Go implementation of Regexp::Assemble
Go
69
star
18

vim-qfedit

Edit the quickfix/location list freely
Vim Script
65
star
19

miv

Vim plugin manager written in Haskell
Haskell
65
star
20

json2yaml

An efficient JSON to YAML converter written in Go language
Go
64
star
21

vim-parenmatch

An efficient alternative to the standard matchparen plugin
Vim Script
59
star
22

dictionary.vim

Dictionary.app interface for Vim
Vim Script
58
star
23

screensaver.vim

Screensavers for Vim
Vim Script
53
star
24

gojo

Yet another Go implementation of jo
Go
49
star
25

fastinvsqrt

Fast inverse square root in programming languages
Makefile
49
star
26

github-migrator

GitHub repository migrator
Go
46
star
27

vim-highlighturl

URL highlight everywhere
Vim Script
45
star
28

dotfiles

my dotfiles
Vim Script
37
star
29

llvm-brainfuck

Brainfuck compiler based on LLVM API
C++
35
star
30

mkrg

Mackerel graph viewer in terminal
Go
34
star
31

qhs

SQL queries on CSV and TSV files
Haskell
33
star
32

lightline-powerful

Powerful settings for lightline.vim
Vim Script
26
star
33

procout

procout peeks write(2) of another process using ptrace(2), written in Rust
Rust
26
star
34

s3-cache-action

GitHub Action to save cache files and restore them from Amazon S3
TypeScript
22
star
35

event-go

Simple synchronous event pub-sub package for Golang
Go
20
star
36

github-better-header

Brings back a better GitHub header
HTML
20
star
37

cam

unix command cam: view images inside terminal
C
20
star
38

vim-winfix

Fix the focus and the size of windows in Vim
Vim Script
18
star
39

setup

DO NOT USE THIS
Shell
15
star
40

pihex-rs

Arbitrary place hexadecimal digits viewer of pi written in Rust
Rust
15
star
41

vim-gof

Vim Script
13
star
42

minivm

C
13
star
43

vim-external

Switch to external applications from Vim
Vim Script
11
star
44

mackerel-plugin-rs

Mackerel plugin helper library for Rust
Rust
11
star
45

jsparser

A JavaScript parser in JavaScript generated by Jison
CoffeeScript
11
star
46

ChromePlayer

A music player for local files, working on Google Chrome
JavaScript
11
star
47

astgen-go

interface{} => ast.Node
Go
9
star
48

screensaver.c

A clock screensaver in terminal
C
9
star
49

uptime-rs

Multi-platform uptime library for Rust
Rust
9
star
50

golang-simple-server-sample

A simple server sample in Go
Go
8
star
51

vim-pdf

pdf filetype plugin for Vim
Vim Script
8
star
52

shell-function-and

shell function: and
8
star
53

vim-grep

The only grep in Vim that I need...
Vim Script
8
star
54

zshhist-go

zsh histfile utility for Go
Go
7
star
55

brainfuck

brainfuck
Brainfuck
7
star
56

bin

My utility executables.
Shell
7
star
57

git-branch-name

Optimally fast branch name command for Git.
C
7
star
58

homebrew-tap

Homebrew formulae
Ruby
6
star
59

mackerel-client-rs

An API client library for Mackerel written in Rust (still in the developing stage; host APIs are not implemented yet)
Rust
6
star
60

minivm-go

Golang implementation of a stack-machine based programming language interpreter
Go
6
star
61

maze-c

unix command maze: generating a maze
C
6
star
62

vim-haskell-sort-import

Sort import statements in Haskell codes
Haskell
6
star
63

mackerel-plugin-battery

Battery plugin for Mackerel
Go
5
star
64

vim-closebuffer

Close buffers in Vim
Vim Script
5
star
65

vim-extracmd

Define extra commands.
Vim Script
5
star
66

unite-preview

A preview plugin for vimfiler, unite
Vim Script
4
star
67

mackerel-client-hs

Mackerel API client in Haskell
Haskell
4
star
68

2bf

2bf - generates a Brainfuck code
C
4
star
69

vim-term

Vim Script
3
star
70

maketen-go

Create 10 from numbers!
Go
3
star
71

vim-cmdline-ranges

Quickly start/edit cmdline-ranges in Vim
Vim Script
3
star
72

Filter.js

A sample of image processing with JavaScript and canvas
JavaScript
3
star
73

vim-quickrun-lightline-hooks

Vim Script
3
star
74

vim-autoft

Set filetype automatically in Vim
Vim Script
3
star
75

itchyny

itchyny's profile page
3
star
76

tie

Go
3
star
77

fractal

fractal figures in gnuplot
3
star
78

procalive

procalive keeps your process alive
Rust
3
star
79

zsh-auto-fillin

Automatic fillin https://github.com/itchyny/fillin
Shell
3
star
80

c2bf.hs

Convert C to Brainfuck (not working, deprecated product)
Haskell
2
star
81

homebrew-rexdep

Deprecated in favor of https://github.com/itchyny/homebrew-tap
2
star
82

vim-cmdline-escape

Escape special characters on cmdline
Vim Script
2
star
83

vim-extra-snippets

My own snippets
Vim Snippet
2
star
84

bf

bf - executes a Brainfuck code
C
2
star
85

setupfiles-go

Create files and directories easily for tests in Go
Go
2
star
86

syscall-study

Go
2
star
87

vim-histexclude

Exclude by patterns from the histories.
Vim Script
2
star
88

vim-spellbad-pattern

Register regexps to SpellBad
Vim Script
2
star
89

mackerel-plugin-dice-sh

Dice plugin for Mackerel
Shell
2
star
90

unite-auto-open

starting or opening files action for unite
Vim Script
2
star
91

mtimedir

Go
2
star
92

sbt-compile-warn

Aggregate sbt compile warnings
Go
2
star
93

codeforces

Codeforces in Haskell
Haskell
2
star
94

browsershell

A shell in your browser
JavaScript
2
star
95

autolatex

A shell script to compile a LaTeX file
Shell
2
star
96

formulate

Homebrew formula managing script
Shell
2
star
97

mmpp

Mackerel metric pretty printer
Rust
1
star
98

atcoder

AtCoder in Rust
Rust
1
star
99

mackerel-plugin-dice-rs

Dice plugin example using mackerel-plugin-rs
Rust
1
star
100

unite-changetime

An action for unite/vimfiler, changing modified time of a file
Vim Script
1
star