# -*- coding: utf-8; mode: ruby -*-
#
# Copyright (C) 2011-2015  Kouhei Sutou <kou@clear-code.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 2.1 as published by the Free Software Foundation.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

require "pathname"
require "open-uri"

base_dir = Pathname.new(__FILE__).dirname

groonga_win32_x86_p = ENV["ARCHITECTURE"] == "x86"
groonga_version = ENV["VERSION"]
groonga_source = Pathname.new(ENV["SOURCE"]).expand_path
debug_build_p = ENV["DEBUG_BUILD"] == "yes"

if groonga_win32_x86_p
  dist_dir = Pathname.new("dist-x86").expand_path
else
  dist_dir = Pathname.new("dist-x64").expand_path
end
license_dir = dist_dir + "share" + "license"
binary_dir = base_dir + dist_dir
include_dir = binary_dir + "include"
lib_dir = binary_dir + "lib"
bin_dir = binary_dir + "bin"

patches_dir = (base_dir + "patches").expand_path
mecab_patches = [
  "mecab-0.98-not-use-locale-on-mingw.diff",
  "mecab-0.98-add-missing-dll-export.diff",
]
nginx_patches = [
  "nginx-1.9-5-fix-build-error.diff",
]
if groonga_win32_x86_p
  host = "i686-w64-mingw32"
else
  host = "x86_64-w64-mingw32"
  mecab_patches << "mecab-0.98-mingw-w64.diff"
end

def download(url, download_dir)
  base_name = url.split("/").last
  absolute_output_path = download_dir + base_name

  unless absolute_output_path.exist?
    mkdir_p(download_dir)
    rake_output_message "Downloading... #{url}"
    open(url) do |downloaded_file|
      absolute_output_path.open("wb") do |output_file|
        output_file.print(downloaded_file.read)
      end
    end
  end

  absolute_output_path
end

