Skip to content
maio 6, 2008 / cassiomarques

Contando as linhas e arquivos de seu projeto com Ruby

Muito provavelmente você um dia já precisou saber certos informações sobre seus projetos, como o número de linhas de código escritas, quantas linhas de comentário existem, quantas linhas em branco, total de arquivos etc. Existem algumas ferramentas por ai que fazem isso pra você, sendo que algumas são até pagas.

Escrevi um simples script em Ruby mais como prova de conceito, mas que durante alguns testes que eu realizei aqui se mostrou bastante útil. Ele recebe o caminho para a pasta de um projeto e lista o total de arquivos, agrupado por extensão (.rb, .java, .js, etc). Lista também os totais de linhas de código, linhas em branco e linhas com comentários.

Você pode passar o caminho para um projeto escrito em praticamente qualquer linguagem. Caso sua linguagem de escolha ainda não seja suportada pelo script, basta adicioná-la, é simples.

Já testei o script com arquivos fonte C, C++, Java, Javascript, CSS, JSP, Ruby e vários outros.

O script também lista o total de arquivos desconhecidos (cujas extensões ainda não foram configuradas) e quais são essas extensões.

Reconhece comentários em diversos formatos, como:

  • Comentários HTML (iniciados por — )
  • Comentários estilo C (iniciados por // )
  • Comentários estilo C++ (/* … */, com uma ou mais linhas)
  • Comentários estilo XML (, com uma ou mais linhas)
  • Comentários estilo Ruby/Shell Script (iniciados com #)

Observação importante: Testei apenas no Linux, mas acredito que funcione ok no Windows.

Observaçào importante 2: O script não lista diretórios e arquivos ocultos do Linux, cujo nome inicia com .(ponto). Fiz isso para que ele não saisse varrendo os diretórios ocultos do Subversion ou os arquivos com as configurações de projeto do Eclipse.

Um exemplo de saída da execução do script:


$ ruby project_statistics.rb exemplo_projeto_rails/

===> Estatisticas de :
*Arquivos Javascript (.js) ==> 5
(Codigo:4488 | Comentarios:195 | Em branco:711 )

*Arquivos RAKE (.rake) ==> 2
(Codigo:92 | Comentarios:0 | Em branco:9 )

*Paginas HTML (.html) ==> 3
(Codigo:297 | Comentarios:2 | Em branco:38 )

*Arquivos Cascading Style Sheets (.css) ==> 1
(Codigo:151 | Comentarios:10 | Em branco:28 )

*Arquivos de configuracao YAML (.yml) ==> 4
(Codigo:33 | Comentarios:0 | Em branco:4 )

*Templates RHTML (.rhtml) ==> 6
(Codigo:207 | Comentarios:15 | Em branco:10 )

*Arquivos Ruby (.rb) ==> 29
(Codigo:276 | Comentarios:118 | Em branco:82 )

84 arquivos no total, 34 arquivos desconhecidos.
Extensoes desconhecidas encontradas =>
.ico
.jpg
.gif
.log
.cgi
.fcgi
.txt
.htaccess
.png

O script está no final do post. Ficou meio longo (cerca de 170 linhas). Quem quiser fazer sugestões ou alterar o script, sinta-se à vontade, ele é todo nosso :-)

Para executar, basta fazer:
$ ruby project_statistics caminho_para_o_seu_projeto

O caminho para o arquivo pode ser um caminho absoluto ou relativo.

Espero que seja útil para alguém!

# Realiza a contagem de arquivos e linhas de um projeto, fornecendo os totais
# por extensao (.rb, .jsp, .java, etc)
#
# Autor: Cassio Marques (cassiommc@gmail.com)
#
# Este script eh LIVRE. Altere o que quiser, use onde quiser, distribua
# para quem quiser.
# Ninguem ira lhe processar.
# Porem, nao me responsabilizo por nada que aconteca ao seu computador
# ou aos seus arquivos caso voce execute este script.
# Use por sua conta e risco.
require 'find'

file_types = {
  ".jsp"          => "Paginas JSP",
  ".html"         => "Paginas HTML",
  ".xml"          => "Arquivos XML",
  ".java"         => "Arquivos Java",
  ".js"           => "Arquivos Javascript",
  ".css"          => "Arquivos Cascading Style Sheets",
  ".rb"           => "Arquivos Ruby",
  ".properties"   => "Arquivos de propriedades",
  ".sql"          => "Arquivos para banco de dados",
  ".pgsql"        => "Arquivos com stored procedures do PostgreSQL",
  ".cpp"          => "Arquivos fonte em C++",
  ".c"            => "Arquivos fonte em C",
  ".h"            => "Arquivos header C/C++",
  ".jar"          => "Arquivos de biblioteca Java",
  ".py"           => "Arquivos Phyton",
  ".pl"           => "Arquivos Perl",
  ".sh"           => "Arquivos Shell Script",
  ".php"          => "Arquivos PHP",
  ".swf"          => "Arquivos Flash",
  ".rake"         => "Arquivos RAKE",
  ".yml"          => "Arquivos de configuracao YAML",
  ".erb"          => "Templates HTML ERB",
  ".rhtml"        => "Templates RHTML"
}

file_quantities = {}
@loc = {}
@blank = {}
@comments = {}
@unknown_files = 0
@unknown_extensions = {}
@total_files = 0

file_types.keys.each do |k|
  file_quantities[k] = 0
  @loc[k] = 0
  @comments[k] = 0
  @blank[k] = 0
end

def increment_unknown_extensions(ext)
  if @unknown_extensions.include? ext then
    @unknown_extensions[ext] += 1
  else
    @unknown_extensions[ext] = 0
  end
end

def should_list?(f)
  f !~ /doc[s]?\/|documentacao|svn|classes|logs|work/i
end

def increment(hash, ext)
  hash[ext] += 1 unless !hash.include? ext
end

def increment_comments(ext)
  increment(@comments, ext)
end

def increment_blanks(ext)
  increment(@blank, ext)
end

def increment_code(ext)
  increment(@loc, ext)
end

def count_lines(file_name)
  file_name =~ /(.+)(\..+)/i
  ext = $2
  @multiline_comment = false
  @cpp_style = false
  @xml_style = false
  File.new(file_name).each do |line|
    line.strip!
    unless line != ""
      increment_blanks(ext)
      next
    end
    # procurando final do comentario com varias linhas
    if @multiline_comment then
      if (line =~ /\*\/$/ && @cpp_style) || (line =~ /-->$/ && @xml_style) then
        increment_comments(ext)
        @multiline_comment = false
        next
      else
        increment_comments(ext)
        next
      end
    end
    # padroes para comentarios com uma unica linha
    if line =~ /^\/\// ||
       line =~ /^\/\*.+\*\// ||
       line =~ /<!--.+-->/ ||
       (file_name =~ /\.rb|sh|properties$/i && line =~ /^#/) ||
       (file_name =~ /\.pgsql|sql$/i && line=~ /^--/) then
      increment_comments(ext)
      next
    end
    if line =~ /^\/\*/ && !@multiline_comment then
      @multiline_comment = true     # encontrado comentario com varias linhas,
                                    # no formato /*...(linhas)...*/
      @cpp_style = true
      increment_comments(ext)
      next
    end
    if line =~ /^<!--/ && !@multiline_comment then
      @multiline_comment = true     # encontrado comentario com varias linhas,
                                    # no formato <!-- (linhas)... -->
      @xml_style = true
      increment_comments(ext)
    end
    increment_code(ext)
  end
end

if ARGV.length != 1
	puts "Modo de usar: ruby #{$0} caminho_do_projeto"
	Kernel.exit(0)
end

cur_dir = Dir.pwd
Dir.chdir(ARGV[0])

Find.find( './' ) do |f|
  Find.prune if !should_list?(f)
  f =~ /(.+)(\..+)/i
  ext = $2
  #puts "Processando arquivo #{f}..."
  file_quantities[ext] += 1 unless !file_quantities.include? ext
  if !File.directory?(f) && f !~ /~$/ then
     @total_files += 1 if !File.directory?(f)
     if !file_types.include? ext
      @unknown_files += 1
      increment_unknown_extensions(ext)
     end
  end 

  if !File.directory?(f) && f !~ /\.(jar|swf)$/
    count_lines(f)
  end
end

Dir.chdir(cur_dir)

puts "\n===> Estatisticas de <#{ARGV&#91;0&#93;}>:"
file_quantities.each do |k,v|
  puts "*#{file_types[k]} (#{k}) ==> #{v}\n(Codigo:#{@loc[k]} | Comentarios:#{@comments[k]} | Em branco:#{@blank[k]})\n\n" unless file_quantities[k] == 0
end
puts "#{@total_files} arquivos no total, #{@unknown_files} arquivos desconhecidos."
puts "Extensoes desconhecidas encontradas => \n#{@unknown_extensions.keys.join("\n")}" unless @unknown_extensions.empty?
puts "\n"

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: