#! /usr/bin/env ruby
#
# AdLint project generator.
#
# Author::    Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>
# Copyright:: Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
# License::   GPLv3+: GNU General Public License version 3 or later
#
# Owner::     Yutaka Yanoh <mailto:yanoh@users.sourceforge.net>

#--
#     ___    ____  __    ___   _________
#    /   |  / _  |/ /   / / | / /__  __/           Source Code Static Analyzer
#   / /| | / / / / /   / /  |/ /  / /                   AdLint - Advanced Lint
#  / __  |/ /_/ / /___/ / /|  /  / /
# /_/  |_|_____/_____/_/_/ |_/  /_/   Copyright (C) 2010-2012, OGIS-RI Co.,Ltd.
#
# This file is part of AdLint.
#
# AdLint 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 3 of the License, or (at your option) any later
# version.
#
# AdLint 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# AdLint.  If not, see <http://www.gnu.org/licenses/>.
#
#++

require "pathname"

$bindir = Pathname.new(__FILE__).realpath.dirname
$prefix = Pathname.new("..").expand_path($bindir)
$libdir = Pathname.new("lib").expand_path($prefix)
$etcdir = Pathname.new("etc").expand_path($prefix)

$:.unshift($libdir.to_s)

require "adlint"

ADLINT_VERSION = "AdLint project generator #{AdLint::VERSION}"

USAGE = <<EOS
Usage: adlintize [options] [project-name]
Options:
  -t FILE, --traits FILE         Write traits to FILE
                                 If omitted, `adlint_traits.yml' will be used
  -p FILE, --pinit-hdr FILE      Write project initial header to FILE
                                 If omitted, `adlint_pinit.h' will be used
  -c FILE, --cinit-hdr FILE      Write compiler initial header to FILE
                                 If omitted, `adlint_cinit.h' will be used
  -l FILE, --list-file FILE      Write list file to FILE
                                 If omitted, `adlint_files.txt' will be used
  -m FILE, --makefile FILE       Write analysis procedure GNUmakefile to FILE
                                 If omitted, `GNUmakefile' will be used
  -s FILE, --sh-script FILE      Write analysis procedure sh script to FILE
                                 If omitted, `adlint_all.sh' will be used
  -b FILE, --bat-file FILE       Write analysis procedure bat file to FILE
                                 If omitted, `adlint_all.bat' will be used
  -o DIR, --output-dir DIR       Set output directory
                                 If omitted, `.' will be used
  -e ENV, --environment ENV      Assume ENV as target build environment
  -L, --list-environment         List all preset build environments
  -f, --force                    Force to overwrite existing files
      --version                  Display version information
      --copyright                Display copyright information
      --prefix                   Display prefix directory of AdLint
  -h, --help                     Display this message
EOS

def print_environments
  Dir.entries(Pathname.new("conf.d").expand_path($etcdir)).each do |dirent|
    case dirent
    when ".", "..", "fallback", "noarch"
      next
    end

    Dir.glob("#{$etcdir}/conf.d/#{dirent}/traits-*.erb").each do |traits_fpath|
      basename = File.basename(traits_fpath, ".erb")
      puts "#{dirent}-#{basename.sub(/\Atraits-/, "")}"
    end
  end
end

require "getoptlong"

parser = GetoptLong.new(["--traits", "-t", GetoptLong::REQUIRED_ARGUMENT],
                        ["--pinit-hdr", "-p", GetoptLong::REQUIRED_ARGUMENT],
                        ["--cinit-hdr", "-c", GetoptLong::REQUIRED_ARGUMENT],
                        ["--list-file", "-l", GetoptLong::REQUIRED_ARGUMENT],
                        ["--makefile", "-m", GetoptLong::REQUIRED_ARGUMENT],
                        ["--sh-script", "-s", GetoptLong::REQUIRED_ARGUMENT],
                        ["--bat-file", "-b", GetoptLong::REQUIRED_ARGUMENT],
                        ["--output-dir", "-o", GetoptLong::REQUIRED_ARGUMENT],
                        ["--environment", "-e", GetoptLong::REQUIRED_ARGUMENT],
                        ["--list-environment", "-L", GetoptLong::NO_ARGUMENT],
                        ["--force", "-f", GetoptLong::NO_ARGUMENT],
                        ["--version", GetoptLong::NO_ARGUMENT],
                        ["--copyright", GetoptLong::NO_ARGUMENT],
                        ["--prefix", GetoptLong::NO_ARGUMENT],
                        ["--help", "-h", GetoptLong::NO_ARGUMENT])

