# -*- ruby -*-
#
# Copyright (C) 2011-2019  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 "digest"
require "pathname"
require "open-uri"

base_dir = Pathname.new(__FILE__).dirname

architecture = ENV["ARCHITECTURE"]
groonga_win32_x86_p = architecture == "x86"
groonga_version = ENV["VERSION"]
debug_build_p = ENV["DEBUG_BUILD"] == "yes"
memory_debug_build_p = ENV["MEMORY_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
source_dir = Pathname.new("source").expand_path
data_dir = dist_dir + "share"
groonga_data_dir = data_dir + "groonga"
license_dir = data_dir + "license"
binary_dir = base_dir + dist_dir
include_dir = binary_dir + "include"
lib_dir = binary_dir + "lib"
bin_dir = binary_dir + "bin"
base_tmp_dir = Pathname.new(ENV["TMP_DIR"] || (base_dir + "tmp")).expand_path

patches_dir = (base_dir + "patches").expand_path
mecab_patches = [
  "mecab-0.996.diff",
]
nginx_patches = [
]
if groonga_win32_x86_p
  architecture = "x86"
  host = "i686-w64-mingw32"
else
  architecture = "x64"
  host = "x86_64-w64-mingw32"
end

def groonga_source
  Pathname.new(ENV["SOURCE"]).expand_path
end

def download(url, download_dir)
  base_name = url.split("/").last.split("?", 2)[0]
  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

download_dir = base_tmp_dir + "download"
namespace :build do
  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 = base_tmp_dir + "zlib"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    zlib_version = "1.2.11"
    zlib_base = "zlib-#{zlib_version}"
    zlib_tar_gz_url = "http://zlib.net/#{zlib_base}.tar.gz"
    zlib_tar_gz = download(zlib_tar_gz_url, download_dir)
    cp(zlib_tar_gz, source_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
      build_parameters = ["PREFIX=#{host}-"]
      if debug_build_p
        build_parameters << "LOC=-DDEBUG"
        build_parameters << "CFLAGS=$(LOC) -g3 -O0"
      end
      sh("make",
         *build_parameters,
         "-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 = base_tmp_dir + "lz4"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    lz4_version_path = base_dir + "tmp" + "bundled_lz4_version"
    lz4_version = lz4_version_path.read.strip
    lz4_base = "lz4-#{lz4_version}"
    lz4_tar_gz_url_base =
      "https://github.com/lz4/lz4/archive"
    lz4_tar_gz_url =
      "#{lz4_tar_gz_url_base}/v#{lz4_version}.tar.gz"
    lz4_tar_gz = download(lz4_tar_gz_url, download_dir)
    cp(lz4_tar_gz, source_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
      # TODO: Remove me when https://github.com/lz4/lz4/pull/556 is merged.
      sh("curl https://github.com/kou/lz4/commit/9c457ccb7aed26edda7a3299207a4f329057866d.patch | patch -p1")
      parameters = [
        "CC=#{host}-gcc",
        "EXT=.exe",
        "SHARED_EXT=dll",
        "PREFIX=#{binary_dir}",
      ]
      parameters << "CFLAGS=-O0 -g3" if debug_build_p
      sh("make",
         *parameters,
         "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 = base_tmp_dir + "msgpack"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    msgpack_version_path = base_dir + "tmp" + "bundled_message_pack_version"
    msgpack_version = msgpack_version_path.read.strip
    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)
    cp(msgpack_tar_gz, source_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
      cmake_parameters = [
        "-DCMAKE_INSTALL_PREFIX=#{binary_dir}",
        "-DCMAKE_SYSTEM_NAME=Windows",
        "-DCMAKE_SYSTEM_PROCESSOR=#{architecture}",
        "-DCMAKE_C_COMPILER=#{host}-gcc",
        "-DCMAKE_CXX_COMPILER=#{host}-g++",
      ]
      if debug_build_p
        cmake_parameters << "-DCMAKE_C_FLAGS=-O0 -g3"
        cmake_parameters << "-DCMAKE_CXX_FLAGS=-O0 -g3"
      end
      sh("cmake", *cmake_parameters) 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.md",
        "COPYING",
        "NOTICE",
        "LICENSE_1_0.txt",
      ]
      cp(files, msgpack_license_dir)
    end
  end

  desc "Build MeCab and install it into #{dist_dir}."
  task :mecab => :env do
    tmp_dir = base_tmp_dir + "mecab"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    mecab_version = "0.996"
    mecab_base = "mecab-#{mecab_version}"
    mecab_tar_gz_url = "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE"
    mecab_tar_gz = download(mecab_tar_gz_url, download_dir)
    cp(mecab_tar_gz, source_dir + "#{mecab_base}.tar.gz")
    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)
      build_parameters = []
      if debug_build_p
        build_parameters << "CFLAGS=-O0 -Wall -g3"
        build_parameters << "CXXFLAGS=-O0 -Wall -g3"
      end
      sh("env", "GREP_OPTIONS=--text", "nice",
         "make", "-j8", *build_parameters) 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 = base_tmp_dir + "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://iij.dl.osdn.jp/naist-jdic/48487/#{naist_jdic_base}.tar.gz"
    naist_jdic_tar_gz = download(naist_jdic_tar_gz_url, download_dir)
    cp(naist_jdic_tar_gz, source_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
      mecab_config = binary_dir + "bin" + "mecab-config"
      sh("./configure",
         "--with-mecab-config=#{mecab_config}",
         "--with-dicdir=#{binary_dir}/share/mecab/dic/naist-jdic",
         "--with-charset=utf-8") or exit(false)

      mecab_dict_index_original =
        binary_dir + "libexec" + "mecab" + "mecab-dict-index.exe"
      mecab_dict_index_bin = binary_dir + "bin" + "mecab-dict-index.exe"
      cp(mecab_dict_index_original,
         mecab_dict_index_bin)
      mecab_dict_index = "wine #{mecab_dict_index_bin}"
      rm_rf(File.expand_path("~/.wine"))
      sh("make", "mecab_dict_index=#{mecab_dict_index}") or exit(false)
      rm(mecab_dict_index_bin)

      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 PCRE and install it into #{dist_dir}."
  task :pcre => :env do
    tmp_dir = base_tmp_dir + "pcre"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    pcre_version = "8.42"
    pcre_base = "pcre-#{pcre_version}"
    pcre_tar_gz_url_base = "https://ftp.pcre.org/pub/pcre/"
    pcre_tar_gz_url =
      "#{pcre_tar_gz_url_base}/pcre-#{pcre_version}.tar.gz"
    pcre_tar_gz = download(pcre_tar_gz_url, download_dir)
    cp(pcre_tar_gz, source_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", pcre_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + pcre_base) do
      parameters = [
        "CC=#{host}-gcc",
        "EXT=.exe",
        "SHARED_EXT=dll",
        "PREFIX=#{binary_dir}",
      ]
      parameters << "CFLAGS=-O0 -g3" if debug_build_p
      sh("./configure",
         "--prefix=#{binary_dir}",
         "--host=#{host}",
         "--enable-utf8",
         "--enable-unicode-properties") or exit(false)
      sh("make",
         *parameters,
         "install") or exit(false)

      pcre_license_dir = license_dir + "pcre"
      mkdir_p(pcre_license_dir)
      files = ["LICENCE"]
      cp(files, pcre_license_dir)
    end
  end

  desc "Build Groonga and install it into #{dist_dir}/."
  task :groonga => :env do
    tmp_dir = base_tmp_dir + "groonga"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    cp(groonga_source, source_dir)
    groonga_source_path = groonga_source.to_s
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", groonga_source_path) 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",
        "--disable-groonga-httpd",
      ]
      if mecab_config.exist?
        options << "--with-mecab-config=#{mecab_config}"
      else
        options << "--without-mecab"
      end
      options << "--enable-debug" if debug_build_p
      options << "--enable-memory-debug" if memory_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)

      groonga_log_data_dir = groonga_data_dir + "groonga-log"
      groonga_log_license_dir = license_dir + "groonga-log"
      cp_r(groonga_log_data_dir,
           groonga_log_license_dir)
    end
  end

  desc "Build groonga-normalizer-mysql and install it into #{dist_dir}/."
  task :groonga_normalizer_mysql => :env do
    tmp_dir = base_tmp_dir + "groonga-normalizer-mysql"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    groonga_normalizer_mysql_base = "groonga-normalizer-mysql-1.1.3"
    groonga_normalizer_mysql_tar_gz_url =
      "http://packages.groonga.org/source/groonga-normalizer-mysql/" +
      "#{groonga_normalizer_mysql_base}.tar.gz"
    groonga_normalizer_mysql_tar_gz =
      download(groonga_normalizer_mysql_tar_gz_url, download_dir)
    cp(groonga_normalizer_mysql_tar_gz, source_dir)
    Dir.chdir(tmp_dir) do
      sh("tar", "xzf", groonga_normalizer_mysql_tar_gz.to_s) or exit(false)
    end
    Dir.chdir(tmp_dir + groonga_normalizer_mysql_base) do
      options = [
        "--prefix=#{binary_dir}",
        "--host=#{host}",
      ]
      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_normalizer_mysql_license_dir =
        license_dir + "groonga-normalizer-mysql"
      mkdir_p(groonga_normalizer_mysql_license_dir)
      files = ["README.md", "doc/text/lgpl-2.0.txt"]
      cp(files, groonga_normalizer_mysql_license_dir)
    end
  end

  desc "Install Groonga Admin into #{dist_dir}/."
  task :groonga_admin => :env do
    tmp_dir = base_tmp_dir + "groonga-admin"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    groonga_admin_version = "0.9.5"
    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)
    cp(groonga_admin_tar_gz, source_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)
    rm_rf(source_dir)
    mkdir_p(source_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",
        "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

namespace :pdb do
  namespace :extractor do
    desc "Generate PDB extractor"
    task :generate do
      cv2pdb_url = "https://ci.appveyor.com/api/projects/rainers/visuald/artifacts/cv2pdb.exe?job=Environment%3A%20os%3DVisual%20Studio%202013%2C%20VS%3D12%2C%20APPVEYOR_BUILD_WORKER_IMAGE%3DVisual%20Studio%202015"
      cv2pdb = download(cv2pdb_url, download_dir)
      chmod(0755, cv2pdb)
      generate_pdb_bat = bin_dir + "generate-pdb.bat"
      generate_pdb_bat.open("w") do |bat|
        cd(binary_dir) do
          Dir.glob("**/*.{exe,dll}") do |binary|
            binary = binary.gsub("/", "\\")
            pdb = binary.gsub(/\.(?:exe|dll)\z/, ".pdb")
            bat.print("bin\\#{cv2pdb.basename} #{binary} #{binary} #{pdb}\r\n")
          end
        end
      end
      chmod(0755, generate_pdb_bat)
      cp(cv2pdb, bin_dir)

      cv2pdb_license_dir = license_dir + "cv2pdb"
      mkdir_p(cv2pdb_license_dir)
      files = ["README.MD", "LICENSE"].collect do |file|
        download("https://raw.githubusercontent.com/rainers/cv2pdb/master/#{file}",
                 download_dir)
      end
      cp(files, cv2pdb_license_dir)
    end
  end
end

task "build:mecab_dict" => "gcc:dll:bundle"
task "build:post" => ["gcc:dll:bundle", "pdb:extractor:generate"]

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

namespace :pdb do
  desc "Generate *.pdb for *.exe and *.dll in zip archive"
  task :generate do
    suffix = ENV["SUFFIX"]
    dir_name = "groonga-#{groonga_version}-#{architecture}#{suffix}"
    zip_path = "#{dir_name}.zip"
    sha256_path = "#{zip_path}.sha256"
    url = "https://packages.groonga.org/tmp/#{zip_path}"
    rm_f(zip_path)
    download(url, Pathname.new("."))
    sh("7z", "x", zip_path)
    cd(dir_name) do
      sh("bin/generate-pdb.bat")
    end
    rm_f(zip_path)
    sh("7z", "a", zip_path, dir_name)
    File.open(sha256_path, "wb") do |sha256|
      sha256.puts("#{Digest::SHA256.file(zip_path).hexdigest}  #{zip_path}")
    end
    sh("scp",
       zip_path,
       sha256_path,
       "packages@packages.groonga.org:public/tmp/")
  end
end