namespace :build do
  download_dir = Pathname.new("tmp/download").expand_path

  task :pkg_config do
    ENV["PKG_CONFIG_PATH"] = nil
    ENV["PKG_CONFIG_LIBDIR"] = (lib_dir + "pkgconfig").to_s
  end

  task :flags do
    ENV["CPPFLAGS"] = "-I#{include_dir}"
    ENV["LDFLAGS"] = "-L#{lib_dir}"
  end

  task :env => [:pkg_config, :flags]

  desc "Build zlib and install it into #{dist_dir}."
  task :zlib => :env do
    tmp_dir = Pathname.new("tmp/zlib")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    zlib_version = "1.2.8"
    zlib_base = "zlib-#{zlib_version}"
    zlib_tar_gz_url_base =
      "http://sourceforge.net/projects/libpng/files/zlib"
    zlib_tar_gz_url =
      "#{zlib_tar_gz_url_base}/#{zlib_version}/#{zlib_base}.tar.gz"
    zlib_tar_gz = download(zlib_tar_gz_url, download_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", zlib_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + zlib_base) do
      sh("make", "PREFIX=#{host}-", "-f", "win32/Makefile.gcc") or exit(false)
      sh("make",
         "INCLUDE_PATH=#{include_dir}",
         "LIBRARY_PATH=#{lib_dir}",
         "BINARY_PATH=#{bin_dir}",
         "SHARED_MODE=1",
         "-f",
         "win32/Makefile.gcc",
         "install") or exit(false)

      zlib_license_dir = license_dir + "zlib"
      mkdir_p(zlib_license_dir)
      files = ["README"]
      cp(files, zlib_license_dir)
    end
  end

  desc "Build LZ4 and install it into #{dist_dir}."
  task :lz4 => :env do
    tmp_dir = Pathname.new("tmp/lz4")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    lz4_version = "r126"
    lz4_base = "lz4-#{lz4_version}"
    lz4_tar_gz_url_base =
      "https://github.com/Cyan4973/lz4/archive"
    lz4_tar_gz_url =
      "#{lz4_tar_gz_url_base}/#{lz4_version}.tar.gz"
    lz4_tar_gz = download(lz4_tar_gz_url, download_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", lz4_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + lz4_base) do
      sh("make",
         "CC=#{host}-gcc",
         "EXT=.exe",
         "SHARED_EXT=dll",
         "PREFIX=#{binary_dir}",
         "install") or exit(false)

      lz4_license_dir = license_dir + "lz4"
      mkdir_p(lz4_license_dir)
      files = ["lib/LICENSE"]
      cp(files, lz4_license_dir)
    end
  end

  desc "Build MessagePack and install it into #{dist_dir}."
  task :msgpack => :env do
    tmp_dir = Pathname.new("tmp/msgpack")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    msgpack_version = "1.0.1"
    msgpack_base = "msgpack-#{msgpack_version}"
    msgpack_tar_gz = "#{msgpack_base}.tar.gz"
    msgpack_tar_gz_url_base =
      "https://github.com/msgpack/msgpack-c/releases/download"
    msgpack_tar_gz_url =
      "#{msgpack_tar_gz_url_base}/cpp-#{msgpack_version}/#{msgpack_tar_gz}"
    msgpack_tar_gz = download(msgpack_tar_gz_url, download_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", msgpack_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + msgpack_base) do
      sh("autoreconf", "--install", "--force")
      sh("./configure",
         "--prefix=#{binary_dir}",
         "--host=#{host}") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)

      msgpack_license_dir = license_dir + "msgpack"
      mkdir_p(msgpack_license_dir)
      files = ["README", "COPYING", "LICENSE"]
      cp(files, msgpack_license_dir)
    end
  end

  desc "Build MeCab and install it into #{dist_dir}."
  task :mecab => :env do
    tmp_dir = Pathname.new("tmp/mecab")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    mecab_version = "0.98"
    mecab_base = "mecab-#{mecab_version}"
    mecab_tar_gz = "#{mecab_base}.tar.gz"
    mecab_tar_gz_url = "http://mecab.googlecode.com/files/#{mecab_tar_gz}"
    mecab_tar_gz = download(mecab_tar_gz_url, download_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", mecab_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + mecab_base) do
      mecab_patches.each do |patch|
        sh("patch -p1 < #{patches_dir + patch}")
      end
      sh("autoreconf", "--install", "--force")
      sh("./configure",
         "--prefix=#{binary_dir}",
         "--host=#{host}") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)

      mecab_rc_path = binary_dir + "etc" + "mecabrc"
      win32_mecab_rc_path = binary_dir + "bin" + "mecabrc"
      mv(mecab_rc_path, win32_mecab_rc_path)

      mecab_license_dir = license_dir + "mecab"
      mkdir_p(mecab_license_dir)
      files = ["AUTHORS", "BSD", "COPYING", "GPL", "LGPL"]
      cp(files, mecab_license_dir)
    end
  end

  desc "Build MeCab dictionary and install it into #{dist_dir}."
  task :mecab_dict => :env do
    tmp_dir = Pathname.new("tmp/mecab_dict")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    naist_jdic_base = "mecab-naist-jdic-0.6.3-20100801"
    naist_jdic_tar_gz_url =
      "http://osdn.dl.sourceforge.jp/naist-jdic/48487/#{naist_jdic_base}.tar.gz"
    naist_jdic_tar_gz = download(naist_jdic_tar_gz_url, download_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", naist_jdic_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + naist_jdic_base) do
      sh("./configure",
         "--with-dicdir=#{binary_dir}/share/mecab/dic/naist-jdic",
         "--with-charset=utf-8") or exit(false)
      sh("make", "-j8") or exit(false)
      sh("make", "install-data") or exit(false)

      naist_jdic_license_dir = license_dir + "naist-jdic"
      mkdir_p(naist_jdic_license_dir)
      files = ["AUTHORS", "COPYING"]
      cp(files, naist_jdic_license_dir)
    end
    dictionary_dir = "$(rcpath)\\..\\share\\mecab\\dic\\naist-jdic"
    mecab_rc_path = binary_dir + "bin" + "mecabrc"
    mecab_rc_content = mecab_rc_path.read
    File.open(mecab_rc_path, "w") do |mecab_rc|
      mecab_rc.print(mecab_rc_content.gsub(/^dicdir\s*=.+$/,
                                           "dicdir = #{dictionary_dir}"))
    end
  end

  desc "Build Groonga and install it into #{dist_dir}/."
  task :groonga => :env do
    tmp_dir = Pathname.new("tmp/groonga")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", groonga_source.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + "groonga-#{groonga_version}") do
      Dir.chdir("vendor") do
        nginx_patches.each do |patch|
          sh("patch -p0 < #{patches_dir + patch}")
        end
      end

      mecab_config = binary_dir + "bin" + "mecab-config"
      options = [
        "--prefix=#{binary_dir}",
        "--host=#{host}",
        "--disable-libedit",
        "--with-zlib",
        "--with-lz4",
        "--without-libstemmer",
        "--without-kytea",
        "--without-cutter",
        "--disable-benchmark",
        "--with-message-pack=#{binary_dir}",
        "--enable-mruby",
        "--enable-shared-onigmo",
      ]
      if mecab_config.exist?
        options << "--with-mecab-config=#{mecab_config}"
      else
        options << "--without-mecab"
      end
      options << "--enable-debug" if debug_build_p
      sh("./configure", *options) or exit(false)
      sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)

      groonga_license_dir = license_dir + "groonga"
      mkdir_p(groonga_license_dir)
      files = ["README.md", "COPYING"]
      cp(files, groonga_license_dir)

      mruby_license_dir = license_dir + "mruby"
      mkdir_p(mruby_license_dir)
      files = [
        "vendor/mruby-source/README.md",
        "vendor/mruby-source/AUTHORS",
        "vendor/mruby-source/LEGAL",
        "vendor/mruby-source/MITL",
      ]
      cp(files, mruby_license_dir)

      onigmo_license_dir = license_dir + "onigmo"
      mkdir_p(onigmo_license_dir)
      files = [
        "vendor/onigmo-source/README",
        "vendor/onigmo-source/AUTHORS",
        "vendor/onigmo-source/COPYING",
      ]
      cp(files, onigmo_license_dir)
    end
  end

  desc "Install Groonga Admin into #{dist_dir}/."
  task :groonga_admin => :env do
    tmp_dir = Pathname.new("tmp/groonga-admin")
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    groonga_admin_version = "0.9.3"
    groonga_admin_base = "groonga-admin-#{groonga_admin_version}"
    groonga_admin_tar_gz_url_base =
      "http://packages.groonga.org/source/groonga-admin"
    groonga_admin_tar_gz_url =
      "#{groonga_admin_tar_gz_url_base}/#{groonga_admin_base}.tar.gz"
    groonga_admin_tar_gz = download(groonga_admin_tar_gz_url, download_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", groonga_admin_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + groonga_admin_base) do
      admin_path = binary_dir + "share/groonga/html/admin"
      mv("#{admin_path}",
         "#{admin_path}.old")
      mv("html", "#{admin_path}")

      groonga_admin_license_dir = license_dir + "groonga-admin"
      mkdir_p(groonga_admin_license_dir)
      files = ["README.md", "LICENSE"]
      cp(files, groonga_admin_license_dir)
    end
  end

  task :clean do
    rm_rf(dist_dir)
  end

  task :pre => [:clean]
  task :post
end

namespace :gcc do
  namespace :dll do
    desc "Bundle GCC related DLLs"
    task :bundle do
      dlls = [
        "libstdc++-6.dll",
        "libwinpthread-1.dll",
        "libgcc_s_sjlj-1.dll",
        "libgcc_s_seh-1.dll",
      ]
      dlls.each do |dll|
        full_path = Pathname.new(`#{host}-g++ -print-file-name=#{dll}`.strip)
        next unless full_path.absolute?
        destination_path = (binary_dir + "bin" + dll).to_s
        cp(full_path.to_s, destination_path)
        chmod(0755, destination_path)
      end
    end
  end
end

task "build:post" => "gcc:dll:bundle"

build_dependencies = [
  "build:pre",
  "build:zlib",
  "build:lz4",
  "build:msgpack",
  "build:mecab",
  "build:mecab_dict",
  "build:groonga",
  "build:groonga_admin",
  "build:post",
]
desc "Build MeCab and groonga and install them into #{dist_dir}/."
task :build => build_dependencies