begin
  traits_fpath = Pathname.new("adlint_traits.yml")
  pinit_fpath = Pathname.new("adlint_pinit.h")
  cinit_fpath = Pathname.new("adlint_cinit.h")
  list_fpath = Pathname.new("adlint_files.txt")
  make_fpath = Pathname.new("GNUmakefile")
  sh_fpath = Pathname.new("adlint_all.sh")
  bat_fpath = Pathname.new("adlint_all.bat")
  output_dpath = Pathname.new(".")
  environment = nil
  force_overwrite = false

  parser.each do |option, value|
    case option
    when "--traits"
      traits_fpath = Pathname.new(value)
    when "--pinit-hdr"
      pinit_fpath = Pathname.new(value)
    when "--cinit-hdr"
      cinit_fpath = Pathname.new(value)
    when "--list-file"
      list_fpath = Pathname.new(value)
    when "--makefile"
      make_fpath = Pathname.new(value)
    when "--sh-script"
      sh_fpath = Pathname.new(value)
    when "--bat-file"
      bat_fpath = Pathname.new(value)
    when "--output-dir"
      output_dpath = Pathname.new(value)
    when "--environment"
      environment = value
    when "--list-environment"
      print_environments
      exit 0
    when "--force"
      force_overwrite = true
    when "--version"
      puts ADLINT_VERSION, AdLint::AUTHOR
      exit 0
    when "--copyright"
      puts AdLint::COPYRIGHT
      exit 0
    when "--prefix"
      puts $prefix
      exit 0
    when "--help"
      puts USAGE
      exit 0
    end
  end
rescue
  $stderr.puts USAGE
  exit 1
end

project_name = ARGV.empty? ? Pathname.getwd.each_filename.to_a.last : ARGV[0]

FileUtils.mkdir_p(output_dpath) if output_dpath && !Dir.exist?(output_dpath)

traits_fpath = traits_fpath.expand_path(output_dpath)
pinit_fpath = pinit_fpath.expand_path(output_dpath)
cinit_fpath = cinit_fpath.expand_path(output_dpath)
list_fpath = list_fpath.expand_path(output_dpath)
make_fpath = make_fpath.expand_path(output_dpath)
sh_fpath = sh_fpath.expand_path(output_dpath)
bat_fpath = bat_fpath.expand_path(output_dpath)

if environment
  arch, os, compiler = environment.split("-")

  cinit_templ_fpath =
    Pathname.new("#{$etcdir}/conf.d/#{arch}-#{os}/cinit-#{compiler}.erb")
  unless File.readable?(cinit_templ_fpath)
    $stderr.puts "#{File.basename(__FILE__)}: no such preset build environment"
    exit 1
  end

  traits_templ_fpath =
    Pathname.new("#{$etcdir}/conf.d/#{arch}-#{os}/traits-#{compiler}.erb")
  unless File.readable?(traits_templ_fpath)
    $stderr.puts "#{File.basename(__FILE__)}: no such preset build environment"
    exit 1
  end
else
  cinit_templ_fpath = Pathname.new("#{$etcdir}/conf.d/fallback/cinit.erb")
  traits_templ_fpath = Pathname.new("#{$etcdir}/conf.d/fallback/traits.erb")
end

pinit_templ_fpath = Pathname.new("#{$etcdir}/conf.d/noarch/pinit.erb")
make_templ_fpath = Pathname.new("#{$etcdir}/conf.d/noarch/GNUmakefile.erb")
sh_templ_fpath = Pathname.new("#{$etcdir}/conf.d/noarch/adlint_all_sh.erb")
bat_templ_fpath = Pathname.new("#{$etcdir}/conf.d/noarch/adlint_all_bat.erb")

require "erb"

if ENV["LANG"] =~ /ja_JP/
  lang = "ja_JP"
else
  case Encoding.default_external
  when Encoding::Windows_31J
    lang = "ja_JP"
  else
    lang = "en_US"
  end
end

if !File.exist?(traits_fpath) || force_overwrite
  File.open(traits_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    io.puts(ERB.new(File.read(traits_templ_fpath)).result(binding))
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{traits_fpath} already exists"
  exit 1
end

if !File.exist?(pinit_fpath) || force_overwrite
  File.open(pinit_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    io.puts(ERB.new(File.read(pinit_templ_fpath)).result(binding))
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{pinit_fpath} already exists"
  exit 1
end

if !File.exist?(cinit_fpath) || force_overwrite
  File.open(cinit_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    io.puts(ERB.new(File.read(cinit_templ_fpath)).result(binding))
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{cinit_fpath} already exists"
  exit 1
end

vpath = Pathname.pwd.relative_path_from(Pathname.new(output_dpath).realpath)

if !File.exist?(list_fpath) || force_overwrite
  File.open(list_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    Dir.glob("**/*.c").each { |str| io.puts(vpath.join(Pathname.new(str))) }
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{list_fpath} already exists"
  exit 1
end

if !File.exist?(make_fpath) || force_overwrite
  File.open(make_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    io.puts(ERB.new(File.read(make_templ_fpath)).result(binding))
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{make_fpath} already exists"
  exit 1
end

if !File.exist?(sh_fpath) || force_overwrite
  File.open(sh_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    io.puts(ERB.new(File.read(sh_templ_fpath)).result(binding))
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{sh_fpath} already exists"
  exit 1
end

if !File.exist?(bat_fpath) || force_overwrite
  File.open(bat_fpath, "w") do |io|
    io.set_encoding(Encoding::UTF_8)
    io.puts(ERB.new(File.read(bat_templ_fpath)).result(binding))
  end
else
  $stderr.puts "#{File.basename(__FILE__)}: #{bat_fpath} already exists"
  exit 1
end

exit 0
