Wednesday, November 21, 2007

New toy..

Managed to get some nice time off today, read some of the library books, watched Tron, Ghost Busters I, and Spaced Invaders, but most of all got to relax. I've also placed my VIMRC file into a cvs repository on my OpenBSD box, that should make syncing changes with my Desktop/Laptop easier, plus allow me to see what I've changed hehe.


Had an interesting idea and started playing with some Ruby today also.


Normally when I start a new program I make a new directory in ~/code/Lang/src/ and copy a template makefile over from ../../makefiles/ and edit it to my needs. In one of the library books, I found a GNU Makefile that essentially automates building a C/C++ program where by all C/C++ source files (subject to make handling the dependencies) are compiled into corresponding files and get linked together into the binary, nice little thing when you look at it. With a little tinkering it could probably be made semi-language independent but I don't really have time to dig into GNU Make's syntax and features to do that.

I've basically written makefiles in the past that should work with just about any ol'make program. For much the same reason I write shell scripts that should run on most any /bin/sh, because I don't want to edit the freaking thing if I ever meet a Unix like OS it won't run on lool.


The idea I had was a enlazynating program that by knowing a fair bit about a given languages normal build process (such as C, C++, and Java), it could then automate the process of building it without having to generate a makefile, kind of like a 'smart-makefile' of sorts. I've got a alias set in my shell so that gcc is called on the command line with my usual settings. I also tend to include a few more once the things done.


It's probably a stupid idea to do this, a decent BSD or GNU Makefile would likely serve much better (and be worth the learning of each ones extensions to make). But I rather like to be able to just 'have fun' playing with and testing stuff.


Right now, it seems to be able to handle a simple compile/link of files in one directory but needs some work to properly link files in subdir's, lots of other stuff to do. But working on it has been a good way to pass the time.


Currently I classify this as a toy rather then a program for serious usage. But if I ever finish it, I might use it just for the sake of keeping a fairly fast build/test cycle.


If I could, I would've made a machine to manage VHS-Tape/DVD-Disk changing but hey, I'm not an inventor when it comes to to building some thing out of thin air...


Here is what I've done over the past couple hours, it's not very good but it's a starting point for later play.

#!/usr/local/bin/ruby -w
# usage: make [opt] project [dir]
# TODO:
#   rdoc...
#   handle header files and linking properly in C
#   support more then C
#   implement a way to set lang opts/debug/profiler stuff correctly
#   reduce usage of $globals
#   clean up code... looks like sludge

require 'getoptlong'
require 'rdoc/usage'

$lang=nil
$project=''
$startdir=Dir.pwd
$debug=false
$profiler=false
$verbose=false

def main()

  opts = GetoptLong.new(
    [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
    [ '--lang', '-l', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--lang-opts', '-o', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--debug', '-d', GetoptLong::OPTIONAL_ARGUMENT ],
    [ '--prof', '-p', GetoptLong::OPTIONAL_ARGUMENT ],
    [ '--clean', '-c', GetoptLong::NO_ARGUMENT ],
    [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ]
  )

  begin
    opts.each do |opt, arg|
      case opt
        when '--help'
          RDoc::usage()
        when '--lang'
          proc_lang( arg )
        when '--lang-opts'
          puts "#{arg} -> --lang-opts not implemented!"
        when '--debug'
          $debug = true
        when '--prof'
          $profiler = arg
        when '--verbose'
          $verbose=true
      end
    end

    if ARGV[0]
      $project = ARGV[0]
    end
    if ARGV[1]
      $startdir = ARGV[1]
    end
    puts ARGV #debug
    puts "\n\n"
    n=LangC.new()
    n.make()

    unless $lang
      $stderr.puts( 'WARNING: --lang not set!, defaulting to ISO C99' )
    end
 
    # call usage() on *any* error and print the problem
    rescue StandardError 
      puts( $! )
      RDoc::usage()

  end

end

def proc_lang( str )

  case str
    when 'c','C'
      # set up for C
      $lang=str
    when 'c++','cpp','C++','C++','cc','CC','cxx','CXX'
      # set up for C++
    when 'java','Java'
      # set up for Java
    when 'Perl','pl', 'plx'
      # Setup for Perl
    when 'Python','py'
      # Setup for Python
    when 'Ruby','rb'
      # set up for Ruby ;-)
  end
end

class Lang

  def make()
  end

  def each( startdir, &block )
    dp = Dir.open(startdir) do |dir|
      dir.each do |node|
        if node == '.' or node == '..' 
          next
        end
        p = File.expand_path( node, startdir )
        yield( p )
        if File.directory?( p )
          self.each( p, &block )
        end
      end
    end
  end

end # !class Lang


class LangC < Lang

  @@compiler  =   'gcc'
  @@cflags    =   "-Wall -Wpointer-arith -Wcast-qual -Wcast-align " +
                  "-Wconversion -Waggregate-return -Wstrict-prototypes " +
                  "-Wmissing-prototypes -Wmissing-declarations " +
                  "-Wredundant-decls -Winline -Wnested-externs " +
                  "-std=c99 -march=i686 -pipe "
  @@ldfags    =   nil
  @@debug     =   '-g'
  @@prof      =   nil
  @filelist = Array.new()

  # The very bloody boring constructor which serves only to override the
  # defaults for our static class variables.
  #
  def initialize( compiler=false, cflags=false, ldflags=false, 
                  debug=false, prof=false )

    if compiler then @@compiler = compiler end
    if cflags then @@cflags = cflags end
    if ldflags then @@ldfags = ldflags end
    if debug then @@debug = debug end
    if prof then @@prof = prof end
    @@ldflags=nil;@@prof=nil#debug

  end

  # Assemble project
  def make()

    @filelist = Array.new()
    self.compile()
    self.link()

  end

  # Walk the directory tree and compile all .c files into .o
  #
  def compile()

    $stdout.puts '------- COMPILING -------' if $verbose

    self.each( $startdir ) do |file|
      if file =~ /.*\.c$/ 
        p = file.to_s.gsub( File.extname(file),".o" )
        @filelist.push( p )
        # Don't compile unless the source is newer then the object files
        if File.exists?( p )
          next unless timecheck(file,p)
        end
          
        system( "#{@@compiler} #{@@cflags} #{@@debug} #{@@prof} " +
                      "#{file} -c -o #{p}" )
      end
    end
  end

  # Walk the directory tree and link all object files
  def link()

    $stdout.puts '------- LINKING -------' if $verbose

    @filelist.each do |obj|
      system( "#{@@compiler} #{@@cflags} #{@@ldflags} #{@@debug} " +
                    "#{@@prof} #{obj} -o #{$startdir}/#{$project}" )
    end
  end

  # Return true if first filename has a newer time stamp then the second
  #
  def timecheck( f1, f2 )
    File.mtime( f1 ) > File.mtime( f2 ) ? true : false
  end

end

if __FILE__ == $0
  main()
end

No comments:

Post a Comment