#!/usr/bin/ruby
# -*- coding: utf-8 -*-
#
# Makefile から Makefile.am を生成するスクリプト
# Satofumi KAMIMURA
# $Id: amgen.rb 1810 2010-04-29 21:52:07Z satofumi $
# 基本的に、Makefile.am 内の <program>_SOURCES に対する追加のみを行う
#
# \todo symlink が既に存在してもエラーにならないようにする

require 'fileutils.rb'
require 'kconv'


# Makefile.am 中の _SOURCES の項目を管理する
$sources_list = []

# 指定ファイルをインクルードパスから探しだし、cpp 共々、追加する
def searchAndRegister(add_file, include_pathes)

  # 指定ファイルが include_pathes に存在するか、を探す
  file_rpath = nil
  include_pathes.each { |rpath|
    # ファイルの存在確認
    if File.exist?(rpath + add_file)
      file_rpath = rpath
      break
    end
  }
  if file_rpath == nil
    return false
  end
  add_file_path = file_rpath + add_file
  add_files = [ add_file_path ]

  # 同じ場所に cpp が存在するか、確認する
  add_also_cpp = nil
  if File.extname(add_file) != '.cpp'
    check_file = file_rpath + File.basename(add_file, '.*') + '.cpp'
    if File.exist?(check_file)
      add_files.push(check_file)
    end
  end

  # No such file or directory なはずなので、シンボリックリンクを作成する
  add_files.each { |file|
    File.symlink(file, File.basename(file))
  }

  # Makefile.am を追記モードで開き、追記を行う
  File.open('Makefile.am', 'a') { |io|
    add_files.each { |file|
      add_basename = File.basename(file)
      if add_files.assoc(add_basename) == nil
        io.write(' ' + add_basename)
        $sources_list.push(add_basename)
      end
    }
  }

  return true
end

# ----- メイン処理 -----
# 引数がなければ、使い方を表示して終了
if ARGV.size <= 0
  print "usage:\n\t" + __FILE__ + " <Makefile>\n\n"
  exit
end

# パース情報の初期化
makefile = ARGV[0]
makefile_rpath = File.dirname(makefile) + '/' # Makefile への相対パス
macros = {}
include_pathes = []

# 引数渡しのファイルを Makefile と見なして読み出す
File.open(makefile) { |io|
  while line = io.gets

    # INCLUDE 指定のパスを抜き出す
    if line =~ /^INCLUDES = (.+)/
      includes = Regexp.last_match[1].split(' ')
      includes.each { |include|
        include_pathes.push(makefile_rpath + include[2, 128] + '/')
      }
      # Makfile のある場所も追加しておく
      include_pathes.push(makefile_rpath)
    end

    # REQUIRE_LIBS 指定のパスも、探索パスに含める
    if line =~ /^REQUIRE_LIBS = (.+)/
      require_libs = Regexp.last_match[1].split(' ')
      require_libs.each { |lib|
        include_pathes.push(makefile_rpath + File.dirname(lib) + '/')
      }
    end

    # マクロ定義を記録する
    if line =~ /^([A-Z_]+) = (.+)/
      name = Regexp.last_match[1]
      value = Regexp.last_match[2]
      macros["$(" + name + ")"] = value
    end
  end
}


# include_pathes 中のマクロを展開する
macros.each { |key, value|
  include_pathes.each { |path|
    path.gsub!(key, value)
  }
}


# Makefile.am のバックアップを作っておく
FileUtils.copy('Makefile.am', 'Makefile.am.bak')

# Makefile.am から、SOURCES に登録済みのファイルを抜き出す
File.open('Makefile.am') { |io|
  while line = io.gets
    if line =~ /_SOURCES = (.+)/
      $sources_list = Regexp.last_match[1].split(' ')
    end
  end
}

# make を実行し、見つからないファイルを追加していく
begin
  `make 2> errors_output.txt`
  make_status = $?.to_i / 256

  changed = false
  File.open('errors_output.txt') { |io|
    while line = io.gets
      jp_error_message = 'そのようなファイルやディレクトリはありません'.toeuc
      if (line =~ / (.+): No such file or directory/) ||
          (line =~ /error: (.+): #{jp_error_message}/)

        add_file = Regexp.last_match[1]

        # ファイルの探索と追加
        print 'Adding ' + add_file + ' ... '
        ret = searchAndRegister(add_file, include_pathes)
        if ret
          print "O.K.\n"
        else
          # エラーになったら、強制終了させる
          print "Fail !\n"

          # 変更がなかったことにしてループを抜け、メッセージを表示させる
          changed = false
          break;
        end
        changed = true
      end
    end
  }
  if changed == false
    # エラーメッセージを表示させるための処理
    # !!! ひどいな...
    # !!! errors_output.txt のサイズによっては、正常終了とみなすべきかと
    if make_status != 0
      print "Fail !!\n"
      `make`
    end
  end
end while (make_status != 0) && changed

# 生成したファイルを削除して、おしまい
FileUtils.remove('errors_output.txt')
