#!/usr/bin/python

#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#

#                           #
# Build Boss (buildboss)    #
#                           #

#
# By Aleksander Demko <ademko@shaw.ca>
# Copyright 2003-2004
# Feb 7, 2004
# Version 1.1.2
#

# by Aleksander Demko       #

# should work with python 1.x too, I think! #

import os
import os.path
import sys
import re
import string
from sys import stdout

# source file record
class SourceFile:
  def __init__(self, filename, cppname):
    self.name = filename
    self.cname = cppname
    self.libs = []
    self.targets = []

# a target
class TargetFile:
  def __init__(self):
    self.islib = 0
    self.objs = []
    self.libs = {}

# works on a file
# refs globals: Files RElibs REtargets
def do_file(filename):
  (root, ext) = os.path.splitext(filename)
  if not (ext == ".c" or ext == ".cpp"):
    return 0
  rec = SourceFile(root + ".o", filename)
  f = open(filename)
# operate on each line, looking for //BB lines
  line = f.readline()
  while line != "":
    mato = RElibs.match(line)
    if mato != None:
      rec.libs = rec.libs + string.split(mato.group(1))
    mato = REtargets.match(line)
    if mato != None:
      rec.targets = rec.targets + string.split(mato.group(1))
    line = f.readline()
  f.close()
  Files.append(rec)
  return 1

# works on a directory
def do_dir(dirname):
  count = 0
  # skips .. and . automatically
  files = os.listdir(dirname)
  for f in files:
    fullf = dirname + '/' + f
    if os.path.isdir(fullf):
      do_dir(fullf) # recurse on dirs
    else:
      count += do_file(fullf) # process the file
  if count>0:
    stdout.write("Directory: " + dirname + " (" + ("%d"%count) + " files)\n")

# strips the lib/.so off a name
def strip_libname(libname):
  name = libname
  if name[0:3] == "lib":
    name = name[3:]
  if name[-3:] == ".so":
    name = name[0:-3]
  return name

# adds lib and .so to a short name (opposite of strip_libname)
def add_libname(name):
  return "lib" + name + ".so"

# print banner
if len(sys.argv) <= 1:
  print "%s basesourcedir(s)... [-O]" % sys.argv[0]
  sys.exit(0)

# load params
opt = "-g"
srcbase = []
# skip the first entry
for parm in sys.argv[1:]:
  if (parm == "-O"):
    opt = "-O3 -s -DNDEBUG"
  elif os.path.isdir(parm):
    srcbase.append(parm)

# read source files
Files = []
RElibs = re.compile("//BBlibs(.+)$")
REtargets = re.compile("//BBtargets(.+)$")
for dir in srcbase:
  do_dir(dir)

# build the target->file struct
reisLib = re.compile("/?lib[^/]+$")
Targets = {}
for r in Files:
  for tar in r.targets:
    if not Targets.has_key(tar):
      Targets[tar] = TargetFile()  # make a new target entry
      if reisLib.match(tar):    # is a lib?
        Targets[tar].islib = 1
    Targets[tar].objs.append(r.name) # add this object to the target
    for l in r.libs:    # add a the object's libs to the target, if needed
      if not Targets[tar].libs.has_key(l):
        Targets[tar].libs[l] = 1    # using a map/hash as a set, whatever

# output the makefile
makefile = open("Makefile", "w+")

makefile.write("# buildboss generated Makefile\n\n")
makefile.write("BBCPP?=g++\n\n")

# all target
makefile.write("all:")
for k in Targets.keys():
  makefile.write(" ")
  makefile.write(k)
makefile.write("\n\n")

# each target
for k,v in Targets.items():
  makefile.write(k + ":")
# list any pkg-config dependancies that WE"RE building right now
  for pd in v.libs.keys():
    if Targets.has_key(add_libname(pd)):
      makefile.write(" " + add_libname(pd))
# dependancies
  for ob in v.objs:
    makefile.write(" " + ob)
  makefile.write("\n")
# link line
  makefile.write("\t$(BBCPP) -L. " + opt)
  if v.islib:
    makefile.write(" -shared -fPIC")
# object files
  for ob in v.objs:
    makefile.write(" " + ob)
# libs
  for l in v.libs.keys():
    if l[0] == "-":
      makefile.write(" %s" % l)
    else:
      makefile.write(" `pkg-config --libs %s`" % l)
  makefile.write(" -o " + k + "\n\n")

# each object file
for r in Files:
  makefile.write("%s: %s\n" % (r.name, r.cname))
  makefile.write("\t$(BBCPP) -I. " + opt)
  for dir in srcbase:
    makefile.write(" -I" + dir)
  for l in r.libs:
    if l[0] != "-":
      makefile.write(" `pkg-config --cflags %s`" % l)
  makefile.write(" -c " + r.cname + " -o " + r.name + "\n\n")

# make the clean rule
makefile.write("clean:\n")
makefile.write("\trm")
for k in Targets.keys():
  makefile.write(" ")
  makefile.write(k)
for r in Files:
  makefile.write(" ")
  makefile.write(r.name)
makefile.write("\n\n")

makefile.write("\n\n")

makefile.close()

# now, write out a .pc file for each target
for k,v in Targets.items():
  shortname = strip_libname(k)
  pcfile = open(shortname + ".pc", "w+")
  pcfile.write("Name: " + shortname)
  pcfile.write("\nVersion: 1.0\nDescription: buildboss made\nRequires:")
  for l in v.libs.keys():
    pcfile.write(" " + l)
  pcfile.write("\nLibs: -l" + shortname)
  pcfile.write("\nCflags:\n")
  pcfile.close()
  stdout.write("Wrote PC File: " + shortname + ".pc\n")

stdout.write("Wrote Makefile: Makefile\n")

